1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. 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,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.orchestra.annotation.spring;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.myfaces.orchestra.annotation.AnnotationInfo;
24 import org.apache.myfaces.orchestra.annotation.AnnotationInfoManager;
25 import org.apache.myfaces.orchestra.conversation.annotations.ConversationName;
26 import org.apache.myfaces.orchestra.conversation.spring.BeanDefinitionConversationNameAttrDecorator;
27 import org.apache.myfaces.orchestra.conversation.spring._SpringUtils;
28 import org.apache.myfaces.shared_orchestra.util.ClassUtils;
29 import org.springframework.beans.BeansException;
30 import org.springframework.beans.factory.config.BeanDefinition;
31 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
32 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
33 import org.springframework.core.Ordered;
34
35 /**
36 * Parse all configured spring beans and extract Orchestra annotations out of
37 * them.
38 * <p>
39 * Just declaring an instance of this type as a Spring Singleton will cause the
40 * postProcessBeanFactory to be called passing in info about all the bean
41 * declarations in the spring context, allowing Orchestra annotations on any
42 * directly declared class to be discovered and processed.
43 * <p>
44 * Every class referenced from a bean declaration is then passed to the
45 * AnnotationInfoManager instance that has been injected into this object.
46 */
47 public class AnnotationsInfoInitializer implements BeanFactoryPostProcessor, Ordered
48 {
49 private Log log = LogFactory.getLog(AnnotationsInfoInitializer.class);
50
51 private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
52
53 private AnnotationInfoManager annotationInfoManager;
54
55 /**
56 * Implement the Spring Ordered interface.
57 */
58 public int getOrder()
59 {
60 return order;
61 }
62
63 public void setOrder(int order)
64 {
65 this.order = order;
66 }
67
68 /**
69 * Inject the object that actually inspects each Class for Orchestra annotations.
70 */
71 public void setAnnotationInfoManager(AnnotationInfoManager annotationInfoManager)
72 {
73 this.annotationInfoManager = annotationInfoManager;
74 }
75
76 /**
77 * For each bean in the beanFactory, load the appropriate Class object and
78 * pass it to the annotationInfoManager object for inspection.
79 */
80 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
81 throws BeansException
82 {
83 String[] beanNames = beanFactory.getBeanDefinitionNames();
84 for (String beanName : beanNames)
85 {
86 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
87 String className = beanDefinition.getBeanClassName();
88 if (className != null)
89 {
90 Class<?> beanClass = null;
91 try
92 {
93 beanClass = ClassUtils.classForName(className);
94 }
95 catch (ClassNotFoundException e)
96 {
97 log.debug(e.getLocalizedMessage(), e);
98 }
99
100 if (beanClass != null)
101 {
102 // XXX: Hack to deal with aop:scope-proxy (scopedTarget.) beans
103 if (!_SpringUtils.isAlternateBeanName(beanName))
104 {
105 // we are not on a scopedTarget ... check if there is one
106 String alternateBeanName = _SpringUtils.getAlternateBeanName(beanName);
107 if (beanFactory.containsBeanDefinition(alternateBeanName))
108 {
109 // ... yes, we are just interested in the alternate one.
110 continue;
111 }
112 }
113 String realBeanName = _SpringUtils.getRealBeanName(beanName);
114
115 // check whether the bean is an orchestra-annotated bean,
116 // and if so cache its annotation info for later use.
117 annotationInfoManager.processBeanAnnotations(realBeanName, beanClass);
118
119 // Now deal with any annotation data that must be processed at startup.
120 AnnotationInfo info = annotationInfoManager
121 .getAnnotationInfoByBeanName(realBeanName);
122 if (info != null)
123 {
124 processStartupAnnotations(beanDefinition, info);
125 }
126 }
127 }
128 }
129 }
130
131 /**
132 * Handle any annotations on a bean which should be processed on startup.
133 * <p>
134 * One such annotation is the
135 *
136 * @ConversationName annotation, which should modify the beanDefinition
137 * object, as it is equivalent to putting an
138 * orchestra:conversationName attribute in the spring bean
139 * declaration.
140 */
141 private void processStartupAnnotations(BeanDefinition beanDefinition, AnnotationInfo info)
142 {
143 ConversationName conversationName = info.getConversationName();
144 if (conversationName != null)
145 {
146 String convNameFromAnno = conversationName.value();
147 if (convNameFromAnno != null && convNameFromAnno.length() > 0)
148 {
149 String convNameFromDef = getConversationName(beanDefinition);
150 if (convNameFromDef == null)
151 {
152 setConversationName(beanDefinition, convNameFromAnno);
153 }
154 }
155 }
156 }
157
158 /**
159 * Get the orchestra conversationName attribute (if any) from the spring
160 * bean definition.
161 */
162 // TODO: Move this method into orchestra-core, then call it from here, from
163 // AbstractSpringOrchestraScope and BeanDefinition*Decorator. This of course
164 // creates a dependency from this code onto that modified orchestra-core
165 // version.
166 private static String getConversationName(BeanDefinition def)
167 {
168 return (String) def
169 .getAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE);
170 }
171
172 // See getConversationName
173 private static void setConversationName(BeanDefinition def, String convName)
174 {
175 def.setAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE,
176 convName);
177 }
178 }