3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
;
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
;
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
;
29 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
30 import eu
.etaxonomy
.cdm
.persistence
.dao
.IMethodCache
;
33 * @author a.kohlbecker
37 public abstract class AbstractBeanInitializer
implements IBeanInitializer
{
39 public static final Logger logger
= Logger
.getLogger(AbstractBeanInitializer
.class);
42 IMethodCache methodCache
;
44 private Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> beanAutoInitializers
= null;
47 * @param beanAutoInitializers the beanAutoInitializers to set
49 public void setBeanAutoInitializers(Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> beanAutoInitializers
) {
50 this.beanAutoInitializers
= beanAutoInitializers
;
54 * @return the beanAutoInitializers
56 public Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> getBeanAutoInitializers() {
57 return beanAutoInitializers
;
61 * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#initializeInstance(java.lang.Object)
64 public abstract Object
initializeInstance(Object proxy
);
67 * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#load(eu.etaxonomy.cdm.model.common.CdmBase)
70 public void load(Object bean
) {
71 initializeBean(bean
, true, false);
75 * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#loadFully(eu.etaxonomy.cdm.model.common.CdmBase)
78 public void loadFully(Object bean
) {
79 initializeBean(bean
, true, true);
83 * Initializes all *toOne relations of the given bean and all *toMany
84 * relations, depending on the state of the boolean parameters
85 * <code>cdmEntities</code> and <code>collections</code>
88 * the bean to initialize
90 * initialize all *toOne relations to cdm entities
92 * initialize all *toMany relations
94 public void initializeBean(Object bean
, boolean cdmEntities
, boolean collections
){
96 if(logger
.isDebugEnabled()){
97 logger
.debug(">> starting initializeBean() of " + bean
+ " ;class:" + bean
.getClass().getSimpleName());
99 Set
<Class
> restrictions
= new HashSet
<Class
>();
101 restrictions
.add(CdmBase
.class);
104 restrictions
.add(Collections
.class);
106 Set
<PropertyDescriptor
> props
= getProperties(bean
, restrictions
);
107 for(PropertyDescriptor propertyDescriptor
: props
){
110 invokeInitialization(bean
, propertyDescriptor
);
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");
120 if(logger
.isDebugEnabled()){
121 logger
.debug(" completed initializeBean() of " + bean
);
126 * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#initializeProperties(java.lang.Object, java.util.List)
128 //TODO optimize algorithm ..
130 public void initialize(Object bean
, List
<String
> propertyPaths
) {
132 invokePropertyAutoInitializers(bean
);
134 if(propertyPaths
== null){
138 Collections
.sort(propertyPaths
);
139 //long startTime1 = System.nanoTime();
140 if(logger
.isDebugEnabled()){
141 logger
.debug(">> starting to initialize " + bean
+ " ;class:" + bean
.getClass().getSimpleName());
143 for(String propPath
: propertyPaths
){
144 initializePropertyPath(bean
, propPath
);
146 //long estimatedTime1 = System.nanoTime() - startTime1;
147 //System.err.println(".");
148 //long startTime2 = System.nanoTime();
149 //for(String propPath : propertyPaths){
150 // initializePropertyPath(bean, propPath);
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
);
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
);
171 * Initializes the given single <code>propPath</code> String.
176 //changed form private to protected (AM)
177 protected void initializePropertyPath(Object bean
, String propPath
) {
178 if(logger
.isDebugEnabled()){
179 logger
.debug("processing " + propPath
);
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);
192 initializeBean(bean
, true, false);
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);
200 initializeBean(bean
, true, true);
204 // propPath contains either a single field or a nested path
206 // split next path token off and keep the remaining as nestedPath
208 String nestedPath
= null;
210 if((pos
= propPath
.indexOf('.')) > 0){
211 nestedPath
= propPath
.substring(pos
+ 1);
212 property
= propPath
.substring(0, pos
);
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
);
226 //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging
228 // [2.a] initialize the bean named by property
230 PropertyDescriptor propertyDescriptor
= PropertyUtils
.getPropertyDescriptor(bean
, property
);
231 Object unwrappedPropertyBean
= invokeInitialization(bean
, propertyDescriptor
);
234 // recurse into nested properties
235 if(unwrappedPropertyBean
!= null && nestedPath
!= null){
236 if (Collection
.class.isAssignableFrom(unwrappedPropertyBean
.getClass())) {
239 for (Object entrybean
: (Collection
) unwrappedPropertyBean
) {
241 initializePropertyPath(entrybean
, nestedPath
);
242 } else if(index
.equals(i
)){
243 initializePropertyPath(entrybean
, nestedPath
);
248 } else if(Map
.class.isAssignableFrom(unwrappedPropertyBean
.getClass())) {
251 for (Object entrybean
: ((Map
) unwrappedPropertyBean
).values()) {
253 initializePropertyPath(entrybean
, nestedPath
);
254 } else if(index
.equals(i
)){
255 initializePropertyPath(entrybean
, nestedPath
);
262 initializePropertyPath(unwrappedPropertyBean
, nestedPath
);
263 setProperty(bean
, property
, unwrappedPropertyBean
);
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");
277 private Object
invokeInitialization(Object bean
, PropertyDescriptor propertyDescriptor
) throws IllegalAccessException
, InvocationTargetException
, NoSuchMethodException
{
279 if(propertyDescriptor
== null || bean
== null){
284 // initialialization of the bean
286 Object propertyProxy
= PropertyUtils
.getProperty( bean
, propertyDescriptor
.getName());
287 Object propertyBean
= initializeInstance(propertyProxy
);
289 if(propertyBean
!= null){
291 // auto initialialization of sub properties
293 if(CdmBase
.class.isAssignableFrom(propertyBean
.getClass())){
295 // initialization of a single bean
296 CdmBase cdmBaseBean
= (CdmBase
)propertyBean
;
297 invokePropertyAutoInitializers(cdmBaseBean
);
299 } else if(Collection
.class.isAssignableFrom(propertyBean
.getClass()) ||
300 Map
.class.isAssignableFrom(propertyBean
.getClass()) ) {
302 // it is a collection or map
303 Method readMethod
= propertyDescriptor
.getReadMethod();
304 Type genericReturnType
= readMethod
.getGenericReturnType();
306 if(genericReturnType
instanceof ParameterizedType
){
307 ParameterizedType type
= (ParameterizedType
) genericReturnType
;
308 Type
[] typeArguments
= type
.getActualTypeArguments();
310 if(typeArguments
.length
> 0
311 && typeArguments
[0] instanceof Class
<?
>
312 && CdmBase
.class.isAssignableFrom((Class
<?
>) typeArguments
[0])){
314 if(Collection
.class.isAssignableFrom((Class
<?
>) type
.getRawType())){
315 for(CdmBase entry
: ((Collection
<CdmBase
>)propertyBean
)){
316 invokePropertyAutoInitializers(entry
);
333 protected final void invokePropertyAutoInitializers(Object bean
) {
335 if(beanAutoInitializers
== null || bean
== null){
338 if(!CdmBase
.class.isAssignableFrom(bean
.getClass())){
341 CdmBase cdmBaseBean
= (CdmBase
)bean
;
342 for(Class
<?
extends CdmBase
> superClass
: beanAutoInitializers
.keySet()){
343 if(superClass
.isAssignableFrom(bean
.getClass())){
344 beanAutoInitializers
.get(superClass
).initialize(cdmBaseBean
);
349 protected void setProperty(Object object
, String property
, Object value
){
350 Method method
= methodCache
.getMethod(object
.getClass(), "set" + StringUtils
.capitalize(property
), value
.getClass());
353 method
.invoke(object
, value
);
354 } catch (IllegalArgumentException e
) {
355 // TODO Auto-generated catch block
357 } catch (IllegalAccessException e
) {
358 // TODO Auto-generated catch block
360 } catch (InvocationTargetException e
) {
361 // TODO Auto-generated catch block
368 * @param collection of which all entities are to be initialized
369 * @param cdmEntities initialize all *toOne relations to cdm entities
370 * @param collections initialize all *toMany relations
372 private void initializeAllEntries(Collection collection
, boolean cdmEntities
, boolean collections
) {
373 for(Object bean
: collection
){
374 initializeBean(bean
, cdmEntities
, collections
);
379 * Return all public bean properties which, exclusive those whose return type match any class defined in
380 * the parameter <code>typeRestrictions</code> or which are transient properties.
383 * @param typeRestrictions
386 @SuppressWarnings("unchecked")
387 public static Set
<PropertyDescriptor
> getProperties(Object bean
, Set
<Class
> typeRestrictions
) {
389 Set
<PropertyDescriptor
> properties
= new HashSet
<PropertyDescriptor
>();
390 PropertyDescriptor
[] prop
= PropertyUtils
.getPropertyDescriptors(bean
);
392 for (int i
= 0; i
< prop
.length
; i
++) {
393 //String propName = prop[i].getName();
395 // only read methods & skip transient getters
396 if( prop
[i
].getReadMethod() != null ){
398 Class transientClass
= Class
.forName( "javax.persistence.Transient" );
399 if( prop
[i
].getReadMethod().getAnnotation( transientClass
) != null ){
402 }catch( ClassNotFoundException cnfe
){
405 if(typeRestrictions
!= null && typeRestrictions
.size() > 1){
406 for(Class type
: typeRestrictions
){
407 if(type
.isAssignableFrom(prop
[i
].getPropertyType())){
408 properties
.add(prop
[i
]);
412 properties
.add(prop
[i
]);