KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > aop > framework > AdvisedSupport


1 /*
2  * Copyright 2002-2007 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.aop.framework;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.LinkedList JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.aopalliance.aop.Advice;
27
28 import org.springframework.aop.Advisor;
29 import org.springframework.aop.DynamicIntroductionAdvice;
30 import org.springframework.aop.IntroductionAdvisor;
31 import org.springframework.aop.IntroductionInfo;
32 import org.springframework.aop.TargetSource;
33 import org.springframework.aop.support.DefaultIntroductionAdvisor;
34 import org.springframework.aop.support.DefaultPointcutAdvisor;
35 import org.springframework.aop.target.EmptyTargetSource;
36 import org.springframework.aop.target.SingletonTargetSource;
37 import org.springframework.core.CollectionFactory;
38 import org.springframework.util.Assert;
39 import org.springframework.util.ClassUtils;
40 import org.springframework.util.ObjectUtils;
41
42 /**
43  * Base class for AOP proxy configuration managers.
44  * These are not themselves AOP proxies, but subclasses of this class are
45  * normally factories from which AOP proxy instances are obtained directly.
46  *
47  * <p>This class frees subclasses of the housekeeping of Advices
48  * and Advisors, but doesn't actually implement proxy creation
49  * methods, which are provided by subclasses.
50  *
51  * <p>This class is serializable; subclasses need not be.
52  * This class is used to hold snapshots of proxies.
53  *
54  * @author Rod Johnson
55  * @author Juergen Hoeller
56  * @see org.springframework.aop.framework.AopProxy
57  */

58 public class AdvisedSupport extends ProxyConfig implements Advised {
59
60     /** use serialVersionUID from Spring 2.0 for interoperability */
61     private static final long serialVersionUID = 2651364800145442165L;
62
63
64     /**
65      * Canonical TargetSource when there's no target, and behavior is
66      * supplied by the advisors.
67      */

68     public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
69
70
71     /** Package-protected to allow direct access for efficiency */
72     TargetSource targetSource = EMPTY_TARGET_SOURCE;
73
74     /** The AdvisorChainFactory to use */
75     transient AdvisorChainFactory advisorChainFactory;
76
77     /** Cache with Method as key and advisor chain List as value */
78     private transient Map JavaDoc methodCache;
79
80     /**
81      * Interfaces to be implemented by the proxy. Held in List to keep the order
82      * of registration, to create JDK proxy with specified order of interfaces.
83      */

84     private List JavaDoc interfaces = new ArrayList JavaDoc();
85
86     /**
87      * List of Advisors. If an Advice is added, it will be wrapped
88      * in an Advisor before being added to this List.
89      */

90     private List JavaDoc advisors = new LinkedList JavaDoc();
91
92     /**
93      * Array updated on changes to the advisors list, which is easier
94      * to manipulate internally.
95      */

96     private Advisor[] advisorArray = new Advisor[0];
97
98
99     /**
100      * No-arg constructor for use as a JavaBean.
101      */

102     public AdvisedSupport() {
103         initDefaultAdvisorChainFactory();
104     }
105
106     /**
107      * Create a AdvisedSupport instance with the given parameters.
108      * @param interfaces the proxied interfaces
109      */

110     public AdvisedSupport(Class JavaDoc[] interfaces) {
111         this();
112         setInterfaces(interfaces);
113     }
114
115     /**
116      * Initialize the default AdvisorChainFactory.
117      */

118     private void initDefaultAdvisorChainFactory() {
119         setAdvisorChainFactory(new DefaultAdvisorChainFactory());
120         this.methodCache = CollectionFactory.createIdentityMapIfPossible(32);
121     }
122
123
124     /**
125      * Set the given object as target.
126      * Will create a SingletonTargetSource for the object.
127      * @see #setTargetSource
128      * @see org.springframework.aop.target.SingletonTargetSource
129      */

130     public void setTarget(Object JavaDoc target) {
131         setTargetSource(new SingletonTargetSource(target));
132     }
133
134     public void setTargetSource(TargetSource targetSource) {
135         this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
136     }
137
138     public TargetSource getTargetSource() {
139         return this.targetSource;
140     }
141
142     /**
143      * Set a target class to be proxied, indicating that the proxy
144      * should be castable to the given class.
145      * <p>Internally, an {@link org.springframework.aop.target.EmptyTargetSource}
146      * for the given target class will be used. The kind of proxy needed
147      * will be determined on actual creation of the proxy.
148      * <p>This is a replacement for setting a "targetSource" or "target",
149      * for the case where we want a proxy based on a target class
150      * (which can be an interface or a concrete class) without having
151      * a fully capable TargetSource available.
152      * @see #setTargetSource
153      * @see #setTarget
154      */

155     public void setTargetClass(Class JavaDoc targetClass) {
156         this.targetSource = EmptyTargetSource.forClass(targetClass);
157     }
158
159     public Class JavaDoc getTargetClass() {
160         return this.targetSource.getTargetClass();
161     }
162
163     /**
164      * Set the advisor chain factory to use.
165      * <p>Default is a {@link DefaultAdvisorChainFactory}.
166      */

167     public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
168         Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null");
169         this.advisorChainFactory = advisorChainFactory;
170     }
171
172     /**
173      * Return the advisor chain factory to use (never <code>null</code>).
174      */

175     public AdvisorChainFactory getAdvisorChainFactory() {
176         return this.advisorChainFactory;
177     }
178
179
180     /**
181      * Set the interfaces to be proxied.
182      */

183     public void setInterfaces(Class JavaDoc[] interfaces) {
184         Assert.notNull(interfaces, "Interfaces must not be null");
185         this.interfaces.clear();
186         for (int i = 0; i < interfaces.length; i++) {
187             addInterface(interfaces[i]);
188         }
189     }
190
191     /**
192      * Add a new proxied interface.
193      * @param intf the additional interface to proxy
194      */

195     public void addInterface(Class JavaDoc intf) {
196         Assert.notNull(intf, "Interface must not be null");
197         if (!intf.isInterface()) {
198             throw new IllegalArgumentException JavaDoc("[" + intf.getName() + "] is not an interface");
199         }
200         if (!this.interfaces.contains(intf)) {
201             this.interfaces.add(intf);
202             adviceChanged();
203         }
204     }
205
206     /**
207      * Remove a proxied interface.
208      * <p>Does nothing if the given interface isn't proxied.
209      * @param intf the interface to remove from the proxy
210      * @return <code>true</code> if the interface was removed; <code>false</code>
211      * if the interface was not found and hence could not be removed
212      */

213     public boolean removeInterface(Class JavaDoc intf) {
214         return this.interfaces.remove(intf);
215     }
216
217     public Class JavaDoc[] getProxiedInterfaces() {
218         return (Class JavaDoc[]) this.interfaces.toArray(new Class JavaDoc[this.interfaces.size()]);
219     }
220
221     public boolean isInterfaceProxied(Class JavaDoc intf) {
222         for (Iterator JavaDoc it = this.interfaces.iterator(); it.hasNext();) {
223             Class JavaDoc proxyIntf = (Class JavaDoc) it.next();
224             if (intf.isAssignableFrom(proxyIntf)) {
225                 return true;
226             }
227         }
228         return false;
229     }
230
231
232     public final Advisor[] getAdvisors() {
233         return this.advisorArray;
234     }
235
236     public void addAdvisor(Advisor advisor) {
237         int pos = this.advisors.size();
238         addAdvisor(pos, advisor);
239     }
240
241     public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
242         if (advisor instanceof IntroductionAdvisor) {
243             validateIntroductionAdvisor((IntroductionAdvisor) advisor);
244         }
245         addAdvisorInternal(pos, advisor);
246     }
247
248     public boolean removeAdvisor(Advisor advisor) {
249         int index = indexOf(advisor);
250         if (index == -1) {
251             return false;
252         }
253         else {
254             removeAdvisor(index);
255             return true;
256         }
257     }
258
259     public void removeAdvisor(int index) throws AopConfigException {
260         if (isFrozen()) {
261             throw new AopConfigException("Cannot remove Advisor: Configuration is frozen.");
262         }
263         if (index < 0 || index > this.advisors.size() - 1) {
264             throw new AopConfigException("Advisor index " + index + " is out of bounds: " +
265                     "This configuration only has " + this.advisors.size() + " advisors.");
266         }
267
268         Advisor advisor = (Advisor) this.advisors.get(index);
269         if (advisor instanceof IntroductionAdvisor) {
270             IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
271             // We need to remove introduction interfaces.
272
for (int j = 0; j < ia.getInterfaces().length; j++) {
273                 removeInterface(ia.getInterfaces()[j]);
274             }
275         }
276
277         this.advisors.remove(index);
278         updateAdvisorArray();
279         adviceChanged();
280     }
281
282     public int indexOf(Advisor advisor) {
283         Assert.notNull(advisor, "Advisor must not be null");
284         return this.advisors.indexOf(advisor);
285     }
286
287     public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
288         Assert.notNull(a, "Advisor a must not be null");
289         Assert.notNull(b, "Advisor b must not be null");
290         int index = indexOf(a);
291         if (index == -1) {
292             return false;
293         }
294         removeAdvisor(index);
295         addAdvisor(index, b);
296         return true;
297     }
298
299     /**
300      * Add all of the given advisors to this proxy configuration.
301      * @param advisors the advisors to register
302      */

303     public void addAllAdvisors(Advisor[] advisors) {
304         if (isFrozen()) {
305             throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
306         }
307         if (!ObjectUtils.isEmpty(advisors)) {
308             for (int i = 0; i < advisors.length; i++) {
309                 Advisor advisor = advisors[i];
310                 if (advisor instanceof IntroductionAdvisor) {
311                     validateIntroductionAdvisor((IntroductionAdvisor) advisor);
312                 }
313                 Assert.notNull(advisor, "Advisor must not be null");
314                 this.advisors.add(advisor);
315             }
316             updateAdvisorArray();
317             adviceChanged();
318         }
319     }
320
321     private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
322         advisor.validateInterfaces();
323         // If the advisor passed validation, we can make the change.
324
Class JavaDoc[] ifcs = advisor.getInterfaces();
325         for (int i = 0; i < ifcs.length; i++) {
326             addInterface(ifcs[i]);
327         }
328     }
329
330     private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
331         Assert.notNull(advisor, "Advisor must not be null");
332         if (isFrozen()) {
333             throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
334         }
335         if (pos > this.advisors.size()) {
336             throw new IllegalArgumentException JavaDoc(
337                     "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
338         }
339         this.advisors.add(pos, advisor);
340         updateAdvisorArray();
341         adviceChanged();
342     }
343
344     /**
345      * Bring the array up to date with the list.
346      */

347     protected final void updateAdvisorArray() {
348         this.advisorArray = (Advisor[]) this.advisors.toArray(new Advisor[this.advisors.size()]);
349     }
350
351     /**
352      * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}.
353      * <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array}
354      * and {@link #adviceChanged() fire advice changed events} when making any modifications.
355      */

356     protected final List JavaDoc getAdvisorsInternal() {
357         return this.advisors;
358     }
359
360
361     public void addAdvice(Advice advice) throws AopConfigException {
362         int pos = this.advisors.size();
363         addAdvice(pos, advice);
364     }
365
366     /**
367      * Cannot add introductions this way unless the advice implements IntroductionInfo.
368      */

369     public void addAdvice(int pos, Advice advice) throws AopConfigException {
370         Assert.notNull(advice, "Advice must not be null");
371         if (advice instanceof IntroductionInfo) {
372             // We don't need an IntroductionAdvisor for this kind of introduction:
373
// It's fully self-describing.
374
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
375         }
376         else if (advice instanceof DynamicIntroductionAdvice) {
377             // We need an IntroductionAdvisor for this kind of introduction.
378
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
379         }
380         else {
381             addAdvisor(pos, new DefaultPointcutAdvisor(advice));
382         }
383     }
384
385     public boolean removeAdvice(Advice advice) throws AopConfigException {
386         int index = indexOf(advice);
387         if (index == -1) {
388             return false;
389         }
390         else {
391             removeAdvisor(index);
392             return true;
393         }
394     }
395
396     public int indexOf(Advice advice) {
397         Assert.notNull(advice, "Advice must not be null");
398         for (int i = 0; i < this.advisors.size(); i++) {
399             Advisor advisor = (Advisor) this.advisors.get(i);
400             if (advisor.getAdvice() == advice) {
401                 return i;
402             }
403         }
404         return -1;
405     }
406
407     /**
408      * Is the given advice included in any advisor within this proxy configuration?
409      * @param advice the advice to check inclusion of
410      * @return whether this advice instance is included
411      */

412     public boolean adviceIncluded(Advice advice) {
413         Assert.notNull(advice, "Advice must not be null");
414         for (int i = 0; i < this.advisors.size(); i++) {
415             Advisor advisor = (Advisor) this.advisors.get(i);
416             if (advisor.getAdvice() == advice) {
417                 return true;
418             }
419         }
420         return false;
421     }
422
423     /**
424      * Count advices of the given class.
425      * @param adviceClass the advice class to check
426      * @return the count of the interceptors of this class or subclasses
427      */

428     public int countAdvicesOfType(Class JavaDoc adviceClass) {
429         Assert.notNull(adviceClass, "Advice class must not be null");
430         int count = 0;
431         for (int i = 0; i < this.advisors.size(); i++) {
432             Advisor advisor = (Advisor) this.advisors.get(i);
433             if (advisor.getAdvice() != null &&
434                     adviceClass.isAssignableFrom(advisor.getAdvice().getClass())) {
435                 count++;
436             }
437         }
438         return count;
439     }
440
441
442     /**
443      * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
444      * for the given method, bsaed on this configuration.
445      * @param method the proxied method
446      * @param targetClass the target class
447      * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
448      */

449     public List JavaDoc getInterceptorsAndDynamicInterceptionAdvice(Method JavaDoc method, Class JavaDoc targetClass) {
450         synchronized (this.methodCache) {
451             List JavaDoc cached = (List JavaDoc) this.methodCache.get(method);
452             if (cached == null) {
453                 cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
454                         this, null, method, targetClass);
455                 this.methodCache.put(method, cached);
456             }
457             return cached;
458         }
459     }
460
461     /**
462      * Invoked when advice has changed.
463      */

464     protected void adviceChanged() {
465         synchronized (this.methodCache) {
466             this.methodCache.clear();
467         }
468     }
469
470     /**
471      * Call this method on a new instance created by the no-arg constructor
472      * to create an independent copy of the configuration from the given object.
473      * @param other the AdvisedSupport object to copy configuration from
474      */

475     protected void copyConfigurationFrom(AdvisedSupport other) {
476         copyConfigurationFrom(other, other.targetSource, new ArrayList JavaDoc(other.advisors));
477     }
478
479     /**
480      * Copy the AOP configuration from the given AdvisedSupport object,
481      * but allow substitution of a fresh TargetSource and a given interceptor chain.
482      * @param other the AdvisedSupport object to take proxy configuration from
483      * @param targetSource the new TargetSource
484      * @param advisors the Advisors for the chain
485      */

486     protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSource, List JavaDoc advisors) {
487         copyFrom(other);
488         this.targetSource = targetSource;
489         this.advisorChainFactory = other.advisorChainFactory;
490         this.interfaces = new ArrayList JavaDoc(other.interfaces);
491         for (Iterator JavaDoc it = advisors.iterator(); it.hasNext();) {
492             Advisor advisor = (Advisor) it.next();
493             if (advisor instanceof IntroductionAdvisor) {
494                 validateIntroductionAdvisor((IntroductionAdvisor) advisor);
495             }
496             Assert.notNull(advisor, "Advisor must not be null");
497             this.advisors.add(advisor);
498         }
499         updateAdvisorArray();
500         adviceChanged();
501     }
502
503
504     //---------------------------------------------------------------------
505
// Serialization support
506
//---------------------------------------------------------------------
507

508     /**
509      * Serializes a copy of the state of this class, ignoring subclass state.
510      */

511     protected Object JavaDoc writeReplace() {
512         // Copy state to avoid dependencies on BeanFactory etc that subclasses may have.
513
AdvisedSupport copy = this;
514
515         // If we're in a non-serializable subclass, copy into an AdvisedSupport object.
516
if (!getClass().equals(AdvisedSupport.class)) {
517             copy = new AdvisedSupport();
518             copy.copyConfigurationFrom(this);
519         }
520
521         // May return this.
522
return copy;
523     }
524
525     /**
526      * Initializes transient fields.
527      */

528     private Object JavaDoc readResolve() {
529         initDefaultAdvisorChainFactory();
530         return this;
531     }
532
533
534     public String JavaDoc toProxyConfigString() {
535         return toString();
536     }
537
538     /**
539      * For debugging/diagnostic use.
540      */

541     public String JavaDoc toString() {
542         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(getClass().getName() + ": ");
543         sb.append(this.interfaces.size()).append(" interfaces ");
544         sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; ");
545         sb.append(this.advisors.size()).append(" advisors ");
546         sb.append(this.advisors).append("; ");
547         sb.append("targetSource [").append(this.targetSource).append("]; ");
548         sb.append(super.toString());
549         return sb.toString();
550     }
551
552 }
553
Popular Tags