Project

General

Profile

Download (15.9 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;
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 org.apache.commons.beanutils.PropertyUtils;
25
import org.apache.commons.lang.StringUtils;
26
import org.apache.log4j.Logger;
27
import org.springframework.beans.factory.annotation.Autowired;
28

    
29
import eu.etaxonomy.cdm.model.common.CdmBase;
30

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

    
38
    public static final Logger logger = Logger.getLogger(AbstractBeanInitializer.class);
39

    
40
    @Autowired
41
    IMethodCache methodCache;
42

    
43
    private Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> beanAutoInitializers = null;
44

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

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

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

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

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

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

    
95
        if(logger.isDebugEnabled()){
96
            logger.debug(">> starting initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName());
97
        }
98
        Set<Class> restrictions = new HashSet<Class>();
99
        if(cdmEntities){
100
            restrictions.add(CdmBase.class);
101
        }
102
        if(collections){
103
            restrictions.add(Collections.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
    private void initializePropertyPath(Object bean, String propPath) {
176
        if(logger.isDebugEnabled()){
177
            logger.debug("processing " + propPath);
178
        }
179

    
180

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

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

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

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

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

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

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

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

    
275
    private Object invokeInitialization(Object bean, PropertyDescriptor propertyDescriptor) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
276

    
277
        if(propertyDescriptor == null || bean == null){
278
            return null;
279
        }
280

    
281
        // (1)
282
        // initialialization of the bean
283
        //
284
        Object propertyProxy = PropertyUtils.getProperty( bean, propertyDescriptor.getName());
285
        Object propertyBean = initializeInstance(propertyProxy);
286

    
287
        if(propertyBean != null){
288
            // (2)
289
            // auto initialialization of sub properties
290
            //
291
            if(CdmBase.class.isAssignableFrom(propertyBean.getClass())){
292

    
293
                // initialization of a single bean
294
                CdmBase cdmBaseBean = (CdmBase)propertyBean;
295
                invokePropertyAutoInitializers(cdmBaseBean);
296

    
297
            } else if(Collection.class.isAssignableFrom(propertyBean.getClass()) ||
298
                    Map.class.isAssignableFrom(propertyBean.getClass()) ) {
299

    
300
                // it is a collection or map
301
                Method readMethod = propertyDescriptor.getReadMethod();
302
                Type genericReturnType = readMethod.getGenericReturnType();
303

    
304
                if(genericReturnType instanceof ParameterizedType){
305
                    ParameterizedType type = (ParameterizedType) genericReturnType;
306
                    Type[] typeArguments = type.getActualTypeArguments();
307

    
308
                    if(typeArguments.length > 0
309
                            && typeArguments[0] instanceof Class<?>
310
                            && CdmBase.class.isAssignableFrom((Class<?>) typeArguments[0])){
311

    
312
                        if(Collection.class.isAssignableFrom((Class<?>) type.getRawType())){
313
                            for(CdmBase entry : ((Collection<CdmBase>)propertyBean)){
314
                                invokePropertyAutoInitializers(entry);
315
                            }
316
                        }
317
                    }
318

    
319
                }
320
            }
321
        }
322

    
323
        return propertyBean;
324
    }
325

    
326
    /**
327
     * @param beanClass
328
     * @param bean
329
     * @return
330
     */
331
    private void invokePropertyAutoInitializers(Object bean) {
332

    
333
        if(beanAutoInitializers == null || bean == null){
334
            return;
335
        }
336
        if(!CdmBase.class.isAssignableFrom(bean.getClass())){
337
            return;
338
        }
339
        CdmBase cdmBaseBean = (CdmBase)bean;
340
        for(Class<? extends CdmBase> superClass : beanAutoInitializers.keySet()){
341
            if(superClass.isAssignableFrom(bean.getClass())){
342
                beanAutoInitializers.get(superClass).initialize(cdmBaseBean);
343
            }
344
        }
345
    }
346

    
347
    private void setProperty(Object object, String property, Object value){
348
        Method method = methodCache.getMethod(object.getClass(), "set" + StringUtils.capitalize(property), value.getClass());
349
        if(method != null){
350
            try {
351
                method.invoke(object, value);
352
            } catch (IllegalArgumentException e) {
353
                // TODO Auto-generated catch block
354
                e.printStackTrace();
355
            } catch (IllegalAccessException e) {
356
                // TODO Auto-generated catch block
357
                e.printStackTrace();
358
            } catch (InvocationTargetException e) {
359
                // TODO Auto-generated catch block
360
                e.printStackTrace();
361
            }
362
        }
363
    }
364

    
365
    /**
366
     * @param collection of which all entities are to be initialized
367
     * @param cdmEntities initialize all *toOne relations to cdm entities
368
     * @param collections initialize all *toMany relations
369
     */
370
    private void initializeAllEntries(Collection collection, boolean cdmEntities, boolean collections) {
371
        for(Object bean : collection){
372
            initializeBean(bean, cdmEntities, collections);
373
        }
374
    }
375

    
376
    /**
377
     * Return all public bean properties which, exclusive those whose return type match any class defined in
378
     * the parameter <code>typeRestrictions</code> or which are transient properties.
379
     *
380
     * @param bean
381
     * @param typeRestrictions
382
     * @return
383
     */
384
    @SuppressWarnings("unchecked")
385
    public static Set<PropertyDescriptor> getProperties(Object bean, Set<Class> typeRestrictions) {
386

    
387
        Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
388
        PropertyDescriptor[] prop = PropertyUtils.getPropertyDescriptors(bean);
389

    
390
        for (int i = 0; i < prop.length; i++) {
391
            //String propName = prop[i].getName();
392

    
393
            // only read methods & skip transient getters
394
            if( prop[i].getReadMethod() != null ){
395
                  try{
396
                     Class transientClass = Class.forName( "javax.persistence.Transient" );
397
                     if( prop[i].getReadMethod().getAnnotation( transientClass ) != null ){
398
                        continue;
399
                     }
400
                  }catch( ClassNotFoundException cnfe ){
401
                     // ignore
402
                  }
403
                  if(typeRestrictions != null && typeRestrictions.size() > 1){
404
                      for(Class type : typeRestrictions){
405
                          if(type.isAssignableFrom(prop[i].getPropertyType())){
406
                              properties.add(prop[i]);
407
                          }
408
                      }
409
                  } else {
410
                      properties.add(prop[i]);
411
                  }
412
            }
413
        }
414
        return properties;
415
    }
416

    
417
}
(1-1/14)