latest changes to bean initializing
authorAndreas Müller <a.mueller@bgbm.org>
Mon, 4 Nov 2013 00:26:34 +0000 (00:26 +0000)
committerAndreas Müller <a.mueller@bgbm.org>
Mon, 4 Nov 2013 00:26:34 +0000 (00:26 +0000)
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/initializer/AbstractBeanInitializer.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/initializer/AdvancedBeanInitializer.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/initializer/BeanInitNode.java

index bdfdbc7b935b2aeea9a3205d3564fe5907bfdea2..e57cb6b4506551ebb277804f1d181bbe44beae1a 100644 (file)
@@ -21,6 +21,8 @@ import java.util.List;
 import java.util.Map;\r
 import java.util.Set;\r
 \r
+import javax.persistence.Transient;\r
+\r
 import org.apache.commons.beanutils.PropertyUtils;\r
 import org.apache.commons.lang.StringUtils;\r
 import org.apache.log4j.Logger;\r
@@ -93,10 +95,8 @@ public abstract class AbstractBeanInitializer implements IBeanInitializer{
      */\r
     public void initializeBean(Object bean, boolean cdmEntities, boolean collections){\r
 \r
-        if(logger.isDebugEnabled()){\r
-            logger.debug(">> starting initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName());\r
-        }\r
-        Set<Class> restrictions = new HashSet<Class>();\r
+        if(logger.isDebugEnabled()){logger.debug(">> starting wildcard initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName()); }\r
+        Set<Class<?>> restrictions = new HashSet<Class<?>>();\r
         if(cdmEntities){\r
             restrictions.add(CdmBase.class);\r
         }\r
@@ -383,33 +383,32 @@ public abstract class AbstractBeanInitializer implements IBeanInitializer{
      * @param typeRestrictions\r
      * @return\r
      */\r
-    @SuppressWarnings("unchecked")\r
-    public static Set<PropertyDescriptor> getProperties(Object bean, Set<Class> typeRestrictions) {\r
+    public static Set<PropertyDescriptor> getProperties(Object bean, Set<Class<?>> typeRestrictions) {\r
 \r
         Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();\r
-        PropertyDescriptor[] prop = PropertyUtils.getPropertyDescriptors(bean);\r
+        PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(bean);\r
 \r
-        for (int i = 0; i < prop.length; i++) {\r
+        for (PropertyDescriptor prop : props) {\r
             //String propName = prop[i].getName();\r
 \r
             // only read methods & skip transient getters\r
-            if( prop[i].getReadMethod() != null ){\r
+            if( prop.getReadMethod() != null ){\r
                   try{\r
-                     Class transientClass = Class.forName( "javax.persistence.Transient" );\r
-                     if( prop[i].getReadMethod().getAnnotation( transientClass ) != null ){\r
+                     Class<Transient> transientClass = (Class<Transient>)Class.forName( "javax.persistence.Transient" );\r
+                     if( prop.getReadMethod().getAnnotation( transientClass ) != null ){\r
                         continue;\r
                      }\r
                   }catch( ClassNotFoundException cnfe ){\r
                      // ignore\r
                   }\r
                   if(typeRestrictions != null && typeRestrictions.size() > 1){\r
-                      for(Class type : typeRestrictions){\r
-                          if(type.isAssignableFrom(prop[i].getPropertyType())){\r
-                              properties.add(prop[i]);\r
+                      for(Class<?> restrictedType : typeRestrictions){\r
+                          if(restrictedType.isAssignableFrom(prop.getPropertyType())){\r
+                              properties.add(prop);\r
                           }\r
                       }\r
                   } else {\r
-                      properties.add(prop[i]);\r
+                      properties.add(prop);\r
                   }\r
             }\r
         }\r
index d6a5123fcdd93af8a08d202f7996ed65097ab3bc..c21342ab6244006305b832ee7cacd5484632bd64 100644 (file)
@@ -4,17 +4,29 @@
 package eu.etaxonomy.cdm.persistence.dao.initializer;\r
 \r
 import java.beans.PropertyDescriptor;\r
+import java.io.Serializable;\r
 import java.lang.reflect.InvocationTargetException;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Set;\r
 \r
+import javax.persistence.Transient;\r
+\r
 import org.apache.commons.beanutils.PropertyUtils;\r
-import org.apache.commons.lang.StringUtils;\r
 import org.apache.log4j.Logger;\r
+import org.hibernate.Hibernate;\r
+import org.hibernate.HibernateException;\r
+import org.hibernate.Query;\r
+import org.hibernate.collection.internal.AbstractPersistentCollection;\r
+import org.hibernate.proxy.HibernateProxy;\r
+import org.springframework.beans.factory.annotation.Autowired;\r
 \r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;\r
 import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;\r
 \r
 /**\r
@@ -25,8 +37,11 @@ import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;
  */\r
 public class AdvancedBeanInitializer extends HibernateBeanInitializer {\r
 \r
-          public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);\r
+           public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);\r
 \r
+           @Autowired\r
+           ICdmGenericDao genericDao;\r
+           \r
            @Override\r
            public void initialize(Object bean, List<String> propertyPaths) {\r
                List<Object> beanList = new ArrayList<Object>(1);\r
@@ -55,32 +70,31 @@ public class AdvancedBeanInitializer extends HibernateBeanInitializer {
                System.out.println(rootPath.toStringTree());\r
                \r
 \r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList.iterator().next().getClass().getSimpleName());\r
-               }\r
+               if(logger.isDebugEnabled()){ logger.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList.iterator().next().getClass().getSimpleName());}\r
                rootPath.addBeans(beanList);\r
-               initializeBean(rootPath);\r
+               initializeNodeRecursive(rootPath);\r
                \r
                \r
-//             //old\r
-//             if(logger.isDebugEnabled()){logger.debug("Start old initalizer ... ");};\r
-//             Collections.sort(propertyPaths);\r
-//             for(String propPath : propertyPaths){\r
-//                 initializePropertyPath(bean, propPath);\r
-//             }\r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug("   Completed initialization of beanlist ");\r
+               //old - keep for safety (this may help to initialize those beans that are not yet correctly initialized by the AdvancedBeanInitializer\r
+               for (Object bean :beanList){\r
+                       if(logger.isDebugEnabled()){logger.debug("Start old initalizer ... ");};\r
+                       Collections.sort(propertyPaths);\r
+                       for(String propPath : propertyPaths){\r
+                           initializePropertyPath(bean, propPath);\r
+                       }\r
                }\r
+               \r
+               if(logger.isDebugEnabled()){ logger.debug("   Completed initialization of beanlist "); }\r
                return beanList;\r
 \r
            }\r
            \r
 \r
                //new\r
-           private void initializeBean(BeanInitNode rootPath) {\r
-                       initializePropertyPath(rootPath);\r
+           private void initializeNodeRecursive(BeanInitNode rootPath) {\r
+                       initializeNode(rootPath);\r
                        for (BeanInitNode childPath : rootPath.getChildrenList()){\r
-                               initializeBean(childPath);\r
+                               initializeNodeRecursive(childPath);\r
                        }\r
                        rootPath.resetBeans();\r
                }\r
@@ -91,40 +105,98 @@ public class AdvancedBeanInitializer extends HibernateBeanInitializer {
             * @param bean\r
             * @param propPath\r
             */\r
-           private void initializePropertyPath(BeanInitNode node) {\r
-               if(logger.isDebugEnabled()){logger.debug("processing " + node.toString());}\r
-               if (StringUtils.isBlank(node.getPath())){\r
+           private void initializeNode(BeanInitNode node) {\r
+               if(logger.isDebugEnabled()){logger.debug(" processing " + node.toString());}\r
+               if (node.isRoot()){\r
                        return;\r
-               }\r
-\r
-               if (node.isWildcard()){\r
-                       initializeWildcardPropertyPath(node);\r
+               }else if (node.isWildcard()){\r
+                       initializeNodeWildcard(node);\r
                } else {\r
-                   initializeNoWildcardPropertyPath(node);\r
+                   initializeNodeNoWildcard(node);\r
                }\r
            }\r
 \r
                // if propPath only contains a wildcard (* or $)\r
         // => do a batch initialization of *toOne or *toMany relations\r
-        private void initializeWildcardPropertyPath(BeanInitNode node) {\r
-                       boolean initToMany = node.getPath().equals(LOAD_2ONE_2MANY_WILDCARD);\r
-                   for (Class<?> clazz : node.getParentBeans().keySet()){\r
-                               for (Object bean : node.getParentBeans().get(clazz)){\r
-                               \r
+        private void initializeNodeWildcard(BeanInitNode node) {\r
+                       boolean initToMany = node.isToManyWildcard();\r
+                       Map<Class<?>, Set<Object>> parentBeans = node.getParentBeans();\r
+                   for (Class<?> clazz : parentBeans.keySet()){\r
+                       //new\r
+                       for (Object bean : parentBeans.get(clazz)){\r
+                                       \r
                                        if(Collection.class.isAssignableFrom(bean.getClass())){\r
-                                       initializeAllEntries((Collection<?>)bean, true, initToMany);\r
+//                                     old: initializeAllEntries((Collection<?>)bean, true, initToMany);  //TODO is this a possible case at all??\r
+                                               throw new RuntimeException("Collection no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");\r
                                    } else if(Map.class.isAssignableFrom(bean.getClass())) {\r
-                                       initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);\r
+                                       initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);  ////TODO is this a possible case at all?? \r
+                                               throw new RuntimeException("Map no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");\r
                                    } else{\r
-                                       initializeBean(bean, true, initToMany);\r
+                                       prepareBeanWildcardForBulkLoad(node, bean);\r
                                    }\r
                            }\r
+                       //end new\r
+                       \r
+//                     initializeNodeWildcardOld(initToMany, beans, clazz);  //if switched on move bulkLoadLazies up\r
                    }\r
+                   \r
+               //\r
+               bulkLoadLazies(node);\r
+               }\r
+\r
+               /**\r
+                * @param initToMany\r
+                * @param beans\r
+                * @param clazz\r
+                */\r
+               private void initializeNodeWildcardOld(boolean initToMany,\r
+                               Map<Class<?>, Set<Object>> beans, Class<?> clazz) {\r
+                       for (Object bean : beans.get(clazz)){\r
+                       \r
+                               if(Collection.class.isAssignableFrom(bean.getClass())){\r
+                               initializeAllEntries((Collection<?>)bean, true, initToMany);\r
+                           } else if(Map.class.isAssignableFrom(bean.getClass())) {\r
+                               initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);\r
+                           } else{\r
+                               initializeBean(bean, true, initToMany);\r
+                           }\r
+                       }\r
                }\r
+        \r
+        private void prepareBeanWildcardForBulkLoad(BeanInitNode node, Object bean){\r
+\r
+            if(logger.isDebugEnabled()){logger.debug(">> prepare bulk wildcard initialization of a bean of type " + bean.getClass().getSimpleName()); }\r
+            Set<Class<?>> restrictions = new HashSet<Class<?>>();\r
+            restrictions.add(CdmBase.class);\r
+            if(node.isToManyWildcard()){\r
+                restrictions.add(Collections.class);\r
+            }\r
+            Set<PropertyDescriptor> props = getProperties(bean, restrictions);\r
+            for(PropertyDescriptor propertyDescriptor : props){\r
+                try {\r
+                       String property = propertyDescriptor.getName();\r
+                       \r
+//                  invokeInitialization(bean, propertyDescriptor);\r
+                    Object propertyValue = PropertyUtils.getProperty( bean, property);\r
+                    \r
+                    preparePropertyValueForBulkLoad(node, bean, property,  propertyValue );\r
+\r
+                } catch (IllegalAccessException e) {\r
+                    logger.error("Illegal access on property " + propertyDescriptor.getName());\r
+                } catch (InvocationTargetException e) {\r
+                    logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");\r
+                } catch (NoSuchMethodException e) {\r
+                    logger.info("Property " + propertyDescriptor.getName() + " not found");\r
+                }\r
+            }\r
+            if(logger.isDebugEnabled()){logger.debug(" completed bulk wildcard initialization of a bean");}\r
+        }\r
+\r
+        \r
 \r
        // propPath contains either a single field or a nested path\r
                // split next path token off and keep the remaining as nestedPath\r
-        private void initializeNoWildcardPropertyPath(BeanInitNode node) {\r
+        private void initializeNodeNoWildcard(BeanInitNode node) {\r
                        \r
                String property = node.getPath();\r
                        int pos;\r
@@ -140,55 +212,24 @@ public class AdvancedBeanInitializer extends HibernateBeanInitializer {
                        try {\r
                            //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging\r
 \r
-                           // [1] initialize the bean named by property\r
-                           for (Class<?> clazz : node.getParentBeans().keySet()){\r
-                               Set<Object> beans = node.getParentBeans().get(clazz);\r
-                               if (logger.isDebugEnabled()){logger.debug("invoke initialization beans of class "+clazz+" ... ");}\r
-                                           \r
-                               for (Object bean : beans){\r
-               \r
-                                           PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);\r
-                                           if (logger.isDebugEnabled()){logger.debug("invoke initialization "+node+" ... ");}\r
-                                               Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);\r
-                                           if (logger.isDebugEnabled()){logger.debug("invoke initialization "+node+" - DONE ");}\r
-               \r
-       \r
-                                           // [2]\r
-                                           // recurse into nested properties\r
-                                           if(unwrappedPropertyBean != null ){\r
-       //                                      for (BeanInitNode childNode : node.getChildrenList()){\r
-                                                       Collection<?> collection = null;\r
-                                                       if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
-                                                               collection = ((Map<?,?>)unwrappedPropertyBean).values();\r
-                                                       }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
-                                                       collection =  (Collection<?>) unwrappedPropertyBean;    \r
-                                                       }\r
-                                                       if (collection != null){\r
-                                                       //collection or map\r
-                                                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + node.toString() + " ... ");}\r
-                                                           int i = 0;\r
-                                                               for (Object entrybean : collection) {\r
-                                                           if(index == null){\r
-                                                                           node.addBean(entrybean);\r
-       //                                                      initializePropertyPath(entrybean, childNode);\r
-                                                           } else if(index.equals(i)){\r
-                                                               node.addBean(entrybean);\r
-       //                                                      initializePropertyPath(entrybean, childNode);\r
-                                                               break;\r
-                                                           }\r
-                                                           i++;\r
-                                                       }\r
-                                                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + node.toString() + " - DONE ");}\r
-                                                           \r
-                                                   }else {\r
-                                                       // nested bean\r
-                                                       node.addBean(unwrappedPropertyBean);\r
-       //                                              initializePropertyPath(unwrappedPropertyBean, childNode);\r
-                                                       setProperty(bean, property, unwrappedPropertyBean);\r
-                                                   }\r
-                                               }\r
-                                               }\r
-//                                 }\r
+                           for (Class<?> parentClazz : node.getParentBeans().keySet()){\r
+                               if (logger.isDebugEnabled()){logger.debug(" invoke initialization on "+ node.toString()+ " beans of class " + parentClazz.getSimpleName() + " ... ");}\r
+                                       \r
+                               Set<Object> parentBeans = node.getParentBeans().get(parentClazz);\r
+                               \r
+                               if (index != null){\r
+                                       logger.warn("Property path index not yet implemented for 'new'");\r
+                               }\r
+                               //new                           \r
+                               for (Object parentBean : parentBeans){\r
+                                       Object propertyValue = PropertyUtils.getProperty(parentBean, property);\r
+                                           preparePropertyValueForBulkLoad(node, parentBean, property, propertyValue);\r
+                               }\r
+                               bulkLoadLazies(node);\r
+                               \r
+                               //end new\r
+                               \r
+//                             initializeNodeNoWildcardOld(node, property, index, parentBeans);\r
                                }                               \r
 \r
                        } catch (IllegalAccessException e) {\r
@@ -200,4 +241,253 @@ public class AdvancedBeanInitializer extends HibernateBeanInitializer {
                        }\r
                }\r
 \r
+               /**\r
+                * @param node\r
+                * @param property\r
+                * @param index\r
+                * @param parentBeans\r
+                * @throws IllegalAccessException\r
+                * @throws InvocationTargetException\r
+                * @throws NoSuchMethodException\r
+                */\r
+               private void initializeNodeNoWildcardOld(BeanInitNode node,\r
+                               String property, Integer index, Set<Object> parentBeans)\r
+                               throws IllegalAccessException, InvocationTargetException,\r
+                               NoSuchMethodException {\r
+                       for (Object bean : parentBeans){\r
+\r
+                           PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);\r
+                           if (logger.isDebugEnabled()){logger.debug("   unwrap " + node.toStringNoWildcard() + " ... ");}\r
+                           // [1] initialize the bean named by property\r
+                               Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);\r
+                           if (logger.isDebugEnabled()){logger.debug("   unwrap " + node.toStringNoWildcard() + " - DONE ");}\r
+\r
+\r
+                           // [2]\r
+                           // handle property\r
+                           if(unwrappedPropertyBean != null ){\r
+                                       initializeNodeSinglePropertyOld(node, property, index, bean, unwrappedPropertyBean);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * @param node\r
+                * @param propertyValue\r
+                * @param parentBean \r
+                * @param param \r
+                */\r
+               private void preparePropertyValueForBulkLoad(BeanInitNode node, Object parentBean, String param, Object propertyValue) {\r
+                       BeanInitNode sibling = node.getSibling(param);\r
+                       if (propertyValue instanceof AbstractPersistentCollection ){\r
+                               if (!node.hasWildcardToManySibling()){  //if wildcard exists the lazies are already prepared\r
+                                       Class<?> parentClass = parentBean.getClass();\r
+                                       int parentId = ((CdmBase)parentBean).getId();\r
+                                       if (sibling != null){\r
+                                               sibling.putLazyCollection(parentClass, param, parentId);\r
+                                       }else{\r
+                                               node.putLazyCollection(parentClass, param, parentId);\r
+                                       }\r
+                               }\r
+                       }else{\r
+                               if (!node.hasWildcardToOneSibling()){  //if wildcard exists the lazies are already prepared\r
+                                       if (! Hibernate.isInitialized(propertyValue)){\r
+                                               if (propertyValue instanceof HibernateProxy){\r
+                                                       Serializable id = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getIdentifier();\r
+                                                       Class<?> persistedClass = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getPersistentClass();\r
+                                                       if (sibling != null){\r
+                                                               sibling.putLazyBean(persistedClass, id);\r
+                                                       }else{\r
+                                                               node.putLazyBean(persistedClass, id);\r
+                                                       }\r
+                                                       \r
+                                               }else{\r
+                                               logger.warn("Lazy value is not of type HibernateProxy. This is not yet handled.");\r
+                                               }\r
+                                       }else if (propertyValue == null){\r
+       //                              System.out.println("Single: property is null");\r
+                                       }else{\r
+       //                              System.out.println("Single: " + propertyValue);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               private void bulkLoadLazies(BeanInitNode node) {\r
+                       \r
+                       if (logger.isDebugEnabled()){logger.debug("bulk load " +  node);}\r
+                       \r
+                       //beans\r
+                       for (Class<?> clazz : node.getLazyBeans().keySet()){\r
+                               Set<Serializable> idSet = node.getLazyBeans().get(clazz);\r
+                               if (idSet != null && ! idSet.isEmpty()){\r
+                                       \r
+                                       if (logger.isDebugEnabled()){logger.debug("bulk load beans of class " +  clazz.getSimpleName());}\r
+                                       //TODO use entity name\r
+                                       String hql = " FROM " + clazz.getSimpleName() + " WHERE id IN (:idSet) ";\r
+                                       Query query = genericDao.getHqlQuery(hql);\r
+                                       query.setParameterList("idSet", idSet);\r
+                                       List<Object> list = query.list();\r
+                                       \r
+                                       if (logger.isDebugEnabled()){logger.debug("initialize bulk loaded beans of class " +  clazz.getSimpleName());}\r
+                                       for (Object object : list){\r
+                                               if (object instanceof HibernateProxy){\r
+                                                       object = initializeInstance(object);\r
+                                               }\r
+                                               node.addBean(object);\r
+                                       }\r
+                                       if (logger.isDebugEnabled()){logger.debug("bulk load - DONE");}\r
+                               }       \r
+                       }\r
+                       node.resetLazyBeans();\r
+                       \r
+                       //collections\r
+                       for (Class<?> ownerClazz : node.getLazyCollections().keySet()){\r
+                               Map<String, Set<Serializable>> lazyParams = node.getLazyCollections().get(ownerClazz);\r
+                               for (String param : lazyParams.keySet()){\r
+                                       Set<Serializable> idSet = lazyParams.get(param);\r
+                                       if (idSet != null && ! idSet.isEmpty()){\r
+                                               if (logger.isDebugEnabled()){logger.debug("bulk load " + node + " collections ; ownerClass=" +  ownerClazz.getSimpleName() + " ; param = " + param);}\r
+                                               \r
+                                               //TODO use entity name ??\r
+                                               //get from repository\r
+                                               List<Object> list;\r
+                                               try {\r
+                                                       String hql = "SELECT oc.%s " +\r
+                                                                       " FROM %s as oc WHERE oc.id IN (:idSet) ";\r
+                                                       param = workAroundBeanInconsistency(param, ownerClazz.getSimpleName());\r
+                                                       hql = String.format(hql, param, ownerClazz.getSimpleName());\r
+                                                       Query query = genericDao.getHqlQuery(hql);\r
+                                                       query.setParameterList("idSet", idSet);\r
+                                                       list = query.list();\r
+                                               } catch (HibernateException e) {\r
+                                                       // TODO Auto-generated catch block\r
+                                                       e.printStackTrace();\r
+                                                       throw e;\r
+                                               }\r
+                                               \r
+                                               //getTarget and add to child node\r
+                                               if (logger.isDebugEnabled()){logger.debug("initialize bulk loaded " + node + " collections - DONE");}\r
+                                               for (Object newBean : list){\r
+                                                       if (newBean instanceof HibernateProxy){\r
+                                                               newBean = initializeInstance(newBean);\r
+                                                       }\r
+                                                       node.addBean(newBean);\r
+                                               }\r
+                                               if (logger.isDebugEnabled()){logger.debug("bulk load " + node + " collections - DONE");}\r
+                                               \r
+                                               \r
+                                       }       \r
+                                       \r
+                               }\r
+                               \r
+                               \r
+                               \r
+                               \r
+                       }\r
+                       node.resetLazyCollections();\r
+                       \r
+                       if (logger.isDebugEnabled()){logger.debug("bulk load " +  node + " - DONE ");}\r
+                       \r
+               }\r
+\r
+               \r
+               /**\r
+                * Rename bean attributes to hibernate (field) attribute, due to bean inconsistencies\r
+                * #3841\r
+                * @param param\r
+                * @param ownerClass \r
+                * @return\r
+                */\r
+               private String workAroundBeanInconsistency(String param, String ownerClass) {\r
+                       if (ownerClass.contains("Description") && param.equals("elements")){\r
+                               //DescriptionBase.descriptionElements -> elements\r
+                               return "descriptionElements";\r
+                       }else if(ownerClass.equals("Classification") && param.equals("childNodes")){\r
+                               return "rootNodes";\r
+                       }else{\r
+                               return param;\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * @param node\r
+                * @param property\r
+                * @param index\r
+                * @param bean\r
+                * @param unwrappedPropertyBean\r
+                */\r
+               private void initializeNodeSinglePropertyOld(BeanInitNode node, String property,\r
+                               Integer index, Object bean, Object unwrappedPropertyBean) {\r
+                       Collection<?> collection = null;\r
+                       if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
+                               collection = ((Map<?,?>)unwrappedPropertyBean).values();\r
+                       }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
+                           collection =  (Collection<?>) unwrappedPropertyBean;        \r
+                       }\r
+                       if (collection != null){\r
+                           //collection or map\r
+                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + node.toStringNoWildcard() + " ... ");}\r
+                           int i = 0;\r
+                               for (Object entrybean : collection) {\r
+                               if(index == null){\r
+                                           node.addBean(entrybean);\r
+                               } else if(index.equals(i)){\r
+                                       node.addBean(entrybean);\r
+                                   break;\r
+                               }\r
+                               i++;\r
+                           }\r
+                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + node.toString() + " - DONE ");}\r
+                           \r
+                       }else {\r
+                           // nested bean\r
+                               node.addBean(unwrappedPropertyBean);\r
+                           setProperty(bean, property, unwrappedPropertyBean);\r
+                       }\r
+               }\r
+               \r
+               /**\r
+            * This is an adaptation of {@link AbstractBeanInitializer#getProperties(Object, Set)}\r
+            * which handles typeRestrictions different (correct IMO).\r
+            * The only difference is that it handles type restrictions also when they have\r
+            * size = 1\r
+            * \r
+            * \r
+            * @param bean\r
+            * @param typeRestrictions\r
+            * @return\r
+            */\r
+               public static Set<PropertyDescriptor> getProperties(Object bean, Set<Class<?>> typeRestrictions) {\r
+\r
+               Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();\r
+               PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(bean);\r
+\r
+               for (PropertyDescriptor prop : props) {\r
+                   //String propName = prop[i].getName();\r
+\r
+                   // only read methods & skip transient getters\r
+                   if( prop.getReadMethod() != null ){\r
+                         try{\r
+                            Class<Transient> transientClass = (Class<Transient>)Class.forName( "javax.persistence.Transient" );\r
+                            if( prop.getReadMethod().getAnnotation( transientClass ) != null ){\r
+                               continue;\r
+                            }\r
+                         }catch( ClassNotFoundException cnfe ){\r
+                            // ignore\r
+                         }\r
+                         if(typeRestrictions != null ){\r
+                             for(Class<?> restrictedType : typeRestrictions){\r
+                                 if(restrictedType.isAssignableFrom(prop.getPropertyType())){\r
+                                     properties.add(prop);\r
+                                 }\r
+                             }\r
+                         } else {\r
+                             properties.add(prop);\r
+                         }\r
+                   }\r
+               }\r
+               return properties;\r
+           }\r
+\r
 }\r
index 0f8d438af273edca02d58e0d438efd1a2e2cd43a..413dc83839090f116cb4d76d08f617666e3492c3 100644 (file)
@@ -3,6 +3,7 @@
  */\r
 package eu.etaxonomy.cdm.persistence.dao.initializer;\r
 \r
+import java.io.Serializable;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
 import java.util.Collections;\r
@@ -12,6 +13,8 @@ import java.util.List;
 import java.util.Map;\r
 import java.util.Set;\r
 \r
+import org.apache.commons.lang.StringUtils;\r
+\r
 import eu.etaxonomy.cdm.common.CdmUtils;\r
 \r
 /**\r
@@ -20,6 +23,8 @@ import eu.etaxonomy.cdm.common.CdmUtils;
  */\r
 public class BeanInitNode implements Comparable<BeanInitNode>{\r
 \r
+       // ************************ ATTRIBUTES *******************************/\r
+       \r
        private BeanInitNode parent;\r
        \r
        private Map<String, BeanInitNode> children = new HashMap<String, BeanInitNode>();\r
@@ -30,29 +35,17 @@ public class BeanInitNode implements Comparable<BeanInitNode>{
        \r
        private boolean isToOneInitialized = false;\r
        \r
+       private BeanInitNode wildcardChildCache;\r
+       \r
+       \r
        private  Map<Class<?>, Set<Object>> beans = new HashMap<Class<?>, Set<Object>>();\r
 \r
+       private  Map<Class<?>, Set<Serializable>> lazyBeans = new HashMap<Class<?>, Set<Serializable>>();\r
        \r
+       private  Map<Class<?>, Map<String, Set<Serializable>>> lazyCollections = new HashMap<Class<?>, Map<String,Set<Serializable>>>();\r
+       \r
+// *************************** STATIC METHODS *****************************************/\r
        \r
-       public BeanInitNode(BeanInitNode parent, String part) {\r
-               this.path = CdmUtils.Nz(part);\r
-               this.parent = parent;\r
-               if (parent != null){\r
-                       parent.addChild(part, this);\r
-               }\r
-       }\r
-\r
-       private void addChild(String part, BeanInitNode child) {\r
-               children.put(part, child);\r
-               if (child.isWildcard()){\r
-                       if (part.equals(AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD)){\r
-                               this.isToManyInitialized = true;\r
-                       }\r
-                       this.isToOneInitialized = true;\r
-               }\r
-       }\r
-\r
-\r
        public static BeanInitNode createInitTree(List<String> propertyPaths) {\r
                \r
                //sort paths  //TODO needed?\r
@@ -74,67 +67,193 @@ public class BeanInitNode implements Comparable<BeanInitNode>{
                 \r
                return root;\r
        }\r
-\r
-\r
-       public List<BeanInitNode> getChildrenList() {\r
-               List<BeanInitNode> result = new ArrayList<BeanInitNode>(children.values());\r
-               Collections.sort(result);\r
-               return result;\r
+       \r
+       private static boolean isWildcard(String pathPart) {\r
+               return pathPart.equals(AbstractBeanInitializer.LOAD_2ONE_WILDCARD) || \r
+                               pathPart.equals(AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD);\r
        }\r
 \r
 \r
-       public void addBean(Object bean) {\r
-               if (bean != null){\r
-                       Class<?> key = bean.getClass();\r
-                       Set<Object> classedBeans = beans.get(key);\r
-                       if (classedBeans == null){\r
-                               classedBeans = new HashSet<Object>();\r
-                               beans.put(key, classedBeans);\r
-                       }\r
-                       classedBeans.add(bean);\r
+//***************************** CONSTRUCTOR RELATED ****************************************/  \r
+       \r
+       public BeanInitNode(BeanInitNode parent, String part) {\r
+               this.path = CdmUtils.Nz(part);\r
+               this.parent = parent;\r
+               this.isToManyInitialized = this.path.equals(AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD);\r
+               this.isToOneInitialized = this.path.equals(AbstractBeanInitializer.LOAD_2ONE_WILDCARD) || this.isToManyInitialized;\r
+               if (parent != null){\r
+                       parent.addChild(part, this);\r
                }\r
        }\r
-       public void addBeans(Collection<?> beans) {\r
-               for (Object bean : beans){\r
-                       addBean(bean);\r
+\r
+       private void addChild(String part, BeanInitNode child) {\r
+               children.put(part, child);\r
+               if (child.isWildcard()){\r
+                       //set wildcard child if not exists or if child is stronger then existing wildcard child \r
+                       if (this.wildcardChildCache == null || (! this.wildcardChildCache.isToManyInitialized) && child.isToManyInitialized ){\r
+                               this.wildcardChildCache = child;\r
+                       }\r
                }\r
        }\r
        \r
-       private Map<Class<?>, Set<Object>> getClassedBeans(){\r
-               return this.beans;\r
-       }\r
+// **************************  ***********************************************************/\r
        \r
-       public void resetBeans(){\r
-               beans.clear();\r
+       public BeanInitNode getChild(String param){\r
+               return children.get(param);\r
        }\r
 \r
+\r
+\r
+       public List<BeanInitNode> getChildrenList() {\r
+               List<BeanInitNode> result = new ArrayList<BeanInitNode>(children.values());\r
+               Collections.sort(result);\r
+               return result;\r
+       }\r
+       \r
+       public BeanInitNode getSibling(String param) {\r
+               if (parent == null){\r
+                       return null;\r
+               }else{\r
+                       return parent.getChild(param);\r
+               }\r
+       }\r
+       \r
        public String getPath() {\r
                return path;\r
        }\r
+       \r
+       public boolean isRoot() {\r
+               return StringUtils.isBlank(path);\r
+       }\r
 \r
        public boolean hasChildren() {\r
                return children.size() > 0;\r
        }\r
 \r
-\r
+       public BeanInitNode getWildcardChild(){\r
+               return this.wildcardChildCache;\r
+       }\r
        public boolean isWildcard() {\r
-               return path.equals(AbstractBeanInitializer.LOAD_2ONE_WILDCARD) || \r
-                               path.equals(AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD);\r
+               return this.isToManyInitialized || this.isToOneInitialized;\r
        }\r
+       public boolean hasWildcardChild() {\r
+               return this.wildcardChildCache != null;\r
+       }\r
+       public boolean hasToManyWildcardChild() {\r
+               return this.wildcardChildCache != null;\r
+       }\r
+\r
+       public boolean hasWildcardToManySibling() {\r
+               BeanInitNode sibl = getWildcardSibling();\r
+               return (sibl != null && sibl.isToManyInitialized);\r
+       }\r
+\r
+       private BeanInitNode getWildcardSibling() {\r
+               if (!isWildcard() && parent == null){\r
+                       return parent.getWildcardChild();\r
+               }else{\r
+                       return null;\r
+               }\r
+       }\r
+\r
        \r
        public boolean isToManyWildcard() {\r
                return path.equals(AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD);\r
        }\r
 \r
 \r
-       public boolean isInitializedIfSingle(){\r
-               return parent.isToOneInitialized;\r
+// ******************* LAZY COLLECTION *****************************************/\r
+       public void putLazyCollection(Class<?> ownerClazz, String parameter, Serializable id) {\r
+               if (ownerClazz != null && parameter != null && id != null){\r
+                       Map<String, Set<Serializable>> lazyParams = lazyCollections.get(ownerClazz);\r
+                       if (lazyParams == null){\r
+                               lazyParams = new HashMap<String, Set<Serializable>>();\r
+                               lazyCollections.put(ownerClazz, lazyParams);\r
+                       }\r
+                       Set<Serializable> layzIds = lazyParams.get(parameter);\r
+                       if (layzIds == null){\r
+                               layzIds = new HashSet<Serializable>();\r
+                               lazyParams.put(parameter, layzIds);\r
+                       }\r
+                       layzIds.add(id);\r
+               }else{\r
+                       throw new IllegalArgumentException("Class, parameter and id should not be null");\r
+               }\r
+       }\r
+       public Map<Class<?>, Map<String, Set<Serializable>>> getLazyCollections(){\r
+               return this.lazyCollections;\r
+       }\r
+       public void resetLazyCollections(){\r
+               this.lazyCollections.clear();\r
+       }\r
+\r
+       \r
+// ******************* LAZY BEAN *****************************************/\r
+       \r
+       public void putLazyBean(Class<?> clazz, Serializable id) {\r
+               if (clazz != null && id != null){\r
+                       Set<Serializable> classedLazies= lazyBeans.get(clazz);\r
+                       if (classedLazies == null){\r
+                               classedLazies = new HashSet<Serializable>();\r
+                               lazyBeans.put(clazz, classedLazies);\r
+                       }\r
+                       classedLazies.add(id);\r
+               }else{\r
+                       throw new IllegalArgumentException("Class and id should not be null");\r
+               }\r
+       }\r
+       public Map<Class<?>, Set<Serializable>> getLazyBeans(){\r
+               return this.lazyBeans;\r
+       }\r
+       public void resetLazyBeans(){\r
+               this.lazyBeans.clear();\r
+       }\r
+       \r
+// ********************* BEANS ******************************/\r
+       \r
+       private Map<Class<?>, Set<Object>> getClassedBeans(){\r
+               return this.beans;\r
+       }\r
+       \r
+       \r
+       public Map<Class<?>, Set<Object>> getBeans() {\r
+               return this.beans;\r
        }\r
        \r
-       public boolean isInitializedAny(){\r
-               return parent.isToManyInitialized;\r
+       public Map<Class<?>, Set<Object>> getParentBeans() {\r
+               if (parent == null){\r
+                       return new HashMap<Class<?>, Set<Object>>();\r
+               }else{\r
+                       return parent.getClassedBeans();\r
+               }\r
        }\r
        \r
+       public void addBean(Object bean) {\r
+               if (! isWildcard()){\r
+                       if (bean != null){\r
+                               Class<?> key = bean.getClass();\r
+                               Set<Object> classedBeans = beans.get(key);\r
+                               if (classedBeans == null){\r
+                                       classedBeans = new HashSet<Object>();\r
+                                       beans.put(key, classedBeans);\r
+                               }\r
+                               classedBeans.add(bean);\r
+                       }\r
+               }\r
+       }\r
+       public void addBeans(Collection<?> beans) {\r
+               for (Object bean : beans){\r
+                       addBean(bean);\r
+               }\r
+       }\r
+       \r
+       public void resetBeans(){\r
+               beans.clear();\r
+       }\r
+\r
+\r
+// ************************* OVERRIDES **********************************/\r
+       \r
        @Override\r
        public int compareTo(BeanInitNode o) {\r
                String toMany = AbstractBeanInitializer.LOAD_2ONE_2MANY_WILDCARD;\r
@@ -157,29 +276,32 @@ public class BeanInitNode implements Comparable<BeanInitNode>{
        public String toString() {\r
                \r
                if(parent == null){\r
-                       return "/";\r
+                       return "/";    //for root node\r
                }else{\r
-                       return parent.toString() +  ((parent.parent == null) ? "" : ".") + path;\r
+                       String result = parent.toString() + ((parent.parent == null) ? "" : ".") + path;\r
+                       return result;\r
                }\r
        }\r
        \r
+       public String toStringNoWildcard(){\r
+               return toString().replace(".$", "").replace(".*", "");\r
+       }\r
+       \r
        public String toStringTree() {\r
                \r
-               String result = (path.equals("")? "/": path) + "\n";\r
+               String result = (path.equals("") ? "/" : path) + "\n";\r
+               \r
                for (BeanInitNode child : getChildrenList()){\r
-                       result += toString() + "." + child.toStringTree();\r
+                       result += toString() + (result.endsWith("/\n") ? "" : ".") + child.toStringTree();\r
                }\r
                \r
                return result;\r
        }\r
 \r
-       public Map<Class<?>, Set<Object>> getParentBeans() {\r
-               if (parent == null){\r
-                       return new HashMap<Class<?>, Set<Object>>();\r
-               }else{\r
-                       return parent.getClassedBeans();\r
-               }\r
+       public boolean hasWildcardToOneSibling() {\r
+               BeanInitNode sibl = getWildcardSibling();\r
+               return (sibl != null && sibl.isToOneInitialized);\r
        }\r
 \r
-\r
+               \r
 }\r