Project

General

Profile

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

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

    
24
import javax.persistence.Transient;
25

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

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

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

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

    
43
    @Autowired
44
    private IMethodCache methodCache;
45

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

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

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

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

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

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

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

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

    
110
                invokeInitialization(bean, propertyDescriptor);
111

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

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

    
132
        invokePropertyAutoInitializers(bean);
133

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

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

    
158
    }
159

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

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

    
182

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
331
                }
332
            }
333
        }
334

    
335
        return propertyBean;
336
    }
337

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

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

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

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

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

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

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

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

    
430
}
(1-1/13)