Project

General

Profile

Download (16.4 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2009 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9
package eu.etaxonomy.cdm.persistence.dao.initializer;
10

    
11
import java.beans.PropertyDescriptor;
12
import java.lang.reflect.InvocationTargetException;
13
import java.lang.reflect.Method;
14
import java.lang.reflect.ParameterizedType;
15
import java.lang.reflect.Type;
16
import java.util.Collection;
17
import java.util.Collections;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22

    
23
import javax.persistence.Transient;
24

    
25
import org.apache.commons.beanutils.PropertyUtils;
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.log4j.Logger;
28
import org.springframework.beans.factory.annotation.Autowired;
29

    
30
import eu.etaxonomy.cdm.model.common.CdmBase;
31
import eu.etaxonomy.cdm.persistence.dao.IMethodCache;
32

    
33
/**
34
 * @author a.kohlbecker
35
 * @date 26.03.2009
36
 *
37
 */
38
public abstract class AbstractBeanInitializer implements IBeanInitializer{
39

    
40
    public static final Logger logger = Logger.getLogger(AbstractBeanInitializer.class);
41

    
42
    @Autowired
43
    private IMethodCache methodCache;
44

    
45
    private Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> beanAutoInitializers = null;
46

    
47
    /**
48
     * @param beanAutoInitializers the beanAutoInitializers to set
49
     */
50
    public void setBeanAutoInitializers(Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> beanAutoInitializers) {
51
        this.beanAutoInitializers = beanAutoInitializers;
52
    }
53

    
54
    /**
55
     * @return the beanAutoInitializers
56
     */
57
    public Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> getBeanAutoInitializers() {
58
        return beanAutoInitializers;
59
    }
60

    
61
    /* (non-Javadoc)
62
     * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#initializeInstance(java.lang.Object)
63
     */
64
    @Override
65
    public abstract Object initializeInstance(Object proxy);
66

    
67
    /* (non-Javadoc)
68
     * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#load(eu.etaxonomy.cdm.model.common.CdmBase)
69
     */
70
    @Override
71
    public void load(Object bean) {
72
        initializeBean(bean, true, false);
73
    }
74

    
75
    /* (non-Javadoc)
76
     * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#loadFully(eu.etaxonomy.cdm.model.common.CdmBase)
77
     */
78
    @Override
79
    public void loadFully(Object bean) {
80
        initializeBean(bean, true, true);
81
    }
82

    
83
    /**
84
     * Initializes all *toOne relations of the given bean and all *toMany
85
     * relations, depending on the state of the boolean parameters
86
     * <code>cdmEntities</code> and <code>collections</code>
87
     *
88
     * @param bean
89
     *            the bean to initialize
90
     * @param cdmEntities
91
     *            initialize all *toOne relations to cdm entities
92
     * @param collections
93
     *            initialize all *toMany relations
94
     */
95
    public void initializeBean(Object bean, boolean cdmEntities, boolean collections){
96

    
97
        if(logger.isDebugEnabled()){logger.debug(">> starting wildcard initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName()); }
98
        Set<Class<?>> restrictions = new HashSet<Class<?>>();
99
        if(cdmEntities){
100
            restrictions.add(CdmBase.class);
101
        }
102
        if(collections){
103
            restrictions.add(Collection.class);
104
        }
105
        Set<PropertyDescriptor> props = getProperties(bean, restrictions);
106
        for(PropertyDescriptor propertyDescriptor : props){
107
            try {
108

    
109
                invokeInitialization(bean, propertyDescriptor);
110

    
111
            } catch (IllegalAccessException e) {
112
                logger.error("Illegal access on property " + propertyDescriptor.getName());
113
            } catch (InvocationTargetException e) {
114
                logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");
115
            } catch (NoSuchMethodException e) {
116
                logger.info("Property " + propertyDescriptor.getName() + " not found");
117
            }
118
        }
119
        if(logger.isDebugEnabled()){
120
            logger.debug("  completed initializeBean() of " + bean);
121
        }
122
    }
123

    
124
    /* (non-Javadoc)
125
     * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#initializeProperties(java.lang.Object, java.util.List)
126
     */
127
    //TODO optimize algorithm ..
128
    @Override
129
    public void initialize(Object bean, List<String> propertyPaths) {
130

    
131
        invokePropertyAutoInitializers(bean);
132

    
133
        if(propertyPaths == null){
134
            return;
135
        }
136

    
137
        Collections.sort(propertyPaths);
138
        //long startTime1 = System.nanoTime();
139
        if(logger.isDebugEnabled()){
140
            logger.debug(">> starting to initialize " + bean + " ;class:" + bean.getClass().getSimpleName());
141
        }
142
        for(String propPath : propertyPaths){
143
            initializePropertyPath(bean, propPath);
144
        }
145
        //long estimatedTime1 = System.nanoTime() - startTime1;
146
        //System.err.println(".");
147
        //long startTime2 = System.nanoTime();
148
        //for(String propPath : propertyPaths){
149
        //	initializePropertyPath(bean, propPath);
150
        //}
151
        //long estimatedTime2 = System.nanoTime() - startTime2;
152
        //System.err.println("first pas: "+estimatedTime1+" ns; second pas: "+estimatedTime2+ " ns");
153
        if(logger.isDebugEnabled()){
154
            logger.debug("   Completed initialization of " + bean);
155
        }
156

    
157
    }
158

    
159
    @Override
160
    public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {
161
        if(propertyPaths != null){
162
            for(Object bean : beanList){
163
                initialize(bean, propertyPaths);
164
            }
165
        }
166
        return beanList;
167
    }
168

    
169
    /**
170
     * Initializes the given single <code>propPath</code> String.
171
     *
172
     * @param bean
173
     * @param propPath
174
     */
175
    //changed form private to protected  (AM)
176
    protected void initializePropertyPath(Object bean, String propPath) {
177
        if(logger.isDebugEnabled()){
178
            logger.debug("processing " + propPath);
179
        }
180

    
181

    
182
        // [1]
183
        // if propPath only contains a wildcard (* or $)
184
        // => do a batch initialization of *toOne or *toMany relations
185
        if(propPath.equals(LOAD_2ONE_WILDCARD)){
186
            if(Collection.class.isAssignableFrom(bean.getClass())){
187
                initializeAllEntries((Collection)bean, true, false);
188
            } else if(Map.class.isAssignableFrom(bean.getClass())) {
189
                initializeAllEntries(((Map)bean).values(), true, false);
190
            } else{
191
                initializeBean(bean, true, false);
192
            }
193
        } else if(propPath.equals(LOAD_2ONE_2MANY_WILDCARD)){
194
            if(Collection.class.isAssignableFrom(bean.getClass())){
195
                initializeAllEntries((Collection)bean, true, true);
196
            } else if(Map.class.isAssignableFrom(bean.getClass())) {
197
                initializeAllEntries(((Map)bean).values(), true, true);
198
            } else {
199
                initializeBean(bean, true, true);
200
            }
201
        } else {
202
            // [2]
203
            // propPath contains either a single field or a nested path
204

    
205
            // split next path token off and keep the remaining as nestedPath
206
            String property;
207
            String nestedPath = null;
208
            int pos;
209
            if((pos = propPath.indexOf('.')) > 0){
210
                nestedPath = propPath.substring(pos + 1);
211
                property = propPath.substring(0, pos);
212
            } else {
213
                property = propPath;
214
            }
215

    
216
            // is the property indexed?
217
            Integer index = null;
218
            if((pos = property.indexOf('[')) > 0){
219
                String indexString = property.substring(pos + 1, property.indexOf(']'));
220
                index = Integer.valueOf(indexString);
221
                property = property.substring(0, pos);
222
            }
223

    
224
            try {
225
                //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
226

    
227
                // [2.a] initialize the bean named by property
228

    
229
                PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);
230
                Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);
231

    
232
                // [2.b]
233
                // recurse into nested properties
234
                if(unwrappedPropertyBean != null && nestedPath != null){
235
                    if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
236
                        // nested collection
237
                        int i = 0;
238
                        for (Object entrybean : (Collection<?>) unwrappedPropertyBean) {
239
                            if(index == null){
240
                                initializePropertyPath(entrybean, nestedPath);
241
                            } else if(index.equals(i)){
242
                                initializePropertyPath(entrybean, nestedPath);
243
                                break;
244
                            }
245
                            i++;
246
                        }
247
                    } else if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
248
                        // nested map
249
                        int i = 0;
250
                        for (Object entrybean : ((Map) unwrappedPropertyBean).values()) {
251
                            if(index == null){
252
                                initializePropertyPath(entrybean, nestedPath);
253
                            } else if(index.equals(i)){
254
                                initializePropertyPath(entrybean, nestedPath);
255
                                break;
256
                            }
257
                            i++;
258
                        }
259
                    }else {
260
                        // nested bean
261
                        initializePropertyPath(unwrappedPropertyBean, nestedPath);
262
                        setProperty(bean, property, unwrappedPropertyBean);
263
                    }
264
                }
265

    
266
            } catch (IllegalAccessException e) {
267
                logger.error("Illegal access on property " + property);
268
            } catch (InvocationTargetException e) {
269
                logger.error("Cannot invoke property " + property + " not found");
270
            } catch (NoSuchMethodException e) {
271
                logger.info("Property " + property + " not found");
272
            }
273
        }
274
    }
275

    
276
    /**
277
     * Initializes the property of the given bean and returns the bean which is returned be that property.
278
     *
279
     * @param bean
280
     * @param propertyDescriptor
281
     * @return
282
     * @throws IllegalAccessException
283
     * @throws InvocationTargetException
284
     * @throws NoSuchMethodException
285
     */
286
    protected Object invokeInitialization(Object bean, PropertyDescriptor propertyDescriptor) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
287

    
288
        if(propertyDescriptor == null || bean == null){
289
            return null;
290
        }
291

    
292
        // (1)
293
        // initialialization of the bean
294
        //
295
        Object propertyProxy = PropertyUtils.getProperty( bean, propertyDescriptor.getName());
296
        Object propertyBean = initializeInstance(propertyProxy);
297

    
298
        if(propertyBean != null){
299
            // (2)
300
            // auto initialialization of sub properties
301
            //
302
            if(CdmBase.class.isAssignableFrom(propertyBean.getClass())){
303

    
304
                // initialization of a single bean
305
                CdmBase cdmBaseBean = (CdmBase)propertyBean;
306
                invokePropertyAutoInitializers(cdmBaseBean);
307

    
308
            } else if(Collection.class.isAssignableFrom(propertyBean.getClass()) ||
309
                    Map.class.isAssignableFrom(propertyBean.getClass()) ) {
310

    
311
                // it is a collection or map
312
                Method readMethod = propertyDescriptor.getReadMethod();
313
                Type genericReturnType = readMethod.getGenericReturnType();
314

    
315
                if(genericReturnType instanceof ParameterizedType){
316
                    ParameterizedType type = (ParameterizedType) genericReturnType;
317
                    Type[] typeArguments = type.getActualTypeArguments();
318

    
319
                    if(typeArguments.length > 0
320
                            && typeArguments[0] instanceof Class<?>
321
                            && CdmBase.class.isAssignableFrom((Class<?>) typeArguments[0])){
322

    
323
                        if(Collection.class.isAssignableFrom((Class<?>) type.getRawType())){
324
                            for(CdmBase entry : ((Collection<CdmBase>)propertyBean)){
325
                                invokePropertyAutoInitializers(entry);
326
                            }
327
                        }
328
                    }
329

    
330
                }
331
            }
332
        }
333

    
334
        return propertyBean;
335
    }
336

    
337
    /**
338
     * @param beanClass
339
     * @param bean
340
     * @return
341
     */
342
    protected final void invokePropertyAutoInitializers(Object bean) {
343

    
344
        if(beanAutoInitializers == null || bean == null){
345
            return;
346
        }
347
        if(!CdmBase.class.isAssignableFrom(bean.getClass())){
348
            return;
349
        }
350
        CdmBase cdmBaseBean = (CdmBase)bean;
351
        for(Class<? extends CdmBase> superClass : beanAutoInitializers.keySet()){
352
            if(superClass.isAssignableFrom(bean.getClass())){
353
                beanAutoInitializers.get(superClass).initialize(cdmBaseBean);
354
            }
355
        }
356
    }
357

    
358
    protected void setProperty(Object object, String property, Object value){
359
        Method method = methodCache.getMethod(object.getClass(), "set" + StringUtils.capitalize(property), value.getClass());
360
        if(method != null){
361
            try {
362
                method.invoke(object, value);
363
            } catch (IllegalArgumentException e) {
364
                // TODO Auto-generated catch block
365
                e.printStackTrace();
366
            } catch (IllegalAccessException e) {
367
                // TODO Auto-generated catch block
368
                e.printStackTrace();
369
            } catch (InvocationTargetException e) {
370
                // TODO Auto-generated catch block
371
                e.printStackTrace();
372
            }
373
        }
374
    }
375

    
376
    /**
377
     * @param collection of which all entities are to be initialized
378
     * @param cdmEntities initialize all *toOne relations to cdm entities
379
     * @param collections initialize all *toMany relations
380
     */
381
    protected void initializeAllEntries(Collection collection, boolean cdmEntities, boolean collections) {
382
        for(Object bean : collection){
383
            initializeBean(bean, cdmEntities, collections);
384
        }
385
    }
386

    
387
    /**
388
     * Return all public bean properties exclusive those whose return type
389
     * match any class defined in the parameter <code>typeRestrictions</code>
390
     * or which are transient properties.
391
     *
392
     *
393
     * @param bean
394
     * @param typeRestrictions
395
     * @return
396
     */
397
    public static Set<PropertyDescriptor> getProperties(Object bean, Set<Class<?>> typeRestrictions) {
398

    
399
        Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
400
        PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(bean);
401

    
402
        for (PropertyDescriptor prop : props) {
403
            //String propName = prop[i].getName();
404

    
405
            // only read methods & skip transient getters
406
            if( prop.getReadMethod() != null ){
407
                  try{
408
                     Class<Transient> transientClass = (Class<Transient>)Class.forName( "javax.persistence.Transient" );
409
                     if( prop.getReadMethod().getAnnotation( transientClass ) != null ){
410
                        continue;
411
                     }
412
                  }catch( ClassNotFoundException cnfe ){
413
                     // ignore
414
                  }
415
                  if(typeRestrictions != null){
416
                      for(Class<?> restrictedType : typeRestrictions){
417
                          if(restrictedType.isAssignableFrom(prop.getPropertyType())){
418
                              properties.add(prop);
419
                          }
420
                      }
421
                  } else {
422
                      properties.add(prop);
423
                  }
424
            }
425
        }
426
        return properties;
427
    }
428

    
429
}
(1-1/13)