View Javadoc
1   /*
2    * Copyright 2008-2009 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.sys.batch.dataaccess.impl;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.commons.beanutils.PropertyUtils;
26  import org.apache.log4j.Logger;
27  import org.apache.ojb.broker.core.proxy.ProxyHelper;
28  import org.apache.ojb.broker.query.Criteria;
29  import org.kuali.ole.sys.OLEConstants;
30  import org.kuali.ole.sys.OLEPropertyConstants;
31  import org.kuali.ole.sys.batch.dataaccess.FiscalYearMaker;
32  import org.kuali.ole.sys.businessobject.FiscalYearBasedBusinessObject;
33  import org.kuali.ole.sys.businessobject.SystemOptions;
34  import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
35  import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
36  import org.kuali.rice.krad.bo.PersistableBusinessObject;
37  import org.kuali.rice.krad.service.BusinessObjectService;
38  import org.kuali.rice.krad.service.PersistenceStructureService;
39  import org.kuali.rice.krad.util.ObjectUtils;
40  
41  /**
42   * Default implementation of fiscal year maker process for an entity. This implementation can be used for a table in the fiscal year
43   * maker process by defining a spring bean and setting the businessObjectClass property.
44   */
45  public class FiscalYearMakerImpl extends PlatformAwareDaoBaseOjb implements FiscalYearMaker {
46      private static final Logger LOG = org.apache.log4j.Logger.getLogger(FiscalYearMakerImpl.class);
47  
48      protected static final Long ONE = new Long(1);
49  
50      protected PersistenceStructureService persistenceStructureService;
51      protected BusinessObjectService businessObjectService; 
52  
53      protected Class<? extends FiscalYearBasedBusinessObject> businessObjectClass;
54      protected Set<Class<? extends FiscalYearBasedBusinessObject>> parentClasses;
55  
56      protected boolean fiscalYearOneBehind;
57      protected boolean fiscalYearOneAhead;
58      protected boolean twoYearCopy;
59      protected boolean carryForwardInactive;
60      protected boolean allowOverrideTargetYear;
61  
62      protected Boolean hasExtension = null;
63      protected List<String> primaryKeyPropertyNames = null;
64      protected List<String> propertyNames = null;
65      @SuppressWarnings("rawtypes")
66      protected Map<String, Class> referenceObjects = null;
67      @SuppressWarnings("rawtypes")
68      protected Map<String, Class> collectionObjects = null;
69      protected Map<String,Map<String,String>> referenceForeignKeys = new HashMap<String, Map<String,String>>();
70      
71      /**
72       * Constructs a FiscalYearMakerImpl.java.
73       */
74      public FiscalYearMakerImpl() {
75          fiscalYearOneBehind = false;
76          fiscalYearOneAhead = false;
77          twoYearCopy = false;
78          carryForwardInactive = false;
79          allowOverrideTargetYear = true;
80          parentClasses = new HashSet<Class<? extends FiscalYearBasedBusinessObject>>();
81      }
82  
83      protected boolean hasExtension() {
84          if ( hasExtension == null ) {
85              hasExtension = persistenceStructureService.hasReference(businessObjectClass, OLEPropertyConstants.EXTENSION);
86          }
87          return hasExtension.booleanValue();
88      }
89      
90      @Override
91      public List<String> getPrimaryKeyPropertyNames() {
92          if ( primaryKeyPropertyNames == null ) {
93              primaryKeyPropertyNames = persistenceStructureService.listPrimaryKeyFieldNames(businessObjectClass);
94          }
95          return primaryKeyPropertyNames;
96      }
97  
98      @Override
99      public List<String> getPropertyNames() {
100         if ( propertyNames == null ) {
101             propertyNames = persistenceStructureService.listFieldNames(businessObjectClass);
102         }
103         return propertyNames;
104     }
105 
106     @Override
107     @SuppressWarnings("rawtypes")
108     public Map<String,Class> getReferenceObjectProperties() {
109         if ( referenceObjects == null ) {
110             referenceObjects = persistenceStructureService.listReferenceObjectFields(businessObjectClass);
111         }
112         return referenceObjects;
113     }
114 
115     @Override
116     @SuppressWarnings("rawtypes")
117     public Map<String,Class> getCollectionProperties() {
118         if ( collectionObjects == null ) {
119             collectionObjects = persistenceStructureService.listCollectionObjectTypes(businessObjectClass);
120         }
121         return collectionObjects;
122     }
123     
124     @Override
125     public Map<String,String> getForeignKeyMappings( String referenceName ) {
126         if ( !referenceForeignKeys.containsKey(referenceName) ) {
127             referenceForeignKeys.put(referenceName, persistenceStructureService.getForeignKeysForReference(businessObjectClass, referenceName) );
128         }
129         return referenceForeignKeys.get(referenceName);
130     }
131     
132     /**
133      * Sets fiscal year field up one, resets version number and assigns a new Guid for the object id
134      * 
135      * @see org.kuali.ole.coa.dataaccess.FiscalYearMaker#changeForNewYear(java.lang.Integer,
136      *      org.kuali.rice.krad.bo.PersistableBusinessObject)
137      */
138     @Override
139     public void changeForNewYear(Integer baseFiscalYear, FiscalYearBasedBusinessObject currentRecord) {
140         if ( LOG.isDebugEnabled() ) {
141             LOG.debug("starting changeForNewYear() for bo class " + businessObjectClass.getName());
142         }
143 
144         try {
145             // increment fiscal year by 1
146             Integer newFiscalYear = currentRecord.getUniversityFiscalYear() + 1;
147 
148             // update extension, must be done before updating main record so we can retrieve the extension record by reference
149             updateExtensionRecord(newFiscalYear, currentRecord);
150 
151             // update main record fields
152             currentRecord.setUniversityFiscalYear(newFiscalYear);
153 
154             currentRecord.setVersionNumber(ONE);
155             currentRecord.setObjectId(java.util.UUID.randomUUID().toString());
156         }
157         catch (Exception e) {
158             String msg = String.format("Failed to set properties for class %s due to %s", businessObjectClass.getName(), e.getMessage());
159             LOG.error(msg);
160             throw new RuntimeException(msg, e);
161         }
162     }
163 
164     /**
165      * Determines if an extension record is mapped up and exists for the current record. If so then updates the version number,
166      * object id, and clears the primary keys so they will be relinked when storing the main record
167      * 
168      * @param newFiscalYear fiscal year to set
169      * @param currentRecord main record with possible extension reference
170      */
171     protected void updateExtensionRecord(Integer newFiscalYear, PersistableBusinessObject currentRecord) throws Exception {
172         // check if reference is mapped up
173         if ( !hasExtension() ) {
174             return;
175         }
176 
177         // try to retrieve extension record
178         currentRecord.refreshReferenceObject(OLEPropertyConstants.EXTENSION);
179         PersistableBusinessObject extension = currentRecord.getExtension();
180 
181         // if found then update fields
182         if (ObjectUtils.isNotNull(extension)) {
183             extension = (PersistableBusinessObject)ProxyHelper.getRealObject(extension);
184             extension.setVersionNumber(ONE);
185             extension.setObjectId(java.util.UUID.randomUUID().toString());
186 
187             // since this could be a new object (no extension object present on the source record)
188             // we need to set the keys
189             // But...we only need to do this if this was a truly new object, which we can tell by checking
190             // the fiscal year field
191             if ( ((FiscalYearBasedBusinessObject)extension).getUniversityFiscalYear() == null ) {
192                 for ( String pkField : getPrimaryKeyPropertyNames() ) {
193                     PropertyUtils.setSimpleProperty(extension, pkField, PropertyUtils.getSimpleProperty(currentRecord, pkField));
194                 }
195             }
196             ((FiscalYearBasedBusinessObject)extension).setUniversityFiscalYear(newFiscalYear);
197         }
198     }
199 
200     /**
201      * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable
202      * @see org.kuali.ole.coa.dataaccess.FiscalYearMaker#createSelectionCriteria(java.lang.Integer)
203      */
204     @Override
205     public Criteria createNextYearSelectionCriteria(Integer baseFiscalYear) {
206         if ( LOG.isDebugEnabled() ) {
207             LOG.debug("starting createNextYearSelectionCriteria() for bo class " + businessObjectClass.getName());
208         }
209 
210         Criteria criteria = new Criteria();
211         addYearCriteria(criteria, baseFiscalYear + 1, twoYearCopy);
212 
213         return criteria;
214     }
215     
216     /**
217      * Selects records for the given base year or base year minus one if this is a lagging copy. If this is a two year copy base
218      * year plus one records will be selected as well. In addition will only select active records if the business object class
219      * implements the MutableInactivatable interface and has the active property.
220      * 
221      * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable
222      * @see org.kuali.ole.coa.dataaccess.FiscalYearMaker#createSelectionCriteria(java.lang.Integer)
223      */
224     @Override
225     public Criteria createSelectionCriteria(Integer baseFiscalYear) {
226         if ( LOG.isDebugEnabled() ) {
227             LOG.debug("starting createSelectionCriteria() for bo class " + businessObjectClass.getName());
228         }
229 
230         Criteria criteria = new Criteria();
231         addYearCriteria(criteria, baseFiscalYear, false);
232 
233         // add active criteria if the business object class supports the inactivateable interface
234         List<String> fields = getPropertyNames();
235         if (MutableInactivatable.class.isAssignableFrom(businessObjectClass) && fields.contains(OLEPropertyConstants.ACTIVE) && !carryForwardInactive) {
236             criteria.addEqualTo(OLEPropertyConstants.ACTIVE, OLEConstants.ACTIVE_INDICATOR);
237         }
238 
239         return criteria;
240     }
241 
242     /**
243      * Selects records to delete for base year + 1 (or base year for lagging, and base year + 2 for two year)
244      * 
245      * @see org.kuali.ole.coa.batch.dataaccess.FiscalYearMakerHelper#createDeleteCriteria(java.lang.Integer)
246      */
247     @Override
248     public Criteria createDeleteCriteria(Integer baseFiscalYear) {
249         if ( LOG.isDebugEnabled() ) {
250             LOG.debug("starting createDeleteCriteria() for bo class " + businessObjectClass.getName());
251         }
252 
253         Criteria criteria = new Criteria();
254         addYearCriteria(criteria, baseFiscalYear + 1, twoYearCopy);
255 
256         return criteria;
257     }
258 
259     /**
260      * Adds fiscal year criteria based on the configuration (copy two years, lagging, or normal)
261      * 
262      * @param criteria OJB Criteria object
263      * @param baseFiscalYear Fiscal year for critiera
264      * @param createTwoYears indicates whether two years of fiscal year criteria should be added
265      */
266     protected void addYearCriteria(Criteria criteria, Integer baseFiscalYear, boolean createTwoYears) {
267         if (fiscalYearOneBehind) {
268             baseFiscalYear = baseFiscalYear - 1;
269         }
270         else if (fiscalYearOneAhead) {
271             baseFiscalYear = baseFiscalYear + 1;
272         }
273 
274         if (createTwoYears) {
275             List<Integer> copyYears = new ArrayList<Integer>();
276             copyYears.add(baseFiscalYear);
277             copyYears.add(baseFiscalYear + 1);
278 
279             criteria.addIn(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, copyYears);
280         }
281         else {
282             criteria.addEqualTo(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, baseFiscalYear);
283         }
284     }
285 
286     /**
287      * Default implementation does nothing
288      * 
289      * @see org.kuali.ole.coa.batch.dataaccess.FiscalYearMakerHelper#performCustomProcessing(java.lang.Integer)
290      */
291     @Override
292     public void performCustomProcessing(Integer baseFiscalYear, boolean firstCopyYear) {
293 
294     }
295 
296     /**
297      * Default to doing both normal FYM process and custom
298      * 
299      * @see org.kuali.ole.coa.batch.dataaccess.FiscalYearMakerHelper#doCustomProcessingOnly()
300      */
301     @Override
302     public boolean doCustomProcessingOnly() {
303         return false;
304     }
305 
306     /**
307      * @see org.kuali.ole.coa.dataaccess.FiscalYearMaker#getBusinessObjectClass()
308      */
309     @Override
310     public Class<? extends FiscalYearBasedBusinessObject> getBusinessObjectClass() {
311         return businessObjectClass;
312     }
313 
314     /**
315      * <code>Options</code> is the parent for univFiscalYear which all our copy objects should have. Added to list here by default.
316      * 
317      * @see org.kuali.ole.coa.batch.dataaccess.FiscalYearMakerHelper#getParentClasses()
318      * @see org.kuali.ole.sys.businessobject.Options
319      */
320     @Override
321     public Set<Class<? extends FiscalYearBasedBusinessObject>> getParentClasses() {
322         if (!businessObjectClass.equals(SystemOptions.class) && !parentClasses.contains(SystemOptions.class)) {
323             parentClasses.add(SystemOptions.class);
324         }
325 
326         return parentClasses;
327     }
328 
329     /**
330      * Sets the businessObjectClass attribute value.
331      * 
332      * @param businessObjectClass The businessObjectClass to set.
333      */
334     public void setBusinessObjectClass(Class<? extends FiscalYearBasedBusinessObject> businessObjectClass) {
335         this.businessObjectClass = businessObjectClass;
336     }
337 
338     /**
339      * Sets the parentClasses attribute value.
340      * 
341      * @param parentClasses The parentClasses to set.
342      */
343     public void setParentClasses(Set<Class<? extends FiscalYearBasedBusinessObject>> parentClasses) {
344         this.parentClasses = parentClasses;
345     }
346 
347     /**
348      * Gets the fiscalYearOneBehind attribute.
349      * 
350      * @return Returns the fiscalYearOneBehind.
351      */
352     public boolean isFiscalYearOneBehind() {
353         return fiscalYearOneBehind;
354     }
355 
356     /**
357      * Sets the fiscalYearOneBehind attribute value.
358      * 
359      * @param fiscalYearOneBehind The fiscalYearOneBehind to set.
360      */
361     public void setFiscalYearOneBehind(boolean fiscalYearOneBehind) {
362         this.fiscalYearOneBehind = fiscalYearOneBehind;
363     }
364 
365     /**
366      * Gets the fiscalYearOneAhead attribute.
367      * 
368      * @return Returns the fiscalYearOneAhead.
369      */
370     public boolean isFiscalYearOneAhead() {
371         return fiscalYearOneAhead;
372     }
373 
374     /**
375      * Sets the fiscalYearOneAhead attribute value.
376      * 
377      * @param fiscalYearOneAhead The fiscalYearOneAhead to set.
378      */
379     public void setFiscalYearOneAhead(boolean fiscalYearOneAhead) {
380         this.fiscalYearOneAhead = fiscalYearOneAhead;
381     }
382 
383     /**
384      * Gets the twoYearCopy attribute.
385      * 
386      * @return Returns the twoYearCopy.
387      */
388     @Override
389     public boolean isTwoYearCopy() {
390         return twoYearCopy;
391     }
392 
393     /**
394      * Sets the twoYearCopy attribute value.
395      * 
396      * @param twoYearCopy The twoYearCopy to set.
397      */
398     public void setTwoYearCopy(boolean twoYearCopy) {
399         this.twoYearCopy = twoYearCopy;
400     }
401 
402     /**
403      * Gets the carryForwardInactive attribute.
404      * 
405      * @return Returns the carryForwardInactive.
406      */
407     public boolean isCarryForwardInactive() {
408         return carryForwardInactive;
409     }
410 
411     /**
412      * Sets the carryForwardInactive attribute value.
413      * 
414      * @param carryForwardInactive The carryForwardInactive to set.
415      */
416     public void setCarryForwardInactive(boolean carryForwardInactive) {
417         this.carryForwardInactive = carryForwardInactive;
418     }
419 
420     /**
421      * Sets the persistenceStructureService attribute value.
422      * 
423      * @param persistenceStructureService The persistenceStructureService to set.
424      */
425     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
426         this.persistenceStructureService = persistenceStructureService;
427     }
428 
429     /**
430      * Gets the allowOverrideTargetYear attribute.
431      * 
432      * @return Returns the allowOverrideTargetYear.
433      */
434     @Override
435     public boolean isAllowOverrideTargetYear() {
436         return allowOverrideTargetYear;
437     }
438 
439     /**
440      * Sets the allowOverrideTargetYear attribute value.
441      * 
442      * @param allowOverrideTargetYear The allowOverrideTargetYear to set.
443      */
444     public void setAllowOverrideTargetYear(boolean allowOverrideTargetYear) {
445         this.allowOverrideTargetYear = allowOverrideTargetYear;
446     }
447 
448     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
449         this.businessObjectService = businessObjectService;
450     }
451 
452 }