1
2
3
4
5 package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
6
7 import org.apache.log4j.Logger;
8 import java.util.ArrayList;
9 import java.util.List;
10
11 import org.kuali.student.r2.core.acal.service.AcademicCalendarService;
12 import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
13 import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo;
14 import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
15 import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
16 import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultItemInfo;
17 import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
18 import org.kuali.student.r2.lum.course.dto.CourseInfo;
19 import org.kuali.student.r2.lum.course.service.CourseService;
20 import org.kuali.student.r2.common.dto.ContextInfo;
21 import org.kuali.student.r2.common.exceptions.DoesNotExistException;
22 import org.kuali.student.r2.common.exceptions.OperationFailedException;
23 import org.kuali.student.r2.common.util.RichTextHelper;
24 import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
25
26
27
28
29
30 public class CourseOfferingReverseRolloverRunner implements Runnable {
31
32 final static Logger logger = Logger.getLogger(CourseOfferingRolloverRunner.class);
33 private CourseOfferingService coService;
34 private CourseOfferingSetService socService;
35 private CourseService courseService;
36 private AcademicCalendarService acalService;
37 private ContextInfo context;
38 private SocRolloverResultInfo rolloverResult;
39 private SocRolloverResultInfo reverseResult;
40
41 public CourseOfferingService getCoService() {
42 return coService;
43 }
44
45 public void setCoService(CourseOfferingService coService) {
46 this.coService = coService;
47 }
48
49 public CourseOfferingSetService getSocService() {
50 return socService;
51 }
52
53 public void setSocService(CourseOfferingSetService socService) {
54 this.socService = socService;
55 }
56
57 public CourseService getCourseService() {
58 return courseService;
59 }
60
61 public void setCourseService(CourseService courseService) {
62 this.courseService = courseService;
63 }
64
65 public AcademicCalendarService getAcalService() {
66 return acalService;
67 }
68
69 public void setAcalService(AcademicCalendarService acalService) {
70 this.acalService = acalService;
71 }
72
73 public ContextInfo getContext() {
74 return context;
75 }
76
77 public void setContext(ContextInfo context) {
78 this.context = context;
79 }
80
81 public SocRolloverResultInfo getReverseResult() {
82 return reverseResult;
83 }
84
85 public void setReverseResult(SocRolloverResultInfo reverseResult) {
86 this.reverseResult = reverseResult;
87 }
88
89 public SocRolloverResultInfo getRolloverResult() {
90 return rolloverResult;
91 }
92
93 public void setRolloverResult(SocRolloverResultInfo rolloverResult) {
94 this.rolloverResult = rolloverResult;
95 }
96
97 private void loadOptions() {
98 this.justCreates = getBooleanOption(CourseOfferingSetServiceConstants.REVERSE_JUST_CREATES_OPTION_KEY, false);
99 this.logSuccesses = getBooleanOption(CourseOfferingSetServiceConstants.LOG_SUCCESSES_OPTION_KEY, false);
100 this.progressFrequency = getIntOption(CourseOfferingSetServiceConstants.LOG_FREQUENCY_OPTION_KEY_PREFIX, 10);
101 this.haltErrorsMax = getIntOption(CourseOfferingSetServiceConstants.HALT_ERRORS_MAX_OPTION_KEY_PREFIX, 10);
102
103 }
104
105 private boolean justCreates = false;
106 private boolean logSuccesses = false;
107 private int progressFrequency = 100;
108 private int haltErrorsMax = -1;
109
110 private boolean getBooleanOption(String key, boolean defValue) {
111 for (String optionKey : this.reverseResult.getOptionKeys()) {
112 if (optionKey.equals(key)) {
113 return true;
114 }
115 }
116
117 return defValue;
118 }
119
120 private int getIntOption(String keyPrefix, int defValue) {
121 for (String optionKey : this.reverseResult.getOptionKeys()) {
122 if (optionKey.startsWith(keyPrefix)) {
123 return Integer.parseInt(optionKey);
124 }
125 }
126
127 return defValue;
128 }
129
130
131
132
133 @Override
134 public void run() {
135 try {
136 runInternal();
137 } catch (Exception ex) {
138 try {
139 reverseResult = this.socService.getSocRolloverResult(reverseResult.getId(), context);
140 this.reverseResult.setStateKey(CourseOfferingSetServiceConstants.ABORTED_RESULT_STATE_KEY);
141 this.reverseResult.setMessage(new RichTextHelper().fromPlain("Got an unexpected exception running rolloever:\n" +
142 ex.toString()));
143 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
144 } catch (Exception ex1) {
145 logger.fatal(reverseResult, ex);
146 throw new RuntimeException(ex1);
147 }
148 }
149 }
150
151 private void runInternal() throws Exception {
152 this.loadOptions();
153
154 reverseResult.setStateKey(CourseOfferingSetServiceConstants.RUNNING_RESULT_STATE_KEY);
155 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
156
157 List<String> targetCoIds = socService.getCourseOfferingIdsBySoc(this.reverseResult.getTargetSocId(), context);
158 reverseResult.setItemsProcessed(0);
159 reverseResult.setItemsExpected(targetCoIds.size());
160 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
161
162
163 int i = 0;
164 int errors = 0;
165 List<SocRolloverResultItemInfo> items = new ArrayList<SocRolloverResultItemInfo>();
166 for (String targetCoId : targetCoIds) {
167 logger.info("Processing" + targetCoId);
168 try {
169 SocRolloverResultItemInfo item = reverseOneCourseOffering(targetCoId);
170 items.add(item);
171 reportProgressIfModulo(items, i);
172 if (!CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
173 errors++;
174 if (this.haltErrorsMax != -1) {
175 if (errors > this.haltErrorsMax) {
176 throw new OperationFailedException("Too many errors, exceeded the halt threshold: " + errors +
177 " out of " + i + " course offerings rolled over");
178 }
179 }
180 }
181 } catch (Exception ex) {
182
183 logger.fatal("failed while processing the " + i + "th course offering " + targetCoId, ex);
184 throw ex;
185 }
186 i++;
187 }
188 reportProgress(items, i);
189
190 reverseResult = this.socService.getSocRolloverResult(reverseResult.getId(), context);
191 reverseResult.setStateKey(CourseOfferingSetServiceConstants.FINISHED_RESULT_STATE_KEY);
192 this.socService.updateSocRolloverResult(reverseResult.getId(), reverseResult, context);
193 }
194
195 private void reportProgressIfModulo(List<SocRolloverResultItemInfo> items, int i) throws Exception {
196 int modulo = i % progressFrequency;
197 if (modulo != 0) {
198 return;
199 }
200 this.reportProgress(items, i);
201 }
202
203 private void reportProgress(List<SocRolloverResultItemInfo> items, int i) throws Exception {
204 this.socService.updateSocRolloverProgress(reverseResult.getId(), i, context);
205 if (!this.logSuccesses) {
206 stripSuccesses(items);
207 }
208 if (!items.isEmpty()) {
209 this.socService.createSocRolloverResultItems(reverseResult.getId(),
210 CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY,
211 items,
212 context);
213 }
214 items.clear();
215 }
216
217 private void stripSuccesses(List<SocRolloverResultItemInfo> items) {
218 List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>();
219 for (SocRolloverResultItemInfo item : items) {
220 if (!CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
221 list.add(item);
222 }
223 }
224 if (items.size() != list.size()) {
225 items.clear();
226 items.addAll(list);
227 }
228 }
229
230
231
232 private SocRolloverResultItemInfo reverseOneCourseOffering(String coId) throws Exception {
233 if (this.justCreates) {
234 if (!this.wasCreatedInRollover(coId)) {
235 SocRolloverResultItemInfo item = new SocRolloverResultItemInfo();
236 item.setSocRolloverResultId(reverseResult.getId());
237 item.setTypeKey(CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY);
238 item.setStateKey(CourseOfferingSetServiceConstants.INFO_RESULT_ITEM_STATE_KEY);
239 item.setSourceCourseOfferingId(coId);
240 item.setTargetCourseOfferingId(coId);
241 item.setMessage(new RichTextHelper().fromPlain(
242 "Skipped because course offering was not created in the original rollover"));
243 return item;
244 }
245 }
246
247 for (FormatOfferingInfo fo : this.coService.getFormatOfferingsByCourseOffering(coId, context)) {
248 for (ActivityOfferingInfo ao : this.coService.getActivityOfferingsByFormatOffering(fo.getId(), context)) {
249 coService.deleteActivityOffering(ao.getId(), context);
250 }
251 coService.deleteFormatOffering(fo.getId(), context);
252 }
253 this.coService.deleteCourseOffering(coId, context);
254
255 SocRolloverResultItemInfo item = new SocRolloverResultItemInfo();
256 item.setSocRolloverResultId(reverseResult.getId());
257 item.setTypeKey(CourseOfferingSetServiceConstants.DELETE_RESULT_ITEM_TYPE_KEY);
258 item.setStateKey(CourseOfferingSetServiceConstants.DELETED_RESULT_ITEM_STATE_KEY);
259 item.setSourceCourseOfferingId(coId);
260 item.setTargetCourseOfferingId(coId);
261 return item;
262 }
263
264 private boolean wasCreatedInRollover(String coId) throws Exception {
265 List<SocRolloverResultItemInfo> list = this.socService.getSocRolloverResultItemsByResultIdAndTargetCourseOfferingId(
266 rolloverResult.getId(), coId, context);
267 if (list.isEmpty()) {
268 return false;
269 }
270 for (SocRolloverResultItemInfo item : list) {
271 if (coId.equals(item.getTargetCourseOfferingId())) {
272 if (CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
273 if (item.getTypeKey().equals(CourseOfferingSetServiceConstants.CREATE_RESULT_ITEM_TYPE_KEY)) {
274 return true;
275 }
276 }
277 }
278 }
279 return false;
280 }
281
282
283 private CourseInfo getCourse(String courseId) throws Exception {
284 CourseInfo course = null;
285 try {
286 course = courseService.getCourse(courseId, context);
287 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
288 throw new DoesNotExistException("The course does not exist. course: " + courseId, e);
289 } catch (Exception e) {
290 throw new OperationFailedException("unxpected trying to get course " + courseId, e);
291 }
292 return course;
293 }
294 }