1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.ole.sys.batch.service.impl;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.collections.CollectionUtils;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.log4j.Logger;
29 import org.kuali.ole.sys.FinancialSystemModuleConfiguration;
30 import org.kuali.ole.sys.OLEConstants;
31 import org.kuali.ole.sys.batch.FiscalYearMakerStep;
32 import org.kuali.ole.sys.batch.dataaccess.FiscalYearMaker;
33 import org.kuali.ole.sys.batch.dataaccess.FiscalYearMakersDao;
34 import org.kuali.ole.sys.batch.service.FiscalYearMakerService;
35 import org.kuali.ole.sys.businessobject.FiscalYearBasedBusinessObject;
36 import org.kuali.rice.coreservice.framework.parameter.ParameterService;
37 import org.kuali.rice.krad.bo.PersistableBusinessObject;
38 import org.kuali.rice.krad.service.KualiModuleService;
39 import org.kuali.rice.krad.service.ModuleService;
40 import org.springframework.transaction.annotation.Transactional;
41
42
43
44
45 @Transactional
46 public class FiscalYearMakerServiceImpl implements FiscalYearMakerService {
47 private static final Logger LOG = org.apache.log4j.Logger.getLogger(FiscalYearMakerServiceImpl.class);
48
49 protected FiscalYearMakersDao fiscalYearMakersDao;
50 protected ParameterService parameterService;
51 protected KualiModuleService kualiModuleService;
52
53 protected List<FiscalYearMaker> fiscalYearMakers;
54
55
56
57
58 public void runProcess() {
59 String parmBaseYear = parameterService.getParameterValueAsString(FiscalYearMakerStep.class, OLEConstants.ChartApcParms.FISCAL_YEAR_MAKER_SOURCE_FISCAL_YEAR);
60 if (StringUtils.isBlank(parmBaseYear)) {
61 throw new RuntimeException("Required fiscal year parameter " + OLEConstants.ChartApcParms.FISCAL_YEAR_MAKER_SOURCE_FISCAL_YEAR + " has not been set.");
62 }
63
64 Integer baseYear = Integer.parseInt(parmBaseYear);
65 boolean replaceMode = parameterService.getParameterValueAsBoolean(FiscalYearMakerStep.class, OLEConstants.ChartApcParms.FISCAL_YEAR_MAKER_REPLACE_MODE);
66
67 if (fiscalYearMakers == null || fiscalYearMakers.isEmpty()) {
68 this.initialize();
69 }
70
71 validateFiscalYearMakerConfiguration();
72
73
74 List<FiscalYearMaker> copyList = getFiscalYearMakerHelpersInCopyOrder();
75
76
77 if (replaceMode) {
78 List<FiscalYearMaker> deleteList = getFiscalYearMakerHelpersInDeleteOrder(copyList);
79 for (FiscalYearMaker fiscalYearMakerHelper : deleteList) {
80 if (fiscalYearMakerHelper.isAllowOverrideTargetYear()) {
81 fiscalYearMakersDao.deleteNewYearRows(baseYear, fiscalYearMakerHelper);
82 }
83 }
84 }
85
86
87 Map<Class<? extends FiscalYearBasedBusinessObject>, Set<String>> parentKeysWritten = new HashMap<Class<? extends FiscalYearBasedBusinessObject>, Set<String>>();
88
89
90 for (FiscalYearMaker fiscalYearMaker : copyList) {
91 try {
92 boolean isParent = isParentClass(fiscalYearMaker.getBusinessObjectClass());
93 if (!fiscalYearMaker.doCustomProcessingOnly()) {
94 Collection<String> copyErrors = fiscalYearMakersDao.createNewYearRows(baseYear, fiscalYearMaker, replaceMode, parentKeysWritten, isParent);
95 writeCopyFailureMessages(copyErrors);
96 }
97
98 fiscalYearMaker.performCustomProcessing(baseYear, true);
99
100
101 if (fiscalYearMaker.isTwoYearCopy()) {
102 if (!fiscalYearMaker.doCustomProcessingOnly()) {
103 Collection<String> copyErrors = fiscalYearMakersDao.createNewYearRows(baseYear + 1, fiscalYearMaker, replaceMode, parentKeysWritten, isParent);
104 writeCopyFailureMessages(copyErrors);
105 }
106
107 fiscalYearMaker.performCustomProcessing(baseYear + 1, false);
108 }
109 } catch ( Exception ex ) {
110 throw new RuntimeException( "Internal exception while processing fiscal year for " + fiscalYearMaker.getBusinessObjectClass(), ex );
111 }
112 }
113 }
114
115
116
117
118
119
120
121 protected List<FiscalYearMaker> getFiscalYearMakerHelpersInCopyOrder() {
122 List<Class<? extends FiscalYearBasedBusinessObject>> classCopyOrder = new ArrayList<Class<? extends FiscalYearBasedBusinessObject>>();
123
124
125 Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> parentChildren = getParentChildrenMap();
126
127
128 while (!parentChildren.isEmpty()) {
129 Set<Class<? extends FiscalYearBasedBusinessObject>> parents = parentChildren.keySet();
130 Set<Class<? extends FiscalYearBasedBusinessObject>> children = getChildren(parentChildren);
131
132 Set<Class<? extends FiscalYearBasedBusinessObject>> rootParents = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>(CollectionUtils.subtract(parents, children));
133
134
135 if (rootParents.isEmpty()) {
136 findCircularReferenceAndThrowException(parentChildren);
137 }
138
139 for (Class<? extends FiscalYearBasedBusinessObject> rootParent : rootParents) {
140 classCopyOrder.add(rootParent);
141 parentChildren.remove(rootParent);
142 }
143 }
144
145
146 for (FiscalYearMaker fiscalYearMakerHelper : this.fiscalYearMakers) {
147 if (!classCopyOrder.contains(fiscalYearMakerHelper.getBusinessObjectClass())) {
148 classCopyOrder.add(fiscalYearMakerHelper.getBusinessObjectClass());
149 }
150 }
151
152
153 List<FiscalYearMaker> fiscalYearMakerHelpersCopyOrder = new ArrayList<FiscalYearMaker>();
154
155 Map<Class<? extends FiscalYearBasedBusinessObject>, FiscalYearMaker> copyMap = getFiscalYearMakerMap();
156 for (Class<? extends FiscalYearBasedBusinessObject> copyClass : classCopyOrder) {
157 fiscalYearMakerHelpersCopyOrder.add(copyMap.get(copyClass));
158 }
159
160 return fiscalYearMakerHelpersCopyOrder;
161 }
162
163
164
165
166
167
168
169
170 protected List<FiscalYearMaker> getFiscalYearMakerHelpersInDeleteOrder(List<FiscalYearMaker> fiscalYearMakerHelpersCopyOrder) {
171 List<FiscalYearMaker> fiscalYearMakerHelpersDeleteOrder = new ArrayList<FiscalYearMaker>();
172 for (int i = fiscalYearMakerHelpersCopyOrder.size() - 1; i >= 0; i--) {
173 fiscalYearMakerHelpersDeleteOrder.add(fiscalYearMakerHelpersCopyOrder.get(i));
174 }
175
176 return fiscalYearMakerHelpersDeleteOrder;
177 }
178
179
180
181
182
183
184
185
186 protected void findCircularReferenceAndThrowException(Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> parentChildren) {
187 Set<Class<? extends FiscalYearBasedBusinessObject>> classesWithCircularReference = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>();
188
189
190 for (Class<? extends FiscalYearBasedBusinessObject> parent : parentChildren.keySet()) {
191 boolean circularReferenceFound = checkChildrenForParentReference(parent, parentChildren.get(parent), parentChildren, new HashSet<Class<? extends FiscalYearBasedBusinessObject>>());
192 if (circularReferenceFound) {
193 classesWithCircularReference.add(parent);
194 }
195 }
196
197 if (!classesWithCircularReference.isEmpty()) {
198 String error = "Circular reference found for class(s): " + StringUtils.join(classesWithCircularReference, ", ");
199 LOG.error(error);
200 throw new RuntimeException(error);
201 }
202 }
203
204
205
206
207
208
209
210
211
212
213
214 protected boolean checkChildrenForParentReference(Class<? extends FiscalYearBasedBusinessObject> parent, Set<Class<? extends FiscalYearBasedBusinessObject>> children, Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> parentChildren, Set<Class<? extends FiscalYearBasedBusinessObject>> checkedParents) {
215
216 if (children.contains(parent)) {
217 return true;
218 }
219
220
221 for (Class<? extends FiscalYearBasedBusinessObject> child : children) {
222 if (parentChildren.containsKey(child) && !checkedParents.contains(child)) {
223 checkedParents.add(child);
224 Set<Class<? extends FiscalYearBasedBusinessObject>> childChildren = parentChildren.get(child);
225
226 boolean foundParent = checkChildrenForParentReference(parent, childChildren, parentChildren, checkedParents);
227 if (foundParent) {
228 return true;
229 }
230 }
231 }
232
233 return false;
234 }
235
236
237
238
239
240
241 protected Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> getParentChildrenMap() {
242 Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> parentChildren = new HashMap<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>>();
243
244 for (FiscalYearMaker fiscalYearMakerHelper : fiscalYearMakers) {
245 for (Class<? extends FiscalYearBasedBusinessObject> parentClass : fiscalYearMakerHelper.getParentClasses()) {
246 Set<Class<? extends FiscalYearBasedBusinessObject>> children = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>();
247 if (parentChildren.containsKey(parentClass)) {
248 children = parentChildren.get(parentClass);
249 }
250 children.add(fiscalYearMakerHelper.getBusinessObjectClass());
251
252 parentChildren.put(parentClass, children);
253 }
254 }
255
256 return parentChildren;
257 }
258
259
260
261
262
263
264
265 protected boolean isParentClass(Class<? extends FiscalYearBasedBusinessObject> businessObjectClass) {
266 for (FiscalYearMaker fiscalYearMakerHelper : fiscalYearMakers) {
267 for (Class<? extends FiscalYearBasedBusinessObject> parentClass : fiscalYearMakerHelper.getParentClasses()) {
268 if (businessObjectClass.isAssignableFrom(parentClass)) {
269 return true;
270 }
271 }
272 }
273
274 return false;
275 }
276
277
278
279
280
281
282
283
284 protected Set<Class<? extends FiscalYearBasedBusinessObject>> getChildren(Map<Class<? extends FiscalYearBasedBusinessObject>, Set<Class<? extends FiscalYearBasedBusinessObject>>> parentChildren) {
285 Set<Class<? extends FiscalYearBasedBusinessObject>> children = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>();
286
287 for (Class<? extends FiscalYearBasedBusinessObject> parentClass : parentChildren.keySet()) {
288 children.addAll(parentChildren.get(parentClass));
289 }
290
291 return children;
292 }
293
294
295
296
297
298
299
300 protected Map<Class<? extends FiscalYearBasedBusinessObject>, FiscalYearMaker> getFiscalYearMakerMap() {
301 Map<Class<? extends FiscalYearBasedBusinessObject>, FiscalYearMaker> fiscalYearMakerMap = new HashMap<Class<? extends FiscalYearBasedBusinessObject>, FiscalYearMaker>();
302
303 for (FiscalYearMaker fiscalYearMakerHelper : fiscalYearMakers) {
304 fiscalYearMakerMap.put(fiscalYearMakerHelper.getBusinessObjectClass(), fiscalYearMakerHelper);
305 }
306
307 return fiscalYearMakerMap;
308 }
309
310
311
312
313 protected void validateFiscalYearMakerConfiguration() {
314 Set<Class<? extends FiscalYearBasedBusinessObject>> businessObjectClasses = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>();
315
316 for (FiscalYearMaker fiscalYearMaker : fiscalYearMakers) {
317 Class<? extends FiscalYearBasedBusinessObject> businessObjectClass = fiscalYearMaker.getBusinessObjectClass();
318 if (businessObjectClass == null) {
319 String error = "Business object class is null for fiscal year maker";
320 LOG.error(error);
321 throw new RuntimeException(error);
322 }
323
324 if (!FiscalYearBasedBusinessObject.class.isAssignableFrom(businessObjectClass)) {
325 String error = String.format("Business object class %s does not implement %s", businessObjectClass.getName(), FiscalYearBasedBusinessObject.class.getName());
326 LOG.error(error);
327 throw new RuntimeException(error);
328 }
329
330 if (businessObjectClasses.contains(businessObjectClass)) {
331 String error = String.format("Business object class %s has two fiscal year maker implementations defined", businessObjectClass.getName());
332 LOG.error(error);
333 throw new RuntimeException(error);
334 }
335
336 businessObjectClasses.add(businessObjectClass);
337 }
338
339
340 Set<Class<? extends PersistableBusinessObject>> parentsNotInCopyList = new HashSet<Class<? extends PersistableBusinessObject>>();
341 for (FiscalYearMaker fiscalYearMaker : fiscalYearMakers) {
342 parentsNotInCopyList.addAll(CollectionUtils.subtract(fiscalYearMaker.getParentClasses(), businessObjectClasses));
343 }
344
345 if (!parentsNotInCopyList.isEmpty()) {
346 String error = "Parent classes not in copy list: " + StringUtils.join(parentsNotInCopyList, ",");
347 LOG.error(error);
348 throw new RuntimeException(error);
349 }
350 }
351
352
353
354
355
356
357 protected void writeCopyFailureMessages(Collection<String> copyErrors) {
358 if (!copyErrors.isEmpty()) {
359 LOG.warn("\n");
360 for (String copyError : copyErrors) {
361 LOG.warn(String.format("\n%s", copyError));
362 }
363 LOG.warn("\n");
364 }
365 }
366
367
368
369
370 public void initialize() {
371 fiscalYearMakers = new ArrayList<FiscalYearMaker>();
372 for (ModuleService moduleService : kualiModuleService.getInstalledModuleServices()) {
373 if (moduleService.getModuleConfiguration() instanceof FinancialSystemModuleConfiguration) {
374 fiscalYearMakers.addAll(((FinancialSystemModuleConfiguration) moduleService.getModuleConfiguration()).getFiscalYearMakers());
375 }
376 }
377 }
378
379
380
381
382
383
384 protected void setFiscalYearMakers(List<FiscalYearMaker> fiscalYearMakers) {
385 this.fiscalYearMakers = fiscalYearMakers;
386 }
387
388
389
390
391
392
393 public void setParameterService(ParameterService parameterService) {
394 this.parameterService = parameterService;
395 }
396
397
398
399
400
401
402 public void setFiscalYearMakersDao(FiscalYearMakersDao fiscalYearMakersDao) {
403 this.fiscalYearMakersDao = fiscalYearMakersDao;
404 }
405
406
407
408
409
410
411 public void setKualiModuleService(KualiModuleService kualiModuleService) {
412 this.kualiModuleService = kualiModuleService;
413 }
414
415
416
417
418
419
420 protected FiscalYearMakersDao getFiscalYearMakersDao() {
421 return fiscalYearMakersDao;
422 }
423
424
425
426
427
428
429 protected ParameterService getParameterService() {
430 return parameterService;
431 }
432
433
434
435
436
437
438 protected KualiModuleService getKualiModuleService() {
439 return kualiModuleService;
440 }
441
442
443
444
445
446
447 protected List<FiscalYearMaker> getFiscalYearMakers() {
448 return fiscalYearMakers;
449 }
450
451 }