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 }