View Javadoc

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  
98                      if (log.isDebugEnabled())
99                      {
100                         log.debug(e.getLocalizedMessage(), e);
101                     }
102                 }
103 
104                 if (beanClass != null)
105                 {
106                     // XXX: Hack to deal with aop:scope-proxy (scopedTarget.) beans
107                     if (!_SpringUtils.isAlternateBeanName(beanName))
108                     {
109                         // we are not on a scopedTarget ... check if there is one
110                         String alternateBeanName = _SpringUtils.getAlternateBeanName(beanName);
111                         if (beanFactory.containsBeanDefinition(alternateBeanName))
112                         {
113                             // ... yes, we are just interested in the alternate one.
114                             continue;
115                         }
116                     }
117                     String realBeanName = _SpringUtils.getRealBeanName(beanName);
118 
119                     // check whether the bean is an orchestra-annotated bean,
120                     // and if so cache its annotation info for later use.
121                     annotationInfoManager.processBeanAnnotations(realBeanName, beanClass);
122 
123                     // Now deal with any annotation data that must be processed at startup.
124                     AnnotationInfo info = annotationInfoManager
125                             .getAnnotationInfoByBeanName(realBeanName);
126                     if (info != null)
127                     {
128                         processStartupAnnotations(beanDefinition, info);
129                     }
130                 }
131             }
132         }
133     }
134 
135     /**
136      * Handle any annotations on a bean which should be processed on startup.
137      * <p>
138      * One such annotation is the
139      * 
140      * @ConversationName annotation, which should modify the beanDefinition
141      *                   object, as it is equivalent to putting an
142      *                   orchestra:conversationName attribute in the spring bean
143      *                   declaration.
144      */
145     private void processStartupAnnotations(BeanDefinition beanDefinition, AnnotationInfo info)
146     {
147         ConversationName conversationName = info.getConversationName();
148         if (conversationName != null)
149         {
150             String convNameFromAnno = conversationName.value();
151             if (convNameFromAnno != null && convNameFromAnno.length() > 0)
152             {
153                 String convNameFromDef = getConversationName(beanDefinition);
154                 if (convNameFromDef == null)
155                 {
156                     setConversationName(beanDefinition, convNameFromAnno);
157                 }
158             }
159         }
160     }
161 
162     /**
163      * Get the orchestra conversationName attribute (if any) from the spring
164      * bean definition.
165      */
166     // TODO: Move this method into orchestra-core, then call it from here, from
167     // AbstractSpringOrchestraScope and BeanDefinition*Decorator. This of course
168     // creates a dependency from this code onto that modified orchestra-core
169     // version.
170     private static String getConversationName(BeanDefinition def)
171     {
172         return (String) def
173                 .getAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE);
174     }
175 
176     // See getConversationName
177     private static void setConversationName(BeanDefinition def, String convName)
178     {
179         def.setAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE,
180                 convName);
181     }
182 }