4 package eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
;
6 import java
.beans
.PropertyDescriptor
;
7 import java
.io
.Serializable
;
8 import java
.lang
.reflect
.InvocationTargetException
;
9 import java
.util
.ArrayList
;
10 import java
.util
.Collection
;
11 import java
.util
.Collections
;
12 import java
.util
.HashSet
;
13 import java
.util
.List
;
17 import javax
.persistence
.Transient
;
19 import org
.apache
.commons
.beanutils
.PropertyUtils
;
20 import org
.apache
.log4j
.Logger
;
21 import org
.hibernate
.Hibernate
;
22 import org
.hibernate
.HibernateException
;
23 import org
.hibernate
.Query
;
24 import org
.hibernate
.collection
.internal
.AbstractPersistentCollection
;
25 import org
.hibernate
.proxy
.HibernateProxy
;
26 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
28 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
29 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
30 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.HibernateBeanInitializer
;
33 * For now this is a test if we can improve performance for bean initializing
38 public class AdvancedBeanInitializer
extends HibernateBeanInitializer
{
40 public static final Logger logger
= Logger
.getLogger(AdvancedBeanInitializer
.class);
43 ICdmGenericDao genericDao
;
46 public void initialize(Object bean
, List
<String
> propertyPaths
) {
47 List
<Object
> beanList
= new ArrayList
<Object
>(1);
49 initializeAll(beanList
, propertyPaths
);
52 //TODO optimize algorithm ..
54 public <C
extends Collection
<?
>> C
initializeAll(C beanList
, List
<String
> propertyPaths
) {
56 if (beanList
== null || beanList
.isEmpty()){
61 // invokePropertyAutoInitializers(bean);
63 if(propertyPaths
== null){ //TODO if AutoInitializer is not requiredfor top level bean, this can be merged with previous "if"
69 BeanInitNode rootPath
= BeanInitNode
.createInitTree(propertyPaths
);
70 System
.out
.println(rootPath
.toStringTree());
73 if(logger
.isDebugEnabled()){ logger
.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList
.iterator().next().getClass().getSimpleName());}
74 rootPath
.addBeans(beanList
);
75 initializeNodeRecursive(rootPath
);
78 //old - keep for safety (this may help to initialize those beans that are not yet correctly initialized by the AdvancedBeanInitializer
79 if(logger
.isDebugEnabled()){logger
.debug("Start old initalizer ... ");};
80 for (Object bean
:beanList
){
81 Collections
.sort(propertyPaths
);
82 for(String propPath
: propertyPaths
){
83 // initializePropertyPath(bean, propPath);
87 if(logger
.isDebugEnabled()){ logger
.debug(" Completed initialization of beanlist "); }
94 private void initializeNodeRecursive(BeanInitNode rootPath
) {
95 initializeNode(rootPath
);
96 for (BeanInitNode childPath
: rootPath
.getChildrenList()){
97 initializeNodeRecursive(childPath
);
99 rootPath
.resetBeans();
103 * Initializes the given single <code>propPath</code> String.
108 private void initializeNode(BeanInitNode node
) {
109 if(logger
.isDebugEnabled()){logger
.debug(" processing " + node
.toString());}
112 }else if (node
.isWildcard()){
113 initializeNodeWildcard(node
);
115 initializeNodeNoWildcard(node
);
119 // if propPath only contains a wildcard (* or $)
120 // => do a batch initialization of *toOne or *toMany relations
121 private void initializeNodeWildcard(BeanInitNode node
) {
122 // boolean initToMany = node.isToManyWildcard();
123 Map
<Class
<?
>, Set
<Object
>> parentBeans
= node
.getParentBeans();
124 for (Class
<?
> clazz
: parentBeans
.keySet()){
126 for (Object bean
: parentBeans
.get(clazz
)){
128 if(Collection
.class.isAssignableFrom(bean
.getClass())){
129 // old: initializeAllEntries((Collection<?>)bean, true, initToMany); //TODO is this a possible case at all??
130 throw new RuntimeException("Collection no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
131 } else if(Map
.class.isAssignableFrom(bean
.getClass())) {
132 // old: initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany); ////TODO is this a possible case at all??
133 throw new RuntimeException("Map no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
135 prepareBeanWildcardForBulkLoad(node
, bean
);
140 // initializeNodeWildcardOld(initToMany, beans, clazz); //if switched on move bulkLoadLazies up
144 bulkLoadLazies(node
);
152 private void initializeNodeWildcardOld(boolean initToMany
,
153 Map
<Class
<?
>, Set
<Object
>> beans
, Class
<?
> clazz
) {
154 for (Object bean
: beans
.get(clazz
)){
156 if(Collection
.class.isAssignableFrom(bean
.getClass())){
157 initializeAllEntries((Collection
<?
>)bean
, true, initToMany
);
158 } else if(Map
.class.isAssignableFrom(bean
.getClass())) {
159 initializeAllEntries(((Map
<?
,?
>)bean
).values(), true, initToMany
);
161 initializeBean(bean
, true, initToMany
);
166 private void prepareBeanWildcardForBulkLoad(BeanInitNode node
, Object bean
){
168 if(logger
.isDebugEnabled()){logger
.debug(">> prepare bulk wildcard initialization of a bean of type " + bean
.getClass().getSimpleName()); }
169 Set
<Class
<?
>> restrictions
= new HashSet
<Class
<?
>>();
170 restrictions
.add(CdmBase
.class);
171 if(node
.isToManyWildcard()){
172 restrictions
.add(Collection
.class);
174 Set
<PropertyDescriptor
> props
= getProperties(bean
, restrictions
);
175 for(PropertyDescriptor propertyDescriptor
: props
){
177 String property
= propertyDescriptor
.getName();
179 // invokeInitialization(bean, propertyDescriptor);
180 Object propertyValue
= PropertyUtils
.getProperty( bean
, property
);
182 preparePropertyValueForBulkLoadOrStore(node
, bean
, property
, propertyValue
);
184 } catch (IllegalAccessException e
) {
185 logger
.error("Illegal access on property " + propertyDescriptor
.getName());
186 } catch (InvocationTargetException e
) {
187 logger
.info("Cannot invoke property " + propertyDescriptor
.getName() + " not found");
188 } catch (NoSuchMethodException e
) {
189 logger
.info("Property " + propertyDescriptor
.getName() + " not found");
192 if(logger
.isDebugEnabled()){logger
.debug(" completed bulk wildcard initialization of a bean");}
197 // propPath contains either a single field or a nested path
198 // split next path token off and keep the remaining as nestedPath
199 private void initializeNodeNoWildcard(BeanInitNode node
) {
201 String property
= node
.getPath();
204 // is the property indexed?
205 Integer index
= null;
206 if((pos
= property
.indexOf('[')) > 0){
207 String indexString
= property
.substring(pos
+ 1, property
.indexOf(']'));
208 index
= Integer
.valueOf(indexString
);
209 property
= property
.substring(0, pos
);
213 //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
215 for (Class
<?
> parentClazz
: node
.getParentBeans().keySet()){
216 if (logger
.isDebugEnabled()){logger
.debug(" invoke initialization on "+ node
.toString()+ " beans of class " + parentClazz
.getSimpleName() + " ... ");}
218 Set
<Object
> parentBeans
= node
.getParentBeans().get(parentClazz
);
221 logger
.warn("Property path index not yet implemented for 'new'");
224 for (Object parentBean
: parentBeans
){
225 Object propertyValue
= PropertyUtils
.getProperty(parentBean
, property
);
226 preparePropertyValueForBulkLoadOrStore(node
, parentBean
, property
, propertyValue
);
231 // initializeNodeNoWildcardOld(node, property, index, parentBeans); //move bulkLoadLazies up again, if uncomment this line
233 bulkLoadLazies(node
);
235 } catch (IllegalAccessException e
) {
236 logger
.error("Illegal access on property " + property
);
237 } catch (InvocationTargetException e
) {
238 logger
.error("Cannot invoke property " + property
+ " not found");
239 } catch (NoSuchMethodException e
) {
240 logger
.info("Property " + property
+ " not found");
249 * @throws IllegalAccessException
250 * @throws InvocationTargetException
251 * @throws NoSuchMethodException
253 private void initializeNodeNoWildcardOld(BeanInitNode node
,
254 String property
, Integer index
, Set
<Object
> parentBeans
)
255 throws IllegalAccessException
, InvocationTargetException
,
256 NoSuchMethodException
{
257 for (Object bean
: parentBeans
){
259 PropertyDescriptor propertyDescriptor
= PropertyUtils
.getPropertyDescriptor(bean
, property
);
260 if (logger
.isDebugEnabled()){logger
.debug(" unwrap " + node
.toStringNoWildcard() + " ... ");}
261 // [1] initialize the bean named by property
262 Object unwrappedPropertyBean
= invokeInitialization(bean
, propertyDescriptor
);
263 if (logger
.isDebugEnabled()){logger
.debug(" unwrap " + node
.toStringNoWildcard() + " - DONE ");}
268 if(unwrappedPropertyBean
!= null ){
269 initializeNodeSinglePropertyOld(node
, property
, index
, bean
, unwrappedPropertyBean
);
276 * @param propertyValue
280 private void preparePropertyValueForBulkLoadOrStore(BeanInitNode node
, Object parentBean
, String param
, Object propertyValue
) {
281 BeanInitNode sibling
= node
.getSibling(param
);
283 if (propertyValue
instanceof AbstractPersistentCollection
){
285 if (!node
.hasWildcardToManySibling()){ //if wildcard sibling exists the lazies are already prepared there
286 AbstractPersistentCollection collection
= (AbstractPersistentCollection
)propertyValue
;
287 if (collection
.wasInitialized()){
288 storeInitializedCollection(collection
, node
, param
);
290 // Class<?> parentClass = parentBean.getClass();
291 // int parentId = ((CdmBase)parentBean).getId();
292 if (sibling
!= null){
293 sibling
.putLazyCollection(collection
);
295 node
.putLazyCollection(collection
);
301 if (!node
.hasWildcardToOneSibling()){ //if wildcard exists the lazies are already prepared there
302 if (! Hibernate
.isInitialized(propertyValue
)){
303 if (propertyValue
instanceof HibernateProxy
){
304 Serializable id
= ((HibernateProxy
)propertyValue
).getHibernateLazyInitializer().getIdentifier();
305 Class
<?
> persistedClass
= ((HibernateProxy
)propertyValue
).getHibernateLazyInitializer().getPersistentClass();
306 if (sibling
!= null){
307 sibling
.putLazyBean(persistedClass
, id
);
309 node
.putLazyBean(persistedClass
, id
);
313 logger
.warn("Lazy value is not of type HibernateProxy. This is not yet handled.");
315 }else if (propertyValue
== null){
318 if (propertyValue
instanceof HibernateProxy
){ //TODO remove hibernate dependency
319 propertyValue
= initializeInstance(propertyValue
);
321 autoinitializeBean(propertyValue
);
322 node
.addBean(propertyValue
);
328 private void autoinitializeBean(Object bean
) {
329 invokePropertyAutoInitializers(bean
);
332 private void storeInitializedCollection(AbstractPersistentCollection persistedCollection
,
333 BeanInitNode node
, String param
) {
334 Collection
<?
> collection
;
336 if (persistedCollection
instanceof Collection
) {
337 collection
= (Collection
<?
>) persistedCollection
;
338 }else if (persistedCollection
instanceof Map
) {
339 collection
= ((Map
<?
,?
>)persistedCollection
).values();
341 throw new RuntimeException ("Non Map and non Collection cas not handled in storeInitializedCollection()");
343 for (Object value
: collection
){
344 preparePropertyValueForBulkLoadOrStore(node
, null, param
, value
);
348 private void bulkLoadLazies(BeanInitNode node
) {
350 if (logger
.isDebugEnabled()){logger
.debug("bulk load " + node
);}
353 for (Class
<?
> clazz
: node
.getLazyBeans().keySet()){
354 Set
<Serializable
> idSet
= node
.getLazyBeans().get(clazz
);
355 if (idSet
!= null && ! idSet
.isEmpty()){
357 if (logger
.isDebugEnabled()){logger
.debug("bulk load beans of class " + clazz
.getSimpleName());}
358 //TODO use entity name
359 String hql
= " SELECT c FROM %s as c %s WHERE c.id IN (:idSet) ";
360 hql
= String
.format(hql
, clazz
.getSimpleName(), addAutoinitFetchLoading(clazz
, "c"));
361 Query query
= genericDao
.getHqlQuery(hql
);
362 query
.setParameterList("idSet", idSet
);
363 List
<Object
> list
= query
.list();
365 if (logger
.isDebugEnabled()){logger
.debug("initialize bulk loaded beans of class " + clazz
.getSimpleName());}
366 for (Object object
: list
){
367 if (object
instanceof HibernateProxy
){ //TODO remove hibernate dependency
368 object
= initializeInstance(object
);
370 autoinitializeBean(object
);
371 node
.addBean(object
);
373 if (logger
.isDebugEnabled()){logger
.debug("bulk load - DONE");}
376 node
.resetLazyBeans();
379 for (Class
<?
> ownerClazz
: node
.getLazyCollections().keySet()){
380 Map
<String
, Set
<Serializable
>> lazyParams
= node
.getLazyCollections().get(ownerClazz
);
381 for (String param
: lazyParams
.keySet()){
382 Set
<Serializable
> idSet
= lazyParams
.get(param
);
383 if (idSet
!= null && ! idSet
.isEmpty()){
384 if (logger
.isDebugEnabled()){logger
.debug("bulk load " + node
+ " collections ; ownerClass=" + ownerClazz
.getSimpleName() + " ; param = " + param
);}
386 //TODO use entity name ??
387 //get from repository
389 String hql
= "SELECT oc, oc.%s " +
390 " FROM %s as oc INNER JOIN FETCH oc.%s as col " +
391 " WHERE oc.id IN (:idSet) ";
393 // String hql = "SELECT oc.%s " +
394 // " FROM %s as oc WHERE oc.id IN (:idSet) ";
395 // param = workAroundBeanInconsistency(param, ownerClazz.getSimpleName());
396 hql
= String
.format(hql
, param
, ownerClazz
.getSimpleName(), param
);
400 Query query
= genericDao
.getHqlQuery(hql
);
401 query
.setParameterList("idSet", idSet
);
403 } catch (HibernateException e
) {
408 //getTarget and add to child node
409 if (logger
.isDebugEnabled()){logger
.debug("initialize bulk loaded " + node
+ " collections - DONE");}
410 for (Object
[] listItems
: list
){
411 Object newBean
= listItems
[1];
412 if (newBean
instanceof HibernateProxy
){
413 newBean
= initializeInstance(newBean
);
415 autoinitializeBean(newBean
);
416 node
.addBean(newBean
);
418 if (logger
.isDebugEnabled()){logger
.debug("bulk load " + node
+ " collections - DONE");}
422 for (AbstractPersistentCollection collection
: node
.getUninitializedCollections()){
423 if (! collection
.wasInitialized()){ //should not happen anymore
424 collection
.forceInitialization();
428 node
.resetLazyCollections();
430 if (logger
.isDebugEnabled()){logger
.debug("bulk load " + node
+ " - DONE ");}
435 private String
addAutoinitFetchLoading(Class
<?
> clazz
, String beanAlias
) {
436 Set
<AutoPropertyInitializer
<CdmBase
>> inits
= getAutoInitializers(clazz
);
438 for (AutoPropertyInitializer
<CdmBase
> init
: inits
){
439 result
+=init
.hibernateFetchJoin(clazz
, beanAlias
);
444 private Set
<AutoPropertyInitializer
<CdmBase
>> getAutoInitializers(Class
<?
> clazz
) {
445 Set
<AutoPropertyInitializer
<CdmBase
>> result
= new HashSet
<AutoPropertyInitializer
<CdmBase
>>();
446 for(Class
<?
extends CdmBase
> superClass
: getBeanAutoInitializers().keySet()){
447 if(superClass
.isAssignableFrom(clazz
)){
448 result
.add(getBeanAutoInitializers().get(superClass
));
455 * Rename bean attributes to hibernate (field) attribute, due to bean inconsistencies
461 private String
workAroundBeanInconsistency(String param
, String ownerClass
) {
462 if (ownerClass
.contains("Description") && param
.equals("elements")){
463 //DescriptionBase.descriptionElements -> elements
464 return "descriptionElements";
465 }else if(ownerClass
.equals("Classification") && param
.equals("childNodes")){
477 * @param unwrappedPropertyBean
479 private void initializeNodeSinglePropertyOld(BeanInitNode node
, String property
,
480 Integer index
, Object bean
, Object unwrappedPropertyBean
) {
481 Collection
<?
> collection
= null;
482 if(Map
.class.isAssignableFrom(unwrappedPropertyBean
.getClass())) {
483 collection
= ((Map
<?
,?
>)unwrappedPropertyBean
).values();
484 }else if (Collection
.class.isAssignableFrom(unwrappedPropertyBean
.getClass())) {
485 collection
= (Collection
<?
>) unwrappedPropertyBean
;
487 if (collection
!= null){
489 if (logger
.isDebugEnabled()){logger
.debug(" initialize collection for " + node
.toStringNoWildcard() + " ... ");}
491 for (Object entrybean
: collection
) {
493 node
.addBean(entrybean
);
494 } else if(index
.equals(i
)){
495 node
.addBean(entrybean
);
500 if (logger
.isDebugEnabled()){logger
.debug(" initialize collection for " + node
.toString() + " - DONE ");}
504 node
.addBean(unwrappedPropertyBean
);
505 setProperty(bean
, property
, unwrappedPropertyBean
);