Java tutorial
/** * Copyright 2005-2014 The Kuali Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl2.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.rice.krad.util; import org.apache.commons.beanutils.NestedNullException; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.ojb.broker.core.proxy.ProxyHelper; import org.kuali.rice.core.api.CoreApiServiceLocator; import org.kuali.rice.core.api.encryption.EncryptionService; import org.kuali.rice.core.api.search.SearchOperator; import org.kuali.rice.core.api.util.cache.CopiedObject; import org.kuali.rice.core.web.format.CollectionFormatter; import org.kuali.rice.core.web.format.FormatException; import org.kuali.rice.core.web.format.Formatter; import org.kuali.rice.kns.service.KNSServiceLocator; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.bo.ExternalizableBusinessObject; import org.kuali.rice.krad.bo.PersistableBusinessObject; import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension; import org.kuali.rice.krad.exception.ClassNotPersistableException; import org.kuali.rice.krad.maintenance.MaintenanceUtils; import org.kuali.rice.krad.service.KRADServiceLocatorWeb; import org.kuali.rice.krad.service.ModuleService; import org.kuali.rice.krad.service.PersistenceStructureService; import javax.persistence.EntityNotFoundException; import java.beans.PropertyDescriptor; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Contains various Object, Proxy, and serialization utilities * * @author Kuali Rice Team (rice.collab@kuali.org) * * @deprecated use new KRAD data framework instead */ @Deprecated public final class ObjectUtils { private static final Logger LOG = Logger.getLogger(ObjectUtils.class); private ObjectUtils() { throw new UnsupportedOperationException("do not call"); } /** * Uses Serialization mechanism to create a deep copy of the given Object. As a special case, deepCopy of null * returns null, * just to make using this method simpler. For a detailed discussion see: * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html * * @param src * @return deep copy of the given Serializable */ public static Serializable deepCopy(Serializable src) { CopiedObject co = deepCopyForCaching(src); return co.getContent(); } /** * Uses Serialization mechanism to create a deep copy of the given Object, and returns a CacheableObject instance * containing the * deepCopy and its size in bytes. As a special case, deepCopy of null returns a cacheableObject containing null and * a size of * 0, to make using this method simpler. For a detailed discussion see: * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html * * @param src * @return CopiedObject containing a deep copy of the given Serializable and its size in bytes */ public static CopiedObject deepCopyForCaching(Serializable src) { CopiedObject co = new CopiedObject(); co.setContent(src); return co; } /** * Converts the object to a byte array using the output stream. * * @param object * @return byte array of the object */ public static byte[] toByteArray(Object object) throws Exception { ObjectOutputStream oos = null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); // A oos = new ObjectOutputStream(bos); // B // serialize and pass the object oos.writeObject(object); // C // oos.flush(); // D return bos.toByteArray(); } catch (Exception e) { LOG.warn("Exception in ObjectUtil = " + e); throw (e); } finally { if (oos != null) { oos.close(); } } } /** * reconsitiutes the object that was converted into a byte array by toByteArray * * @param bytes * @return * @throws Exception */ public static Object fromByteArray(byte[] bytes) throws Exception { ObjectInputStream ois = null; try { ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ois = new ObjectInputStream(bis); Object obj = ois.readObject(); return obj; } catch (Exception e) { LOG.warn("Exception in ObjectUtil = " + e); throw (e); } finally { if (ois != null) { ois.close(); } } } /** * use MD5 to create a one way hash of an object * * @param object * @return */ public static String getMD5Hash(Object object) throws Exception { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(toByteArray(object)); return new String(md.digest()); } catch (Exception e) { LOG.warn(e); throw e; } } /** * Creates a new instance of a given BusinessObject, copying fields specified in template from the given source BO. * For example, * this can be used to create an AccountChangeDetail based on a particular Account. * * @param template a map defining the relationships between the fields of the newly created BO, and the source BO. * For each K (key), V (value) * entry, the value of property V on the source BO will be assigned to the K property of the newly created BO * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws FormatException * @see MaintenanceUtils */ public static BusinessObject createHybridBusinessObject(Class businessObjectClass, BusinessObject source, Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { BusinessObject obj = null; try { ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService() .getResponsibleModuleService(businessObjectClass); if (moduleService != null && moduleService.isExternalizable(businessObjectClass)) { obj = moduleService.createNewObjectFromExternalizableClass(businessObjectClass); } else { obj = (BusinessObject) businessObjectClass.newInstance(); } } catch (Exception e) { throw new RuntimeException("Cannot instantiate " + businessObjectClass.getName(), e); } createHybridBusinessObject(obj, source, template); return obj; } public static void createHybridBusinessObject(BusinessObject businessObject, BusinessObject source, Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { for (String name : template.keySet()) { String sourcePropertyName = template.get(name); setObjectProperty(businessObject, name, easyGetPropertyType(source, sourcePropertyName), getPropertyValue(source, sourcePropertyName)); } } /** * This method simply uses PojoPropertyUtilsBean logic to get the Class of a Class property. * This method does not have any of the logic needed to obtain the Class of an element of a Collection specified in * the DataDictionary. * * @param object An instance of the Class of which we're trying to get the property Class. * @param propertyName The name of the property. * @return * @throws IllegalAccessException * @throws NoSuchMethodException * @throws InvocationTargetException */ static public Class easyGetPropertyType(Object object, String propertyName) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { // FIXME (laran) This dependence should be inverted. Instead of having a core class // depend on PojoPropertyUtilsBean, which is in the web layer, the web layer // should depend downward to the core. return PropertyUtils.getPropertyType(object, propertyName); } /** * Returns the type of the property in the object. This implementation is not smart enough to look through a * Collection to get the property type * of an attribute of an element in the collection. * <p/> * NOTE: A patch file attached to https://test.kuali.org/jira/browse/KULRNE-4435 contains a modified version of this * method which IS smart enough * to look through Collections. This patch is currently under review. * * @param object An instance of the Class for which we're trying to get the property type. * @param propertyName The name of the property of the Class the Class of which we're trying to get. Dot notation is * used to separate properties. * TODO: The rules about this dot notation needs to be explained in Confluence using examples. * @param persistenceStructureService Needed to get the type of elements in a Collection from OJB. * @return Object will be null if any parent property for the given property is null. */ @Deprecated public static Class getPropertyType(Object object, String propertyName, PersistenceStructureService persistenceStructureService) { if (object == null || propertyName == null) { throw new RuntimeException("Business object and property name can not be null"); } Class propertyType = null; try { try { // Try to simply use the default or simple way of getting the property type. propertyType = PropertyUtils.getPropertyType(object, propertyName); } catch (IllegalArgumentException ex) { // swallow the exception, propertyType stays null } catch (NoSuchMethodException nsme) { // swallow the exception, propertyType stays null } // if the property type as determined from the object is PersistableBusinessObject, // then this must be an extension attribute -- attempt to get the property type from the // persistence structure service if (propertyType != null && propertyType.equals(PersistableBusinessObjectExtension.class)) { propertyType = persistenceStructureService .getBusinessObjectAttributeClass(ProxyHelper.getRealClass(object), propertyName); } // If the easy way didn't work ... if (null == propertyType && -1 != propertyName.indexOf('.')) { if (null == persistenceStructureService) { LOG.info( "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."); } else { String prePeriod = StringUtils.substringBefore(propertyName, "."); String postPeriod = StringUtils.substringAfter(propertyName, "."); Class prePeriodClass = getPropertyType(object, prePeriod, persistenceStructureService); Object prePeriodClassInstance = prePeriodClass.newInstance(); propertyType = getPropertyType(prePeriodClassInstance, postPeriod, persistenceStructureService); } } else if (Collection.class.isAssignableFrom(propertyType)) { Map<String, Class> map = persistenceStructureService.listCollectionObjectTypes(object.getClass()); propertyType = map.get(propertyName); } } catch (Exception e) { LOG.debug("unable to get property type for " + propertyName + " " + e.getMessage()); // continue and return null for propertyType } return propertyType; } /** * Returns the value of the property in the object. * * @param businessObject * @param propertyName * @return Object will be null if any parent property for the given property is null. * * @deprecated use {@link org.kuali.rice.krad.data.DataObjectWrapper#getPropertyValueNullSafe(String)} instead */ @Deprecated public static Object getPropertyValue(Object businessObject, String propertyName) { if (businessObject == null || propertyName == null) { throw new RuntimeException("Business object and property name can not be null"); } Object propertyValue = null; try { propertyValue = PropertyUtils.getProperty(businessObject, propertyName); } catch (NestedNullException e) { // continue and return null for propertyValue } catch (IllegalAccessException e1) { LOG.error("error getting property value for " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage()); throw new RuntimeException("error getting property value for " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage(), e1); } catch (InvocationTargetException e1) { // continue and return null for propertyValue } catch (NoSuchMethodException e1) { LOG.error("error getting property value for " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage()); throw new RuntimeException("error getting property value for " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage(), e1); } return propertyValue; } /** * Gets the property value from the business object, then based on the value * type select a formatter and format the value * * @param businessObject BusinessObject instance that contains the property * @param propertyName Name of property in BusinessObject to get value for * @param formatter Default formatter to use (or null) * @return Formatted property value as String, or empty string if value is null */ @Deprecated public static String getFormattedPropertyValue(BusinessObject businessObject, String propertyName, Formatter formatter) { String propValue = KRADConstants.EMPTY_STRING; Object prop = ObjectUtils.getPropertyValue(businessObject, propertyName); if (formatter == null) { propValue = formatPropertyValue(prop); } else { final Object formattedValue = formatter.format(prop); if (formattedValue != null) { propValue = String.valueOf(formattedValue); } } return propValue; } /** * References the data dictionary to find any registered formatter class then if not found checks for associated * formatter for the * property type. Value is then formatted using the found Formatter * * @param businessObject BusinessObject instance that contains the property * @param propertyName Name of property in BusinessObject to get value for * @return Formatted property value as String, or empty string if value is null */ public static String getFormattedPropertyValueUsingDataDictionary(BusinessObject businessObject, String propertyName) { Formatter formatter = getFormatterWithDataDictionary(businessObject, propertyName); return getFormattedPropertyValue(businessObject, propertyName, formatter); } /** * Based on the value type selects a formatter and returns the formatted * value as a string * * @param propertyValue Object value to be formatted * @return formatted value as a String */ public static String formatPropertyValue(Object propertyValue) { Object propValue = KRADConstants.EMPTY_STRING; Formatter formatter = null; if (propertyValue != null) { if (propertyValue instanceof Collection) { formatter = new CollectionFormatter(); } else { formatter = Formatter.getFormatter(propertyValue.getClass()); } propValue = formatter != null ? formatter.format(propertyValue) : propertyValue; } return propValue != null ? String.valueOf(propValue) : KRADConstants.EMPTY_STRING; } /** * Sets the property of an object with the given value. Converts using the formatter of the type for the property. * Note: propertyType does not need passed, is found by util method. */ public static void setObjectProperty(Object bo, String propertyName, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Class propertyType = easyGetPropertyType(bo, propertyName); setObjectProperty(bo, propertyName, propertyType, propertyValue); } /** * Sets the property of an object with the given value. Converts using the formatter of the given type if one is * found. * * @param bo * @param propertyName * @param propertyType * @param propertyValue * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public static void setObjectProperty(Object bo, String propertyName, Class propertyType, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // reformat propertyValue, if necessary boolean reformat = false; if (propertyType != null) { if (propertyValue != null && propertyType.isAssignableFrom(String.class)) { // always reformat if the destination is a String reformat = true; } else if (propertyValue != null && !propertyType.isAssignableFrom(propertyValue.getClass())) { // otherwise, only reformat if the propertyValue can't be assigned into the property reformat = true; } // attempting to set boolean fields to null throws an exception, set to false instead if (boolean.class.isAssignableFrom(propertyType) && propertyValue == null) { propertyValue = false; } } Formatter formatter = getFormatterWithDataDictionary(bo, propertyName); if (reformat && formatter != null) { LOG.debug("reformatting propertyValue using Formatter " + formatter.getClass().getName()); propertyValue = formatter.convertFromPresentationFormat(propertyValue); } // set property in the object PropertyUtils.setNestedProperty(bo, propertyName, propertyValue); } /** * Sets the property of an object with the given value. Converts using the given formatter, if it isn't null. * * @param formatter * @param bo * @param propertyName * @param type * @param propertyValue * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public static void setObjectProperty(Formatter formatter, Object bo, String propertyName, Class type, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // convert value using formatter for type if (formatter != null) { propertyValue = formatter.convertFromPresentationFormat(propertyValue); } // KULRICE-8412 Changes so that values passed back through via the URL such as // lookups are decrypted where applicable if (propertyValue instanceof String) { String propVal = (String) propertyValue; if (propVal.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { propVal = StringUtils.removeEnd(propVal, EncryptionService.ENCRYPTION_POST_PREFIX); } if (KNSServiceLocator.getBusinessObjectAuthorizationService() .attributeValueNeedsToBeEncryptedOnFormsAndLinks(bo.getClass(), propertyName)) { try { if (CoreApiServiceLocator.getEncryptionService().isEnabled()) { propertyValue = CoreApiServiceLocator.getEncryptionService().decrypt(propVal); } } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } } // set property in the object PropertyUtils.setNestedProperty(bo, propertyName, propertyValue); } /** * Returns a Formatter instance for the given property name in the given given business object. First * checks if a formatter is defined for the attribute in the data dictionary, is not found then returns * the registered formatter for the property type in Formatter * * @param bo - business object instance with property to get formatter for * @param propertyName - name of property to get formatter for * @return Formatter instance */ @Deprecated public static Formatter getFormatterWithDataDictionary(Object bo, String propertyName) { Formatter formatter = null; Class boClass = bo.getClass(); String boPropertyName = propertyName; // for collections, formatter should come from property on the collection type if (StringUtils.contains(propertyName, "]")) { Object collectionParent = getNestedValue(bo, StringUtils.substringBeforeLast(propertyName, "].") + "]"); if (collectionParent != null) { boClass = collectionParent.getClass(); boPropertyName = StringUtils.substringAfterLast(propertyName, "]."); } } Class<? extends Formatter> formatterClass = KRADServiceLocatorWeb.getDataDictionaryService() .getAttributeFormatter(boClass, boPropertyName); if (formatterClass == null) { try { formatterClass = Formatter.findFormatter(getPropertyType(boClass.newInstance(), boPropertyName, KNSServiceLocator.getPersistenceStructureService())); } catch (InstantiationException e) { LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName); // just swallow the exception and let formatter be null } catch (IllegalAccessException e) { LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName); // just swallow the exception and let formatter be null } } if (formatterClass != null) { try { formatter = formatterClass.newInstance(); } catch (Exception e) { throw new RuntimeException( "cannot create new instance of formatter class " + formatterClass.toString(), e); } } return formatter; } /** * Recursive; sets all occurences of the property in the object, its nested objects and its object lists with the * given value. * * @param bo * @param propertyName * @param type * @param propertyValue * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public static void setObjectPropertyDeep(Object bo, String propertyName, Class type, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // Base return cases to avoid null pointers & infinite loops if (isNull(bo) || !PropertyUtils.isReadable(bo, propertyName) || (propertyValue != null && propertyValue.equals(getPropertyValue(bo, propertyName))) || (type != null && !type.equals(easyGetPropertyType(bo, propertyName)))) { return; } // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval materializeUpdateableCollections(bo); // Set the property in the BO setObjectProperty(bo, propertyName, type, propertyValue); // Now drill down and check nested BOs and BO lists PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass()); for (int i = 0; i < propertyDescriptors.length; i++) { PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; // Business Objects if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo, propertyDescriptor.getName())) { Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName()); if (nestedBo instanceof BusinessObject) { setObjectPropertyDeep(nestedBo, propertyName, type, propertyValue); } } // Lists else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName()) != null) { List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName()); for (Object listedBo : propertyList) { if (listedBo != null && listedBo instanceof BusinessObject) { setObjectPropertyDeep(listedBo, propertyName, type, propertyValue); } } // end for } } // end for } /* * 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. */ public static void setObjectPropertyDeep(Object bo, String propertyName, Class type, Object propertyValue, int depth) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // Base return cases to avoid null pointers & infinite loops if (depth == 0 || isNull(bo) || !PropertyUtils.isReadable(bo, propertyName)) { return; } // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval try { materializeUpdateableCollections(bo); } catch (ClassNotPersistableException ex) { //Not all classes will be persistable in a collection. For e.g. externalizable business objects. LOG.info("Not persistable dataObjectClass: " + bo.getClass().getName() + ", field: " + propertyName); } // Set the property in the BO setObjectProperty(bo, propertyName, type, propertyValue); // Now drill down and check nested BOs and BO lists PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass()); for (int i = 0; i < propertyDescriptors.length; i++) { PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; // Business Objects if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo, propertyDescriptor.getName())) { Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName()); if (nestedBo instanceof BusinessObject) { setObjectPropertyDeep(nestedBo, propertyName, type, propertyValue, depth - 1); } } // Lists else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName()) != null) { List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName()); // Complete Hibernate Hack - fetches the proxied List into the PersistenceContext and sets it on the BO Copy. // if (propertyList instanceof PersistentBag) { // try { // PersistentBag bag = (PersistentBag) propertyList; // PersistableBusinessObject pbo = // (PersistableBusinessObject) KRADServiceLocator.getEntityManagerFactory() // .createEntityManager().find(bo.getClass(), bag.getKey()); // Field field1 = pbo.getClass().getDeclaredField(propertyDescriptor.getName()); // Field field2 = bo.getClass().getDeclaredField(propertyDescriptor.getName()); // field1.setAccessible(true); // field2.setAccessible(true); // field2.set(bo, field1.get(pbo)); // propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName()); // ; // } catch (Exception e) { // LOG.error(e.getMessage(), e); // } // } // End Complete Hibernate Hack for (Object listedBo : propertyList) { if (listedBo != null && listedBo instanceof BusinessObject) { setObjectPropertyDeep(listedBo, propertyName, type, propertyValue, depth - 1); } } // end for } } // end for } /** * This method checks for updateable collections on the business object provided and materializes the corresponding * collection proxies * * @param bo The business object for which you want unpdateable, proxied collections materialized * @throws FormatException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ public static void materializeUpdateableCollections(Object bo) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (isNotNull(bo)) { PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass()); for (int i = 0; i < propertyDescriptors.length; i++) { if (KNSServiceLocator.getPersistenceStructureService().hasCollection(bo.getClass(), propertyDescriptors[i].getName()) && KNSServiceLocator.getPersistenceStructureService().isCollectionUpdatable(bo.getClass(), propertyDescriptors[i].getName())) { Collection updateableCollection = (Collection) getPropertyValue(bo, propertyDescriptors[i].getName()); if ((updateableCollection != null) && ProxyHelper.isCollectionProxy(updateableCollection)) { materializeObjects(updateableCollection); } } } } } /** * Removes all query characters from a string. * * @param string * @return Cleaned string */ public static String clean(String string) { for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { string = StringUtils.replace(string, op.op(), KRADConstants.EMPTY_STRING); } return string; } /** * Compares two {@link PersistableBusinessObject} instances for equality of type and key values. * * @param bo1 * @param bo2 * @return boolean indicating whether the two objects are equal. * * @deprecated use {@link org.kuali.rice.krad.data.DataObjectWrapper#equalsByPrimaryKey(Object)} */ @Deprecated public static boolean equalByKeys(PersistableBusinessObject bo1, PersistableBusinessObject bo2) { boolean equal = true; if (bo1 == null && bo2 == null) { equal = true; } else if (bo1 == null || bo2 == null) { equal = false; } else if (!bo1.getClass().getName().equals(bo2.getClass().getName())) { equal = false; } else { Map bo1Keys = KNSServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo1); Map bo2Keys = KNSServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo2); for (Iterator iter = bo1Keys.keySet().iterator(); iter.hasNext();) { String keyName = (String) iter.next(); if (bo1Keys.get(keyName) != null && bo2Keys.get(keyName) != null) { if (!bo1Keys.get(keyName).toString().equals(bo2Keys.get(keyName).toString())) { equal = false; } } else { equal = false; } } } return equal; } /** * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the * same key as the BO exists in the list. * * @param controlList - The list of items to check * @param bo - The BO whose keys we are looking for in the controlList * @return boolean */ @Deprecated public static boolean collectionContainsObjectWithIdentitcalKey( Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) { boolean objectExistsInList = false; for (Iterator i = controlList.iterator(); i.hasNext();) { if (equalByKeys((PersistableBusinessObject) i.next(), bo)) { return true; } } return objectExistsInList; } /** * Compares a business object with a Collection of {@link PersistableBusinessObject}s to count how many have the * same key as the BO. * * @param collection - The collection of items to check * @param bo - The BO whose keys we are looking for in the collection * @return how many have the same keys */ public static int countObjectsWithIdentitcalKey(Collection<? extends PersistableBusinessObject> collection, PersistableBusinessObject bo) { // todo: genericize collectionContainsObjectWithIdentitcalKey() to leverage this method? int n = 0; for (PersistableBusinessObject item : collection) { if (equalByKeys(item, bo)) { n++; } } return n; } /** * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the * same key as the BO exists in the list. If it * does, the item is removed from the List. This is functionally similar to List.remove() that operates only on Key * values. * * @param controlList - The list of items to check * @param bo - The BO whose keys we are looking for in the controlList * * @deprecated there is no replacement for this method */ @Deprecated public static void removeObjectWithIdentitcalKey(Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) { for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext();) { PersistableBusinessObject listBo = i.next(); if (equalByKeys(listBo, bo)) { i.remove(); } } } /** * Compares a business object with a List of BOs to determine if an object with the same key as the BO exists in the * list. If it * does, the item is returned. * * @param controlList - The list of items to check * @param bo - The BO whose keys we are looking for in the controlList * * @deprecated there is no replacement for this method */ @Deprecated public static BusinessObject retrieveObjectWithIdentitcalKey( Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) { BusinessObject returnBo = null; for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext();) { PersistableBusinessObject listBo = i.next(); if (equalByKeys(listBo, bo)) { returnBo = listBo; } } return returnBo; } /** * Determines if a given string could represent a nested attribute of an object. * * @param attributeName * @return true if the attribute is nested */ public static boolean isNestedAttribute(String attributeName) { boolean isNested = false; if (StringUtils.contains(attributeName, ".")) { isNested = true; } return isNested; } /** * Returns the prefix of a nested attribute name, or the empty string if the attribute name is not nested. * * @param attributeName * @return everything BEFORE the last "." character in attributeName */ public static String getNestedAttributePrefix(String attributeName) { String prefix = ""; if (StringUtils.contains(attributeName, ".")) { prefix = StringUtils.substringBeforeLast(attributeName, "."); } return prefix; } /** * Returns the primitive part of an attribute name string. * * @param attributeName * @return everything AFTER the last "." character in attributeName */ public static String getNestedAttributePrimitive(String attributeName) { String primitive = attributeName; if (StringUtils.contains(attributeName, ".")) { primitive = StringUtils.substringAfterLast(attributeName, "."); } return primitive; } /** * This method is a OJB Proxy-safe way to test for null on a proxied object that may or may not be materialized yet. * It is safe * to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this will * force a * materialization of the proxy if the object is a proxy and unmaterialized. * * @param object - any object, proxied or not, materialized or not * @return true if the object (or underlying materialized object) is null, false otherwise */ public static boolean isNull(Object object) { // regardless, if its null, then its null if (object == null) { return true; } // only try to materialize the object to see if its null if this is a // proxy object if (ProxyHelper.isProxy(object) || ProxyHelper.isCollectionProxy(object)) { if (ProxyHelper.getRealObject(object) == null) { return true; } } // JPA does not provide a way to determine if an object is a proxy, instead we invoke // the equals method and catch an EntityNotFoundException try { object.equals(null); } catch (EntityNotFoundException e) { return true; } return false; } /** * This method is a OJB Proxy-safe way to test for notNull on a proxied object that may or may not be materialized * yet. It is * safe to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this * will force a * materialization of the proxy if the object is a proxy and unmaterialized. * * @param object - any object, proxied or not, materialized or not * @return true if the object (or underlying materialized object) is not null, true if its null */ public static boolean isNotNull(Object object) { return !ObjectUtils.isNull(object); } /** * Attempts to find the Class for the given potentially proxied object * * @param object the potentially proxied object to find the Class of * @return the best Class which could be found for the given object */ public static Class materializeClassForProxiedObject(Object object) { if (object == null) { return null; } // if (object instanceof HibernateProxy) { // final Class realClass = ((HibernateProxy) object).getHibernateLazyInitializer().getPersistentClass(); // return realClass; // } if (ProxyHelper.isProxy(object) || ProxyHelper.isCollectionProxy(object)) { return ProxyHelper.getRealClass(object); } return object.getClass(); } /** * This method runs the ObjectUtils.isNotNull() method for each item in a list of BOs. ObjectUtils.isNotNull() will * materialize * the objects if they are currently OJB proxies. * * @param possiblyProxiedObjects - a Collection of objects that may be proxies */ public static void materializeObjects(Collection possiblyProxiedObjects) { for (Iterator i = possiblyProxiedObjects.iterator(); i.hasNext();) { ObjectUtils.isNotNull(i.next()); } } /** * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the * passed-in BO * object. It will do it down to the specified depth. An IllegalArgumentException will be thrown if the bo object * passed in is * itself a non-materialized proxy object. If the bo passed in has no proxied sub-objects, then the object will not * be modified, * and no errors will be thrown. WARNING: Be careful using depth any greater than 2. The number of DB hits, time, * and memory * consumed grows exponentially with each additional increment to depth. Make sure you really need that depth before * doing so. * * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be * modified in place. * @param depth int Value 0-5 indicating how deep to recurse the materialization. If a zero (0) is passed in, then * no work will * be done. */ public static void materializeSubObjectsToDepth(PersistableBusinessObject bo, int depth) { if (bo == null) { throw new IllegalArgumentException("The bo passed in was null."); } if (depth < 0 || depth > 5) { throw new IllegalArgumentException("The depth passed in was out of bounds. Only values " + "between 0 and 5, inclusively, are allowed."); } // if depth is zero, then we're done recursing and can just exit if (depth == 0) { return; } // deal with the possibility that the bo passed in (ie, the parent object) is an un-materialized proxy if (ProxyHelper.isProxy(bo)) { if (!ProxyHelper.isMaterialized(bo)) { throw new IllegalArgumentException( "The bo passed in is an un-materialized proxy, and cannot be used."); } } // get the list of reference objects hanging off the parent BO if (KRADServiceLocatorWeb.getLegacyDataAdapter().isPersistable(bo.getClass())) { Map<String, Class> references = KNSServiceLocator.getPersistenceStructureService() .listReferenceObjectFields(bo); // initialize our in-loop objects String referenceName = ""; Class referenceClass = null; Object referenceValue = null; Object realReferenceValue = null; // for each reference object on the parent bo for (Iterator iter = references.keySet().iterator(); iter.hasNext();) { referenceName = (String) iter.next(); referenceClass = references.get(referenceName); // if its a proxy, replace it with a non-proxy referenceValue = getPropertyValue(bo, referenceName); if (referenceValue != null) { if (ProxyHelper.isProxy(referenceValue)) { realReferenceValue = ProxyHelper.getRealObject(referenceValue); if (realReferenceValue != null) { try { setObjectProperty(bo, referenceName, referenceClass, realReferenceValue); } catch (FormatException e) { throw new RuntimeException( "FormatException: could not set the property '" + referenceName + "'.", e); } catch (IllegalAccessException e) { throw new RuntimeException("IllegalAccessException: could not set the property '" + referenceName + "'.", e); } catch (InvocationTargetException e) { throw new RuntimeException("InvocationTargetException: could not set the property '" + referenceName + "'.", e); } catch (NoSuchMethodException e) { throw new RuntimeException("NoSuchMethodException: could not set the property '" + referenceName + "'.", e); } } } // recurse down through this reference object if (realReferenceValue instanceof PersistableBusinessObject && depth > 1) { materializeSubObjectsToDepth((PersistableBusinessObject) realReferenceValue, depth - 1); } } } } } /** * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the * passed-in BO * object. It will do it just three levels down. In other words, it will only materialize the objects that are * direct members of * the bo, objects that are direct members of those bos, that one more time, and no further down. An * IllegalArgumentException * will be thrown if the bo object passed in is itself a non-materialized proxy object. If the bo passed in has no * proxied * sub-objects, then the object will not be modified, and no errors will be thrown. * * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be * modified in place. */ public static void materializeAllSubObjects(PersistableBusinessObject bo) { materializeSubObjectsToDepth(bo, 3); } /** * This method safely extracts either simple values OR nested values. For example, if the bo is SubAccount, and the * fieldName is * a21SubAccount.subAccountTypeCode, this thing makes sure it gets the value off the very end attribute, no matter * how deeply * nested it is. The code would be slightly simpler if this was done recursively, but this is safer, and consumes a * constant * amount of memory, no matter how deeply nested it goes. * * @param bo * @param fieldName * @return The field value if it exists. If it doesnt, and the name is invalid, and */ public static Object getNestedValue(Object bo, String fieldName) { if (bo == null) { throw new IllegalArgumentException("The bo passed in was null."); } if (StringUtils.isBlank(fieldName)) { throw new IllegalArgumentException("The fieldName passed in was blank."); } // okay, this section of code is to handle sub-object values, like // SubAccount.a21SubAccount.subAccountTypeCode. it basically walks // through the period-delimited list of names, and ends up with the // final value. String[] fieldNameParts = fieldName.split("\\."); Object currentObject = null; Object priorObject = bo; for (int i = 0; i < fieldNameParts.length; i++) { String fieldNamePart = fieldNameParts[i]; try { if (fieldNamePart.indexOf("]") > 0) { currentObject = PropertyUtils.getIndexedProperty(priorObject, fieldNamePart); } else { currentObject = PropertyUtils.getSimpleProperty(priorObject, fieldNamePart); } } catch (IllegalAccessException e) { throw new RuntimeException("Caller does not have access to the property accessor method.", e); } catch (InvocationTargetException e) { throw new RuntimeException("Property accessor method threw an exception.", e); } catch (NoSuchMethodException e) { throw new RuntimeException("The accessor method requested for this property cannot be found.", e); } // materialize the proxy, if it is a proxy if (ProxyHelper.isProxy(currentObject)) { currentObject = ProxyHelper.getRealObject(currentObject); } // if a node or the leaf is null, then we're done, there's no need to // continue accessing null things if (currentObject == null) { return currentObject; } priorObject = currentObject; } return currentObject; } /** * This method safely creates a object from a class * Convenience method to create new object and throw a runtime exception if it cannot * If the class is an {@link ExternalizableBusinessObject}, this method will determine the interface for the EBO and * query the * appropriate module service to create a new instance. * * @param clazz * @return a newInstance() of clazz */ public static Object createNewObjectFromClass(Class clazz) { if (clazz == null) { throw new RuntimeException("BO class was passed in as null"); } try { if (ExternalizableBusinessObject.class.isAssignableFrom(clazz)) { Class eboInterface = ExternalizableBusinessObjectUtils .determineExternalizableBusinessObjectSubInterface(clazz); ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService() .getResponsibleModuleService(eboInterface); return moduleService.createNewObjectFromExternalizableClass(eboInterface); } else { return clazz.newInstance(); } } catch (Exception e) { throw new RuntimeException("Error occured while trying to create a new instance for class " + clazz, e); } } /** * Return whether or not an attribute is writeable. This method is aware that that Collections may be involved and * handles them * consistently with the way in which OJB handles specifying the attributes of elements of a Collection. * * @param object * @param property * @return * @throws IllegalArgumentException */ public static boolean isWriteable(Object object, String property, PersistenceStructureService persistenceStructureService) throws IllegalArgumentException { if (null == object || null == property) { throw new IllegalArgumentException("Cannot check writeable status with null arguments."); } // Try the easy way. try { if (!(PropertyUtils.isWriteable(object, property))) { // If that fails lets try to be a bit smarter, understanding that Collections may be involved. return isWriteableHelper(object, property, persistenceStructureService); } else { return true; } } catch (NestedNullException nestedNullException) { // If a NestedNullException is thrown then the property has a null // value. Call the helper to find the class of the property and // get a newInstance of it. return isWriteableHelper(object, property, persistenceStructureService); } } /** * This method handles the cases where PropertyUtils.isWriteable is not * sufficient. It handles cases where the parameter in question is a * collection or if the parameter value is null. * @param object * @param property * @param persistenceStructureService * @return */ private static boolean isWriteableHelper(Object object, String property, PersistenceStructureService persistenceStructureService) { if (property.contains(".")) { String propertyName = StringUtils.substringBefore(property, "."); // Get the type of the attribute. Class<?> c = ObjectUtils.getPropertyType(object, propertyName, persistenceStructureService); if (c != null) { Object i = null; // If the next level is a Collection, look into the collection, to find out what type its elements are. if (Collection.class.isAssignableFrom(c)) { Map<String, Class> m = persistenceStructureService.listCollectionObjectTypes(object.getClass()); c = m.get(propertyName); } // Look into the attribute class to see if it is writeable. try { i = c.newInstance(); return isWriteable(i, StringUtils.substringAfter(property, "."), persistenceStructureService); } catch (Exception ex) { LOG.error("Skipping Criteria: " + property + " - Unable to instantiate class : " + c.getName(), ex); } } else { LOG.error("Skipping Criteria: " + property + " - Unable to determine class for object: " + object.getClass().getName() + " - " + propertyName); } } return false; } /** * Helper method for creating a new instance of the given class * * @param clazz - class of object to create * @return T object of type given by the clazz parameter */ public static <T> T newInstance(Class<T> clazz) { T object = null; try { object = clazz.newInstance(); } catch (InstantiationException e) { LOG.error("Unable to create new instance of class: " + clazz.getName()); throw new RuntimeException(e); } catch (IllegalAccessException e) { LOG.error("Unable to create new instance of class: " + clazz.getName()); throw new RuntimeException(e); } return object; } /** * Retrieves all fields including the inherited fields for a given class. The recursion stops if either Object class is reached * or if stopAt is reached first. * * @param fields List of fields (public, private and protected) * @param type Class from which fields retrieval has to start * @param stopAt Parent class where the recursion should stop * @return */ public static List<Field> getAllFields(List<Field> fields, Class<?> type, Class<?> stopAt) { for (Field field : type.getDeclaredFields()) { fields.add(field); } if (type.getSuperclass() != null && !type.getName().equals(stopAt.getName())) { fields = getAllFields(fields, type.getSuperclass(), stopAt); } return fields; } }