1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.ole.sys.batch.dataaccess.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.commons.beanutils.PropertyUtils;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.log4j.Logger;
29  import org.apache.ojb.broker.query.QueryByCriteria;
30  import org.apache.ojb.broker.query.ReportQueryByCriteria;
31  import org.apache.ojb.broker.util.ObjectModification;
32  import org.kuali.ole.sys.batch.dataaccess.FiscalYearMaker;
33  import org.kuali.ole.sys.batch.dataaccess.FiscalYearMakersDao;
34  import org.kuali.ole.sys.businessobject.FiscalYearBasedBusinessObject;
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.util.ObjectUtils;
38  
39  
40  
41  
42  public class FiscalYearMakersDaoOjb extends PlatformAwareDaoBaseOjb implements FiscalYearMakersDao {
43      private static final Logger LOG = org.apache.log4j.Logger.getLogger(FiscalYearMakersDaoOjb.class);
44      
45      protected static final String KEY_STRING_DELIMITER = "|";
46  
47      
48  
49  
50  
51      public void deleteNewYearRows(Integer baseYear, FiscalYearMaker objectFiscalYearMaker) {
52          if ( LOG.isInfoEnabled() ) {
53              LOG.info(String.format("\ndeleting %s for target year(s)", objectFiscalYearMaker.getBusinessObjectClass().getName()));
54          }
55  
56          QueryByCriteria queryID = new QueryByCriteria(objectFiscalYearMaker.getBusinessObjectClass(), objectFiscalYearMaker.createDeleteCriteria(baseYear));
57          getPersistenceBrokerTemplate().deleteByQuery(queryID);
58  
59          getPersistenceBrokerTemplate().clearCache();
60      }
61  
62      
63  
64  
65  
66      public Collection<String> createNewYearRows(Integer baseYear, FiscalYearMaker fiscalYearMaker, boolean replaceMode, Map<Class<? extends FiscalYearBasedBusinessObject>, Set<String>> parentKeysWritten, boolean isParentClass) throws Exception {
67          if ( LOG.isInfoEnabled() ) {
68              LOG.info(String.format("\n copying %s from %d to %d", fiscalYearMaker.getBusinessObjectClass().getName(), baseYear, baseYear + 1));
69          }
70  
71          int rowsRead = 0;
72          int rowsWritten = 0;
73          int rowsFailingRI = 0;
74  
75          
76          List<String> copyErrors = new ArrayList<String>();
77  
78          
79          Set<String> keysWritten = new HashSet<String>();
80  
81          
82          List<String> primaryKeyFields = fiscalYearMaker.getPrimaryKeyPropertyNames();
83  
84          Set<String> nextYearPrimaryKeys = new HashSet<String>(2000);
85          LOG.info( "Loading Next Year's PKs for comparison");        
86          ReportQueryByCriteria nextYearKeyQuery = new ReportQueryByCriteria(fiscalYearMaker.getBusinessObjectClass(), primaryKeyFields.toArray(new String[0]), fiscalYearMaker.createNextYearSelectionCriteria(baseYear) );
87          Iterator<Object[]> nextYearRecords = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(nextYearKeyQuery);
88          StringBuilder keyString = new StringBuilder(40);
89          int numNextYearRecords = 0;
90          while ( nextYearRecords.hasNext() ) {
91              numNextYearRecords++;
92              keyString.setLength(0);
93              Object[] record = nextYearRecords.next();
94              for ( Object f : record ) {
95                  keyString.append( f ).append( KEY_STRING_DELIMITER );
96              }
97              nextYearPrimaryKeys.add(keyString.toString());
98              if ( numNextYearRecords % 10000 == 0 ) {
99                  if ( LOG.isInfoEnabled() ) {
100                     LOG.info("Processing Record: " + numNextYearRecords);
101                 }
102             }
103         }
104         if ( LOG.isInfoEnabled() ) {
105             LOG.info( "Completed load of next year keys.  " + numNextYearRecords + " keys loaded.");
106             LOG.info( "Starting processing of existing FY rows" );
107         }
108         
109         QueryByCriteria queryId = new QueryByCriteria(fiscalYearMaker.getBusinessObjectClass(), fiscalYearMaker.createSelectionCriteria(baseYear));
110         
111         Iterator<FiscalYearBasedBusinessObject> recordsToCopy = getPersistenceBrokerTemplate().getIteratorByQuery(queryId);
112         
113         
114         while ( recordsToCopy.hasNext() ) {
115             FiscalYearBasedBusinessObject objectToCopy = recordsToCopy.next();
116             rowsRead++;
117             if ( LOG.isInfoEnabled() ) {
118                 if ( rowsRead % 1000 == 0 ) {
119                     LOG.info( "*** Processing Record: " + rowsRead + " -- Written So Far: " + rowsWritten + " -- Failing RI: " + rowsFailingRI + " -- Keys Written: " + keysWritten.size() );
120                 }
121             }
122 
123             
124             removeNonPrimitiveFields(fiscalYearMaker, objectToCopy);
125 
126             
127             fiscalYearMaker.changeForNewYear(baseYear, objectToCopy);
128 
129             
130             if ( nextYearPrimaryKeys.contains(getKeyString(fiscalYearMaker, primaryKeyFields, objectToCopy)) ) {
131                 if (isParentClass) {
132                     addToKeysWritten(fiscalYearMaker, primaryKeyFields, objectToCopy, keysWritten);
133                 }
134                 continue;
135             }
136 
137             
138             if (!validateParentRecordsExist(fiscalYearMaker, objectToCopy, parentKeysWritten, copyErrors)) {
139                 rowsFailingRI++;
140                 continue;
141             }
142 
143             
144             getPersistenceBroker(true).store(objectToCopy, ObjectModification.INSERT);
145             rowsWritten++;
146             if (isParentClass) {
147                 addToKeysWritten(fiscalYearMaker, primaryKeyFields, objectToCopy, keysWritten);
148             }
149         }
150 
151         if (isParentClass) {
152             parentKeysWritten.put(fiscalYearMaker.getBusinessObjectClass(), keysWritten);
153         }
154 
155         if ( LOG.isInfoEnabled() ) {
156             LOG.info(String.format("\n%s:\n%d read = %d\n%d written = %d\nfailed RI = %d", fiscalYearMaker.getBusinessObjectClass(), baseYear, rowsRead, baseYear + 1, rowsWritten, rowsFailingRI));
157         }
158 
159         getPersistenceBrokerTemplate().clearCache();
160 
161         return copyErrors;
162     }
163 
164     
165 
166 
167 
168 
169     protected void removeNonPrimitiveFields( FiscalYearMaker fiscalYearMaker, FiscalYearBasedBusinessObject businessObject) {
170         try {
171             @SuppressWarnings("rawtypes")
172             Map<String, Class> referenceFields = fiscalYearMaker.getReferenceObjectProperties();
173             for (String fieldName : referenceFields.keySet()) {
174                 if (!fieldName.equals("extension")) {
175                 PropertyUtils.setSimpleProperty(businessObject, fieldName, null);
176             }
177             }
178 
179             @SuppressWarnings("rawtypes")
180             Map<String, Class> collectionFields = fiscalYearMaker.getCollectionProperties();
181             for (String fieldName : collectionFields.keySet()) {
182                 PropertyUtils.setSimpleProperty(businessObject, fieldName, null);
183             }
184         } catch (Exception e) {
185             throw new RuntimeException("Unable to set non primitive fields to null: " + e.getMessage(), e);
186         }
187     }
188 
189     
190 
191 
192 
193 
194     protected boolean validateParentRecordsExist(FiscalYearMaker objectFiscalYearMaker, FiscalYearBasedBusinessObject childRecord, Map<Class<? extends FiscalYearBasedBusinessObject>, Set<String>> parentKeysWritten, List<String> copyErrors) throws Exception {
195         
196         for (Class<? extends FiscalYearBasedBusinessObject> parentClass : objectFiscalYearMaker.getParentClasses()) {
197             if ( !validateChildParentReferencesExist(objectFiscalYearMaker,childRecord, parentClass, parentKeysWritten.get(parentClass), copyErrors) ) {
198                 return false;
199             }
200         }
201 
202         return true;
203     }
204 
205     
206 
207 
208 
209 
210 
211 
212 
213 
214 
215     protected boolean validateChildParentReferencesExist(FiscalYearMaker objectFiscalYearMaker,FiscalYearBasedBusinessObject childRecord, Class<? extends FiscalYearBasedBusinessObject> parentClass, Set<String> parentKeys, List<String> copyErrors) throws Exception {
216         boolean allChildParentReferencesExist = true;
217         boolean foundParentReference = false;
218 
219         
220         @SuppressWarnings("rawtypes")
221         Map<String, Class> referenceObjects = objectFiscalYearMaker.getReferenceObjectProperties();
222 
223         
224         for (String referenceName : referenceObjects.keySet()) {
225             Class<? extends PersistableBusinessObject> referenceClass = referenceObjects.get(referenceName);
226 
227             if (parentClass.isAssignableFrom(referenceClass)) {
228                 foundParentReference = true;
229 
230                 String foreignKeyString = getForeignKeyStringForReference(objectFiscalYearMaker, childRecord, referenceName);
231                 if (StringUtils.isNotBlank(foreignKeyString) 
232                         && !parentKeys.contains(foreignKeyString)) {
233                     
234                     getPersistenceBroker(true).retrieveReference(childRecord, referenceName);
235                     PersistableBusinessObject reference = (PersistableBusinessObject) PropertyUtils.getSimpleProperty(childRecord, referenceName);
236                     if (ObjectUtils.isNull(reference)) {
237                         allChildParentReferencesExist = false;
238                         writeMissingParentCopyError(childRecord, parentClass, foreignKeyString, copyErrors);
239                         LOG.warn( "Missing Parent Object: " + copyErrors.get(copyErrors.size()-1));
240                     } else {
241                         parentKeys.add(foreignKeyString);
242                     }
243                 }
244             }
245         }
246 
247         if (!foundParentReference) {
248             LOG.warn(String.format("\n!!! NO relationships between child %s and parent %s found in OJB descriptor\n", childRecord.getClass().getName(), parentClass.getName()));
249         }
250 
251         return allChildParentReferencesExist;
252     }
253 
254     
255 
256 
257 
258 
259 
260 
261     protected String getForeignKeyStringForReference( FiscalYearMaker fiscalYearMaker, FiscalYearBasedBusinessObject businessObject, String referenceName) throws Exception {
262         Map<String, String> foreignKeyToPrimaryKeyMap = fiscalYearMaker.getForeignKeyMappings( referenceName );
263 
264         StringBuilder foreignKeyString = new StringBuilder(80);
265         for (String fkFieldName : foreignKeyToPrimaryKeyMap.keySet()) {
266             Object fkFieldValue = PropertyUtils.getSimpleProperty(businessObject, fkFieldName);
267             if (fkFieldValue != null) {
268                 foreignKeyString.append( fkFieldValue.toString() ).append( KEY_STRING_DELIMITER );
269             } else {
270                 foreignKeyString.setLength(0);
271                 break;
272             }
273         }
274 
275         return foreignKeyString.toString();
276     }
277 
278     
279 
280 
281 
282 
283 
284 
285 
286     protected void writeMissingParentCopyError(FiscalYearBasedBusinessObject childRecord, Class<? extends FiscalYearBasedBusinessObject> parentClass, String foreignKeyString, Collection<String> copyErrors) {
287         StringBuilder errorCopyFailedMessage = new StringBuilder(150);
288         errorCopyFailedMessage.append(childRecord.getClass().getName());
289         errorCopyFailedMessage.append(" row for " + childRecord.toString());
290         errorCopyFailedMessage.append(" - " + foreignKeyString);
291         errorCopyFailedMessage.append(" not in ");
292         errorCopyFailedMessage.append(parentClass.getName());
293 
294         copyErrors.add(errorCopyFailedMessage.toString());
295     }
296 
297     
298 
299 
300 
301 
302 
303     protected void addToKeysWritten( FiscalYearMaker fiscalYearMaker, List<String> keyFieldNames, FiscalYearBasedBusinessObject copiedObject, Set<String> keysWritten) throws Exception {
304         keysWritten.add(getKeyString(fiscalYearMaker, keyFieldNames, copiedObject));
305     }
306 
307     protected String getKeyString( FiscalYearMaker fiscalYearMaker, List<String> keyFieldNames, FiscalYearBasedBusinessObject businessObject ) throws Exception {
308         StringBuilder keyString = new StringBuilder(40);
309         for (String keyFieldName : keyFieldNames) {
310             keyString.append( PropertyUtils.getSimpleProperty(businessObject, keyFieldName) ).append( KEY_STRING_DELIMITER );
311         }
312         return keyString.toString();
313     }
314 }