View Javadoc

1   /*
2    * Copyright 2005-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.support;
18  
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.Properties;
24  import javax.servlet.ServletContext;
25  
26  import org.springframework.beans.BeanUtils;
27  import org.springframework.beans.BeansException;
28  import org.springframework.beans.factory.BeanClassLoaderAware;
29  import org.springframework.beans.factory.BeanCreationException;
30  import org.springframework.beans.factory.BeanFactory;
31  import org.springframework.beans.factory.BeanFactoryAware;
32  import org.springframework.beans.factory.BeanInitializationException;
33  import org.springframework.beans.factory.BeanNameAware;
34  import org.springframework.beans.factory.InitializingBean;
35  import org.springframework.context.ApplicationContext;
36  import org.springframework.context.ApplicationContextAware;
37  import org.springframework.context.ApplicationEventPublisherAware;
38  import org.springframework.context.MessageSourceAware;
39  import org.springframework.context.ResourceLoaderAware;
40  import org.springframework.core.OrderComparator;
41  import org.springframework.core.io.ClassPathResource;
42  import org.springframework.core.io.Resource;
43  import org.springframework.core.io.support.PropertiesLoaderUtils;
44  import org.springframework.util.Assert;
45  import org.springframework.util.ClassUtils;
46  import org.springframework.util.StringUtils;
47  import org.springframework.web.context.ServletContextAware;
48  import org.springframework.web.context.WebApplicationContext;
49  
50  /**
51   * Helper class for for loading default implementations of an interface. Encapsulates a properties object, which
52   * contains strategy interface names as keys, and comma-separated class names as values.
53   * <p/>
54   * Simulates the {@link BeanFactory normal lifecycle} for beans, by calling {@link
55   * BeanFactoryAware#setBeanFactory(BeanFactory)}, {@link ApplicationContextAware#setApplicationContext(ApplicationContext)},
56   * etc.
57   *
58   * @author Arjen Poutsma
59   * @since 1.0.0
60   */
61  public class DefaultStrategiesHelper {
62  
63      /** Keys are strategy interface names, values are implementation class names. */
64      private Properties defaultStrategies;
65  
66      /** Initializes a new instance of the <code>DefaultStrategiesHelper</code> based on the given set of properties. */
67      public DefaultStrategiesHelper(Properties defaultStrategies) {
68          Assert.notNull(defaultStrategies, "defaultStrategies must not be null");
69          this.defaultStrategies = defaultStrategies;
70      }
71  
72      /** Initializes a new instance of the <code>DefaultStrategiesHelper</code> based on the given resource. */
73      public DefaultStrategiesHelper(Resource resource) throws IllegalStateException {
74          try {
75              defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
76          }
77          catch (IOException ex) {
78              throw new IllegalStateException("Could not load '" + resource + "': " + ex.getMessage());
79          }
80      }
81  
82      /**
83       * Initializes a new instance of the <code>DefaultStrategiesHelper</code> based on the given type.
84       * <p/>
85       * This constructor will attempt to load a 'typeName'.properties file in the same package as the given type.
86       */
87      public DefaultStrategiesHelper(Class<?> type) {
88          this(new ClassPathResource(ClassUtils.getShortName(type) + ".properties", type));
89      }
90  
91      /**
92       * Create a list of strategy objects for the given strategy interface. Strategies are retrieved from the
93       * <code>Properties</code> object given at construction-time.
94       *
95       * @param strategyInterface the strategy interface
96       * @return a list of corresponding strategy objects
97       * @throws BeansException if initialization failed
98       */
99      public <T> List<T> getDefaultStrategies(Class<T> strategyInterface) throws BeanInitializationException {
100         return getDefaultStrategies(strategyInterface, null);
101     }
102 
103     /**
104      * Create a list of strategy objects for the given strategy interface. Strategies are retrieved from the
105      * <code>Properties</code> object given at construction-time. It instantiates the strategy objects and satisfies
106      * <code>ApplicationContextAware</code> with the supplied context if necessary.
107      *
108      * @param strategyInterface  the strategy interface
109      * @param applicationContext used to satisfy strategies that are application context aware, may be
110      *                           <code>null</code>
111      * @return a list of corresponding strategy objects
112      * @throws BeansException if initialization failed
113      */
114     @SuppressWarnings("unchecked")
115     public <T> List<T> getDefaultStrategies(Class<T> strategyInterface, ApplicationContext applicationContext)
116             throws BeanInitializationException {
117         String key = strategyInterface.getName();
118         try {
119             List<T> result;
120             String value = defaultStrategies.getProperty(key);
121             if (value != null) {
122                 String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
123                 result = new ArrayList<T>(classNames.length);
124                 ClassLoader classLoader = null;
125                 if (applicationContext != null) {
126                     classLoader = applicationContext.getClassLoader();
127                 }
128                 if (classLoader == null) {
129                     classLoader = DefaultStrategiesHelper.class.getClassLoader();
130                 }
131                 for (String className : classNames) {
132                     Class<T> clazz = (Class<T>) ClassUtils.forName(className, classLoader);
133                     Assert.isTrue(strategyInterface.isAssignableFrom(clazz), clazz.getName() + " is not a " + strategyInterface.getName());
134                     T strategy = instantiateBean(clazz, applicationContext);
135                     result.add(strategy);
136                 }
137             }
138             else {
139                 result = Collections.emptyList();
140             }
141             Collections.sort(result, new OrderComparator());
142             return result;
143         }
144         catch (ClassNotFoundException ex) {
145             throw new BeanInitializationException("Could not find default strategy class for interface [" + key + "]",
146                     ex);
147         }
148     }
149 
150     /** Instantiates the given bean, simulating the standard bean life cycle. */
151     private <T> T instantiateBean(Class<T> clazz, ApplicationContext applicationContext) {
152         T strategy = BeanUtils.instantiateClass(clazz);
153         if (strategy instanceof BeanNameAware) {
154             BeanNameAware beanNameAware = (BeanNameAware) strategy;
155             beanNameAware.setBeanName(clazz.getName());
156         }
157         if (applicationContext != null) {
158             if (strategy instanceof BeanClassLoaderAware) {
159                 ((BeanClassLoaderAware) strategy).setBeanClassLoader(applicationContext.getClassLoader());
160             }
161             if (strategy instanceof BeanFactoryAware) {
162                 ((BeanFactoryAware) strategy).setBeanFactory(applicationContext);
163             }
164             if (strategy instanceof ResourceLoaderAware) {
165                 ((ResourceLoaderAware) strategy).setResourceLoader(applicationContext);
166             }
167             if (strategy instanceof ApplicationEventPublisherAware) {
168                 ((ApplicationEventPublisherAware) strategy).setApplicationEventPublisher(applicationContext);
169             }
170             if (strategy instanceof MessageSourceAware) {
171                 ((MessageSourceAware) strategy).setMessageSource(applicationContext);
172             }
173             if (strategy instanceof ApplicationContextAware) {
174                 ApplicationContextAware applicationContextAware = (ApplicationContextAware) strategy;
175                 applicationContextAware.setApplicationContext(applicationContext);
176             }
177             if (applicationContext instanceof WebApplicationContext && strategy instanceof ServletContextAware) {
178                 ServletContext servletContext = ((WebApplicationContext) applicationContext).getServletContext();
179                 ((ServletContextAware) strategy).setServletContext(servletContext);
180             }
181         }
182         if (strategy instanceof InitializingBean) {
183             InitializingBean initializingBean = (InitializingBean) strategy;
184             try {
185                 initializingBean.afterPropertiesSet();
186             }
187             catch (Throwable ex) {
188                 throw new BeanCreationException("Invocation of init method failed", ex);
189             }
190         }
191         return strategy;
192     }
193 
194     /**
195      * Return the default strategy object for the given strategy interface.
196      *
197      * @param strategyInterface the strategy interface
198      * @return the corresponding strategy object
199      * @throws BeansException if initialization failed
200      * @see #getDefaultStrategies
201      */
202     public <T> T getDefaultStrategy(Class<T> strategyInterface) throws BeanInitializationException {
203         return getDefaultStrategy(strategyInterface, null);
204     }
205 
206     /**
207      * Return the default strategy object for the given strategy interface.
208      * <p/>
209      * Delegates to {@link #getDefaultStrategies(Class,ApplicationContext)}, expecting a single object in the list.
210      *
211      * @param strategyInterface  the strategy interface
212      * @param applicationContext used to satisfy strategies that are application context aware, may be
213      *                           <code>null</code>
214      * @return the corresponding strategy object
215      * @throws BeansException if initialization failed
216      */
217     public <T> T getDefaultStrategy(Class<T> strategyInterface, ApplicationContext applicationContext)
218             throws BeanInitializationException {
219         List<T> result = getDefaultStrategies(strategyInterface, applicationContext);
220         if (result.size() != 1) {
221             throw new BeanInitializationException(
222                     "Could not find exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
223         }
224         return result.get(0);
225     }
226 
227 }