Project

General

Profile

Download (15.9 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
 * @since 26.03.2009
36
 *
37
 */
38
public abstract class AbstractBeanInitializer<CDM extends CdmBase> 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<CDM>, AutoPropertyInitializer<CDM>> beanAutoInitializers = null;
46

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

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

    
61
    @Override
62
    public abstract Object initializeInstance(Object proxy);
63

    
64
    @Override
65
    public void load(Object bean) {
66
        initializeBean(bean, true, false);
67
    }
68

    
69
    @Override
70
    public void loadFully(Object bean) {
71
        initializeBean(bean, true, true);
72
    }
73

    
74
    /**
75
     * Initializes all *toOne relations of the given bean and all *toMany
76
     * relations, depending on the state of the boolean parameters
77
     * <code>cdmEntities</code> and <code>collections</code>
78
     *
79
     * @param bean
80
     *            the bean to initialize
81
     * @param cdmEntities
82
     *            initialize all *toOne relations to cdm entities
83
     * @param collections
84
     *            initialize all *toMany relations
85
     */
86
    public void initializeBean(Object bean, boolean cdmEntities, boolean collections){
87

    
88
        if(logger.isDebugEnabled()){logger.debug(">> starting wildcard initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName()); }
89
        Set<Class<?>> restrictions = new HashSet<>();
90
        if(cdmEntities){
91
            restrictions.add(CdmBase.class);
92
        }
93
        if(collections){
94
            restrictions.add(Collection.class);
95
        }
96
        Set<PropertyDescriptor> props = getProperties(bean, restrictions);
97
        for(PropertyDescriptor propertyDescriptor : props){
98
            try {
99

    
100
                invokeInitialization(bean, propertyDescriptor);
101

    
102
            } catch (IllegalAccessException e) {
103
                logger.error("Illegal access on property " + propertyDescriptor.getName());
104
            } catch (InvocationTargetException e) {
105
                logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");
106
            } catch (NoSuchMethodException e) {
107
                logger.info("Property " + propertyDescriptor.getName() + " not found");
108
            }
109
        }
110
        //auto-initialize root bean
111
        invokePropertyAutoInitializers(bean);
112
        if(logger.isDebugEnabled()){
113
            logger.debug("  completed initializeBean() of " + bean);
114
        }
115
    }
116

    
117
    //TODO optimize algorithm ..
118
    @Override
119
    public void initialize(Object bean, List<String> propertyPaths) {
120

    
121
        invokePropertyAutoInitializers(bean);
122

    
123
        if(propertyPaths == null){
124
            return;
125
        }
126

    
127
        Collections.sort(propertyPaths);
128
        //long startTime1 = System.nanoTime();
129
        if(logger.isDebugEnabled()){
130
            logger.debug(">> starting to initialize " + bean + " ;class:" + bean.getClass().getSimpleName());
131
        }
132
        for(String propPath : propertyPaths){
133
            initializePropertyPath(bean, propPath);
134
        }
135
        //long estimatedTime1 = System.nanoTime() - startTime1;
136
        //System.err.println(".");
137
        //long startTime2 = System.nanoTime();
138
        //for(String propPath : propertyPaths){
139
        //	initializePropertyPath(bean, propPath);
140
        //}
141
        //long estimatedTime2 = System.nanoTime() - startTime2;
142
        //System.err.println("first pas: "+estimatedTime1+" ns; second pas: "+estimatedTime2+ " ns");
143
        if(logger.isDebugEnabled()){
144
            logger.debug("   Completed initialization of " + bean);
145
        }
146

    
147
    }
148

    
149
    @Override
150
    public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {
151
        if(propertyPaths != null){
152
            for(Object bean : beanList){
153
                initialize(bean, propertyPaths);
154
            }
155
        }
156
        return beanList;
157
    }
158

    
159
    /**
160
     * Initializes the given single <code>propPath</code> String.
161
     *
162
     * @param bean
163
     * @param propPath
164
     */
165
    //changed form private to protected  (AM)
166
    protected void initializePropertyPath(Object bean, String propPath) {
167
        if(logger.isDebugEnabled()){
168
            logger.debug("processing " + propPath);
169
        }
170

    
171

    
172
        // [1]
173
        // if propPath only contains a wildcard (* or $)
174
        // => do a batch initialization of *toOne or *toMany relations
175
        if(propPath.equals(LOAD_2ONE_WILDCARD)){
176
            if(Collection.class.isAssignableFrom(bean.getClass())){
177
                initializeAllEntries((Collection)bean, true, false);
178
            } else if(Map.class.isAssignableFrom(bean.getClass())) {
179
                initializeAllEntries(((Map)bean).values(), true, false);
180
            } else{
181
                initializeBean(bean, true, false);
182
            }
183
        } else if(propPath.equals(LOAD_2ONE_2MANY_WILDCARD)){
184
            if(Collection.class.isAssignableFrom(bean.getClass())){
185
                initializeAllEntries((Collection)bean, true, true);
186
            } else if(Map.class.isAssignableFrom(bean.getClass())) {
187
                initializeAllEntries(((Map)bean).values(), true, true);
188
            } else {
189
                initializeBean(bean, true, true);
190
            }
191
        } else {
192
            // [2]
193
            // propPath contains either a single field or a nested path
194

    
195
            // split next path token off and keep the remaining as nestedPath
196
            String property;
197
            String nestedPath = null;
198
            int pos;
199
            if((pos = propPath.indexOf('.')) > 0){
200
                nestedPath = propPath.substring(pos + 1);
201
                property = propPath.substring(0, pos);
202
            } else {
203
                property = propPath;
204
            }
205

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

    
214
            try {
215
                //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
216

    
217
                // [2.a] initialize the bean named by property
218

    
219
                PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);
220
                Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);
221

    
222
                // [2.b]
223
                // recurse into nested properties
224
                if(unwrappedPropertyBean != null && nestedPath != null){
225
                    if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
226
                        // nested collection
227
                        int i = 0;
228
                        for (Object entrybean : (Collection<?>) unwrappedPropertyBean) {
229
                            if(index == null){
230
                                initializePropertyPath(entrybean, nestedPath);
231
                            } else if(index.equals(i)){
232
                                initializePropertyPath(entrybean, nestedPath);
233
                                break;
234
                            }
235
                            i++;
236
                        }
237
                    } else if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {
238
                        // nested map
239
                        int i = 0;
240
                        for (Object entrybean : ((Map) unwrappedPropertyBean).values()) {
241
                            if(index == null){
242
                                initializePropertyPath(entrybean, nestedPath);
243
                            } else if(index.equals(i)){
244
                                initializePropertyPath(entrybean, nestedPath);
245
                                break;
246
                            }
247
                            i++;
248
                        }
249
                    }else {
250
                        // nested bean
251
                        initializePropertyPath(unwrappedPropertyBean, nestedPath);
252
                        setProperty(bean, property, unwrappedPropertyBean);
253
                    }
254
                }
255

    
256
            } catch (IllegalAccessException e) {
257
                logger.error("Illegal access on property " + property);
258
            } catch (InvocationTargetException e) {
259
                logger.error("Cannot invoke property " + property + " not found");
260
            } catch (NoSuchMethodException e) {
261
                logger.info("Property " + property + " not found");
262
            }
263
        }
264
    }
265

    
266
    /**
267
     * Initializes the property of the given bean and returns the bean which is returned be that property.
268
     *
269
     * @param bean
270
     * @param propertyDescriptor
271
     * @return
272
     * @throws IllegalAccessException
273
     * @throws InvocationTargetException
274
     * @throws NoSuchMethodException
275
     */
276
    protected Object invokeInitialization(Object bean, PropertyDescriptor propertyDescriptor) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
277

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

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

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

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

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

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

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

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

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

    
320
                }
321
            }
322
        }
323

    
324
        return propertyBean;
325
    }
326

    
327
    /**
328
     * @param beanClass
329
     * @param bean
330
     * @return
331
     */
332
    protected final void invokePropertyAutoInitializers(Object bean) {
333

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

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

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

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

    
389
        Set<PropertyDescriptor> properties = new HashSet<>();
390
        PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(bean);
391

    
392
        for (PropertyDescriptor prop : props) {
393
            //String propName = prop[i].getName();
394

    
395
            // only read methods & skip transient getters
396
            if( prop.getReadMethod() != null ){
397
                  try{
398
                     @SuppressWarnings("unchecked")
399
                    Class<Transient> transientClass = (Class<Transient>)Class.forName( "javax.persistence.Transient" );
400
                     if( prop.getReadMethod().getAnnotation( transientClass ) != null ){
401
                        continue;
402
                     }
403
                  }catch( ClassNotFoundException cnfe ){
404
                     // ignore
405
                  }
406
                  if(typeRestrictions != null){
407
                      for(Class<?> restrictedType : typeRestrictions){
408
                          if(restrictedType.isAssignableFrom(prop.getPropertyType())){
409
                              properties.add(prop);
410
                          }
411
                      }
412
                  } else {
413
                      properties.add(prop);
414
                  }
415
            }
416
        }
417
        return properties;
418
    }
419

    
420
}
(1-1/15)