package org.toobsframework.data.beanutil; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.MethodUtils; import org.apache.commons.beanutils.NestedNullException; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.validation.BindException; import org.springframework.validation.Errors; //import org.toobsframework.data.base.PermissionedObject; import org.toobsframework.biz.collections.ICollectionCreator; import org.toobsframework.biz.validation.IValidator; //import org.toobsframework.exception.BaseException; import org.toobsframework.data.ICollectionLoader; import org.toobsframework.data.IObjectLoader; import org.toobsframework.data.beanutil.converter.StringToDateConverter; import org.toobsframework.exception.PermissionException; import org.toobsframework.exception.ValidationException; import org.toobsframework.servlet.ContextHelper; import org.toobsframework.util.Configuration; import org.toobsframework.util.constants.PlatformConstants; import org.toobsframework.util.string.StringResource; @SuppressWarnings("unchecked") public class BeanMonkey { /** * To get the logger instance */ private static Log log = LogFactory.getLog(BeanMonkey.class); private static BeanFactory beanFactory = BeanMonkey.getBeanFactoryInstance(); private static Class objectClass; static { String objectClassProperty = ""; try { objectClassProperty = Configuration.getInstance().getProperty("toobs.beanmonkey.objectClass"); objectClass = Class.forName(objectClassProperty); } catch (ClassNotFoundException e) { log.error("Class for toobs.beanmonkey.objectClass property: " + objectClassProperty + " not found"); } } /** * A utility method that just returns a new instance of a bean factory object. * @return */ public static BeanFactory getBeanFactoryInstance() { return ContextHelper.getWebApplicationContext(); } public static void populate(Object bean, Map properties, Collection errorMessages) throws IllegalAccessException, InvocationTargetException, ValidationException, PermissionException { IValidator v = null; String className = bean.getClass().getSimpleName(); String validatorName = className + "Validator"; if (beanFactory.containsBean(validatorName)) { v = (IValidator) beanFactory.getBean(validatorName); } else { log.warn("No validator " + validatorName + " for " + className); } if (v != null) { v.prePopulate(bean, properties); } populate(bean, properties, true); Errors e = new BindException(bean, className.substring(0, className.length()-4)); if (properties.containsKey("MultipartValidationError")) { String docProperty = (String)properties.get("MultipartValidationField"); try { PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(bean, docProperty); if (descriptor != null) { BeanUtils.setProperty(bean, docProperty, null); e.rejectValue(docProperty, docProperty +"."+(String)properties.get("MultipartValidationError"), (String)properties.get("MultipartValidationMessage")); } } catch (NoSuchMethodException nsm) { } } if (v != null) { if (e.getAllErrors() == null || e.getAllErrors().size() == 0) { v.prepare(bean, properties); v.validate(bean, e); } } if (e.getAllErrors() != null && e.getAllErrors().size() > 0) { errorMessages.addAll(e.getAllErrors()); } } public static void populate(Object bean, Map properties) throws IllegalAccessException, InvocationTargetException, ValidationException, PermissionException { IValidator v = null; String className = bean.getClass().getSimpleName(); String validatorName = className + "Validator"; if (beanFactory.containsBean(validatorName)) { v = (IValidator) beanFactory.getBean(validatorName); } else { log.warn("No validator " + validatorName + " for " + className); } if (v != null) { v.prePopulate(bean, properties); } populate(bean, properties, true); String objectName = (String) properties.get("returnObjectType") == null ? className : (String) properties.get("returnObjectType"); Errors e = new BindException(bean, objectName); if (properties.containsKey("MultipartValidationError")) { String docProperty = (String)properties.get("MultipartValidationField"); try { PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(bean, docProperty); if (descriptor != null) { BeanUtils.setProperty(bean, docProperty, null); e.rejectValue(docProperty, docProperty +"."+(String)properties.get("MultipartValidationError"), (String)properties.get("MultipartValidationMessage")); } } catch (NoSuchMethodException nsm) { } } if (v != null) { if (e.getAllErrors() == null || e.getAllErrors().size() == 0) { v.prepare(bean, properties); v.validate(bean, e); if (e.getAllErrors() == null || e.getAllErrors().size() == 0) { v.audit(bean, properties); } } } if (e.getAllErrors() != null && e.getAllErrors().size() > 0) { throw new ValidationException(e); } } public static Collection populateCollection(String beanClazz, String indexPropertyName, Map properties, boolean noload, boolean errorMode, Collection errorMessages) throws IllegalAccessException, InvocationTargetException, ValidationException, ClassNotFoundException, InstantiationException, PermissionException { String className = beanClazz.substring(beanClazz.lastIndexOf(".") + 1); IValidator v = null; String validatorName = className + "Validator"; if (beanFactory.containsBean(validatorName)) { v = (IValidator) beanFactory.getBean(validatorName); } else { log.warn("No validator for " + className); } Collection validationErrors = new ArrayList(); Collection returnObjs = populateCollection(v, beanClazz, indexPropertyName, properties, true, noload); if (v == null) { return returnObjs; } String objectName = (String) properties.get("returnObjectType") == null ? className : (String) properties.get("returnObjectType"); /* Validate Collection level properties - size() > 0 etc */ Errors e = new BindException(returnObjs, objectName); v.validateCollection(returnObjs, e); if (e.getAllErrors() != null && e.getAllErrors().size() > 0) { validationErrors.add(e); //return returnObjs; } else { Iterator it = returnObjs.iterator(); while (it.hasNext()) { Object bean = it.next(); v.prepare(bean, properties); boolean doCreate = v.doCreateCollectionMember(bean); if (!doCreate) { it.remove(); continue; } e = new BindException(bean, objectName); v.validate(bean, e); /* Safe bean in DoItRunner validationErrorObjects.add(v.getSafeBean(bean, properties)); */ if (e.getAllErrors() != null && e.getAllErrors().size() > 0) { validationErrors.add(e); } else if (errorMode) { it.remove(); } } } if(validationErrors.size() > 0){ if (errorMessages != null) { Iterator errIter = validationErrors.iterator(); while (errIter.hasNext()) { Errors err = (Errors)errIter.next(); errorMessages.addAll(err.getAllErrors()); } } else { throw new ValidationException(validationErrors); } } /* if(validationErrors.size() > 0){ //properties.put("ValidationErrors", validationErrors); //properties.put("ValidationErrorObject", validationErrorObjects); throw new ValidationException(validationErrors); } */ //If there are no errors, null out the errorsObjects to conserve memory. //validationErrorObjects = null; return returnObjs; } public static Collection populateCollection(IValidator v, String beanClazz, String indexPropertyName, Map properties, boolean validate, boolean noload) throws IllegalAccessException, InvocationTargetException, ValidationException, ClassNotFoundException, InstantiationException, PermissionException { // Do nothing unless all arguments have been specified if ((beanClazz == null) || (properties == null) || (indexPropertyName == null)) { log.warn("Proper parameters not present."); return null; } ArrayList returnObjs = new ArrayList(); Object[] indexProperty = (Object[]) properties.get(indexPropertyName); if (indexProperty == null){ log.warn("indexProperty [" + indexProperty + "] does not exist in the map."); return returnObjs; } Class beanClass = Class.forName(beanClazz); String beanClazzName = beanClass.getSimpleName(); // beanClazz.substring(beanClazz.lastIndexOf(".") + 1); IObjectLoader odao = null; ICollectionLoader cdao = null; if (objectClass.isAssignableFrom(beanClass)) { odao = (IObjectLoader) beanFactory.getBean(Introspector.decapitalize(beanClazzName.substring(0,beanClazzName.length()-4)) + "Dao"); if (odao == null) { throw new InvocationTargetException(new Exception("Object DAO class " + Introspector.decapitalize(beanClazzName) + "Dao could not be loaded")); } } else { cdao = (ICollectionLoader) beanFactory.getBean(Introspector.decapitalize(beanClazzName.substring(0,beanClazzName.length()-4)) + "Dao"); if (cdao == null) { throw new InvocationTargetException(new Exception("Collection DAO class " + Introspector.decapitalize(beanClazzName) + "Dao could not be loaded")); } } boolean namespaceStrict = properties.containsKey("namespaceStrict"); for(int index = 0; index < indexProperty.length; index++) { String guid = (String)indexProperty[index]; boolean newBean = false; Object bean = null; if (!noload && guid != null && guid.length() > 0) { bean = (odao != null) ? odao.load(guid) : cdao.load(Integer.parseInt(guid)); } if (bean == null) { bean = Class.forName(beanClazz).newInstance(); newBean = true; } if (v != null) { v.prePopulate(bean, properties); } if (log.isDebugEnabled()) { log.debug("BeanMonkey.populate(" + bean + ", " + properties + ")"); } Errors e = null; if (validate) { String beanClassName = null; beanClassName = bean.getClass().getName(); beanClassName = beanClassName .substring(beanClassName.lastIndexOf(".") + 1); e = new BindException(bean, beanClassName); } String namespace = null; if (properties.containsKey("namespace") && !"".equals(properties.get("namespace"))) { namespace = (String)properties.get("namespace") + "."; } // Loop through the property name/value pairs to be set Iterator names = properties.keySet().iterator(); while (names.hasNext()) { // Identify the property name and value(s) to be assigned String name = (String) names.next(); if (name == null || (indexPropertyName.equals(name) && !noload) || (namespaceStrict && !name.startsWith(namespace))) { continue; } Object value = null; if(properties.get(name) == null) { log.warn("Property [" + name + "] does not have a value in the map."); continue; } if(properties.get(name).getClass().isArray() && index < ((Object[])properties.get(name)).length){ value = ((Object[])properties.get(name))[index]; } else if(properties.get(name).getClass().isArray()) { value = ((Object[])properties.get(name))[0]; } else { value = properties.get(name); } if (namespace != null) { name = name.replace(namespace, ""); } PropertyDescriptor descriptor = null; Class type = null; // Java type of target property try { descriptor = PropertyUtils.getPropertyDescriptor(bean, name); if (descriptor == null) { continue; // Skip this property setter } } catch (NoSuchMethodException nsm) { continue; // Skip this property setter } catch (IllegalArgumentException iae) { continue; // Skip null nested property } if (descriptor.getWriteMethod() == null) { if (log.isDebugEnabled()) { log.debug("Skipping read-only property"); } continue; // Read-only, skip this property setter } type = descriptor.getPropertyType(); String className = type.getName(); try { value = evaluatePropertyValue(name, className, namespace, value, properties, bean); } catch (NoSuchMethodException nsm) { continue; } try { BeanUtils.setProperty(bean, name, value); } catch (ConversionException ce) { log.error("populate - exception [bean:" + bean.getClass().getName() + " name:" + name + " value:" + value + "] "); if (validate) { e.rejectValue(name, name + ".conversionError", ce.getMessage()); } else { throw new ValidationException(bean, className, name, ce.getMessage()); } } catch (Exception be) { log.error("populate - exception [bean:" + bean.getClass().getName() + " name:" + name + " value:" + value + "] "); if (validate) { e.rejectValue(name, name + ".error", be.getMessage()); } else { throw new ValidationException(bean, className, name, be.getMessage()); } } } /* if (newBean && cdao != null) { BeanUtils.setProperty(bean, "id", -1); } */ if (validate && e.getErrorCount() > 0) { throw new ValidationException(e); } returnObjs.add(bean); } return returnObjs; } public static void populate(Object bean, Map properties, boolean validate) throws IllegalAccessException, InvocationTargetException, ValidationException, PermissionException { // Do nothing unless both arguments have been specified if ((bean == null) || (properties == null)) { return; } if (log.isDebugEnabled()) { log.debug("BeanMonkey.populate(" + bean + ", " + properties + ")"); } Errors e = null; if (validate) { String beanClassName = null; beanClassName = bean.getClass().getName(); beanClassName = beanClassName .substring(beanClassName.lastIndexOf(".") + 1); e = new BindException(bean, beanClassName); } String namespace = null; if (properties.containsKey("namespace") && !"".equals(properties.get("namespace"))) { namespace = (String)properties.get("namespace") + "."; } // Loop through the property name/value pairs to be set Iterator names = properties.keySet().iterator(); while (names.hasNext()) { // Identify the property name and value(s) to be assigned String name = (String) names.next(); if (name == null) { continue; } Object value = properties.get(name); if (namespace != null) { name = name.replace(namespace, ""); } PropertyDescriptor descriptor = null; Class type = null; // Java type of target property try { descriptor = PropertyUtils.getPropertyDescriptor(bean, name); if (descriptor == null) { continue; // Skip this property setter } } catch (NoSuchMethodException nsm) { continue; // Skip this property setter } catch (IllegalArgumentException iae) { continue; // Skip null nested property } if (descriptor.getWriteMethod() == null) { if (log.isDebugEnabled()) { log.debug("Skipping read-only property"); } continue; // Read-only, skip this property setter } type = descriptor.getPropertyType(); String className = type.getName(); try { value = evaluatePropertyValue(name, className, namespace, value, properties, bean); } catch (NoSuchMethodException nsm) { continue; } try { if(value != null) { BeanUtils.setProperty(bean, name, value); } } catch (ConversionException ce) { log.error("populate - exception [bean:" + bean.getClass().getName() + " name:" + name + " value:" + value + "] "); if (validate) { e.rejectValue(name, name + ".conversionError", ce.getMessage()); } else { throw new ValidationException(bean, className, name, ce.getMessage()); } } catch (Exception be) { log.error("populate - exception [bean:" + bean.getClass().getName() + " name:" + name + " value:" + value + "] "); if (validate) { e.rejectValue(name, name + ".error", be.getMessage()); } else { throw new ValidationException(bean, className, name, be.getMessage()); } } } if (validate && e.getErrorCount() > 0) { throw new ValidationException(e); } } private static Object evaluatePropertyValue(String name, String className, String namespace, Object value, Map properties, Object bean) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, PermissionException { // Perform the assignment for this property if (className.startsWith(Configuration.getInstance().getProperty("toobs.beanmonkey.dataPackage"))) { className = className.substring(className.lastIndexOf(".") + 1); IObjectLoader odao = null; ICollectionLoader cdao = null; Object daoObject = beanFactory.getBean(Introspector.decapitalize(className) + "Dao"); if (daoObject == null) { throw new InvocationTargetException(new Exception("DAO class " + Introspector.decapitalize(className) + "Dao could not be loaded")); } if (daoObject instanceof IObjectLoader) { odao = (IObjectLoader)daoObject; } else { cdao = (ICollectionLoader)daoObject; } String guid = null; if (value != null && value.getClass().isArray()) { if(properties.containsKey(PlatformConstants.MULTI_ACTION_INSTANCE)) { Object[] oldValue = (Object[]) value; Integer instance = (Integer)properties.get(PlatformConstants.MULTI_ACTION_INSTANCE); if (oldValue.length >= instance + 1) { guid = ((String[])oldValue)[instance]; } else { throw new RuntimeException("Instance " + instance + " not found in " + oldValue + " for: " + name + " class: " + className + " in: " + bean); } } else { guid = ((String[]) value)[0]; } } else { guid = (String) value; } if (guid != null && guid.length() > 0) { String personId = (String) properties.get("personId"); try { if (odao != null) { value = odao.load(guid); } else { value = cdao.load(Integer.parseInt(guid)); } } catch (PermissionException pe) { log.error("PermissionException loading object " + className + "." + name + " with guid " + guid + " by person " + personId ); throw pe; } finally { } } else { value = null; } } else if ((className.equals("java.util.ArrayList") || className .equals("java.util.List")) && !(value instanceof java.util.List)) { Object[] values = null; if (value != null && value.getClass().isArray()) { values = ((Object[]) value); } else { values = new Object[1]; values[0] = (Object) value; } value = new ArrayList(); for (int aa = 0; aa < values.length; aa++) { if (!"".equals(values[aa])) ((ArrayList) value).add(values[aa]); } if (((ArrayList) value).size() == 0) value = null; } else if (className.equals("java.util.Collection")) { className = null; String typeProp = (namespace != null ? namespace : "") + name + "-Type"; if (properties.get(typeProp) != null && properties.get(typeProp).getClass().isArray()) { className = ((String[]) properties.get(typeProp))[0]; } else { className = (String) properties.get(typeProp); } if (className == null) { throw new InvocationTargetException(new Exception( "Missing collection type for " + name)); } IObjectLoader odao = null; ICollectionLoader cdao = null; Object daoObject = beanFactory.getBean(Introspector.decapitalize(className) + "Dao"); if (daoObject == null) { throw new InvocationTargetException(new Exception("DAO class " + Introspector.decapitalize(className) + "Dao could not be loaded")); } if (daoObject instanceof IObjectLoader) { odao = (IObjectLoader)daoObject; } else { cdao = (ICollectionLoader)daoObject; } Object[] guids = null; if (value != null && value.getClass().isArray()) { guids = ((Object[]) value); } else if (value != null && value instanceof ArrayList) { guids = new Object[((ArrayList)value).size()]; for (int i = 0; i < guids.length; i++) { guids[i] = (Object)((ArrayList)value).get(i); } } else { guids = new Object[1]; guids[0] = (Object) value; } String personId = (String) properties.get("personId"); java.util.Collection valueList = (java.util.Collection) PropertyUtils.getProperty(bean, name); valueList.clear(); for (int i = 0; i < guids.length; i++) { if (odao != null) { if (((String)guids[i]).length() == 0) continue; value = odao.load((String)guids[i]); } else if (cdao != null) { value = cdao.load((Integer)guids[i]); } if (value != null) { valueList.add(value); } else { if (beanFactory.containsBean(className + "CollectionCreator")) { ICollectionCreator collectionCreator = (ICollectionCreator)beanFactory.getBean(className + "CollectionCreator"); try { collectionCreator.addCollectionElements(guids[i], valueList, properties, personId, namespace); } catch (Exception e) { throw new InvocationTargetException(e); } } } } value = valueList; } else if(className.equals("java.lang.String") && value != null && value.getClass().isArray() && ((Object[]) value).length > 1 && properties.containsKey(PlatformConstants.MULTI_ACTION_INSTANCE)) { Object[] oldValue = (Object[]) value; Integer instance = (Integer)properties.get(PlatformConstants.MULTI_ACTION_INSTANCE); if (oldValue.length >= instance + 1) { value = oldValue[instance]; } else { throw new RuntimeException("Instance " + instance + " not found in " + oldValue + " for: " + name + " class: " + className + " in: " + bean); } } else if(className.equals("java.lang.String") && value != null && value.getClass().isArray() && ((Object[]) value).length > 1) { Object[] oldValue = (Object[]) value; String newValue = new String(); for (int i = 0; i < oldValue.length; i++) { if(i > 0) { newValue = newValue + ";"; } newValue = newValue + oldValue[i]; } value = newValue; } else if((className.equals("java.lang.Integer") || className.equals("java.lang.Boolean")) && value != null) { if (value.getClass().isArray() && ((Object[]) value).length == 1 && ((Object[]) value)[0].equals("")) { value = null; } else if (!value.getClass().isArray() && String.valueOf(value).equals("")) { value = null; } } else if(className.equals("java.util.Date") && value != null) { if (value.getClass().isArray()) { value = StringToDateConverter.convert(value); } } return value; } public static Method findMethod(Class clazz, String methodName, Class paramType) { return MethodUtils.getAccessibleMethod(clazz, methodName, paramType); } public static Method findMethod(Class clazz, String methodName, Class[] paramTypes) { return MethodUtils.getAccessibleMethod(clazz, methodName, paramTypes); } public static String concatPropertyList(Object bean, String propertyList, boolean quoted) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { StringBuffer sb = new StringBuffer(); concatPropertyList(bean, propertyList, sb, quoted); if (log.isDebugEnabled()) { log.debug("Concat result: " + sb.toString().trim()); } return sb.toString().trim(); } public static void concatPropertyList(Object bean, String propertyList, StringBuffer result, boolean quoted) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { if (bean == null || propertyList == null || result == null) { return; } StringTokenizer st = new StringTokenizer(propertyList, ","); while (st.hasMoreTokens()) { String name = st.nextToken(); Object value = PropertyUtils.getProperty(bean, name); if (value instanceof String && value != null) { result.append(" "); if (quoted) result.append("\""); result.append(value); if (quoted) result.append("\""); } else if (value instanceof Collection && value != null) { Iterator valIter = ((Collection) value).iterator(); while (valIter.hasNext()) { concatPropertyList(valIter.next(), "displayName", result, quoted); // result.append(" " + ConvertUtils.convert(valIter.next())); } } else if (value != null) { result.append(" "); if (quoted) result.append("\""); result.append(ConvertUtils.convert(value)); if (quoted) result.append("\""); } } } public static String getSummary(String input, int length) { if (input == null) { return input; } String strippedString = StringResource.stripTags(input, false); if (strippedString == null || strippedString.length() <= length - 3) { return strippedString;// + "..." } return strippedString.substring(0, length - 3) + "..."; } public static Object getPropertyValue(Object bean, String propertyPath) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Object value = null; try { value = PropertyUtils.getProperty(bean, propertyPath); } catch (NestedNullException nne) { } return value; } }