View Javadoc
1   /*
2    * AutoDAO - Generic DAO on steroids implementation for Java.
3    *
4    * Copyright 2008-2012  Marat Radchenko <slonopotamus@users.sourceforge.net>
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package net.sf.autodao.impl;
19  
20  import java.lang.reflect.Method;
21  import java.lang.reflect.ParameterizedType;
22  import java.lang.reflect.Type;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import net.sf.autodao.Dao;
28  import net.sf.autodao.PersistentEntity;
29  import net.sf.autodao.QueryArgumentTransformer;
30  import org.jetbrains.annotations.NotNull;
31  import org.jetbrains.annotations.Nullable;
32  import org.springframework.aop.framework.ProxyFactoryBean;
33  import org.springframework.beans.factory.BeanFactory;
34  import org.springframework.beans.factory.InitializingBean;
35  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
36  import org.springframework.transaction.PlatformTransactionManager;
37  import org.springframework.transaction.TransactionDefinition;
38  import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
39  import org.springframework.transaction.interceptor.TransactionAttribute;
40  import org.springframework.transaction.interceptor.TransactionAttributeSource;
41  import org.springframework.transaction.interceptor.TransactionInterceptor;
42  import org.springframework.util.Assert;
43  
44  /**
45   * Base ORM-agnostic DAO factory.
46   *
47   * <p>NOT FOR PUBLIC USE.
48   */
49  public abstract class AbstractDaoFactoryBean extends ProxyFactoryBean implements InitializingBean, TransactionAttributeSource {
50    @NotNull
51    private static final String QUERY_ARGUMENT_TRANSFORMERS_BEAN_NAME = "autodaoQueryArgumentTransformers";
52    private static final TransactionAttribute MANDATORY_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_MANDATORY);
53  
54    @NotNull
55    static final Map<Class<?>, QueryArgumentTransformer<?, ?>> DEFAULT_TRANSFORMERS = defaultTransformers();
56  
57    @NotNull
58    private static Map<Class<?>, QueryArgumentTransformer<?, ?>> defaultTransformers() {
59      final Map<Class<?>, QueryArgumentTransformer<?, ?>> result = new HashMap<>();
60  
61      ReadableInstantTransformer.register(result);
62  
63      return Collections.unmodifiableMap(result);
64    }
65  
66    @NotNull
67    private Map<Class<?>, QueryArgumentTransformer<?, ?>> transformers = DEFAULT_TRANSFORMERS;
68    @NotNull
69    private final Class<?> daoInterface;
70    @NotNull
71    private final PlatformTransactionManager transactionManager;
72    @Nullable
73    private final Class<? extends PersistentEntity<?>> entityType;
74  
75    protected AbstractDaoFactoryBean(@NotNull Class<?> daoInterface,
76                                     @Nullable Class<? extends PersistentEntity<?>> entityType,
77                                     @NotNull PlatformTransactionManager transactionManager) {
78      this.daoInterface = daoInterface;
79      this.entityType = entityType;
80      this.transactionManager = transactionManager;
81    }
82  
83    @NotNull
84    protected abstract FinderExecutor createDao(@Nullable final Class<?> entityType);
85  
86    protected abstract void validate(@NotNull final Class<?> daoInterface, @NotNull Map<Class<?>, QueryArgumentTransformer<?, ?>> transformers);
87  
88    @Override
89    @SuppressWarnings({ "unchecked" })
90    public final void setBeanFactory(final BeanFactory beanFactory) {
91      transformers = new HashMap<>();
92      transformers.putAll(DEFAULT_TRANSFORMERS);
93  
94      try {
95        transformers.putAll((Map<Class<?>, QueryArgumentTransformer<?, ?>>) beanFactory.getBean(QUERY_ARGUMENT_TRANSFORMERS_BEAN_NAME, Map.class));
96      } catch (NoSuchBeanDefinitionException e) {
97        // ignore
98      }
99  
100     super.setBeanFactory(beanFactory);
101   }
102 
103   @Override
104   public final void afterPropertiesSet() {
105     validate(daoInterface, transformers);
106     final Class<? extends PersistentEntity<?>> realEntityType = getEntityType(daoInterface, entityType);
107     addInterface(daoInterface);
108     setTarget(createDao(realEntityType));
109     addAdvice(new TransactionInterceptor(transactionManager, this));
110     addAdvice(new FinderIntroductionInterceptor(transformers));
111   }
112 
113   @Nullable
114   private static Class<? extends PersistentEntity<?>> getEntityType(@NotNull final Class<?> daoInterface,
115                                                                     @Nullable final Class<? extends PersistentEntity<?>> explicit) {
116     final Class<? extends PersistentEntity<?>> result;
117     if (explicit == null && Dao.class.isAssignableFrom(daoInterface)) {
118       result = autoDiscoverEntityType(daoInterface);
119       Assert.notNull(result, "No entity type found for " + daoInterface);
120     } else {
121       result = explicit;
122     }
123     if (result != null) {
124       Assert.isAssignable(PersistentEntity.class, result, "Invalid entity type: ");
125     }
126     return result;
127   }
128 
129   @SuppressWarnings({ "unchecked" })
130   private static Class<? extends PersistentEntity<?>> autoDiscoverEntityType(final Class<?> daoInterface) {
131     //Automagically discover entity type from generics.
132     for (final Type t : daoInterface.getGenericInterfaces()) {
133       if (t instanceof ParameterizedType) {
134         final ParameterizedType pt = (ParameterizedType) t;
135         if (pt.getRawType() == Dao.class) {
136           final Type type = pt.getActualTypeArguments()[0];
137           if (type instanceof ParameterizedType) {
138             //Special handling for cases like Dao<Entity<Foo>>.
139             return (Class<? extends PersistentEntity<?>>) ((ParameterizedType) type).getRawType();
140           } else {
141             Assert.isInstanceOf(
142                 Class.class,
143                 type,
144                 "Don't know how to handle " + type + " entity type for " + daoInterface.getName() + ".\n"
145                     + "Please update to latest AutoDAO release.\n"
146                     + "If that doesn't help, please file a bugreport to AutoDAO bugtracker.");
147             return (Class<? extends PersistentEntity<?>>) type;
148           }
149         }
150       }
151     }
152     return null;
153   }
154 
155   @Override
156   public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
157     return method.getDeclaringClass() == Object.class ? null : MANDATORY_TRANSACTION_ATTRIBUTE;
158   }
159 }