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                      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 }