View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2006 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.core.aop;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  import org.springframework.aop.Advisor;
27  import org.springframework.aop.TargetSource;
28  import org.springframework.aop.framework.Advised;
29  import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
30  import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
31  import org.springframework.beans.factory.BeanFactory;
32  
33  import ch.elca.el4j.services.monitoring.notification.CoreNotificationHelper;
34  
35  /**
36   * Used to enrich an existing proxy.
37   *
38   * @svnLink $Revision: 3883 $;$Date: 2009-08-04 15:35:01 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/core/src/main/java/ch/elca/el4j/core/aop/ProxyEnricher.java $
39   *
40   * @author Martin Zeltner (MZE)
41   */
42  public class ProxyEnricher {
43  	/**
44  	 * Private logger.
45  	 */
46  	private static Logger s_logger
47  		= LoggerFactory.getLogger(ProxyEnricher.class);
48  	
49  	/**
50  	 * Hide default constructor.
51  	 */
52  	protected ProxyEnricher() { }
53  
54  	/**
55  	 * If the target of the given target source is already an advised object,
56  	 * then this proxy will be enriched with the known advisors. If the
57  	 * enrichment failed, this method will return <code>null</code>.
58  	 *
59  	 * @param beanClass
60  	 *            the class of the bean
61  	 * @param beanName
62  	 *            the name of the bean
63  	 * @param specificInterceptors
64  	 *            the set of interceptors that is specific to this bean (may be
65  	 *            empty, but not null)
66  	 * @param targetSource
67  	 *            the TargetSource for the proxy, already pre-configured to
68  	 *            access the bean
69  	 * @param interceptorNames
70  	 *            Are the names of interceptors to add to proxy too.
71  	 * @param beanFactory Is the bean factory.
72  	 * @param advisorAdapterRegistry
73  	 *            Is the advisor adapter registry.
74  	 * @param applyCommonInterceptorsFirst
75  	 *            Flag if common interceptors should be applied before the
76  	 *            specific interceptors.
77  	 * @return Returns the enriched proxy or <code>null</code> if enrichment
78  	 *         failed for some reason.
79  	 */
80  	public static Object enrichProxy(Class<?> beanClass, String beanName,
81  		Object[] specificInterceptors, TargetSource targetSource,
82  		String[] interceptorNames, BeanFactory beanFactory,
83  		AdvisorAdapterRegistry advisorAdapterRegistry,
84  		boolean applyCommonInterceptorsFirst) {
85  		
86  		boolean isProxyEnrichable = isProxyEnrichable(beanClass, beanName,
87  			specificInterceptors, targetSource, beanFactory);
88  		
89  		// Trying to get the advised object.
90  		Advised proxy = null;
91  		if (isProxyEnrichable) {
92  			try {
93  				proxy = (Advised) targetSource.getTarget();
94  			} catch (Exception e) {
95  				isProxyEnrichable = false;
96  				CoreNotificationHelper.notifyMisconfiguration(
97  					"Exception while getting target of target source of bean"
98  					+ " with name " + beanName + " of type " + beanClass
99  					+ ".", e);
100 			}
101 		}
102 		
103 		// If the given target is already an advised object we enrich this
104 		// proxy.
105 		Object result = null;
106 		if (isProxyEnrichable && proxy != null) {
107 			result = enrichProxyInternal(beanClass, beanName,
108 				specificInterceptors, targetSource, interceptorNames,
109 				beanFactory, advisorAdapterRegistry,
110 				applyCommonInterceptorsFirst, proxy);
111 		}
112 		return result;
113 	}
114 	
115 	/**
116 	 * Tests if the given bean is a proxy and if it is possible to enrich it.
117 	 *
118 	 * @param beanClass
119 	 *            the class of the bean
120 	 * @param beanName
121 	 *            the name of the bean
122 	 * @param specificInterceptors
123 	 *            the set of interceptors that is specific to this bean (may be
124 	 *            empty, but not null)
125 	 * @param targetSource
126 	 *            the TargetSource for the proxy, already pre-configured to
127 	 *            access the bean
128 	 * @param beanFactory Is the factory where to get beans.
129 	 * @return Returns <code>true</code> if the given bean is a proxy and if it
130 	 *         is possible to enrich it.
131 	 */
132 	public static boolean isProxyEnrichable(Class<?> beanClass,
133 		String beanName, Object[] specificInterceptors,
134 		TargetSource targetSource, BeanFactory beanFactory) {
135 		boolean isProxyEnrichable = true;
136 		try {
137 			// TODO: Check if singleton is a must. In MZE's opinion
138 			// this should not be a must.
139 //            if (!beanFactory.isSingleton(beanName)) {
140 //                s_logger.warn("Bean '" + beanName + "' is not a singleton and "
141 //                    + "thus can not be enriched currently.");
142 //                isProxyEnrichable = false;
143 //            }
144 			
145 			if (isProxyEnrichable) {
146 				if (!targetSource.isStatic()) {
147 					s_logger.warn("Bean '" + beanName + "' has a non-static "
148 						+ "target source. Anyway, we will now try to get the "
149 						+ "target of the target source. This can cause an "
150 						+ "unexpected behavior of the bean loading mechanism.");
151 				}
152 				Object targetBean = targetSource.getTarget();
153 				if (targetBean == null || !(targetBean instanceof Advised)) {
154 					isProxyEnrichable = false;
155 				}
156 			}
157 		} catch (Exception e) {
158 			s_logger.warn("Exception occured while trying to detect if bean"
159 				+ " with name " + beanName + " of type " + beanClass
160 				+ " is an enrichable proxy.", e);
161 			isProxyEnrichable = false;
162 		}
163 		
164 		return isProxyEnrichable;
165 	}
166 
167 	/**
168 	 * Enriches the given proxy instead of creating a new proxy.
169 	 *
170 	 * @param beanClass
171 	 *            the class of the bean
172 	 * @param beanName
173 	 *            the name of the bean
174 	 * @param specificInterceptors
175 	 *            the set of interceptors that is specific to this bean (may be
176 	 *            empty, but not null)
177 	 * @param targetSource
178 	 *            the TargetSource for the proxy, already pre-configured to
179 	 *            access the bean
180 	 * @param interceptorNames
181 	 *            Are the names of interceptors to add to given proxy too.
182 	 * @param beanFactory Is the bean factory.
183 	 * @param advisorAdapterRegistry
184 	 *            Is the advisor adapter registry.
185 	 * @param applyCommonInterceptorsFirst
186 	 *            Flag if common interceptors should be applied before the
187 	 *            common interceptors.
188 	 * @param proxy
189 	 *            Is the proxy (advised) to enrich.
190 	 * @return Returns the enriched proxy.
191 	 * @see #createProxy(Class, String, Object[], TargetSource)
192 	 */
193 	@SuppressWarnings("unchecked")
194 	protected static Object enrichProxyInternal(Class beanClass,
195 		String beanName, Object[] specificInterceptors,
196 		TargetSource targetSource, String[] interceptorNames,
197 		BeanFactory beanFactory, AdvisorAdapterRegistry advisorAdapterRegistry,
198 		boolean applyCommonInterceptorsFirst, Advised proxy) {
199 		
200 		// Collect all interceptors.
201 		Advisor[] commonInterceptors = resolveInterceptorNames(
202 			interceptorNames, beanFactory, advisorAdapterRegistry);
203 		List allInterceptors = new ArrayList();
204 		if (specificInterceptors != null) {
205 			allInterceptors.addAll(Arrays.asList(specificInterceptors));
206 			if (commonInterceptors != null) {
207 				if (applyCommonInterceptorsFirst) {
208 					allInterceptors.addAll(0,
209 						Arrays.asList(commonInterceptors));
210 				} else {
211 					allInterceptors.addAll(Arrays.asList(commonInterceptors));
212 				}
213 			}
214 		}
215 		if (s_logger.isDebugEnabled()) {
216 			int nrOfCommonInterceptors = commonInterceptors != null
217 				? commonInterceptors.length : 0;
218 			int nrOfSpecificInterceptors = specificInterceptors != null
219 				? specificInterceptors.length : 0;
220 			s_logger.debug("Prepending " + nrOfCommonInterceptors
221 				+ " common interceptors and " + nrOfSpecificInterceptors
222 				+ " specific interceptors on existing proxy bean with name '"
223 				+ beanName + "'");
224 		}
225 		
226 		// Reverse order of interceptor list so interceptors will be added
227 		// to existing proxy in correct order.
228 		Collections.reverse(allInterceptors);
229 		for (Object advice : allInterceptors) {
230 			Advisor advisor = advisorAdapterRegistry.wrap(advice);
231 			proxy.addAdvisor(0, advisor);
232 		}
233 		
234 		return proxy;
235 	}
236 	
237 	/**
238 	 * @param interceptorNames Are the interceptor names to resolve.
239 	 * @param beanFactory Is the used bean factory.
240 	 * @param advisorAdapterRegistry Is the used advisor adapter registry.
241 	 * @return Resolves the specified interceptor names to Advisor objects.
242 	 * @see AbstractAutoProxyCreator#resolveInterceptorNames
243 	 */
244 	protected static Advisor[] resolveInterceptorNames(
245 		String[] interceptorNames, BeanFactory beanFactory,
246 		AdvisorAdapterRegistry advisorAdapterRegistry) {
247 		Advisor[] advisors = new Advisor[interceptorNames.length];
248 		for (int i = 0; i < interceptorNames.length; i++) {
249 			Object advice = beanFactory.getBean(interceptorNames[i]);
250 			advisors[i] = advisorAdapterRegistry.wrap(advice);
251 		}
252 		return advisors;
253 	}
254 }