001package springdao;
002
003import java.lang.reflect.Field;
004import java.lang.reflect.InvocationTargetException;
005import java.lang.reflect.Method;
006import java.lang.reflect.ParameterizedType;
007import java.lang.reflect.Type;
008import java.util.concurrent.atomic.AtomicInteger;
009import org.apache.logging.log4j.Logger;
010import org.apache.logging.log4j.LogManager;
011import org.springframework.beans.BeansException;
012import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
013import org.springframework.beans.factory.NoSuchBeanDefinitionException;
014import org.springframework.beans.factory.config.BeanDefinition;
015import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
016import org.springframework.beans.factory.support.BeanDefinitionRegistry;
017import org.springframework.beans.factory.support.GenericBeanDefinition;
018import org.springframework.context.ApplicationContext;
019import org.springframework.context.ApplicationContextAware;
020import org.springframework.context.ConfigurableApplicationContext;
021import org.springframework.core.Ordered;
022import org.springframework.util.ClassUtils;
023import org.springframework.util.ReflectionUtils;
024import org.springframework.util.StringUtils;
025import springdao.support.AbstractSpringDao;
026import springdao.support.SimpleSpringDao;
027
028/**
029 *
030 * @author Kent Yeh
031 */
032public class DaoAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements Ordered,
033        ApplicationContextAware {
034
035    private static final Logger log = LogManager.getLogger(DaoAnnotationBeanPostProcessor.class);
036    private ApplicationContext context;
037    private ConfigurableApplicationContext regContext;
038    private static final AtomicInteger defcnt = new AtomicInteger(0);
039
040    @Override
041    public int getOrder() {
042        return Ordered.HIGHEST_PRECEDENCE;
043    }
044
045    @Override
046    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
047        this.context = applicationContext;
048        if (applicationContext instanceof ConfigurableApplicationContext) {
049            regContext = (ConfigurableApplicationContext) applicationContext;
050        }
051    }
052
053    public <T> T getBean(String name, Class<T> requiredType) {
054        try {
055            return context.getBean(name, requiredType);
056        } catch (NoSuchBeanDefinitionException ex) {
057            log.warn("Bean name '{}' with {} not exists.", name, requiredType.getSimpleName());
058            return null;
059        } catch (BeansException ex) {
060            log.warn(String.format("Can't get %s[%s] bean.", requiredType.getSimpleName(), name), ex);
061            return null;
062        }
063    }
064
065    /*private void registerBean(String name, Object bean) {
066     if (regContext != null) {
067     regContext.getBeanFactory().registerSingleton(name, bean);
068     logger.debug("register {} with {}->{}", new Object[]{name, bean.getClass(), bean});
069     }
070     }*/
071    private String convertName(String orgName) {
072        String[] names = orgName.split("\\.");
073        StringBuilder sb = new StringBuilder();
074        for (int i = 0; i < names.length; i++) {
075            if (i == 0) {
076                sb.append(String.valueOf(names[i].charAt(0)).toLowerCase()).append(names[i].substring(1));
077            } else {
078                sb.append(String.valueOf(names[i].charAt(0)).toUpperCase()).append(names[i].substring(1));
079            }
080        }
081        return sb.toString();
082    }
083
084    @Override
085    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
086
087        ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
088
089            @Override
090            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
091                final Dao dao = field.getAnnotation(Dao.class);
092
093                if (dao != null) {
094                    Class<?> genericType = dao.value().equals(Object.class) ? null : dao.value();
095                    if (genericType == null) {
096                        if (field.getGenericType() instanceof ParameterizedType) {
097                            ParameterizedType pt = (ParameterizedType) field.getGenericType();
098                            Type[] typeArguments = pt.getActualTypeArguments();
099                            if (typeArguments != null & typeArguments.length > 0) {
100                                genericType = (Class<?>) typeArguments[0];
101                            }
102                        }
103                    }
104                    if (genericType == null) {
105                        throw new IllegalArgumentException("@Dao field should assoicate a Generic ParameterizedType like DaoManager<Type> "
106                                + field.getName() + " or annotated with @Dao(assocationType.class)");
107                    }
108                    final Class<?> assoicateType = genericType;
109                    String daoName = StringUtils.hasText(dao.name()) ? dao.name() : dao.autoRegister()
110                            ? String.format("%sDao", convertName(DaoRepository.class.equals(field.getType()) ? assoicateType.getSimpleName() : field.getType().getName()))
111                            : String.format("%sDao_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
112                    DaoRepository<?> resultDao = daoName != null && !daoName.isEmpty() ? getBean(daoName, DaoRepository.class) : null;
113                    if (resultDao == null) {
114                        if (ClassUtils.isAssignable(field.getType(), AbstractSpringDao.class)) {
115                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
116                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
117                            beanDefinition.setBeanClass(SimpleSpringDao.class);
118                            beanDefinition.setLazyInit(false);
119                            beanDefinition.setAbstract(false);
120                            beanDefinition.setAutowireCandidate(true);
121                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
122                            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, assoicateType);
123                            registry.registerBeanDefinition(daoName, beanDefinition);
124                            resultDao = (DaoRepository<?>) context.getBean(daoName);
125                        } else if (ClassUtils.isAssignable(DaoRepository.class, field.getType())) {
126                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
127                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
128                            beanDefinition.setBeanClass(field.getType());
129                            beanDefinition.setLazyInit(false);
130                            beanDefinition.setAbstract(false);
131                            beanDefinition.setAutowireCandidate(true);
132                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
133                            registry.registerBeanDefinition(daoName, beanDefinition);
134                            resultDao = (DaoRepository<?>) context.getBean(daoName);
135                        } else {
136                            throw new BeanNotOfRequiredTypeException(field.getName(), DaoRepository.class, field.getType());
137                        }
138                        log.debug("Build, and inject field with bean {}@{}<{}>", daoName, resultDao.getClass().getSimpleName(), assoicateType.getSimpleName());
139                    }
140                    ReflectionUtils.makeAccessible(field);
141                    field.set(bean, resultDao);
142                }
143                final DaoManager ormm = field.getAnnotation(DaoManager.class);
144                if (ormm != null) {
145                    Class<?> genericType = ormm.value().equals(Object.class) ? null : ormm.value();
146                    if (genericType == null) {
147                        if (field.getGenericType() instanceof ParameterizedType) {
148                            ParameterizedType pt = (ParameterizedType) field.getGenericType();
149                            Type[] typeArguments = pt.getActualTypeArguments();
150                            if (typeArguments != null & typeArguments.length > 0) {
151                                genericType = (Class<?>) typeArguments[0];
152                            }
153                        }
154                    }
155                    if (genericType == null) {
156                        throw new IllegalArgumentException("@DaoManager field should assoicate a Generic ParameterizedType like RepositoryManager<Type> "
157                                + field.getName() + " or annotated with @DaoManager(assocationType.class)");
158                    }
159                    final Class<?> assoicateType = genericType;
160                    String mgrName = StringUtils.hasText(ormm.name()) ? ormm.name() : ormm.autoRegister()
161                            ? String.format("%sManager", convertName(RepositoryManager.class.equals(field.getType()) ? assoicateType.getSimpleName() : field.getType().getName()))
162                            : String.format("%sManager_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
163                    RepositoryManager resultManager = mgrName != null && !mgrName.isEmpty() ? getBean(mgrName, RepositoryManager.class) : null;
164                    Class<?> fc = field.getType();
165                    if (resultManager == null) {
166                        //target's type equals or extends from ormm.baseManagerType();
167                        if (ClassUtils.isAssignable(ormm.baseManagerType(), fc)) {
168                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
169                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
170                            beanDefinition.setBeanClass(fc);
171                            beanDefinition.setLazyInit(false);
172                            beanDefinition.setAbstract(false);
173                            beanDefinition.setAutowireCandidate(true);
174                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
175                            registry.registerBeanDefinition(mgrName, beanDefinition);
176                            resultManager = (RepositoryManager) context.getBean(mgrName);
177                        } else if (ClassUtils.isAssignable(fc, ormm.baseManagerType())) {
178                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
179                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
180                            beanDefinition.setBeanClass(ormm.baseManagerType());
181                            beanDefinition.setLazyInit(false);
182                            beanDefinition.setAbstract(false);
183                            beanDefinition.setAutowireCandidate(true);
184                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
185                            registry.registerBeanDefinition(mgrName, beanDefinition);
186                            resultManager = (RepositoryManager) context.getBean(mgrName);
187                        }
188                    }
189                    if (resultManager.getDao() == null) {
190                        String daoName = StringUtils.hasText(ormm.daoName()) ? ormm.daoName() : ormm.autoRegister()
191                                ? String.format("%sDao", convertName(assoicateType.getSimpleName()))
192                                : String.format("%sDao_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
193                        DaoRepository<?> resultDao = StringUtils.hasText(daoName) ? getBean(daoName, DaoRepository.class) : null;
194                        if (resultDao == null) {
195                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
196                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
197                            beanDefinition.setBeanClass(SimpleSpringDao.class);
198                            beanDefinition.setLazyInit(false);
199                            beanDefinition.setAbstract(false);
200                            beanDefinition.setAutowireCandidate(true);
201                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
202                            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, assoicateType);
203                            registry.registerBeanDefinition(daoName, beanDefinition);
204                            resultDao = (DaoRepository<?>) context.getBean(daoName);
205                        }
206                        resultManager.setDao(resultDao);
207                    }
208                    ReflectionUtils.makeAccessible(field);
209                    log.debug("Inject {} with {}", field.getName(), resultManager.getClass());
210                    field.set(bean, resultManager);
211                }
212            }
213        });
214        ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
215
216            @Override
217            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
218
219                Class<?> parmatypes[] = method.getParameterTypes();
220                Class<?> fc = parmatypes.length > 0 ? parmatypes[0] : null;
221
222                final Dao dao = method.getAnnotation(Dao.class);
223                if (dao != null && fc != null && void.class.equals(method.getReturnType())
224                        && ClassUtils.isAssignable(DaoRepository.class, fc)) {
225                    Class<?> genericType = dao.value().equals(Object.class) ? null : dao.value();
226                    if (genericType == null) {
227                        Type[] genericParameterTypes = method.getGenericParameterTypes();
228                        if (genericParameterTypes[0] instanceof ParameterizedType) {
229                            ParameterizedType aType = (ParameterizedType) genericParameterTypes[0];
230                            Type[] parameterArgTypes = aType.getActualTypeArguments();
231                            if (parameterArgTypes.length == 1) {
232                                genericType = (Class<?>) parameterArgTypes[0];
233                            }
234                        }
235                    }
236                    if (genericType == null) {
237                        throw new IllegalArgumentException("@Dao Method should assoicate a Generic ParameterizedType like " + 
238                                method.getName() + "(DaoRepository<Type>)  or annotated with @Dao(assocationType.class) ");
239                    }
240                    final Class<?> assoicateType = genericType;
241                    String daoName = StringUtils.hasText(dao.name()) ? dao.name() : dao.autoRegister()
242                            ? String.format("%sDao", convertName(DaoRepository.class.equals(fc) ? assoicateType.getSimpleName() : fc.getName()))
243                            : String.format("%sDao_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
244                    DaoRepository<?> resultDao = daoName != null && !daoName.isEmpty() ? getBean(daoName, DaoRepository.class) : null;
245                    if (resultDao == null) {
246                        if (ClassUtils.isAssignable(fc, AbstractSpringDao.class)) {
247                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
248                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
249                            beanDefinition.setBeanClass(SimpleSpringDao.class);
250                            beanDefinition.setLazyInit(false);
251                            beanDefinition.setAbstract(false);
252                            beanDefinition.setAutowireCandidate(true);
253                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
254                            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, assoicateType);
255                            registry.registerBeanDefinition(daoName, beanDefinition);
256                            resultDao = (DaoRepository<?>) context.getBean(daoName);
257                        } else {
258                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
259                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
260                            beanDefinition.setBeanClass(fc);
261                            beanDefinition.setLazyInit(false);
262                            beanDefinition.setAbstract(false);
263                            beanDefinition.setAutowireCandidate(true);
264                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
265                            registry.registerBeanDefinition(daoName, beanDefinition);
266                            resultDao = (DaoRepository<?>) context.getBean(daoName);
267                        }
268                    }
269                    ReflectionUtils.makeAccessible(method);
270                    try {
271                        log.debug("Inject {}({}) with {}", new Object[]{method.getName(), fc.getSimpleName(), resultDao.getClass()});
272                        method.invoke(bean, resultDao);
273                    } catch (InvocationTargetException ex) {
274                        log.error(ex.getMessage(), ex);
275                    }
276                }
277                final DaoManager ormm = method.getAnnotation(DaoManager.class);
278                if (ormm != null && fc != null && void.class.equals(method.getReturnType())
279                        && (ClassUtils.isAssignable(fc, ormm.baseManagerType()) || ClassUtils.isAssignable(ormm.baseManagerType(), fc))) {
280                    Class<?> genericType = ormm.value().equals(Object.class) ? null : ormm.value();
281                    if (genericType == null) {
282                        Type[] genericParameterTypes = method.getGenericParameterTypes();
283                        if (genericParameterTypes[0] instanceof ParameterizedType) {
284                            ParameterizedType aType = (ParameterizedType) genericParameterTypes[0];
285                            Type[] parameterArgTypes = aType.getActualTypeArguments();
286                            if (parameterArgTypes.length == 1) {
287                                genericType = (Class<?>) parameterArgTypes[0];
288                            }
289                        }
290                    }
291                    if (genericType == null) {
292                        throw new IllegalArgumentException("@DaoMethod Method should assoicate a Generic ParameterizedType like "
293                                + method.getName() + "(RepositoryManager<Type>)  or annotated with @DaoManager(assocationType.class)");
294                    }
295                    final Class<?> assoicateType = genericType;
296                    String mgrName = StringUtils.hasText(ormm.name()) ? ormm.name() : ormm.autoRegister()
297                            ? String.format("%sManager", convertName(RepositoryManager.class.equals(method.getReturnType()) ? assoicateType.getSimpleName() : fc.getName()))
298                            : String.format("%sManager_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
299                    RepositoryManager resultManager = StringUtils.hasText(mgrName) ? getBean(mgrName, RepositoryManager.class) : null;
300                    if (resultManager == null) {
301                        if (ClassUtils.isAssignable(ormm.baseManagerType(), fc)) {
302                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
303                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
304                            beanDefinition.setBeanClass(fc);
305                            beanDefinition.setLazyInit(false);
306                            beanDefinition.setAbstract(false);
307                            beanDefinition.setAutowireCandidate(true);
308                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
309                            registry.registerBeanDefinition(mgrName, beanDefinition);
310                            resultManager = (RepositoryManager) context.getBean(mgrName);
311                        } else if (ClassUtils.isAssignable(fc, ormm.baseManagerType())) {
312                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
313                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
314                            beanDefinition.setBeanClass(ormm.baseManagerType());
315                            beanDefinition.setLazyInit(false);
316                            beanDefinition.setAbstract(false);
317                            beanDefinition.setAutowireCandidate(true);
318                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
319                            registry.registerBeanDefinition(mgrName, beanDefinition);
320                            resultManager = (RepositoryManager) context.getBean(mgrName);
321                        }
322                    }
323                    if (resultManager.getDao() == null) {
324                        String daoName = StringUtils.hasText(ormm.daoName()) ? ormm.daoName() : ormm.autoRegister()
325                                ? String.format("%sDao", convertName(assoicateType.getSimpleName()))
326                                : String.format("%sDao_%d", convertName(assoicateType.getSimpleName()), defcnt.getAndAdd(1));
327                        DaoRepository<?> resultDao = StringUtils.hasText(daoName) ? getBean(daoName, DaoRepository.class) : null;
328                        if (resultDao == null) {
329                            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) regContext.getBeanFactory();
330                            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
331                            beanDefinition.setBeanClass(SimpleSpringDao.class);
332                            beanDefinition.setLazyInit(false);
333                            beanDefinition.setAbstract(false);
334                            beanDefinition.setAutowireCandidate(true);
335                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
336                            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, assoicateType);
337                            registry.registerBeanDefinition(daoName, beanDefinition);
338                            resultDao = (DaoRepository<?>) context.getBean(daoName);
339                        }
340                        resultManager.setDao(resultDao);
341                    }
342                    ReflectionUtils.makeAccessible(method);
343                    try {
344                        log.debug("Inject {}({}) with {}", new Object[]{method.getName(), fc.getSimpleName(), resultManager.getClass()});
345                        method.invoke(bean, resultManager);
346                    } catch (InvocationTargetException ex) {
347                        log.error(ex.getMessage(), ex);
348                    }
349                }
350            }
351        });
352        return bean;
353    }
354}