001/* 002 * Copyright 2002-2013 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.ejb.interceptor; 018 019import java.util.Map; 020import java.util.WeakHashMap; 021import javax.annotation.PostConstruct; 022import javax.annotation.PreDestroy; 023import javax.ejb.EJBException; 024import javax.ejb.PostActivate; 025import javax.ejb.PrePassivate; 026import javax.interceptor.InvocationContext; 027 028import org.springframework.beans.factory.BeanFactory; 029import org.springframework.beans.factory.access.BeanFactoryLocator; 030import org.springframework.beans.factory.access.BeanFactoryReference; 031import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; 032import org.springframework.context.ApplicationContext; 033import org.springframework.context.access.ContextSingletonBeanFactoryLocator; 034 035/** 036 * EJB3-compliant interceptor class that injects Spring beans into 037 * fields and methods which are annotated with {@code @Autowired}. 038 * Performs injection after construction as well as after activation 039 * of a passivated bean. 040 * 041 * <p>To be applied through an {@code @Interceptors} annotation in 042 * the EJB Session Bean or Message-Driven Bean class, or through an 043 * {@code interceptor-binding} XML element in the EJB deployment descriptor. 044 * 045 * <p>Delegates to Spring's {@link AutowiredAnnotationBeanPostProcessor} 046 * underneath, allowing for customization of its specific settings through 047 * overriding the {@link #configureBeanPostProcessor} template method. 048 * 049 * <p>The actual BeanFactory to obtain Spring beans from is determined 050 * by the {@link #getBeanFactory} template method. The default implementation 051 * obtains the Spring {@link ContextSingletonBeanFactoryLocator}, initialized 052 * from the default resource location <strong>classpath*:beanRefContext.xml</strong>, 053 * and obtains the single ApplicationContext defined there. 054 * 055 * <p><b>NOTE: If you have more than one shared ApplicationContext definition available 056 * in your EJB class loader, you need to override the {@link #getBeanFactoryLocatorKey} 057 * method and provide a specific locator key for each autowired EJB.</b> 058 * Alternatively, override the {@link #getBeanFactory} template method and 059 * obtain the target factory explicitly. 060 * 061 * <p><b>WARNING: Do not define the same bean as Spring-managed bean and as 062 * EJB3 session bean in the same deployment unit.</b> In particular, be 063 * careful when using the {@code <context:component-scan>} feature 064 * in combination with the deployment of Spring-based EJB3 session beans: 065 * Make sure that the EJB3 session beans are <i>not</i> autodetected as 066 * Spring-managed beans as well, using appropriate package restrictions. 067 * 068 * @author Juergen Hoeller 069 * @since 2.5.1 070 * @see org.springframework.beans.factory.annotation.Autowired 071 * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 072 * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator 073 * @see #getBeanFactoryLocatorKey 074 */ 075public class SpringBeanAutowiringInterceptor { 076 077 /* 078 * We're keeping the BeanFactoryReference per target object in order to 079 * allow for using a shared interceptor instance on pooled target beans. 080 * This is not strictly necessary for EJB3 Session Beans and Message-Driven 081 * Beans, where interceptor instances get created per target bean instance. 082 * It simply protects against future usage of the interceptor in a shared scenario. 083 */ 084 private final Map<Object, BeanFactoryReference> beanFactoryReferences = 085 new WeakHashMap<Object, BeanFactoryReference>(); 086 087 088 /** 089 * Autowire the target bean after construction as well as after passivation. 090 * @param invocationContext the EJB3 invocation context 091 */ 092 @PostConstruct 093 @PostActivate 094 public void autowireBean(InvocationContext invocationContext) { 095 doAutowireBean(invocationContext.getTarget()); 096 try { 097 invocationContext.proceed(); 098 } 099 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}