View Javadoc

1   /**
2    * Copyright 2005-2014 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.rice.krad.util;
17  
18  import org.apache.commons.beanutils.NestedNullException;
19  import org.apache.commons.beanutils.PropertyUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.log4j.Logger;
22  import org.apache.ojb.broker.core.proxy.ProxyHelper;
23  import org.hibernate.collection.PersistentBag;
24  import org.hibernate.proxy.HibernateProxy;
25  import org.kuali.rice.core.api.CoreApiServiceLocator;
26  import org.kuali.rice.core.api.encryption.EncryptionService;
27  import org.kuali.rice.core.api.search.SearchOperator;
28  import org.kuali.rice.core.api.util.cache.CopiedObject;
29  import org.kuali.rice.core.web.format.CollectionFormatter;
30  import org.kuali.rice.core.web.format.FormatException;
31  import org.kuali.rice.core.web.format.Formatter;
32  import org.kuali.rice.krad.bo.BusinessObject;
33  import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
34  import org.kuali.rice.krad.bo.PersistableBusinessObject;
35  import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension;
36  import org.kuali.rice.krad.exception.ClassNotPersistableException;
37  import org.kuali.rice.krad.service.KRADServiceLocator;
38  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
39  import org.kuali.rice.krad.service.ModuleService;
40  import org.kuali.rice.krad.service.PersistenceStructureService;
41  
42  import javax.persistence.EntityNotFoundException;
43  import java.beans.PropertyDescriptor;
44  import java.io.ByteArrayInputStream;
45  import java.io.ByteArrayOutputStream;
46  import java.io.ObjectInputStream;
47  import java.io.ObjectOutputStream;
48  import java.io.Serializable;
49  import java.lang.reflect.Field;
50  import java.lang.reflect.InvocationTargetException;
51  import java.security.GeneralSecurityException;
52  import java.security.MessageDigest;
53  import java.util.Collection;
54  import java.util.Iterator;
55  import java.util.List;
56  import java.util.Map;
57  
58  /**
59   * Contains various Object, Proxy, and serialization utilities
60   *
61   * @author Kuali Rice Team (rice.collab@kuali.org)
62   */
63  public final class ObjectUtils {
64      private static final Logger LOG = Logger.getLogger(ObjectUtils.class);
65  
66      private ObjectUtils() {
67          throw new UnsupportedOperationException("do not call");
68      }
69  
70      /**
71       * Uses Serialization mechanism to create a deep copy of the given Object. As a special case, deepCopy of null
72       * returns null,
73       * just to make using this method simpler. For a detailed discussion see:
74       * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html
75       *
76       * @param src
77       * @return deep copy of the given Serializable
78       */
79      public static Serializable deepCopy(Serializable src) {
80          CopiedObject co = deepCopyForCaching(src);
81          return co.getContent();
82      }
83  
84      /**
85       * Uses Serialization mechanism to create a deep copy of the given Object, and returns a CacheableObject instance
86       * containing the
87       * deepCopy and its size in bytes. As a special case, deepCopy of null returns a cacheableObject containing null and
88       * a size of
89       * 0, to make using this method simpler. For a detailed discussion see:
90       * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html
91       *
92       * @param src
93       * @return CopiedObject containing a deep copy of the given Serializable and its size in bytes
94       */
95      public static CopiedObject deepCopyForCaching(Serializable src) {
96          CopiedObject co = new CopiedObject();
97  
98          co.setContent(src);
99  
100         return co;
101     }
102 
103     /**
104      * Converts the object to a byte array using the output stream.
105      *
106      * @param object
107      * @return byte array of the object
108      */
109     public static byte[] toByteArray(Object object) throws Exception {
110         ObjectOutputStream oos = null;
111         try {
112             ByteArrayOutputStream bos = new ByteArrayOutputStream(); // A
113             oos = new ObjectOutputStream(bos); // B
114             // serialize and pass the object
115             oos.writeObject(object); // C
116             // oos.flush(); // D
117             return bos.toByteArray();
118         } catch (Exception e) {
119             LOG.warn("Exception in ObjectUtil = " + e);
120             throw (e);
121         } finally {
122             if (oos != null) {
123                 oos.close();
124             }
125         }
126     }
127 
128     /**
129      * reconsitiutes the object that was converted into a byte array by toByteArray
130      *
131      * @param bytes
132      * @return
133      * @throws Exception
134      */
135     public static Object fromByteArray(byte[] bytes) throws Exception {
136         ObjectInputStream ois = null;
137         try {
138             ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
139             ois = new ObjectInputStream(bis);
140             Object obj = ois.readObject();
141             return obj;
142         } catch (Exception e) {
143             LOG.warn("Exception in ObjectUtil = " + e);
144             throw (e);
145         } finally {
146             if (ois != null) {
147                 ois.close();
148             }
149         }
150     }
151 
152     /**
153      * use MD5 to create a one way hash of an object
154      *
155      * @param object
156      * @return
157      */
158     public static String getMD5Hash(Object object) throws Exception {
159         try {
160             MessageDigest md = MessageDigest.getInstance("MD5");
161             md.update(toByteArray(object));
162             return new String(md.digest());
163         } catch (Exception e) {
164             LOG.warn(e);
165             throw e;
166         }
167     }
168 
169     /**
170      * Creates a new instance of a given BusinessObject, copying fields specified in template from the given source BO.
171      * For example,
172      * this can be used to create an AccountChangeDetail based on a particular Account.
173      *
174      * @param template a map defining the relationships between the fields of the newly created BO, and the source BO.
175      * For each K (key), V (value)
176      * entry, the value of property V on the source BO will be assigned to the K property of the newly created BO
177      * @throws NoSuchMethodException
178      * @throws InvocationTargetException
179      * @throws IllegalAccessException
180      * @throws FormatException
181      * @see MaintenanceUtils
182      */
183 
184     public static BusinessObject createHybridBusinessObject(Class businessObjectClass, BusinessObject source,
185             Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
186         BusinessObject obj = null;
187         try {
188             ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(
189                     businessObjectClass);
190             if (moduleService != null && moduleService.isExternalizable(businessObjectClass)) {
191                 obj = (BusinessObject) moduleService.createNewObjectFromExternalizableClass(businessObjectClass);
192             } else {
193                 obj = (BusinessObject) businessObjectClass.newInstance();
194             }
195         } catch (Exception e) {
196             throw new RuntimeException("Cannot instantiate " + businessObjectClass.getName(), e);
197         }
198 
199         createHybridBusinessObject(obj, source, template);
200 
201         return obj;
202     }
203 
204     public static void createHybridBusinessObject(BusinessObject businessObject, BusinessObject source,
205             Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
206         for (String name : template.keySet()) {
207             String sourcePropertyName = template.get(name);
208             setObjectProperty(businessObject, name, easyGetPropertyType(source, sourcePropertyName), getPropertyValue(
209                     source, sourcePropertyName));
210         }
211     }
212 
213     /**
214      * This method simply uses PojoPropertyUtilsBean logic to get the Class of a Class property.
215      * This method does not have any of the logic needed to obtain the Class of an element of a Collection specified in
216      * the DataDictionary.
217      *
218      * @param object An instance of the Class of which we're trying to get the property Class.
219      * @param propertyName The name of the property.
220      * @return
221      * @throws IllegalAccessException
222      * @throws NoSuchMethodException
223      * @throws InvocationTargetException
224      */
225     static public Class easyGetPropertyType(Object object,
226             String propertyName) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
227 
228         // FIXME (laran) This dependence should be inverted. Instead of having a core class
229         // depend on PojoPropertyUtilsBean, which is in the web layer, the web layer
230         // should depend downward to the core.
231         return PropertyUtils.getPropertyType(object, propertyName);
232 
233     }
234 
235     /**
236      * Returns the type of the property in the object. This implementation is not smart enough to look through a
237      * Collection to get the property type
238      * of an attribute of an element in the collection.
239      * <p/>
240      * NOTE: A patch file attached to https://test.kuali.org/jira/browse/KULRNE-4435 contains a modified version of this
241      * method which IS smart enough
242      * to look through Collections. This patch is currently under review.
243      *
244      * @param object An instance of the Class for which we're trying to get the property type.
245      * @param propertyName The name of the property of the Class the Class of which we're trying to get. Dot notation is
246      * used to separate properties.
247      * TODO: The rules about this dot notation needs to be explained in Confluence using examples.
248      * @param persistenceStructureService Needed to get the type of elements in a Collection from OJB.
249      * @return Object will be null if any parent property for the given property is null.
250      */
251     public static Class getPropertyType(Object object, String propertyName,
252             PersistenceStructureService persistenceStructureService) {
253         if (object == null || propertyName == null) {
254             throw new RuntimeException("Business object and property name can not be null");
255         }
256 
257         Class propertyType = null;
258         try {
259             try {
260                 // Try to simply use the default or simple way of getting the property type.
261                 propertyType = PropertyUtils.getPropertyType(object, propertyName);
262             } catch (IllegalArgumentException ex) {
263                 // swallow the exception, propertyType stays null
264             } catch (NoSuchMethodException nsme) {
265                 // swallow the exception, propertyType stays null
266             }
267 
268             // if the property type as determined from the object is PersistableBusinessObject,
269             // then this must be an extension attribute -- attempt to get the property type from the
270             // persistence structure service
271             if (propertyType != null && propertyType.equals(PersistableBusinessObjectExtension.class)) {
272                 propertyType = persistenceStructureService.getBusinessObjectAttributeClass(ProxyHelper.getRealClass(
273                         object), propertyName);
274             }
275 
276             // If the easy way didn't work ...
277             if (null == propertyType && -1 != propertyName.indexOf('.')) {
278                 if (null == persistenceStructureService) {
279                     LOG.info(
280                             "PropertyType couldn't be determined simply and no PersistenceStructureService was given. If you pass in a PersistenceStructureService I can look in other places to try to determine the type of the property.");
281                 } else {
282                     String prePeriod = StringUtils.substringBefore(propertyName, ".");
283                     String postPeriod = StringUtils.substringAfter(propertyName, ".");
284 
285                     Class prePeriodClass = getPropertyType(object, prePeriod, persistenceStructureService);
286                     Object prePeriodClassInstance = prePeriodClass.newInstance();
287                     propertyType = getPropertyType(prePeriodClassInstance, postPeriod, persistenceStructureService);
288                 }
289 
290             } else if (Collection.class.isAssignableFrom(propertyType)) {
291                 Map<String, Class> map = persistenceStructureService.listCollectionObjectTypes(object.getClass());
292                 propertyType = map.get(propertyName);
293             }
294 
295         } catch (Exception e) {
296             LOG.debug("unable to get property type for " + propertyName + " " + e.getMessage());
297             // continue and return null for propertyType
298         }
299 
300         return propertyType;
301     }
302 
303     /**
304      * Returns the value of the property in the object.
305      *
306      * @param businessObject
307      * @param propertyName
308      * @return Object will be null if any parent property for the given property is null.
309      */
310     public static Object getPropertyValue(Object businessObject, String propertyName) {
311         if (businessObject == null || propertyName == null) {
312             throw new RuntimeException("Business object and property name can not be null");
313         }
314 
315         Object propertyValue = null;
316         try {
317             propertyValue = PropertyUtils.getProperty(businessObject, propertyName);
318         } catch (NestedNullException e) {
319             // continue and return null for propertyValue
320         } catch (IllegalAccessException e1) {
321             LOG.error("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1
322                     .getMessage());
323             throw new RuntimeException(
324                     "error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1
325                             .getMessage(), e1);
326         } catch (InvocationTargetException e1) {
327             // continue and return null for propertyValue
328         } catch (NoSuchMethodException e1) {
329             LOG.error("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1
330                     .getMessage());
331             throw new RuntimeException(
332                     "error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1
333                             .getMessage(), e1);
334         }
335 
336         return propertyValue;
337     }
338 
339     /**
340      * Gets the property value from the business object, then based on the value
341      * type select a formatter and format the value
342      *
343      * @param businessObject BusinessObject instance that contains the property
344      * @param propertyName Name of property in BusinessObject to get value for
345      * @param formatter Default formatter to use (or null)
346      * @return Formatted property value as String, or empty string if value is null
347      */
348     public static String getFormattedPropertyValue(BusinessObject businessObject, String propertyName,
349             Formatter formatter) {
350         String propValue = KRADConstants.EMPTY_STRING;
351 
352         Object prop = ObjectUtils.getPropertyValue(businessObject, propertyName);
353         if (formatter == null) {
354             propValue = formatPropertyValue(prop);
355         } else {
356             final Object formattedValue = formatter.format(prop);
357             if (formattedValue != null) {
358                 propValue = String.valueOf(formattedValue);
359             }
360         }
361 
362         return propValue;
363     }
364 
365     /**
366      * References the data dictionary to find any registered formatter class then if not found checks for associated
367      * formatter for the
368      * property type. Value is then formatted using the found Formatter
369      *
370      * @param businessObject BusinessObject instance that contains the property
371      * @param propertyName Name of property in BusinessObject to get value for
372      * @return Formatted property value as String, or empty string if value is null
373      */
374     public static String getFormattedPropertyValueUsingDataDictionary(BusinessObject businessObject,
375             String propertyName) {
376         Formatter formatter = getFormatterWithDataDictionary(businessObject, propertyName);
377 
378         return getFormattedPropertyValue(businessObject, propertyName, formatter);
379     }
380 
381     /**
382      * Based on the value type selects a formatter and returns the formatted
383      * value as a string
384      *
385      * @param propertyValue Object value to be formatted
386      * @return formatted value as a String
387      */
388     public static String formatPropertyValue(Object propertyValue) {
389         Object propValue = KRADConstants.EMPTY_STRING;
390 
391         Formatter formatter = null;
392         if (propertyValue != null) {
393             if (propertyValue instanceof Collection) {
394                 formatter = new CollectionFormatter();
395             } else {
396                 formatter = Formatter.getFormatter(propertyValue.getClass());
397             }
398 
399             propValue = formatter != null ? formatter.format(propertyValue) : propertyValue;
400         }
401 
402         return propValue != null ? String.valueOf(propValue) : KRADConstants.EMPTY_STRING;
403     }
404 
405     /**
406      * Sets the property of an object with the given value. Converts using the formatter of the type for the property.
407      * Note: propertyType does not need passed, is found by util method.
408      */
409     public static void setObjectProperty(Object bo, String propertyName,
410             Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
411         Class propertyType = easyGetPropertyType(bo, propertyName);
412         setObjectProperty(bo, propertyName, propertyType, propertyValue);
413     }
414 
415     /**
416      * Sets the property of an object with the given value. Converts using the formatter of the given type if one is
417      * found.
418      *
419      * @param bo
420      * @param propertyName
421      * @param propertyType
422      * @param propertyValue
423      * @throws NoSuchMethodException
424      * @throws InvocationTargetException
425      * @throws IllegalAccessException
426      */
427     public static void setObjectProperty(Object bo, String propertyName, Class propertyType,
428             Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
429         // reformat propertyValue, if necessary
430         boolean reformat = false;
431         if (propertyType != null) {
432             if (propertyValue != null && propertyType.isAssignableFrom(String.class)) {
433                 // always reformat if the destination is a String
434                 reformat = true;
435             } else if (propertyValue != null && !propertyType.isAssignableFrom(propertyValue.getClass())) {
436                 // otherwise, only reformat if the propertyValue can't be assigned into the property
437                 reformat = true;
438             }
439 
440             // attempting to set boolean fields to null throws an exception, set to false instead
441             if (boolean.class.isAssignableFrom(propertyType) && propertyValue == null) {
442                 propertyValue = false;
443             }
444         }
445 
446         Formatter formatter = getFormatterWithDataDictionary(bo, propertyName);
447         if (reformat && formatter != null) {
448             LOG.debug("reformatting propertyValue using Formatter " + formatter.getClass().getName());
449             propertyValue = formatter.convertFromPresentationFormat(propertyValue);
450         }
451 
452         // set property in the object
453         PropertyUtils.setNestedProperty(bo, propertyName, propertyValue);
454     }
455 
456     /**
457      * Sets the property of an object with the given value. Converts using the given formatter, if it isn't null.
458      *
459      * @param formatter
460      * @param bo
461      * @param propertyName
462      * @param type
463      * @param propertyValue
464      * @throws NoSuchMethodException
465      * @throws InvocationTargetException
466      * @throws IllegalAccessException
467      */
468     public static void setObjectProperty(Formatter formatter, Object bo, String propertyName, Class type,
469             Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
470 
471         // convert value using formatter for type
472         if (formatter != null) {
473             propertyValue = formatter.convertFromPresentationFormat(propertyValue);
474         }
475 
476         // KULRICE-8412 Changes so that values passed back through via the URL such as
477         // lookups are decrypted where applicable
478         if (propertyValue instanceof String) {
479             String propVal = (String) propertyValue;
480 
481             if (propVal.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) {
482                 propVal = StringUtils.removeEnd(propVal, EncryptionService.ENCRYPTION_POST_PREFIX);
483             }
484 
485             if (KRADServiceLocatorWeb.getDataObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(bo.getClass(), propertyName)) {
486                 try {
487                     if (CoreApiServiceLocator.getEncryptionService().isEnabled()) {
488                         propertyValue = CoreApiServiceLocator.getEncryptionService().decrypt(propVal);
489                     }
490                 } catch (GeneralSecurityException e) {
491                     throw new RuntimeException(e);
492                 }
493             }
494         }
495 
496         // set property in the object
497         PropertyUtils.setNestedProperty(bo, propertyName, propertyValue);
498     }
499 
500     /**
501      * Returns a Formatter instance for the given property name in the given given business object. First
502      * checks if a formatter is defined for the attribute in the data dictionary, is not found then returns
503      * the registered formatter for the property type in Formatter
504      *
505      * @param bo - business object instance with property to get formatter for
506      * @param propertyName - name of property to get formatter for
507      * @return Formatter instance
508      */
509     public static Formatter getFormatterWithDataDictionary(Object bo, String propertyName) {
510         Formatter formatter = null;
511 
512         Class boClass = bo.getClass();
513         String boPropertyName = propertyName;
514 
515         // for collections, formatter should come from property on the collection type
516         if (StringUtils.contains(propertyName, "]")) {
517             Object collectionParent = getNestedValue(bo, StringUtils.substringBeforeLast(propertyName, "].") + "]");
518             if (collectionParent != null) {
519                 boClass = collectionParent.getClass();
520                 boPropertyName = StringUtils.substringAfterLast(propertyName, "].");
521             }
522         }
523 
524         Class<? extends Formatter> formatterClass =
525                 KRADServiceLocatorWeb.getDataDictionaryService().getAttributeFormatter(boClass, boPropertyName);
526         if (formatterClass == null) {
527             try {
528                 formatterClass = Formatter.findFormatter(getPropertyType(boClass.newInstance(), boPropertyName,
529                         KRADServiceLocator.getPersistenceStructureService()));
530             } catch (InstantiationException e) {
531                 LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName);
532                 // just swallow the exception and let formatter be null
533             } catch (IllegalAccessException e) {
534                 LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName);
535                 // just swallow the exception and let formatter be null
536             }
537         }
538 
539         if (formatterClass != null) {
540             try {
541                 formatter = formatterClass.newInstance();
542             } catch (Exception e) {
543                 throw new RuntimeException("cannot create new instance of formatter class " + formatterClass.toString(),
544                         e);
545             }
546         }
547 
548         return formatter;
549     }
550 
551     /**
552      * Recursive; sets all occurences of the property in the object, its nested objects and its object lists with the
553      * given value.
554      *
555      * @param bo
556      * @param propertyName
557      * @param type
558      * @param propertyValue
559      * @throws NoSuchMethodException
560      * @throws InvocationTargetException
561      * @throws IllegalAccessException
562      */
563     public static void setObjectPropertyDeep(Object bo, String propertyName, Class type,
564             Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
565 
566         // Base return cases to avoid null pointers & infinite loops
567         if (isNull(bo) || !PropertyUtils.isReadable(bo, propertyName) || (propertyValue != null && propertyValue.equals(
568                 getPropertyValue(bo, propertyName))) || (type != null && !type.equals(easyGetPropertyType(bo,
569                 propertyName)))) {
570             return;
571         }
572 
573         // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval
574         materializeUpdateableCollections(bo);
575 
576         // Set the property in the BO
577         setObjectProperty(bo, propertyName, type, propertyValue);
578 
579         // Now drill down and check nested BOs and BO lists
580         PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
581         for (int i = 0; i < propertyDescriptors.length; i++) {
582 
583             PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
584 
585             // Business Objects
586             if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(
587                     propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo,
588                     propertyDescriptor.getName())) {
589                 Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName());
590                 if (nestedBo instanceof BusinessObject) {
591                     setObjectPropertyDeep((BusinessObject) nestedBo, propertyName, type, propertyValue);
592                 }
593             }
594 
595             // Lists
596             else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(
597                     propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName())
598                     != null) {
599 
600                 List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());
601                 for (Object listedBo : propertyList) {
602                     if (listedBo != null && listedBo instanceof BusinessObject) {
603                         setObjectPropertyDeep(listedBo, propertyName, type, propertyValue);
604                     }
605                 } // end for
606             }
607         } // end for
608     }
609 
610     /*
611     * Recursive up to a given depth; sets all occurences of the property in the object, its nested objects and its object lists with the given value.
612     */
613     public static void setObjectPropertyDeep(Object bo, String propertyName, Class type, Object propertyValue,
614             int depth) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
615         // Base return cases to avoid null pointers & infinite loops
616         if (depth == 0 || isNull(bo) || !PropertyUtils.isReadable(bo, propertyName)) {
617             return;
618         }
619 
620         // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval
621         try {
622             materializeUpdateableCollections(bo);
623         } catch(ClassNotPersistableException ex){
624             //Not all classes will be persistable in a collection. For e.g. externalizable business objects.
625             LOG.info("Not persistable dataObjectClass: "+bo.getClass().getName()+", field: "+propertyName);
626         }
627 
628     // Set the property in the BO
629         setObjectProperty(bo, propertyName, type, propertyValue);
630 
631         // Now drill down and check nested BOs and BO lists
632         PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
633         for (int i = 0; i < propertyDescriptors.length; i++) {
634             PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
635 
636             // Business Objects
637             if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(
638                     propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo,
639                     propertyDescriptor.getName())) {
640                 Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName());
641                 if (nestedBo instanceof BusinessObject) {
642                     setObjectPropertyDeep((BusinessObject) nestedBo, propertyName, type, propertyValue, depth - 1);
643                 }
644             }
645 
646             // Lists
647             else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(
648                     propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName())
649                     != null) {
650 
651                 List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());
652 
653                 // Complete Hibernate Hack - fetches the proxied List into the PersistenceContext and sets it on the BO Copy.
654                 if (propertyList instanceof PersistentBag) {
655                     try {
656                         PersistentBag bag = (PersistentBag) propertyList;
657                         PersistableBusinessObject pbo =
658                                 (PersistableBusinessObject) KRADServiceLocator.getEntityManagerFactory()
659                                         .createEntityManager().find(bo.getClass(), bag.getKey());
660                         Field field1 = pbo.getClass().getDeclaredField(propertyDescriptor.getName());
661                         Field field2 = bo.getClass().getDeclaredField(propertyDescriptor.getName());
662                         field1.setAccessible(true);
663                         field2.setAccessible(true);
664                         field2.set(bo, field1.get(pbo));
665                         propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());
666                         ;
667                     } catch (Exception e) {
668                         LOG.error(e.getMessage(), e);
669                     }
670                 }
671                 // End Complete Hibernate Hack
672 
673                 for (Object listedBo : propertyList) {
674                     if (listedBo != null && listedBo instanceof BusinessObject) {
675                         setObjectPropertyDeep(listedBo, propertyName, type, propertyValue, depth - 1);
676                     }
677                 } // end for
678             }
679         } // end for
680     }
681 
682     /**
683      * This method checks for updateable collections on the business object provided and materializes the corresponding
684      * collection proxies
685      *
686      * @param bo The business object for which you want unpdateable, proxied collections materialized
687      * @throws FormatException
688      * @throws IllegalAccessException
689      * @throws InvocationTargetException
690      * @throws NoSuchMethodException
691      */
692     public static void materializeUpdateableCollections(
693             Object bo) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
694         if (isNotNull(bo)) {
695             PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
696             for (int i = 0; i < propertyDescriptors.length; i++) {
697                 if (KRADServiceLocator.getPersistenceStructureService().hasCollection(bo.getClass(),
698                         propertyDescriptors[i].getName()) && KRADServiceLocator.getPersistenceStructureService()
699                         .isCollectionUpdatable(bo.getClass(), propertyDescriptors[i].getName())) {
700                     Collection updateableCollection = (Collection) getPropertyValue(bo,
701                             propertyDescriptors[i].getName());
702                     if ((updateableCollection != null) && ProxyHelper.isCollectionProxy(updateableCollection)) {
703                         materializeObjects(updateableCollection);
704                     }
705                 }
706             }
707         }
708     }
709 
710     /**
711      * Removes all query characters from a string.
712      *
713      * @param string
714      * @return Cleaned string
715      */
716     public static String clean(String string) {
717         for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) {
718             string = StringUtils.replace(string, op.op(), KRADConstants.EMPTY_STRING);
719         }
720         return string;
721     }
722 
723     /**
724      * Compares two {@link PersistableBusinessObject} instances for equality of type and key values.
725      *
726      * @param bo1
727      * @param bo2
728      * @return boolean indicating whether the two objects are equal.
729      */
730     public static boolean equalByKeys(PersistableBusinessObject bo1, PersistableBusinessObject bo2) {
731         boolean equal = true;
732 
733         if (bo1 == null && bo2 == null) {
734             equal = true;
735         } else if (bo1 == null || bo2 == null) {
736             equal = false;
737         } else if (!bo1.getClass().getName().equals(bo2.getClass().getName())) {
738             equal = false;
739         } else {
740             Map bo1Keys = KRADServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo1);
741             Map bo2Keys = KRADServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo2);
742             for (Iterator iter = bo1Keys.keySet().iterator(); iter.hasNext(); ) {
743                 String keyName = (String) iter.next();
744                 if (bo1Keys.get(keyName) != null && bo2Keys.get(keyName) != null) {
745                     if (!bo1Keys.get(keyName).toString().equals(bo2Keys.get(keyName).toString())) {
746                         equal = false;
747                     }
748                 } else {
749                     equal = false;
750                 }
751             }
752         }
753 
754         return equal;
755     }
756 
757     /**
758      * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the
759      * same key as the BO exists in the list.
760      *
761      * @param controlList - The list of items to check
762      * @param bo - The BO whose keys we are looking for in the controlList
763      * @return boolean
764      */
765     public static boolean collectionContainsObjectWithIdentitcalKey(
766             Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) {
767         boolean objectExistsInList = false;
768 
769         for (Iterator i = controlList.iterator(); i.hasNext(); ) {
770             if (equalByKeys((PersistableBusinessObject) i.next(), bo)) {
771                 return true;
772             }
773         }
774 
775         return objectExistsInList;
776     }
777 
778     /**
779      * Compares a business object with a Collection of {@link PersistableBusinessObject}s to count how many have the
780      * same key as the BO.
781      *
782      * @param collection - The collection of items to check
783      * @param bo - The BO whose keys we are looking for in the collection
784      * @return how many have the same keys
785      */
786     public static int countObjectsWithIdentitcalKey(Collection<? extends PersistableBusinessObject> collection,
787             PersistableBusinessObject bo) {
788         // todo: genericize collectionContainsObjectWithIdentitcalKey() to leverage this method?
789         int n = 0;
790         for (PersistableBusinessObject item : collection) {
791             if (equalByKeys(item, bo)) {
792                 n++;
793             }
794         }
795         return n;
796     }
797 
798     /**
799      * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the
800      * same key as the BO exists in the list. If it
801      * does, the item is removed from the List. This is functionally similar to List.remove() that operates only on Key
802      * values.
803      *
804      * @param controlList - The list of items to check
805      * @param bo - The BO whose keys we are looking for in the controlList
806      */
807 
808     public static void removeObjectWithIdentitcalKey(Collection<? extends PersistableBusinessObject> controlList,
809             PersistableBusinessObject bo) {
810         for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext(); ) {
811             PersistableBusinessObject listBo = i.next();
812             if (equalByKeys(listBo, bo)) {
813                 i.remove();
814             }
815         }
816     }
817 
818     /**
819      * Compares a business object with a List of BOs to determine if an object with the same key as the BO exists in the
820      * list. If it
821      * does, the item is returned.
822      *
823      * @param controlList - The list of items to check
824      * @param bo - The BO whose keys we are looking for in the controlList
825      */
826 
827     public static BusinessObject retrieveObjectWithIdentitcalKey(
828             Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) {
829         BusinessObject returnBo = null;
830 
831         for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext(); ) {
832             PersistableBusinessObject listBo = i.next();
833             if (equalByKeys(listBo, bo)) {
834                 returnBo = listBo;
835             }
836         }
837 
838         return returnBo;
839     }
840 
841     /**
842      * Determines if a given string could represent a nested attribute of an object.
843      *
844      * @param attributeName
845      * @return true if the attribute is nested
846      */
847     public static boolean isNestedAttribute(String attributeName) {
848         boolean isNested = false;
849 
850         if (StringUtils.contains(attributeName, ".")) {
851             isNested = true;
852         }
853 
854         return isNested;
855     }
856 
857     /**
858      * Returns the prefix of a nested attribute name, or the empty string if the attribute name is not nested.
859      *
860      * @param attributeName
861      * @return everything BEFORE the last "." character in attributeName
862      */
863     public static String getNestedAttributePrefix(String attributeName) {
864         String prefix = "";
865 
866         if (StringUtils.contains(attributeName, ".")) {
867             prefix = StringUtils.substringBeforeLast(attributeName, ".");
868         }
869 
870         return prefix;
871     }
872 
873     /**
874      * Returns the primitive part of an attribute name string.
875      *
876      * @param attributeName
877      * @return everything AFTER the last "." character in attributeName
878      */
879     public static String getNestedAttributePrimitive(String attributeName) {
880         String primitive = attributeName;
881 
882         if (StringUtils.contains(attributeName, ".")) {
883             primitive = StringUtils.substringAfterLast(attributeName, ".");
884         }
885 
886         return primitive;
887     }
888 
889     /**
890      * This method is a OJB Proxy-safe way to test for null on a proxied object that may or may not be materialized yet.
891      * It is safe
892      * to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this will
893      * force a
894      * materialization of the proxy if the object is a proxy and unmaterialized.
895      *
896      * @param object - any object, proxied or not, materialized or not
897      * @return true if the object (or underlying materialized object) is null, false otherwise
898      */
899     public static boolean isNull(Object object) {
900 
901         // regardless, if its null, then its null
902         if (object == null) {
903             return true;
904         }
905 
906         // only try to materialize the object to see if its null if this is a
907         // proxy object
908         if (ProxyHelper.isProxy(object) || ProxyHelper.isCollectionProxy(object)) {
909             if (ProxyHelper.getRealObject(object) == null) {
910                 return true;
911             }
912         }
913 
914         // JPA does not provide a way to determine if an object is a proxy, instead we invoke
915         // the equals method and catch an EntityNotFoundException
916         try {
917             object.equals(null);
918         } catch (EntityNotFoundException e) {
919             return true;
920         }
921 
922         return false;
923     }
924 
925     /**
926      * This method is a OJB Proxy-safe way to test for notNull on a proxied object that may or may not be materialized
927      * yet. It is
928      * safe to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this
929      * will force a
930      * materialization of the proxy if the object is a proxy and unmaterialized.
931      *
932      * @param object - any object, proxied or not, materialized or not
933      * @return true if the object (or underlying materialized object) is not null, true if its null
934      */
935     public static boolean isNotNull(Object object) {
936         return !ObjectUtils.isNull(object);
937     }
938 
939     /**
940      * Attempts to find the Class for the given potentially proxied object
941      *
942      * @param object the potentially proxied object to find the Class of
943      * @return the best Class which could be found for the given object
944      */
945     public static Class materializeClassForProxiedObject(Object object) {
946         if (object == null) {
947             return null;
948         }
949 
950         if (object instanceof HibernateProxy) {
951             final Class realClass = ((HibernateProxy) object).getHibernateLazyInitializer().getPersistentClass();
952             return realClass;
953         }
954 
955         if (ProxyHelper.isProxy(object) || ProxyHelper.isCollectionProxy(object)) {
956             return ProxyHelper.getRealClass(object);
957         }
958 
959         return object.getClass();
960     }
961 
962     /**
963      * This method runs the ObjectUtils.isNotNull() method for each item in a list of BOs. ObjectUtils.isNotNull() will
964      * materialize
965      * the objects if they are currently OJB proxies.
966      *
967      * @param possiblyProxiedObjects - a Collection of objects that may be proxies
968      */
969     public static void materializeObjects(Collection possiblyProxiedObjects) {
970         for (Iterator i = possiblyProxiedObjects.iterator(); i.hasNext(); ) {
971             ObjectUtils.isNotNull(i.next());
972         }
973     }
974 
975     /**
976      * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the
977      * passed-in BO
978      * object. It will do it down to the specified depth. An IllegalArgumentException will be thrown if the bo object
979      * passed in is
980      * itself a non-materialized proxy object. If the bo passed in has no proxied sub-objects, then the object will not
981      * be modified,
982      * and no errors will be thrown. WARNING: Be careful using depth any greater than 2. The number of DB hits, time,
983      * and memory
984      * consumed grows exponentially with each additional increment to depth. Make sure you really need that depth before
985      * doing so.
986      *
987      * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be
988      * modified in place.
989      * @param depth int Value 0-5 indicating how deep to recurse the materialization. If a zero (0) is passed in, then
990      * no work will
991      * be done.
992      */
993     public static void materializeSubObjectsToDepth(PersistableBusinessObject bo, int depth) {
994         if (bo == null) {
995             throw new IllegalArgumentException("The bo passed in was null.");
996         }
997         if (depth < 0 || depth > 5) {
998             throw new IllegalArgumentException("The depth passed in was out of bounds.  Only values "
999                     + "between 0 and 5, inclusively, are allowed.");
1000         }
1001 
1002         // if depth is zero, then we're done recursing and can just exit
1003         if (depth == 0) {
1004             return;
1005         }
1006 
1007         // deal with the possibility that the bo passed in (ie, the parent object) is an un-materialized proxy
1008         if (ProxyHelper.isProxy(bo)) {
1009             if (!ProxyHelper.isMaterialized(bo)) {
1010                 throw new IllegalArgumentException("The bo passed in is an un-materialized proxy, and cannot be used.");
1011             }
1012         }
1013 
1014         // get the list of reference objects hanging off the parent BO
1015         if (KRADServiceLocator.getPersistenceStructureService().isPersistable(bo.getClass())) {
1016             Map<String, Class> references =
1017                     KRADServiceLocator.getPersistenceStructureService().listReferenceObjectFields(bo);
1018 
1019             // initialize our in-loop objects
1020             String referenceName = "";
1021             Class referenceClass = null;
1022             Object referenceValue = null;
1023             Object realReferenceValue = null;
1024 
1025             // for each reference object on the parent bo
1026             for (Iterator iter = references.keySet().iterator(); iter.hasNext(); ) {
1027                 referenceName = (String) iter.next();
1028                 referenceClass = references.get(referenceName);
1029 
1030                 // if its a proxy, replace it with a non-proxy
1031                 referenceValue = getPropertyValue(bo, referenceName);
1032                 if (referenceValue != null) {
1033                     if (ProxyHelper.isProxy(referenceValue)) {
1034                         realReferenceValue = ProxyHelper.getRealObject(referenceValue);
1035                         if (realReferenceValue != null) {
1036                             try {
1037                                 setObjectProperty(bo, referenceName, referenceClass, realReferenceValue);
1038                             } catch (FormatException e) {
1039                                 throw new RuntimeException(
1040                                         "FormatException: could not set the property '" + referenceName + "'.", e);
1041                             } catch (IllegalAccessException e) {
1042                                 throw new RuntimeException(
1043                                         "IllegalAccessException: could not set the property '" + referenceName + "'.",
1044                                         e);
1045                             } catch (InvocationTargetException e) {
1046                                 throw new RuntimeException("InvocationTargetException: could not set the property '"
1047                                         + referenceName
1048                                         + "'.", e);
1049                             } catch (NoSuchMethodException e) {
1050                                 throw new RuntimeException(
1051                                         "NoSuchMethodException: could not set the property '" + referenceName + "'.",
1052                                         e);
1053                             }
1054                         }
1055                     }
1056 
1057                     // recurse down through this reference object
1058                     if (realReferenceValue instanceof PersistableBusinessObject && depth > 1) {
1059                         materializeSubObjectsToDepth((PersistableBusinessObject) realReferenceValue, depth - 1);
1060                     }
1061                 }
1062 
1063             }
1064         }
1065     }
1066 
1067     /**
1068      * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the
1069      * passed-in BO
1070      * object. It will do it just three levels down. In other words, it will only materialize the objects that are
1071      * direct members of
1072      * the bo, objects that are direct members of those bos, that one more time, and no further down. An
1073      * IllegalArgumentException
1074      * will be thrown if the bo object passed in is itself a non-materialized proxy object. If the bo passed in has no
1075      * proxied
1076      * sub-objects, then the object will not be modified, and no errors will be thrown.
1077      *
1078      * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be
1079      * modified in place.
1080      */
1081     public static void materializeAllSubObjects(PersistableBusinessObject bo) {
1082         materializeSubObjectsToDepth(bo, 3);
1083     }
1084 
1085     /**
1086      * This method safely extracts either simple values OR nested values. For example, if the bo is SubAccount, and the
1087      * fieldName is
1088      * a21SubAccount.subAccountTypeCode, this thing makes sure it gets the value off the very end attribute, no matter
1089      * how deeply
1090      * nested it is. The code would be slightly simpler if this was done recursively, but this is safer, and consumes a
1091      * constant
1092      * amount of memory, no matter how deeply nested it goes.
1093      *
1094      * @param bo
1095      * @param fieldName
1096      * @return The field value if it exists. If it doesnt, and the name is invalid, and
1097      */
1098     public static Object getNestedValue(Object bo, String fieldName) {
1099 
1100         if (bo == null) {
1101             throw new IllegalArgumentException("The bo passed in was null.");
1102         }
1103         if (StringUtils.isBlank(fieldName)) {
1104             throw new IllegalArgumentException("The fieldName passed in was blank.");
1105         }
1106 
1107         // okay, this section of code is to handle sub-object values, like
1108         // SubAccount.a21SubAccount.subAccountTypeCode. it basically walks
1109         // through the period-delimited list of names, and ends up with the
1110         // final value.
1111         String[] fieldNameParts = fieldName.split("\\.");
1112         Object currentObject = null;
1113         Object priorObject = bo;
1114         for (int i = 0; i < fieldNameParts.length; i++) {
1115             String fieldNamePart = fieldNameParts[i];
1116 
1117             try {
1118                 if (fieldNamePart.indexOf("]") > 0) {
1119                     currentObject = PropertyUtils.getIndexedProperty(priorObject, fieldNamePart);
1120                 } else {
1121                     currentObject = PropertyUtils.getSimpleProperty(priorObject, fieldNamePart);
1122                 }
1123             } catch (IllegalAccessException e) {
1124                 throw new RuntimeException("Caller does not have access to the property accessor method.", e);
1125             } catch (InvocationTargetException e) {
1126                 throw new RuntimeException("Property accessor method threw an exception.", e);
1127             } catch (NoSuchMethodException e) {
1128                 throw new RuntimeException("The accessor method requested for this property cannot be found.", e);
1129             }
1130 
1131             // materialize the proxy, if it is a proxy
1132             if (ProxyHelper.isProxy(currentObject)) {
1133                 currentObject = ProxyHelper.getRealObject(currentObject);
1134             }
1135 
1136             // if a node or the leaf is null, then we're done, there's no need to
1137             // continue accessing null things
1138             if (currentObject == null) {
1139                 return currentObject;
1140             }
1141 
1142             priorObject = currentObject;
1143         }
1144         return currentObject;
1145     }
1146 
1147     /**
1148      * This method safely creates a object from a class
1149      * Convenience method to create new object and throw a runtime exception if it cannot
1150      * If the class is an {@link ExternalizableBusinessObject}, this method will determine the interface for the EBO and
1151      * query the
1152      * appropriate module service to create a new instance.
1153      *
1154      * @param clazz
1155      * @return a newInstance() of clazz
1156      */
1157     public static Object createNewObjectFromClass(Class clazz) {
1158         if (clazz == null) {
1159             throw new RuntimeException("BO class was passed in as null");
1160         }
1161         try {
1162             if (ExternalizableBusinessObject.class.isAssignableFrom(clazz)) {
1163                 Class eboInterface =
1164                         ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(clazz);
1165                 ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(
1166                         eboInterface);
1167                 return moduleService.createNewObjectFromExternalizableClass(eboInterface);
1168             } else {
1169                 return clazz.newInstance();
1170             }
1171         } catch (Exception e) {
1172             throw new RuntimeException("Error occured while trying to create a new instance for class " + clazz, e);
1173         }
1174     }
1175 
1176     /**
1177      * Return whether or not an attribute is writeable. This method is aware that that Collections may be involved and
1178      * handles them
1179      * consistently with the way in which OJB handles specifying the attributes of elements of a Collection.
1180      *
1181      * @param object
1182      * @param property
1183      * @return
1184      * @throws IllegalArgumentException
1185      */
1186     public static boolean isWriteable(Object object, String property,
1187             PersistenceStructureService persistenceStructureService) throws IllegalArgumentException {
1188         if (null == object || null == property) {
1189             throw new IllegalArgumentException("Cannot check writeable status with null arguments.");
1190         }
1191 
1192     	// Try the easy way.
1193     	try {
1194     		if (!(PropertyUtils.isWriteable(object, property))) {
1195     			// If that fails lets try to be a bit smarter, understanding that Collections may be involved.
1196     			return isWriteableHelper(object, property, persistenceStructureService);
1197     		} else {
1198     			return true;
1199     		}
1200     	} catch (NestedNullException nestedNullException) {
1201     		// If a NestedNullException is thrown then the property has a null
1202     		// value.  Call the helper to find the class of the property and
1203     		// get a newInstance of it.
1204     		return isWriteableHelper(object, property, persistenceStructureService);
1205     	}
1206     }
1207 
1208     /**
1209      * This method handles the cases where PropertyUtils.isWriteable is not
1210      * sufficient.  It handles cases where the parameter in question is a
1211      * collection or if the parameter value is null.
1212      * @param object
1213      * @param property
1214      * @param persistenceStructureService
1215      * @return
1216      */
1217     private static boolean isWriteableHelper(Object object, String property, PersistenceStructureService persistenceStructureService) {
1218     	if (property.contains(".")) {
1219             String propertyName = StringUtils.substringBefore(property, ".");
1220 
1221             // Get the type of the attribute.
1222             Class<?> c = ObjectUtils.getPropertyType(object, propertyName, persistenceStructureService);
1223 
1224             if (c != null) {
1225                 Object i = null;
1226 
1227                 // If the next level is a Collection, look into the collection, to find out what type its elements are.
1228                 if (Collection.class.isAssignableFrom(c)) {
1229                     Map<String, Class> m = persistenceStructureService.listCollectionObjectTypes(object.getClass());
1230                     c = m.get(propertyName);
1231                 }
1232 
1233                 // Look into the attribute class to see if it is writeable.
1234                 try {
1235                     i = c.newInstance();
1236                     return isWriteable(i, StringUtils.substringAfter(property, "."), persistenceStructureService);
1237                 } catch (Exception ex) {
1238                     LOG.error("Skipping Criteria: " + property + " - Unable to instantiate class : " + c.getName(), ex);
1239                 }
1240             } else {
1241                 LOG.error("Skipping Criteria: " + property + " - Unable to determine class for object: "
1242                         + object.getClass().getName() + " - " + propertyName);
1243             }
1244         }
1245     	return false;
1246     }
1247 
1248     /**
1249      * Helper method for creating a new instance of the given class
1250      *
1251      * @param clazz - class of object to create
1252      * @return T object of type given by the clazz parameter
1253      */
1254     public static <T> T newInstance(Class<T> clazz) {
1255         T object = null;
1256         try {
1257             object = clazz.newInstance();
1258         } catch (InstantiationException e) {
1259             LOG.error("Unable to create new instance of class: " + clazz.getName());
1260             throw new RuntimeException(e);
1261         } catch (IllegalAccessException e) {
1262             LOG.error("Unable to create new instance of class: " + clazz.getName());
1263             throw new RuntimeException(e);
1264         }
1265 
1266         return object;
1267     }
1268 
1269     /**
1270      * Retrieves all fields including the inherited fields for a given class. The recursion stops if either  Object class is reached
1271      * or if stopAt is reached first.
1272      *
1273      * @param fields  List of fields (public, private and protected)
1274      * @param type   Class from which fields retrieval has to start
1275      * @param stopAt Parent class where the recursion should stop
1276      * @return
1277      */
1278     public static List<Field> getAllFields(List<Field> fields, Class<?> type, Class<?> stopAt) {
1279         for (Field field : type.getDeclaredFields()) {
1280             fields.add(field);
1281         }
1282 
1283         if (type.getSuperclass() != null && !type.getName().equals(stopAt.getName())) {
1284             fields = getAllFields(fields, type.getSuperclass(), stopAt);
1285         }
1286 
1287         return fields;
1288     }
1289 
1290 }