1 /* 2 * Copyright 2002-2013 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.ejb.interceptor; 18 19 import java.util.Map; 20 import java.util.WeakHashMap; 21 import javax.annotation.PostConstruct; 22 import javax.annotation.PreDestroy; 23 import javax.ejb.EJBException; 24 import javax.ejb.PostActivate; 25 import javax.ejb.PrePassivate; 26 import javax.interceptor.InvocationContext; 27 28 import org.springframework.beans.factory.BeanFactory; 29 import org.springframework.beans.factory.access.BeanFactoryLocator; 30 import org.springframework.beans.factory.access.BeanFactoryReference; 31 import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; 32 import org.springframework.context.ApplicationContext; 33 import org.springframework.context.access.ContextSingletonBeanFactoryLocator; 34 35 /** 36 * EJB3-compliant interceptor class that injects Spring beans into 37 * fields and methods which are annotated with {@code @Autowired}. 38 * Performs injection after construction as well as after activation 39 * of a passivated bean. 40 * 41 * <p>To be applied through an {@code @Interceptors} annotation in 42 * the EJB Session Bean or Message-Driven Bean class, or through an 43 * {@code interceptor-binding} XML element in the EJB deployment descriptor. 44 * 45 * <p>Delegates to Spring's {@link AutowiredAnnotationBeanPostProcessor} 46 * underneath, allowing for customization of its specific settings through 47 * overriding the {@link #configureBeanPostProcessor} template method. 48 * 49 * <p>The actual BeanFactory to obtain Spring beans from is determined 50 * by the {@link #getBeanFactory} template method. The default implementation 51 * obtains the Spring {@link ContextSingletonBeanFactoryLocator}, initialized 52 * from the default resource location <strong>classpath*:beanRefContext.xml</strong>, 53 * and obtains the single ApplicationContext defined there. 54 * 55 * <p><b>NOTE: If you have more than one shared ApplicationContext definition available 56 * in your EJB class loader, you need to override the {@link #getBeanFactoryLocatorKey} 57 * method and provide a specific locator key for each autowired EJB.</b> 58 * Alternatively, override the {@link #getBeanFactory} template method and 59 * obtain the target factory explicitly. 60 * 61 * <p><b>WARNING: Do not define the same bean as Spring-managed bean and as 62 * EJB3 session bean in the same deployment unit.</b> In particular, be 63 * careful when using the {@code <context:component-scan>} feature 64 * in combination with the deployment of Spring-based EJB3 session beans: 65 * Make sure that the EJB3 session beans are <i>not</i> autodetected as 66 * Spring-managed beans as well, using appropriate package restrictions. 67 * 68 * @author Juergen Hoeller 69 * @since 2.5.1 70 * @see org.springframework.beans.factory.annotation.Autowired 71 * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 72 * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator 73 * @see #getBeanFactoryLocatorKey 74 */ 75 public class SpringBeanAutowiringInterceptor { 76 77 /* 78 * We're keeping the BeanFactoryReference per target object in order to 79 * allow for using a shared interceptor instance on pooled target beans. 80 * This is not strictly necessary for EJB3 Session Beans and Message-Driven 81 * Beans, where interceptor instances get created per target bean instance. 82 * It simply protects against future usage of the interceptor in a shared scenario. 83 */ 84 private final Map<Object, BeanFactoryReference> beanFactoryReferences = 85 new WeakHashMap<Object, BeanFactoryReference>(); 86 87 88 /** 89 * Autowire the target bean after construction as well as after passivation. 90 * @param invocationContext the EJB3 invocation context 91 */ 92 @PostConstruct 93 @PostActivate 94 public void autowireBean(InvocationContext invocationContext) { 95 doAutowireBean(invocationContext.getTarget()); 96 try { 97 invocationContext.proceed(); 98 } 99 catch (RuntimeException ex) { 100 doReleaseBean(invocationContext.getTarget()); 101 throw ex; 102 } 103 catch (Error err) { 104 doReleaseBean(invocationContext.getTarget()); 105 throw err; 106 } 107 catch (Exception ex) { 108 doReleaseBean(invocationContext.getTarget()); 109 // Cannot declare a checked exception on WebSphere here - so we need to wrap. 110 throw new EJBException(ex); 111 } 112 } 113 114 /** 115 * Actually autowire the target bean after construction/passivation. 116 * @param target the target bean to autowire 117 */ 118 protected void doAutowireBean(Object target) { 119 AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); 120 configureBeanPostProcessor(bpp, target); 121 bpp.setBeanFactory(getBeanFactory(target)); 122 bpp.processInjection(target); 123 } 124 125 /** 126 * Template method for configuring the 127 * {@link AutowiredAnnotationBeanPostProcessor} used for autowiring. 128 * @param processor the AutowiredAnnotationBeanPostProcessor to configure 129 * @param target the target bean to autowire with this processor 130 */ 131 protected void configureBeanPostProcessor(AutowiredAnnotationBeanPostProcessor processor, Object target) { 132 } 133 134 /** 135 * Determine the BeanFactory for autowiring the given target bean. 136 * @param target the target bean to autowire 137 * @return the BeanFactory to use (never {@code null}) 138 * @see #getBeanFactoryReference 139 */ 140 protected BeanFactory getBeanFactory(Object target) { 141 BeanFactory factory = getBeanFactoryReference(target).getFactory(); 142 if (factory instanceof ApplicationContext) { 143 factory = ((ApplicationContext) factory).getAutowireCapableBeanFactory(); 144 } 145 return factory; 146 } 147 148 /** 149 * Determine the BeanFactoryReference for the given target bean. 150 * <p>The default implementation delegates to {@link #getBeanFactoryLocator} 151 * and {@link #getBeanFactoryLocatorKey}. 152 * @param target the target bean to autowire 153 * @return the BeanFactoryReference to use (never {@code null}) 154 * @see #getBeanFactoryLocator 155 * @see #getBeanFactoryLocatorKey 156 * @see org.springframework.beans.factory.access.BeanFactoryLocator#useBeanFactory(String) 157 */ 158 protected BeanFactoryReference getBeanFactoryReference(Object target) { 159 String key = getBeanFactoryLocatorKey(target); 160 BeanFactoryReference ref = getBeanFactoryLocator(target).useBeanFactory(key); 161 this.beanFactoryReferences.put(target, ref); 162 return ref; 163 } 164 165 /** 166 * Determine the BeanFactoryLocator to obtain the BeanFactoryReference from. 167 * <p>The default implementation exposes Spring's default 168 * {@link ContextSingletonBeanFactoryLocator}. 169 * @param target the target bean to autowire 170 * @return the BeanFactoryLocator to use (never {@code null}) 171 * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator#getInstance() 172 */ 173 protected BeanFactoryLocator getBeanFactoryLocator(Object target) { 174 return ContextSingletonBeanFactoryLocator.getInstance(); 175 } 176 177 /** 178 * Determine the BeanFactoryLocator key to use. This typically indicates 179 * the bean name of the ApplicationContext definition in 180 * <strong>classpath*:beanRefContext.xml</strong> resource files. 181 * <p>The default is {@code null}, indicating the single 182 * ApplicationContext defined in the locator. This must be overridden 183 * if more than one shared ApplicationContext definition is available. 184 * @param target the target bean to autowire 185 * @return the BeanFactoryLocator key to use (or {@code null} for 186 * referring to the single ApplicationContext defined in the locator) 187 */ 188 protected String getBeanFactoryLocatorKey(Object target) { 189 return null; 190 } 191 192 193 /** 194 * Release the factory which has been used for autowiring the target bean. 195 * @param invocationContext the EJB3 invocation context 196 */ 197 @PreDestroy 198 @PrePassivate 199 public void releaseBean(InvocationContext invocationContext) { 200 doReleaseBean(invocationContext.getTarget()); 201 try { 202 invocationContext.proceed(); 203 } 204 catch (RuntimeException ex) { 205 throw ex; 206 } 207 catch (Exception ex) { 208 // Cannot declare a checked exception on WebSphere here - so we need to wrap. 209 throw new EJBException(ex); 210 } 211 } 212 213 /** 214 * Actually release the BeanFactoryReference for the given target bean. 215 * @param target the target bean to release 216 */ 217 protected void doReleaseBean(Object target) { 218 BeanFactoryReference ref = this.beanFactoryReferences.remove(target); 219 if (ref != null) { 220 ref.release(); 221 } 222 } 223 224 }