Project

General

Profile

« Previous | Next » 

Revision 749b7448

Added by Andreas Müller over 8 years ago

Throw exceptions in AdvancedBeanInitializer instead of logging only
#5077

View differences:

cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/initializer/AdvancedBeanInitializer.java
1
/**
2
 *
3
 */
4
package eu.etaxonomy.cdm.persistence.dao.initializer;
5

  
6
import java.beans.PropertyDescriptor;
7
import java.io.Serializable;
8
import java.lang.reflect.InvocationTargetException;
9
import java.lang.reflect.Method;
10
import java.lang.reflect.ParameterizedType;
11
import java.lang.reflect.Type;
12
import java.lang.reflect.TypeVariable;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20

  
21
import org.apache.commons.beanutils.PropertyUtils;
22
import org.apache.log4j.Logger;
23
import org.hibernate.Hibernate;
24
import org.hibernate.HibernateException;
25
import org.hibernate.Query;
26
import org.hibernate.collection.internal.AbstractPersistentCollection;
27
import org.hibernate.collection.internal.PersistentMap;
28
import org.hibernate.proxy.HibernateProxy;
29
import org.springframework.beans.factory.annotation.Autowired;
30

  
31
import eu.etaxonomy.cdm.model.common.CdmBase;
32
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
33
import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;
34

  
35
/**
36
 * For now this is a test if we can improve performance for bean initializing
37
 * @author a.mueller
38
 * @date 2013-10-25
39
 *
40
 */
41
public class AdvancedBeanInitializer extends HibernateBeanInitializer {
42

  
43
        public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);
44

  
45
        @Autowired
46
        ICdmGenericDao genericDao;
47

  
48
        @Override
49
        public void initialize(Object bean, List<String> propertyPaths) {
50
            List<Object> beanList = new ArrayList<Object>(1);
51
            beanList.add(bean);
52
            initializeAll(beanList, propertyPaths);
53
        }
54

  
55
        //TODO optimize algorithm ..
56
        @Override
57
        public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {
58

  
59
            if (beanList == null || beanList.isEmpty()){
60
                return beanList;
61
            }
62

  
63
            //autoinitialize
64
            for (Object bean : beanList){
65
                autoinitializeBean(bean);
66
            }
67

  
68
            if(propertyPaths == null){
69
                return beanList;
70
            }
71

  
72

  
73
            //new
74
             BeanInitNode rootPath = BeanInitNode.createInitTree(propertyPaths);
75
            if (logger.isTraceEnabled()){logger.trace(rootPath.toStringTree());}
76

  
77

  
78
            if(logger.isDebugEnabled()){ logger.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList.iterator().next().getClass().getSimpleName());}
79
            rootPath.addBeans(beanList);
80
            initializeNodeRecursive(rootPath);
81

  
82

  
83
            //old - keep for safety (this may help to initialize those beans that are not yet correctly initialized by the AdvancedBeanInitializer
84
            if(logger.isTraceEnabled()){logger.trace("Start old initalizer ... ");};
85
            for (Object bean :beanList){
86
                Collections.sort(propertyPaths);
87
                for(String propPath : propertyPaths){
88
//		            initializePropertyPath(bean, propPath);
89
                }
90
            }
91

  
92
            if(logger.isDebugEnabled()){ logger.debug("   Completed initialization of beanlist "); }
93
            return beanList;
94

  
95
        }
96

  
97

  
98
        //new
99
        private void initializeNodeRecursive(BeanInitNode rootPath) {
100
            initializeNode(rootPath);
101
            for (BeanInitNode childPath : rootPath.getChildrenList()){
102
                initializeNodeRecursive(childPath);
103
            }
104
            rootPath.resetBeans();
105
        }
106

  
107
        /**
108
         * Initializes the given single <code>propPath</code> String.
109
         *
110
         * @param bean
111
         * @param propPath
112
         */
113
        private void initializeNode(BeanInitNode node) {
114
            if(logger.isDebugEnabled()){logger.debug(" processing " + node.toString());}
115
            if (node.isRoot()){
116
                return;
117
            }else if (node.isWildcard()){
118
                initializeNodeWildcard(node);
119
            } else {
120
                initializeNodeNoWildcard(node);
121
            }
122
        }
123

  
124
        // if propPath only contains a wildcard (* or $)
125
        // => do a batch initialization of *toOne or *toMany relations
126
        private void initializeNodeWildcard(BeanInitNode node) {
127
//			boolean initToMany = node.isToManyWildcard();
128
            Map<Class<?>, Set<Object>> parentBeans = node.getParentBeans();
129
            for (Class<?> clazz : parentBeans.keySet()){
130
                //new
131
                for (Object bean : parentBeans.get(clazz)){
132

  
133
                    if(Collection.class.isAssignableFrom(bean.getClass())){
134
//				        old: initializeAllEntries((Collection<?>)bean, true, initToMany);  //TODO is this a possible case at all??
135
                        throw new RuntimeException("Collection no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
136
                    } else if(Map.class.isAssignableFrom(bean.getClass())) {
137
//				        old: initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);  ////TODO is this a possible case at all??
138
                        throw new RuntimeException("Map no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
139
                    } else{
140
                        prepareBeanWildcardForBulkLoad(node, bean);
141
                    }
142
                }
143
                //end new
144

  
145
//		    	initializeNodeWildcardOld(initToMany, beans, clazz);  //if switched on move bulkLoadLazies up
146
            }
147

  
148
            //
149
            bulkLoadLazies(node);
150
        }
151

  
152
        /**
153
         * @param initToMany
154
         * @param beans
155
         * @param clazz
156
         */
157
        private void initializeNodeWildcardOld(boolean initToMany,
158
                Map<Class<?>, Set<Object>> beans, Class<?> clazz) {
159
            for (Object bean : beans.get(clazz)){
160

  
161
                if(Collection.class.isAssignableFrom(bean.getClass())){
162
                    initializeAllEntries((Collection<?>)bean, true, initToMany);
163
                } else if(Map.class.isAssignableFrom(bean.getClass())) {
164
                    initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);
165
                } else{
166
                    initializeBean(bean, true, initToMany);
167
                }
168
            }
169
        }
170

  
171
        private void prepareBeanWildcardForBulkLoad(BeanInitNode node, Object bean){
172

  
173
            if(logger.isTraceEnabled()){logger.trace(">> prepare bulk wildcard initialization of a bean of type " + bean.getClass().getSimpleName()); }
174
            Set<Class<?>> restrictions = new HashSet<Class<?>>();
175
            restrictions.add(CdmBase.class);
176
            if(node.isToManyWildcard()){
177
                restrictions.add(Collection.class);
178
            }
179
            Set<PropertyDescriptor> props = getProperties(bean, restrictions);
180
            for(PropertyDescriptor propertyDescriptor : props){
181
                try {
182
                    String property = propertyDescriptor.getName();
183

  
184
//                  invokeInitialization(bean, propertyDescriptor);
185
                    Object propertyValue = PropertyUtils.getProperty( bean, property);
186

  
187
                    preparePropertyValueForBulkLoadOrStore(node, bean, property,  propertyValue );
188

  
189
                } catch (IllegalAccessException e) {
190
                    logger.error("Illegal access on property " + propertyDescriptor.getName());
191
                } catch (InvocationTargetException e) {
192
                    logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");
193
                } catch (NoSuchMethodException e) {
194
                    logger.info("Property " + propertyDescriptor.getName() + " not found");
195
                }
196
            }
197
            if(logger.isTraceEnabled()){logger.trace(" completed bulk wildcard initialization of a bean");}
198
        }
199

  
200

  
201

  
202
        // propPath contains either a single field or a nested path
203
        // split next path token off and keep the remaining as nestedPath
204
        private void initializeNodeNoWildcard(BeanInitNode node) {
205

  
206
            String property = node.getPath();
207
            int pos;
208

  
209
            // is the property indexed?
210
            Integer index = null;
211
            if((pos = property.indexOf('[')) > 0){
212
                String indexString = property.substring(pos + 1, property.indexOf(']'));
213
                index = Integer.valueOf(indexString);
214
                property = property.substring(0, pos);
215
            }
216

  
217
            //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
218

  
219
            for (Class<?> parentClazz : node.getParentBeans().keySet()){
220
                if (logger.isTraceEnabled()){logger.trace(" invoke initialization on "+ node.toString()+ " beans of class " + parentClazz.getSimpleName() + " ... ");}
221

  
222
                Set<Object> parentBeans = node.getParentBeans().get(parentClazz);
223

  
224
                if (index != null){
225
                    logger.warn("Property path index not yet implemented for 'new'");
226
                }
227
                //new
228
                for (Object parentBean : parentBeans){
229
                    String propertyName = mapFieldToPropertyName(property, parentBean.getClass().getSimpleName());
230
                    try{
231
                        Object propertyValue = PropertyUtils.getProperty(parentBean, propertyName);
232
                        preparePropertyValueForBulkLoadOrStore(node, parentBean, property, propertyValue);
233
                    } catch (IllegalAccessException e) {
234
                        logger.error("Illegal access on property " + property);
235
                    } catch (InvocationTargetException e) {
236
                        logger.error("Cannot invoke property " + property + " not found");
237
                    } catch (NoSuchMethodException e) {
238
                        if (logger.isDebugEnabled()){logger.debug("Property " + propertyName + " not found for class " + parentClazz);}
239
                    }
240
                }
241

  
242
                //end new
243

  
244
//			    	initializeNodeNoWildcardOld(node, property, index, parentBeans);  //move bulkLoadLazies up again, if uncomment this line
245
            }
246
            bulkLoadLazies(node);
247

  
248
        }
249

  
250
        /**
251
         * @param node
252
         * @param property
253
         * @param index
254
         * @param parentBeans
255
         * @throws IllegalAccessException
256
         * @throws InvocationTargetException
257
         * @throws NoSuchMethodException
258
         */
259
        private void initializeNodeNoWildcardOld(BeanInitNode node,
260
                String property, Integer index, Set<Object> parentBeans)
261
                throws IllegalAccessException, InvocationTargetException,
262
                NoSuchMethodException {
263
            for (Object bean : parentBeans){
264

  
265
                PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);
266
                if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " ... ");}
267
                // [1] initialize the bean named by property
268
                Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);
269
                if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " - DONE ");}
270

  
271

  
272
                // [2]
273
                // handle property
274
                if(unwrappedPropertyBean != null ){
275
                    initializeNodeSinglePropertyOld(node, property, index, bean, unwrappedPropertyBean);
276
                }
277
            }
278
        }
279

  
280
        /**
281
         * @param node
282
         * @param propertyValue
283
         * @param parentBean
284
         * @param param
285
         */
286
        private void preparePropertyValueForBulkLoadOrStore(BeanInitNode node, Object parentBean, String param, Object propertyValue) {
287
            BeanInitNode sibling = node.getSibling(param);
288

  
289
            if (propertyValue instanceof AbstractPersistentCollection ){
290
                //collections
291
                if (!node.hasWildcardToManySibling()){  //if wildcard sibling exists the lazies are already prepared there
292
                    AbstractPersistentCollection collection = (AbstractPersistentCollection)propertyValue;
293
                    if (collection.wasInitialized()){
294
                        storeInitializedCollection(collection, node, param);
295
                    }else{
296
//						Class<?> parentClass = parentBean.getClass();
297
//						int parentId = ((CdmBase)parentBean).getId();
298
                        if (sibling != null){
299
                            sibling.putLazyCollection(collection);
300
                        }else{
301
                            node.putLazyCollection(collection);
302
                        }
303
                    }
304
                }
305
            }else{
306
                //singles
307
                if (!node.hasWildcardToOneSibling()){  //if wildcard exists the lazies are already prepared there
308
                    if (! Hibernate.isInitialized(propertyValue)){
309
                        if (propertyValue instanceof HibernateProxy){
310
                            Serializable id = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getIdentifier();
311
                            Class<?> persistedClass = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getPersistentClass();
312
                            if (sibling != null){
313
                                sibling.putLazyBean(persistedClass, id);
314
                            }else{
315
                                node.putLazyBean(persistedClass, id);
316
                            }
317

  
318
                        }else{
319
                            logger.warn("Lazy value is not of type HibernateProxy. This is not yet handled.");
320
                        }
321
                    }else if (propertyValue == null){
322
                        // do nothing
323
                    }else{
324
                        if (propertyValue instanceof HibernateProxy){  //TODO remove hibernate dependency
325
                            propertyValue = initializeInstance(propertyValue);
326
                        }
327
                        autoinitializeBean(propertyValue);
328
                        node.addBean(propertyValue);
329
                    }
330
                }
331
            }
332
        }
333

  
334
        private void autoinitializeBean(Object bean) {
335
            invokePropertyAutoInitializers(bean);
336
        }
337

  
338
        private void autoinitializeBean(CdmBase bean, AutoInit autoInit) {
339
            for(AutoPropertyInitializer<CdmBase> init : autoInit.initlializers) {
340
                init.initialize(bean);
341
            }
342
        }
343

  
344
		private void storeInitializedCollection(AbstractPersistentCollection persistedCollection,
345
				BeanInitNode node, String param) {
346
			Collection<?> collection;
347

  
348
			if (persistedCollection  instanceof Collection) {
349
				collection = (Collection<?>) persistedCollection;
350
			}else if (persistedCollection instanceof Map) {
351
				collection = ((Map<?,?>)persistedCollection).values();
352
			}else{
353
				throw new RuntimeException ("Non Map and non Collection cas not handled in storeInitializedCollection()");
354
			}
355
			for (Object value : collection){
356
				preparePropertyValueForBulkLoadOrStore(node, null, param, value);
357
			}
358
		}
359

  
360
		private void bulkLoadLazies(BeanInitNode node) {
361

  
362
			if (logger.isTraceEnabled()){logger.trace("bulk load " +  node);}
363

  
364
			//beans
365
			for (Class<?> clazz : node.getLazyBeans().keySet()){
366
				Set<Serializable> idSet = node.getLazyBeans().get(clazz);
367
				if (idSet != null && ! idSet.isEmpty()){
368

  
369
					if (logger.isTraceEnabled()){logger.trace("bulk load beans of class " +  clazz.getSimpleName());}
370
					//TODO use entity name
371
					String hql = " SELECT c FROM %s as c %s WHERE c.id IN (:idSet) ";
372
					AutoInit autoInit = addAutoinitFetchLoading(clazz, "c");
373
                    hql = String.format(hql, clazz.getSimpleName(), autoInit.leftJoinFetch);
374
					if (logger.isTraceEnabled()){logger.trace(hql);}
375
					Query query = genericDao.getHqlQuery(hql);
376
					query.setParameterList("idSet", idSet);
377
					List<Object> list = query.list();
378

  
379
					if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded beans of class " +  clazz.getSimpleName());}
380
					for (Object object : list){
381
						if (object instanceof HibernateProxy){  //TODO remove hibernate dependency
382
							object = initializeInstance(object);
383
						}
384
						autoinitializeBean((CdmBase)object, autoInit);
385
						node.addBean(object);
386
					}
387
					if (logger.isTraceEnabled()){logger.trace("bulk load - DONE");}
388
				}
389
			}
390
			node.resetLazyBeans();
391

  
392
			//collections
393
			for (Class<?> ownerClazz : node.getLazyCollections().keySet()){
394
				Map<String, Set<Serializable>> lazyParams = node.getLazyCollections().get(ownerClazz);
395
				for (String param : lazyParams.keySet()){
396
					Set<Serializable> idSet = lazyParams.get(param);
397
					if (idSet != null && ! idSet.isEmpty()){
398
						if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections ; ownerClass=" +  ownerClazz.getSimpleName() + " ; param = " + param);}
399

  
400
						Type collectionEntitiyType = null;
401
						PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(ownerClazz);
402
						for(PropertyDescriptor d : descriptors) {
403
						    if(d.getName().equals(param)) {
404
						        Method readMethod = d.getReadMethod();
405
                                ParameterizedType pt = (ParameterizedType) readMethod.getGenericReturnType();
406
                                Type[] actualTypeArguments = pt.getActualTypeArguments();
407
                                if(actualTypeArguments.length == 2) {
408
                                    // this must be a map of <Language, String> (aka LanguageString) there is no other case like this in the cdm
409
                                    // in case of Maps the returned Collection will be the Collection of the values, so collectionEntitiyType is the
410
                                    // second typeArgument
411
                                    collectionEntitiyType = actualTypeArguments[1];
412
                                } else {
413
                                    collectionEntitiyType = actualTypeArguments[0];
414
                                }
415
                                if(collectionEntitiyType instanceof TypeVariable) {
416
                                    collectionEntitiyType = ((TypeVariable)collectionEntitiyType).getBounds()[0];
417
                                }
418
						    }
419
						}
420

  
421
						//TODO use entity name ??
422
						//get from repository
423
						List<Object[]> list;
424
						String hql = "SELECT oc " +
425
								" FROM %s as oc LEFT JOIN FETCH oc.%s as col %s" +
426
								" WHERE oc.id IN (:idSet) ";
427

  
428
						AutoInit autoInit = addAutoinitFetchLoading((Class)collectionEntitiyType, "col");
429
                        hql = String.format(hql, ownerClazz.getSimpleName(), param,
430
						        autoInit.leftJoinFetch);
431

  
432
						try {
433
							if (logger.isTraceEnabled()){logger.trace(hql);}
434
							Query query = genericDao.getHqlQuery(hql);
435
							query.setParameterList("idSet", idSet);
436
							list = query.list();
437
							if (logger.isTraceEnabled()){logger.trace("size of retrieved list is " + list.size());}
438
						} catch (HibernateException e) {
439
							e.printStackTrace();
440
							throw e;
441
						}
442

  
443
						//getTarget and add to child node
444
						if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded " + node + " collections - DONE");}
445
						for (Object parentBean : list){
446
                            try {
447
							    Object propValue = PropertyUtils.getProperty(
448
							            parentBean,
449
							            mapFieldToPropertyName(param, parentBean.getClass().getSimpleName())
450
							          );
451

  
452
    							if (propValue == null){
453
    							    logger.trace("Collection is null");
454
    							}else {
455
    							    if(propValue instanceof PersistentMap) {
456
    							        propValue = ((PersistentMap)propValue).values();
457
    							    }
458
    							    for(Object newBean : (Collection<Object>)propValue ) {
459
    							        if(newBean instanceof HibernateProxy){
460
    							            newBean = initializeInstance(newBean);
461
    							        }
462

  
463
    							        autoinitializeBean((CdmBase)newBean, autoInit);
464

  
465
    							        node.addBean(newBean);
466
    							    }
467
    							}
468
                            } catch (Exception e) {
469
                                // TODO better throw an exception ?
470
                                logger.error("error while getting collection property", e);
471
                            }
472
						}
473
						if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections - DONE");}
474
					}
475
				}
476
			}
477
			for (AbstractPersistentCollection collection : node.getUninitializedCollections()){
478
				if (! collection.wasInitialized()){  //should not happen anymore
479
					collection.forceInitialization();
480
					if (logger.isTraceEnabled()){logger.trace("forceInitialization of collection " + collection);}
481
				} else {
482
				    if (logger.isTraceEnabled()){logger.trace("collection " + collection + " is initialized - OK!");}
483
				}
484
			}
485

  
486
			node.resetLazyCollections();
487

  
488
			if (logger.isDebugEnabled()){logger.debug("bulk load " +  node + " - DONE ");}
489

  
490
		}
491

  
492

  
493
        private AutoInit addAutoinitFetchLoading(Class<?> clazz, String beanAlias) {
494

  
495
            AutoInit autoInit = new AutoInit();
496
            if(clazz != null) {
497
                Set<AutoPropertyInitializer<CdmBase>> inits = getAutoInitializers(clazz);
498
                for (AutoPropertyInitializer<CdmBase> init: inits){
499
                    try {
500
                        autoInit.leftJoinFetch +=init.hibernateFetchJoin(clazz, beanAlias);
501
                    } catch (Exception e) {
502
                        // the AutoPropertyInitializer is not supporting LEFT JOIN FETCH so it needs to be
503
                        // used explicitly
504
                        autoInit.initlializers.add(init);
505
                    }
506

  
507
                }
508
            }
509
            return autoInit;
510
        }
511

  
512
        private Set<AutoPropertyInitializer<CdmBase>> getAutoInitializers(Class<?> clazz) {
513
            Set<AutoPropertyInitializer<CdmBase>> result = new HashSet<AutoPropertyInitializer<CdmBase>>();
514
            for(Class<? extends CdmBase> superClass : getBeanAutoInitializers().keySet()){
515
                if(superClass.isAssignableFrom(clazz)){
516
                    result.add(getBeanAutoInitializers().get(superClass));
517
                }
518
            }
519
            return result;
520
        }
521

  
522
        /**
523
         * Rename hibernate (field) attribute to Bean property name, due to bean inconsistencies
524
         * #3841
525
         * @param param
526
         * @param ownerClass
527
         * @return
528
         */
529
        private String mapFieldToPropertyName(String param, String ownerClass) {
530
            if (ownerClass.contains("Description") && param.equals("descriptionElements")){
531
                return "elements";
532
            }
533
            if (ownerClass.startsWith("FeatureNode") && param.equals("children")) {
534
                return "childNodes";
535
            }
536
            if (ownerClass.startsWith("Media") && param.equals("description")) {
537
                return "allDescriptions";
538
            }
539
            else{
540
                return param;
541
            }
542
        }
543

  
544
        /**
545
         * @param node
546
         * @param property
547
         * @param index
548
         * @param bean
549
         * @param unwrappedPropertyBean
550
         */
551
        private void initializeNodeSinglePropertyOld(BeanInitNode node, String property,
552
                Integer index, Object bean, Object unwrappedPropertyBean) {
553
            Collection<?> collection = null;
554
            if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
555
                collection = ((Map<?,?>)unwrappedPropertyBean).values();
556
            }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
557
                collection =  (Collection<?>) unwrappedPropertyBean;
558
            }
559
            if (collection != null){
560
                //collection or map
561
                if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toStringNoWildcard() + " ... ");}
562
                int i = 0;
563
                for (Object entrybean : collection) {
564
                    if(index == null){
565
                        node.addBean(entrybean);
566
                    } else if(index.equals(i)){
567
                        node.addBean(entrybean);
568
                        break;
569
                    }
570
                    i++;
571
                }
572
                if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toString() + " - DONE ");}
573

  
574
            }else {
575
                // nested bean
576
                node.addBean(unwrappedPropertyBean);
577
                setProperty(bean, property, unwrappedPropertyBean);
578
            }
579
        }
580

  
581
        private class AutoInit{
582

  
583
            String leftJoinFetch = "";
584
            Set<AutoPropertyInitializer<CdmBase>> initlializers = new HashSet<AutoPropertyInitializer<CdmBase>>();
585

  
586
            /**
587
             * @param leftJoinFetch
588
             * @param initlializers
589
             */
590
            public AutoInit() {
591
            }
592
        }
593

  
594
}
1
/**
2
 *
3
 */
4
package eu.etaxonomy.cdm.persistence.dao.initializer;
5

  
6
import java.beans.PropertyDescriptor;
7
import java.io.Serializable;
8
import java.lang.reflect.InvocationTargetException;
9
import java.lang.reflect.Method;
10
import java.lang.reflect.ParameterizedType;
11
import java.lang.reflect.Type;
12
import java.lang.reflect.TypeVariable;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20

  
21
import org.apache.commons.beanutils.PropertyUtils;
22
import org.apache.log4j.Logger;
23
import org.hibernate.Hibernate;
24
import org.hibernate.HibernateException;
25
import org.hibernate.Query;
26
import org.hibernate.collection.internal.AbstractPersistentCollection;
27
import org.hibernate.collection.internal.PersistentMap;
28
import org.hibernate.proxy.HibernateProxy;
29
import org.springframework.beans.factory.annotation.Autowired;
30

  
31
import eu.etaxonomy.cdm.model.common.CdmBase;
32
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
33
import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;
34

  
35
/**
36
 * For now this is a test if we can improve performance for bean initializing
37
 * @author a.mueller
38
 * @date 2013-10-25
39
 *
40
 */
41
public class AdvancedBeanInitializer extends HibernateBeanInitializer {
42

  
43
    public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);
44

  
45
    @Autowired
46
    ICdmGenericDao genericDao;
47

  
48
    @Override
49
    public void initialize(Object bean, List<String> propertyPaths) {
50
        List<Object> beanList = new ArrayList<Object>(1);
51
        beanList.add(bean);
52
        initializeAll(beanList, propertyPaths);
53
    }
54

  
55
    //TODO optimize algorithm ..
56
    @Override
57
    public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {
58

  
59
        if (beanList == null || beanList.isEmpty()){
60
            return beanList;
61
        }
62

  
63
        //autoinitialize
64
        for (Object bean : beanList){
65
            autoinitializeBean(bean);
66
        }
67

  
68
        if(propertyPaths == null){
69
            return beanList;
70
        }
71

  
72

  
73
        //new
74
         BeanInitNode rootPath = BeanInitNode.createInitTree(propertyPaths);
75
        if (logger.isTraceEnabled()){logger.trace(rootPath.toStringTree());}
76

  
77

  
78
        if(logger.isDebugEnabled()){ logger.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList.iterator().next().getClass().getSimpleName());}
79
        rootPath.addBeans(beanList);
80
        initializeNodeRecursive(rootPath);
81

  
82

  
83
        //old - keep for safety (this may help to initialize those beans that are not yet correctly initialized by the AdvancedBeanInitializer
84
        if(logger.isTraceEnabled()){logger.trace("Start old initalizer ... ");};
85
        for (Object bean :beanList){
86
            Collections.sort(propertyPaths);
87
            for(String propPath : propertyPaths){
88
//		            initializePropertyPath(bean, propPath);
89
            }
90
        }
91

  
92
        if(logger.isDebugEnabled()){ logger.debug("   Completed initialization of beanlist "); }
93
        return beanList;
94

  
95
    }
96

  
97

  
98
    //new
99
    private void initializeNodeRecursive(BeanInitNode rootPath) {
100
        initializeNode(rootPath);
101
        for (BeanInitNode childPath : rootPath.getChildrenList()){
102
            initializeNodeRecursive(childPath);
103
        }
104
        rootPath.resetBeans();
105
    }
106

  
107
    /**
108
     * Initializes the given single <code>propPath</code> String.
109
     *
110
     * @param bean
111
     * @param propPath
112
     */
113
    private void initializeNode(BeanInitNode node) {
114
        if(logger.isDebugEnabled()){logger.debug(" processing " + node.toString());}
115
        if (node.isRoot()){
116
            return;
117
        }else if (node.isWildcard()){
118
            initializeNodeWildcard(node);
119
        } else {
120
            initializeNodeNoWildcard(node);
121
        }
122
    }
123

  
124
    // if propPath only contains a wildcard (* or $)
125
    // => do a batch initialization of *toOne or *toMany relations
126
    private void initializeNodeWildcard(BeanInitNode node) {
127
//			boolean initToMany = node.isToManyWildcard();
128
        Map<Class<?>, Set<Object>> parentBeans = node.getParentBeans();
129
        for (Class<?> clazz : parentBeans.keySet()){
130
            //new
131
            for (Object bean : parentBeans.get(clazz)){
132

  
133
                if(Collection.class.isAssignableFrom(bean.getClass())){
134
//				        old: initializeAllEntries((Collection<?>)bean, true, initToMany);  //TODO is this a possible case at all??
135
                    throw new RuntimeException("Collection no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
136
                } else if(Map.class.isAssignableFrom(bean.getClass())) {
137
//				        old: initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);  ////TODO is this a possible case at all??
138
                    throw new RuntimeException("Map no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");
139
                } else{
140
                    prepareBeanWildcardForBulkLoad(node, bean);
141
                }
142
            }
143
            //end new
144

  
145
//		    	initializeNodeWildcardOld(initToMany, beans, clazz);  //if switched on move bulkLoadLazies up
146
        }
147

  
148
        //
149
        bulkLoadLazies(node);
150
    }
151

  
152
    /**
153
     * @param initToMany
154
     * @param beans
155
     * @param clazz
156
     */
157
    private void initializeNodeWildcardOld(boolean initToMany,
158
            Map<Class<?>, Set<Object>> beans, Class<?> clazz) {
159
        for (Object bean : beans.get(clazz)){
160

  
161
            if(Collection.class.isAssignableFrom(bean.getClass())){
162
                initializeAllEntries((Collection<?>)bean, true, initToMany);
163
            } else if(Map.class.isAssignableFrom(bean.getClass())) {
164
                initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);
165
            } else{
166
                initializeBean(bean, true, initToMany);
167
            }
168
        }
169
    }
170

  
171
    private void prepareBeanWildcardForBulkLoad(BeanInitNode node, Object bean){
172

  
173
        if(logger.isTraceEnabled()){logger.trace(">> prepare bulk wildcard initialization of a bean of type " + bean.getClass().getSimpleName()); }
174
        Set<Class<?>> restrictions = new HashSet<Class<?>>();
175
        restrictions.add(CdmBase.class);
176
        if(node.isToManyWildcard()){
177
            restrictions.add(Collection.class);
178
        }
179
        Set<PropertyDescriptor> props = getProperties(bean, restrictions);
180
        for(PropertyDescriptor propertyDescriptor : props){
181
            try {
182
                String property = propertyDescriptor.getName();
183

  
184
//                  invokeInitialization(bean, propertyDescriptor);
185
                Object propertyValue = PropertyUtils.getProperty( bean, property);
186

  
187
                preparePropertyValueForBulkLoadOrStore(node, bean, property,  propertyValue );
188

  
189
            } catch (IllegalAccessException e) {
190
                logger.error("Illegal access on property " + propertyDescriptor.getName());
191
            } catch (InvocationTargetException e) {
192
                logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");
193
            } catch (NoSuchMethodException e) {
194
                logger.info("Property " + propertyDescriptor.getName() + " not found");
195
            }
196
        }
197
        if(logger.isTraceEnabled()){logger.trace(" completed bulk wildcard initialization of a bean");}
198
    }
199

  
200

  
201

  
202
    // propPath contains either a single field or a nested path
203
    // split next path token off and keep the remaining as nestedPath
204
    private void initializeNodeNoWildcard(BeanInitNode node) {
205

  
206
        String property = node.getPath();
207
        int pos;
208

  
209
        // is the property indexed?
210
        Integer index = null;
211
        if((pos = property.indexOf('[')) > 0){
212
            String indexString = property.substring(pos + 1, property.indexOf(']'));
213
            index = Integer.valueOf(indexString);
214
            property = property.substring(0, pos);
215
        }
216

  
217
        //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
218

  
219
        for (Class<?> parentClazz : node.getParentBeans().keySet()){
220
            if (logger.isTraceEnabled()){logger.trace(" invoke initialization on "+ node.toString()+ " beans of class " + parentClazz.getSimpleName() + " ... ");}
221

  
222
            Set<Object> parentBeans = node.getParentBeans().get(parentClazz);
223

  
224
            if (index != null){
225
                logger.warn("Property path index not yet implemented for 'new'");
226
            }
227
            //new
228
            for (Object parentBean : parentBeans){
229
                String propertyName = mapFieldToPropertyName(property, parentBean.getClass().getSimpleName());
230
                try{
231
                    Object propertyValue = PropertyUtils.getProperty(parentBean, propertyName);
232
                    preparePropertyValueForBulkLoadOrStore(node, parentBean, property, propertyValue);
233
                } catch (IllegalAccessException e) {
234
                    String message = "Illegal access on property " + property;
235
                    logger.error(message);
236
                    throw new RuntimeException(message, e);
237
                } catch (InvocationTargetException e) {
238
                    String message = "Cannot invoke property " + property + " not found";
239
                    logger.error(message);
240
                    throw new RuntimeException(message, e);
241
                } catch (NoSuchMethodException e) {
242
                    String message = "Property " + propertyName + " not found for class " + parentClazz;
243
                    logger.error(message);
244
                    throw new RuntimeException(message, e);
245
                }
246
            }
247

  
248
            //end new
249

  
250
//			 initializeNodeNoWildcardOld(node, property, index, parentBeans);  //move bulkLoadLazies up again, if uncomment this line
251
        }
252
        bulkLoadLazies(node);
253

  
254
    }
255

  
256
    /**
257
     * @param node
258
     * @param property
259
     * @param index
260
     * @param parentBeans
261
     * @throws IllegalAccessException
262
     * @throws InvocationTargetException
263
     * @throws NoSuchMethodException
264
     */
265
    private void initializeNodeNoWildcardOld(BeanInitNode node,
266
            String property, Integer index, Set<Object> parentBeans)
267
            throws IllegalAccessException, InvocationTargetException,
268
            NoSuchMethodException {
269
        for (Object bean : parentBeans){
270

  
271
            PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);
272
            if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " ... ");}
273
            // [1] initialize the bean named by property
274
            Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);
275
            if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " - DONE ");}
276

  
277

  
278
            // [2]
279
            // handle property
280
            if(unwrappedPropertyBean != null ){
281
                initializeNodeSinglePropertyOld(node, property, index, bean, unwrappedPropertyBean);
282
            }
283
        }
284
    }
285

  
286
    /**
287
     * @param node
288
     * @param propertyValue
289
     * @param parentBean
290
     * @param param
291
     */
292
    private void preparePropertyValueForBulkLoadOrStore(BeanInitNode node, Object parentBean, String param, Object propertyValue) {
293
        BeanInitNode sibling = node.getSibling(param);
294

  
295
        if (propertyValue instanceof AbstractPersistentCollection ){
296
            //collections
297
            if (!node.hasWildcardToManySibling()){  //if wildcard sibling exists the lazies are already prepared there
298
                AbstractPersistentCollection collection = (AbstractPersistentCollection)propertyValue;
299
                if (collection.wasInitialized()){
300
                    storeInitializedCollection(collection, node, param);
301
                }else{
302
//						Class<?> parentClass = parentBean.getClass();
303
//						int parentId = ((CdmBase)parentBean).getId();
304
                    if (sibling != null){
305
                        sibling.putLazyCollection(collection);
306
                    }else{
307
                        node.putLazyCollection(collection);
308
                    }
309
                }
310
            }
311
        }else{
312
            //singles
313
            if (!node.hasWildcardToOneSibling()){  //if wildcard exists the lazies are already prepared there
314
                if (! Hibernate.isInitialized(propertyValue)){
315
                    if (propertyValue instanceof HibernateProxy){
316
                        Serializable id = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getIdentifier();
317
                        Class<?> persistedClass = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getPersistentClass();
318
                        if (sibling != null){
319
                            sibling.putLazyBean(persistedClass, id);
320
                        }else{
321
                            node.putLazyBean(persistedClass, id);
322
                        }
323

  
324
                    }else{
325
                        logger.warn("Lazy value is not of type HibernateProxy. This is not yet handled.");
326
                    }
327
                }else if (propertyValue == null){
328
                    // do nothing
329
                }else{
330
                    if (propertyValue instanceof HibernateProxy){  //TODO remove hibernate dependency
331
                        propertyValue = initializeInstance(propertyValue);
332
                    }
333
                    autoinitializeBean(propertyValue);
334
                    node.addBean(propertyValue);
335
                }
336
            }
337
        }
338
    }
339

  
340
    private void autoinitializeBean(Object bean) {
341
        invokePropertyAutoInitializers(bean);
342
    }
343

  
344
    private void autoinitializeBean(CdmBase bean, AutoInit autoInit) {
345
        for(AutoPropertyInitializer<CdmBase> init : autoInit.initlializers) {
346
            init.initialize(bean);
347
        }
348
    }
349

  
350
	private void storeInitializedCollection(AbstractPersistentCollection persistedCollection,
351
			BeanInitNode node, String param) {
352
		Collection<?> collection;
353

  
354
		if (persistedCollection  instanceof Collection) {
355
			collection = (Collection<?>) persistedCollection;
356
		}else if (persistedCollection instanceof Map) {
357
			collection = ((Map<?,?>)persistedCollection).values();
358
		}else{
359
			throw new RuntimeException ("Non Map and non Collection cas not handled in storeInitializedCollection()");
360
		}
361
		for (Object value : collection){
362
			preparePropertyValueForBulkLoadOrStore(node, null, param, value);
363
		}
364
	}
365

  
366
	private void bulkLoadLazies(BeanInitNode node) {
367

  
368
		if (logger.isTraceEnabled()){logger.trace("bulk load " +  node);}
369

  
370
		//beans
371
		for (Class<?> clazz : node.getLazyBeans().keySet()){
372
			Set<Serializable> idSet = node.getLazyBeans().get(clazz);
373
			if (idSet != null && ! idSet.isEmpty()){
374

  
375
				if (logger.isTraceEnabled()){logger.trace("bulk load beans of class " +  clazz.getSimpleName());}
376
				//TODO use entity name
377
				String hql = " SELECT c FROM %s as c %s WHERE c.id IN (:idSet) ";
378
				AutoInit autoInit = addAutoinitFetchLoading(clazz, "c");
379
                hql = String.format(hql, clazz.getSimpleName(), autoInit.leftJoinFetch);
380
				if (logger.isTraceEnabled()){logger.trace(hql);}
381
				Query query = genericDao.getHqlQuery(hql);
382
				query.setParameterList("idSet", idSet);
383
				List<Object> list = query.list();
384

  
385
				if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded beans of class " +  clazz.getSimpleName());}
386
				for (Object object : list){
387
					if (object instanceof HibernateProxy){  //TODO remove hibernate dependency
388
						object = initializeInstance(object);
389
					}
390
					autoinitializeBean((CdmBase)object, autoInit);
391
					node.addBean(object);
392
				}
393
				if (logger.isTraceEnabled()){logger.trace("bulk load - DONE");}
394
			}
395
		}
396
		node.resetLazyBeans();
397

  
398
		//collections
399
		for (Class<?> ownerClazz : node.getLazyCollections().keySet()){
400
			Map<String, Set<Serializable>> lazyParams = node.getLazyCollections().get(ownerClazz);
401
			for (String param : lazyParams.keySet()){
402
				Set<Serializable> idSet = lazyParams.get(param);
403
				if (idSet != null && ! idSet.isEmpty()){
404
					if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections ; ownerClass=" +  ownerClazz.getSimpleName() + " ; param = " + param);}
405

  
406
					Type collectionEntitiyType = null;
407
					PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(ownerClazz);
408
					for(PropertyDescriptor d : descriptors) {
409
					    if(d.getName().equals(param)) {
410
					        Method readMethod = d.getReadMethod();
411
                            ParameterizedType pt = (ParameterizedType) readMethod.getGenericReturnType();
412
                            Type[] actualTypeArguments = pt.getActualTypeArguments();
413
                            if(actualTypeArguments.length == 2) {
414
                                // this must be a map of <Language, String> (aka LanguageString) there is no other case like this in the cdm
415
                                // in case of Maps the returned Collection will be the Collection of the values, so collectionEntitiyType is the
416
                                // second typeArgument
417
                                collectionEntitiyType = actualTypeArguments[1];
418
                            } else {
419
                                collectionEntitiyType = actualTypeArguments[0];
420
                            }
421
                            if(collectionEntitiyType instanceof TypeVariable) {
422
                                collectionEntitiyType = ((TypeVariable)collectionEntitiyType).getBounds()[0];
423
                            }
424
					    }
425
					}
426

  
427
					//TODO use entity name ??
428
					//get from repository
429
					List<Object[]> list;
430
					String hql = "SELECT oc " +
431
							" FROM %s as oc LEFT JOIN FETCH oc.%s as col %s" +
432
							" WHERE oc.id IN (:idSet) ";
433

  
434
					AutoInit autoInit = addAutoinitFetchLoading((Class)collectionEntitiyType, "col");
435
                    hql = String.format(hql, ownerClazz.getSimpleName(), param,
436
					        autoInit.leftJoinFetch);
437

  
438
					try {
439
						if (logger.isTraceEnabled()){logger.trace(hql);}
440
						Query query = genericDao.getHqlQuery(hql);
441
						query.setParameterList("idSet", idSet);
442
						list = query.list();
443
						if (logger.isTraceEnabled()){logger.trace("size of retrieved list is " + list.size());}
444
					} catch (HibernateException e) {
445
						e.printStackTrace();
446
						throw e;
447
					}
448

  
449
					//getTarget and add to child node
450
					if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded " + node + " collections - DONE");}
451
					for (Object parentBean : list){
452
                        try {
453
						    Object propValue = PropertyUtils.getProperty(
454
						            parentBean,
455
						            mapFieldToPropertyName(param, parentBean.getClass().getSimpleName())
456
						          );
457

  
458
							if (propValue == null){
459
							    logger.trace("Collection is null");
460
							}else {
461
							    if(propValue instanceof PersistentMap) {
462
							        propValue = ((PersistentMap)propValue).values();
463
							    }
464
							    for(Object newBean : (Collection<Object>)propValue ) {
465
							        if(newBean instanceof HibernateProxy){
466
							            newBean = initializeInstance(newBean);
467
							        }
468

  
469
							        autoinitializeBean((CdmBase)newBean, autoInit);
470

  
471
							        node.addBean(newBean);
472
							    }
473
							}
474
                        } catch (Exception e) {
475
                            // TODO better throw an exception ?
476
                            logger.error("error while getting collection property", e);
477
                        }
478
					}
479
					if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections - DONE");}
480
				}
481
			}
482
		}
483
		for (AbstractPersistentCollection collection : node.getUninitializedCollections()){
484
			if (! collection.wasInitialized()){  //should not happen anymore
485
				collection.forceInitialization();
486
				if (logger.isTraceEnabled()){logger.trace("forceInitialization of collection " + collection);}
487
			} else {
488
			    if (logger.isTraceEnabled()){logger.trace("collection " + collection + " is initialized - OK!");}
489
			}
490
		}
491

  
492
		node.resetLazyCollections();
493

  
494
		if (logger.isDebugEnabled()){logger.debug("bulk load " +  node + " - DONE ");}
495

  
496
	}
497

  
498

  
499
    private AutoInit addAutoinitFetchLoading(Class<?> clazz, String beanAlias) {
500

  
501
        AutoInit autoInit = new AutoInit();
502
        if(clazz != null) {
503
            Set<AutoPropertyInitializer<CdmBase>> inits = getAutoInitializers(clazz);
504
            for (AutoPropertyInitializer<CdmBase> init: inits){
505
                try {
506
                    autoInit.leftJoinFetch +=init.hibernateFetchJoin(clazz, beanAlias);
507
                } catch (Exception e) {
508
                    // the AutoPropertyInitializer is not supporting LEFT JOIN FETCH so it needs to be
509
                    // used explicitly
510
                    autoInit.initlializers.add(init);
511
                }
512

  
513
            }
514
        }
515
        return autoInit;
516
    }
517

  
518
    private Set<AutoPropertyInitializer<CdmBase>> getAutoInitializers(Class<?> clazz) {
519
        Set<AutoPropertyInitializer<CdmBase>> result = new HashSet<AutoPropertyInitializer<CdmBase>>();
520
        for(Class<? extends CdmBase> superClass : getBeanAutoInitializers().keySet()){
521
            if(superClass.isAssignableFrom(clazz)){
522
                result.add(getBeanAutoInitializers().get(superClass));
523
            }
524
        }
525
        return result;
526
    }
527

  
528
    /**
529
     * Rename hibernate (field) attribute to Bean property name, due to bean inconsistencies
530
     * #3841
531
     * @param param
532
     * @param ownerClass
533
     * @return
534
     */
535
    private String mapFieldToPropertyName(String param, String ownerClass) {
536
        if (ownerClass.contains("Description") && param.equals("descriptionElements")){
537
            return "elements";
538
        }
539
        if (ownerClass.startsWith("FeatureNode") && param.equals("children")) {
540
            return "childNodes";
541
        }
542
        if (ownerClass.startsWith("Media") && param.equals("description")) {
543
            return "allDescriptions";
544
        }
545
        else{
546
            return param;
547
        }
548
    }
549

  
550
    /**
551
     * @param node
552
     * @param property
553
     * @param index
554
     * @param bean
555
     * @param unwrappedPropertyBean
556
     */
557
    private void initializeNodeSinglePropertyOld(BeanInitNode node, String property,
558
            Integer index, Object bean, Object unwrappedPropertyBean) {
559
        Collection<?> collection = null;
560
        if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
561
            collection = ((Map<?,?>)unwrappedPropertyBean).values();
562
        }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
563
            collection =  (Collection<?>) unwrappedPropertyBean;
564
        }
565
        if (collection != null){
566
            //collection or map
567
            if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toStringNoWildcard() + " ... ");}
568
            int i = 0;
569
            for (Object entrybean : collection) {
570
                if(index == null){
571
                    node.addBean(entrybean);
572
                } else if(index.equals(i)){
573
                    node.addBean(entrybean);
574
                    break;
575
                }
576
                i++;
577
            }
578
            if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toString() + " - DONE ");}
579

  
580
        }else {
581
            // nested bean
582
            node.addBean(unwrappedPropertyBean);
583
            setProperty(bean, property, unwrappedPropertyBean);
584
        }
585
    }
586

  
587
    private class AutoInit{
588

  
589
        String leftJoinFetch = "";
590
        Set<AutoPropertyInitializer<CdmBase>> initlializers = new HashSet<AutoPropertyInitializer<CdmBase>>();
591

  
592
        /**
593
         * @param leftJoinFetch
594
         * @param initlializers
595
         */
596
        public AutoInit() {
597
        }
598
    }
599
}

Also available in: Unified diff