/* * Copyright 2005-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.ws.support; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; import javax.servlet.ServletContext; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.MessageSourceAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.OrderComparator; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.ServletContextAware; import org.springframework.web.context.WebApplicationContext; /** * Helper class for for loading default implementations of an interface. Encapsulates a properties object, which * contains strategy interface names as keys, and comma-separated class names as values. * * <p>Simulates the {@link BeanFactory normal lifecycle} for beans, by calling {@link * BeanFactoryAware#setBeanFactory(BeanFactory)}, {@link ApplicationContextAware#setApplicationContext(ApplicationContext)}, * etc. * * @author Arjen Poutsma * @since 1.0.0 */ public class DefaultStrategiesHelper { /** Keys are strategy interface names, values are implementation class names. */ private Properties defaultStrategies; /** Initializes a new instance of the {@code DefaultStrategiesHelper} based on the given set of properties. */ public DefaultStrategiesHelper(Properties defaultStrategies) { Assert.notNull(defaultStrategies, "defaultStrategies must not be null"); this.defaultStrategies = defaultStrategies; } /** Initializes a new instance of the {@code DefaultStrategiesHelper} based on the given resource. */ public DefaultStrategiesHelper(Resource resource) throws IllegalStateException { try { defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + resource + "': " + ex.getMessage()); } } /** * Initializes a new instance of the {@code DefaultStrategiesHelper} based on the given type. * * <p>This constructor will attempt to load a 'typeName'.properties file in the same package as the given type. */ public DefaultStrategiesHelper(Class<?> type) { this(new ClassPathResource(ClassUtils.getShortName(type) + ".properties", type)); } /** * Create a list of strategy objects for the given strategy interface. Strategies are retrieved from the * {@code Properties} object given at construction-time. * * @param strategyInterface the strategy interface * @return a list of corresponding strategy objects * @throws BeansException if initialization failed */ public <T> List<T> getDefaultStrategies(Class<T> strategyInterface) throws BeanInitializationException { return getDefaultStrategies(strategyInterface, null); } /** * Create a list of strategy objects for the given strategy interface. Strategies are retrieved from the * {@code Properties} object given at construction-time. It instantiates the strategy objects and satisfies * {@code ApplicationContextAware} with the supplied context if necessary. * * @param strategyInterface the strategy interface * @param applicationContext used to satisfy strategies that are application context aware, may be * {@code null} * @return a list of corresponding strategy objects * @throws BeansException if initialization failed */ @SuppressWarnings("unchecked") public <T> List<T> getDefaultStrategies(Class<T> strategyInterface, ApplicationContext applicationContext) throws BeanInitializationException { String key = strategyInterface.getName(); try { List<T> result; String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); result = new ArrayList<T>(classNames.length); ClassLoader classLoader = null; if (applicationContext != null) { classLoader = applicationContext.getClassLoader(); } if (classLoader == null) { classLoader = DefaultStrategiesHelper.class.getClassLoader(); } for (String className : classNames) { Class<T> clazz = (Class<T>) ClassUtils.forName(className, classLoader); Assert.isTrue(strategyInterface.isAssignableFrom(clazz), clazz.getName() + " is not a " + strategyInterface.getName()); T strategy = instantiateBean(clazz, applicationContext); result.add(strategy); } } else { result = Collections.emptyList(); } Collections.sort(result, new OrderComparator()); return result; } catch (ClassNotFoundException ex) { throw new BeanInitializationException("Could not find default strategy class for interface [" + key + "]", ex); } } /** Instantiates the given bean, simulating the standard bean life cycle. */ private <T> T instantiateBean(Class<T> clazz, ApplicationContext applicationContext) { T strategy = BeanUtils.instantiateClass(clazz); if (strategy instanceof BeanNameAware) { BeanNameAware beanNameAware = (BeanNameAware) strategy; beanNameAware.setBeanName(clazz.getName()); } if (applicationContext != null) { if (strategy instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) strategy).setBeanClassLoader(applicationContext.getClassLoader()); } if (strategy instanceof BeanFactoryAware) { ((BeanFactoryAware) strategy).setBeanFactory(applicationContext); } if (strategy instanceof ResourceLoaderAware) { ((ResourceLoaderAware) strategy).setResourceLoader(applicationContext); } if (strategy instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) strategy).setApplicationEventPublisher(applicationContext); } if (strategy instanceof MessageSourceAware) { ((MessageSourceAware) strategy).setMessageSource(applicationContext); } if (strategy instanceof ApplicationContextAware) { ApplicationContextAware applicationContextAware = (ApplicationContextAware) strategy; applicationContextAware.setApplicationContext(applicationContext); } if (applicationContext instanceof WebApplicationContext && strategy instanceof ServletContextAware) { ServletContext servletContext = ((WebApplicationContext) applicationContext).getServletContext(); ((ServletContextAware) strategy).setServletContext(servletContext); } } if (strategy instanceof InitializingBean) { InitializingBean initializingBean = (InitializingBean) strategy; try { initializingBean.afterPropertiesSet(); } catch (Throwable ex) { throw new BeanCreationException("Invocation of init method failed", ex); } } return strategy; } /** * Return the default strategy object for the given strategy interface. * * @param strategyInterface the strategy interface * @return the corresponding strategy object * @throws BeansException if initialization failed * @see #getDefaultStrategies */ public <T> T getDefaultStrategy(Class<T> strategyInterface) throws BeanInitializationException { return getDefaultStrategy(strategyInterface, null); } /** * Return the default strategy object for the given strategy interface. * * <p>Delegates to {@link #getDefaultStrategies(Class,ApplicationContext)}, expecting a single object in the list. * * @param strategyInterface the strategy interface * @param applicationContext used to satisfy strategies that are application context aware, may be * {@code null} * @return the corresponding strategy object * @throws BeansException if initialization failed */ public <T> T getDefaultStrategy(Class<T> strategyInterface, ApplicationContext applicationContext) throws BeanInitializationException { List<T> result = getDefaultStrategies(strategyInterface, applicationContext); if (result.size() != 1) { throw new BeanInitializationException( "Could not find exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); } return result.get(0); } }