diff --git a/api-el/src/main/java/org/ocpsoft/rewrite/el/TypeBasedExpression.java b/api-el/src/main/java/org/ocpsoft/rewrite/el/TypeBasedExpression.java index 9c35621b3..d091d6344 100644 --- a/api-el/src/main/java/org/ocpsoft/rewrite/el/TypeBasedExpression.java +++ b/api-el/src/main/java/org/ocpsoft/rewrite/el/TypeBasedExpression.java @@ -15,7 +15,9 @@ */ package org.ocpsoft.rewrite.el; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.ocpsoft.common.services.ServiceLoader; import org.ocpsoft.logging.Logger; @@ -62,43 +64,55 @@ public String getExpression() @SuppressWarnings("unchecked") private String lookupBeanName() { - // load the available SPI implementations Iterator iterator = ServiceLoader.load(BeanNameResolver.class).iterator(); + + List deferred = new ArrayList<>(); while (iterator.hasNext()) { BeanNameResolver resolver = iterator.next(); - // check if this implementation is able to tell the name - String beanName = resolver.getBeanName(clazz); - - if (log.isTraceEnabled()) { - log.trace("Service provider [{}] returned [{}] for class [{}]", new Object[] { - resolver.getClass().getSimpleName(), beanName, clazz.getName() - }); - } - - // the first result is accepted - if (beanName != null) { - - // create the complete EL expression including the component - String el = new StringBuilder() - .append(beanName).append('.').append(component) - .toString(); + try { + // check if this implementation is able to tell the name + String beanName = resolver.getBeanName(clazz); if (log.isTraceEnabled()) { - log.debug("Creation of EL expression for component [{}] of class [{}] successful: {}", new Object[] { - component, clazz.getName(), el + log.trace("Service provider [{}] returned [{}] for class [{}]", new Object[] { + resolver.getClass().getSimpleName(), beanName, clazz.getName() }); } - return el; + // the first result is accepted + if (beanName != null) { + + // create the complete EL expression including the component + String el = new StringBuilder() + .append(beanName).append('.').append(component) + .toString(); + + if (log.isTraceEnabled()) { + log.debug("Creation of EL expression for component [{}] of class [{}] successful: {}", new Object[] { + component, clazz.getName(), el + }); + } + + return el; + } + } + catch (Exception e) { + log.debug("Failed to resolve bean names using [" + resolver.getClass().getName() + "]", e); + deferred.add(e); } } + if (deferred.size() > 1) { + for (Exception e : deferred) { + log.error("Failed to resolve bean names.", e); + } + } throw new IllegalStateException("Unable to obtain EL name for bean of type [" + clazz.getName() + "] from any of the SPI implementations. You should conside placing a @" - + ELBeanName.class.getSimpleName() + " on the class."); + + ELBeanName.class.getSimpleName() + " on the class.", (deferred.size() == 1 ? deferred.get(0) : null)); } diff --git a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringBeanNameResolver.java b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringBeanNameResolver.java index 418b72a9b..1103ed355 100644 --- a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringBeanNameResolver.java +++ b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringBeanNameResolver.java @@ -16,12 +16,12 @@ package org.ocpsoft.rewrite.spring; import java.util.HashSet; -import java.util.Map; import java.util.Set; import org.ocpsoft.logging.Logger; import org.ocpsoft.rewrite.el.spi.BeanNameResolver; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; @@ -29,24 +29,28 @@ * {@link BeanNameResolver} implementation for Spring. * * @author Christian Kaltepoth + * @author Lincoln Baxter, III */ public class SpringBeanNameResolver implements BeanNameResolver { private final Logger log = Logger.getLogger(SpringBeanNameResolver.class); + @Autowired + private WebApplicationContext applicationContext; + @Override public String getBeanName(Class clazz) { - - // try to obtain the WebApplicationContext using ContextLoader - WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext(); - if (context == null) { - throw new IllegalStateException("Unable to get current WebApplicationContext"); + if (applicationContext == null) { + applicationContext = ContextLoader.getCurrentWebApplicationContext(); + if (applicationContext == null) { + throw new IllegalStateException("Unable to get current WebApplicationContext"); + } } // obtain a map of bean names - Set beanNames = resolveBeanNames(context, clazz); + Set beanNames = resolveBeanNames(applicationContext, clazz); // no beans of that type, nothing we can do if (beanNames == null || beanNames.size() == 0) { @@ -76,15 +80,14 @@ private Set resolveBeanNames(ListableBeanFactory beanFactory, Class c final Set result = new HashSet(); - Map beanMap = beanFactory.getBeansOfType(clazz); - if (beanMap != null) { - for (String name : beanMap.keySet()) { + String[] names = beanFactory.getBeanNamesForType(clazz); + if (names != null) { + for (String name : names) { if (name != null && !name.startsWith("scopedTarget.")) { result.add(name); } } } - return result; } diff --git a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringExpressionLanguageProvider.java b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringExpressionLanguageProvider.java index 0fa5916db..0c27ee045 100644 --- a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringExpressionLanguageProvider.java +++ b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringExpressionLanguageProvider.java @@ -33,6 +33,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.expression.spel.support.StandardTypeLocator; +import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; /** @@ -136,6 +137,13 @@ public EvaluationContext getEvaluationContext() // we need a ConfigurableBeanFactory to build the BeanExpressionContext ConfigurableBeanFactory beanFactory = null; + if (applicationContext == null) { + applicationContext = ContextLoader.getCurrentWebApplicationContext(); + if (applicationContext == null) { + throw new IllegalStateException("Unable to get current WebApplicationContext"); + } + } + // the WebApplicationContext MAY implement ConfigurableBeanFactory if (applicationContext instanceof ConfigurableBeanFactory) { beanFactory = (ConfigurableBeanFactory) applicationContext; diff --git a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceEnricher.java b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceEnricher.java index 0c29acaf6..2f4ff3377 100644 --- a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceEnricher.java +++ b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceEnricher.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.Collection; +import javax.servlet.ServletContext; + import org.ocpsoft.common.spi.ServiceEnricher; import org.ocpsoft.logging.Logger; import org.springframework.web.context.support.SpringBeanAutowiringSupport; @@ -35,7 +37,13 @@ public class SpringServiceEnricher implements ServiceEnricher @Override public void enrich(final T service) { - SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(service); + ServletContext context = SpringServletContextLoader.getCurrentServletContext(); + if (context != null) { + SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(service, context); + } + else { + SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(service); + } if (log.isDebugEnabled()) log.debug("Enriched instance of service [" + service.getClass().getName() + "]"); diff --git a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceLocator.java b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceLocator.java index 62f43652b..0644a999d 100644 --- a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceLocator.java +++ b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServiceLocator.java @@ -20,32 +20,41 @@ import java.util.Map; import java.util.Set; +import javax.servlet.ServletContext; + import org.ocpsoft.common.spi.ServiceLocator; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; /** * {@link ServiceLocator} implementation for Spring. * * @author Christian Kaltepoth + * @author Lincoln Baxter, III */ public class SpringServiceLocator implements ServiceLocator { - @Override @SuppressWarnings("unchecked") public Collection> locate(Class clazz) { Set> result = new LinkedHashSet>(); - // use the Spring API to obtain the WebApplicationContext - WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext(); + ServletContext servletContext = SpringServletContextLoader.getCurrentServletContext(); + WebApplicationContext applicationContext = null; + if (servletContext != null) { + applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); + } + else { + applicationContext = ContextLoader.getCurrentWebApplicationContext(); + } // may be null if Spring hasn't started yet - if (context != null) { + if (applicationContext != null) { // ask spring about SPI implementations - Map beans = context.getBeansOfType(clazz); + Map beans = applicationContext.getBeansOfType(clazz); // add the implementations Class objects to the result set for (T type : beans.values()) { diff --git a/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServletContextLoader.java b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServletContextLoader.java new file mode 100644 index 000000000..08374113b --- /dev/null +++ b/integration-spring/src/main/java/org/ocpsoft/rewrite/spring/SpringServletContextLoader.java @@ -0,0 +1,87 @@ +/* + * Copyright 2011 Lincoln Baxter, III + * + * 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.ocpsoft.rewrite.spring; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletRequestEvent; + +import org.ocpsoft.rewrite.servlet.spi.ContextListener; +import org.ocpsoft.rewrite.servlet.spi.RequestListener; + +/** + * Thread-safe {@link ServletContext} loader implementation for Spring. + * + * @author Lincoln Baxter, III + */ +public class SpringServletContextLoader implements ContextListener, RequestListener +{ + private static final Map contextMap = new ConcurrentHashMap<>(1); + + @Override + public void contextInitialized(ServletContextEvent event) + { + ServletContext servletContext = event.getServletContext(); + contextMap.put(Thread.currentThread().getContextClassLoader(), servletContext); + } + + @Override + public void contextDestroyed(ServletContextEvent event) + { + removeContext(event.getServletContext()); + } + + @Override + public void requestInitialized(ServletRequestEvent event) + { + ServletContext servletContext = event.getServletContext(); + contextMap.put(Thread.currentThread().getContextClassLoader(), servletContext); + } + + @Override + public void requestDestroyed(ServletRequestEvent event) + { + removeContext(event.getServletContext()); + + } + + private static void removeContext(ServletContext context) + { + if (contextMap.containsValue(context)) { + for (Entry entry : contextMap.entrySet()) { + if (entry.getValue() == context) { + contextMap.remove(entry.getKey()); + } + } + } + } + + public static ServletContext getCurrentServletContext() + { + return contextMap.get(Thread.currentThread().getContextClassLoader()); + } + + @Override + public int priority() + { + return 0; + } + +} diff --git a/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.ContextListener b/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.ContextListener new file mode 100644 index 000000000..8e610f7fe --- /dev/null +++ b/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.ContextListener @@ -0,0 +1 @@ +org.ocpsoft.rewrite.spring.SpringServletContextLoader \ No newline at end of file diff --git a/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.RequestListener b/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.RequestListener new file mode 100644 index 000000000..8e610f7fe --- /dev/null +++ b/integration-spring/src/main/resources/META-INF/services/org.ocpsoft.rewrite.servlet.spi.RequestListener @@ -0,0 +1 @@ +org.ocpsoft.rewrite.spring.SpringServletContextLoader \ No newline at end of file