001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.aop.framework;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.lang.reflect.Method;
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.aopalliance.aop.Advice;
029:
030: import org.springframework.aop.Advisor;
031: import org.springframework.aop.DynamicIntroductionAdvice;
032: import org.springframework.aop.IntroductionAdvisor;
033: import org.springframework.aop.IntroductionInfo;
034: import org.springframework.aop.TargetSource;
035: import org.springframework.aop.support.DefaultIntroductionAdvisor;
036: import org.springframework.aop.support.DefaultPointcutAdvisor;
037: import org.springframework.aop.target.EmptyTargetSource;
038: import org.springframework.aop.target.SingletonTargetSource;
039: import org.springframework.core.CollectionFactory;
040: import org.springframework.util.Assert;
041: import org.springframework.util.ClassUtils;
042: import org.springframework.util.ObjectUtils;
043:
044: /**
045: * Base class for AOP proxy configuration managers.
046: * These are not themselves AOP proxies, but subclasses of this class are
047: * normally factories from which AOP proxy instances are obtained directly.
048: *
049: * <p>This class frees subclasses of the housekeeping of Advices
050: * and Advisors, but doesn't actually implement proxy creation
051: * methods, which are provided by subclasses.
052: *
053: * <p>This class is serializable; subclasses need not be.
054: * This class is used to hold snapshots of proxies.
055: *
056: * @author Rod Johnson
057: * @author Juergen Hoeller
058: * @see org.springframework.aop.framework.AopProxy
059: */
060: public class AdvisedSupport extends ProxyConfig implements Advised {
061:
062: /** use serialVersionUID from Spring 2.0 for interoperability */
063: private static final long serialVersionUID = 2651364800145442165L;
064:
065: /**
066: * Canonical TargetSource when there's no target, and behavior is
067: * supplied by the advisors.
068: */
069: public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
070:
071: /** Package-protected to allow direct access for efficiency */
072: TargetSource targetSource = EMPTY_TARGET_SOURCE;
073:
074: /** Whether the Advisors are already filtered for the specific target class */
075: private boolean preFiltered = false;
076:
077: /** The AdvisorChainFactory to use */
078: AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
079:
080: /** Cache with Method as key and advisor chain List as value */
081: private transient Map methodCache;
082:
083: /**
084: * Interfaces to be implemented by the proxy. Held in List to keep the order
085: * of registration, to create JDK proxy with specified order of interfaces.
086: */
087: private List interfaces = new ArrayList();
088:
089: /**
090: * List of Advisors. If an Advice is added, it will be wrapped
091: * in an Advisor before being added to this List.
092: */
093: private List advisors = new LinkedList();
094:
095: /**
096: * Array updated on changes to the advisors list, which is easier
097: * to manipulate internally.
098: */
099: private Advisor[] advisorArray = new Advisor[0];
100:
101: /**
102: * No-arg constructor for use as a JavaBean.
103: */
104: public AdvisedSupport() {
105: initMethodCache();
106: }
107:
108: /**
109: * Create a AdvisedSupport instance with the given parameters.
110: * @param interfaces the proxied interfaces
111: */
112: public AdvisedSupport(Class[] interfaces) {
113: this ();
114: setInterfaces(interfaces);
115: }
116:
117: /**
118: * Initialize the method cache.
119: */
120: private void initMethodCache() {
121: this .methodCache = CollectionFactory
122: .createConcurrentMapIfPossible(32);
123: }
124:
125: /**
126: * Set the given object as target.
127: * Will create a SingletonTargetSource for the object.
128: * @see #setTargetSource
129: * @see org.springframework.aop.target.SingletonTargetSource
130: */
131: public void setTarget(Object target) {
132: setTargetSource(new SingletonTargetSource(target));
133: }
134:
135: public void setTargetSource(TargetSource targetSource) {
136: this .targetSource = (targetSource != null ? targetSource
137: : EMPTY_TARGET_SOURCE);
138: }
139:
140: public TargetSource getTargetSource() {
141: return this .targetSource;
142: }
143:
144: /**
145: * Set a target class to be proxied, indicating that the proxy
146: * should be castable to the given class.
147: * <p>Internally, an {@link org.springframework.aop.target.EmptyTargetSource}
148: * for the given target class will be used. The kind of proxy needed
149: * will be determined on actual creation of the proxy.
150: * <p>This is a replacement for setting a "targetSource" or "target",
151: * for the case where we want a proxy based on a target class
152: * (which can be an interface or a concrete class) without having
153: * a fully capable TargetSource available.
154: * @see #setTargetSource
155: * @see #setTarget
156: */
157: public void setTargetClass(Class targetClass) {
158: this .targetSource = EmptyTargetSource.forClass(targetClass);
159: }
160:
161: public Class getTargetClass() {
162: return this .targetSource.getTargetClass();
163: }
164:
165: public void setPreFiltered(boolean preFiltered) {
166: this .preFiltered = preFiltered;
167: }
168:
169: public boolean isPreFiltered() {
170: return this .preFiltered;
171: }
172:
173: /**
174: * Set the advisor chain factory to use.
175: * <p>Default is a {@link DefaultAdvisorChainFactory}.
176: */
177: public void setAdvisorChainFactory(
178: AdvisorChainFactory advisorChainFactory) {
179: Assert.notNull(advisorChainFactory,
180: "AdvisorChainFactory must not be null");
181: this .advisorChainFactory = advisorChainFactory;
182: }
183:
184: /**
185: * Return the advisor chain factory to use (never <code>null</code>).
186: */
187: public AdvisorChainFactory getAdvisorChainFactory() {
188: return this .advisorChainFactory;
189: }
190:
191: /**
192: * Set the interfaces to be proxied.
193: */
194: public void setInterfaces(Class[] interfaces) {
195: Assert.notNull(interfaces, "Interfaces must not be null");
196: this .interfaces.clear();
197: for (int i = 0; i < interfaces.length; i++) {
198: addInterface(interfaces[i]);
199: }
200: }
201:
202: /**
203: * Add a new proxied interface.
204: * @param intf the additional interface to proxy
205: */
206: public void addInterface(Class intf) {
207: Assert.notNull(intf, "Interface must not be null");
208: if (!intf.isInterface()) {
209: throw new IllegalArgumentException("[" + intf.getName()
210: + "] is not an interface");
211: }
212: if (!this .interfaces.contains(intf)) {
213: this .interfaces.add(intf);
214: adviceChanged();
215: }
216: }
217:
218: /**
219: * Remove a proxied interface.
220: * <p>Does nothing if the given interface isn't proxied.
221: * @param intf the interface to remove from the proxy
222: * @return <code>true</code> if the interface was removed; <code>false</code>
223: * if the interface was not found and hence could not be removed
224: */
225: public boolean removeInterface(Class intf) {
226: return this .interfaces.remove(intf);
227: }
228:
229: public Class[] getProxiedInterfaces() {
230: return (Class[]) this .interfaces
231: .toArray(new Class[this .interfaces.size()]);
232: }
233:
234: public boolean isInterfaceProxied(Class intf) {
235: for (Iterator it = this .interfaces.iterator(); it.hasNext();) {
236: Class proxyIntf = (Class) it.next();
237: if (intf.isAssignableFrom(proxyIntf)) {
238: return true;
239: }
240: }
241: return false;
242: }
243:
244: public final Advisor[] getAdvisors() {
245: return this .advisorArray;
246: }
247:
248: public void addAdvisor(Advisor advisor) {
249: int pos = this .advisors.size();
250: addAdvisor(pos, advisor);
251: }
252:
253: public void addAdvisor(int pos, Advisor advisor)
254: throws AopConfigException {
255: if (advisor instanceof IntroductionAdvisor) {
256: validateIntroductionAdvisor((IntroductionAdvisor) advisor);
257: }
258: addAdvisorInternal(pos, advisor);
259: }
260:
261: public boolean removeAdvisor(Advisor advisor) {
262: int index = indexOf(advisor);
263: if (index == -1) {
264: return false;
265: } else {
266: removeAdvisor(index);
267: return true;
268: }
269: }
270:
271: public void removeAdvisor(int index) throws AopConfigException {
272: if (isFrozen()) {
273: throw new AopConfigException(
274: "Cannot remove Advisor: Configuration is frozen.");
275: }
276: if (index < 0 || index > this .advisors.size() - 1) {
277: throw new AopConfigException("Advisor index " + index
278: + " is out of bounds: "
279: + "This configuration only has "
280: + this .advisors.size() + " advisors.");
281: }
282:
283: Advisor advisor = (Advisor) this .advisors.get(index);
284: if (advisor instanceof IntroductionAdvisor) {
285: IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
286: // We need to remove introduction interfaces.
287: for (int j = 0; j < ia.getInterfaces().length; j++) {
288: removeInterface(ia.getInterfaces()[j]);
289: }
290: }
291:
292: this .advisors.remove(index);
293: updateAdvisorArray();
294: adviceChanged();
295: }
296:
297: public int indexOf(Advisor advisor) {
298: Assert.notNull(advisor, "Advisor must not be null");
299: return this .advisors.indexOf(advisor);
300: }
301:
302: public boolean replaceAdvisor(Advisor a, Advisor b)
303: throws AopConfigException {
304: Assert.notNull(a, "Advisor a must not be null");
305: Assert.notNull(b, "Advisor b must not be null");
306: int index = indexOf(a);
307: if (index == -1) {
308: return false;
309: }
310: removeAdvisor(index);
311: addAdvisor(index, b);
312: return true;
313: }
314:
315: /**
316: * Add all of the given advisors to this proxy configuration.
317: * @param advisors the advisors to register
318: */
319: public void addAllAdvisors(Advisor[] advisors) {
320: if (isFrozen()) {
321: throw new AopConfigException(
322: "Cannot add advisor: Configuration is frozen.");
323: }
324: if (!ObjectUtils.isEmpty(advisors)) {
325: for (int i = 0; i < advisors.length; i++) {
326: Advisor advisor = advisors[i];
327: if (advisor instanceof IntroductionAdvisor) {
328: validateIntroductionAdvisor((IntroductionAdvisor) advisor);
329: }
330: Assert.notNull(advisor, "Advisor must not be null");
331: this .advisors.add(advisor);
332: }
333: updateAdvisorArray();
334: adviceChanged();
335: }
336: }
337:
338: private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
339: advisor.validateInterfaces();
340: // If the advisor passed validation, we can make the change.
341: Class[] ifcs = advisor.getInterfaces();
342: for (int i = 0; i < ifcs.length; i++) {
343: addInterface(ifcs[i]);
344: }
345: }
346:
347: private void addAdvisorInternal(int pos, Advisor advisor)
348: throws AopConfigException {
349: Assert.notNull(advisor, "Advisor must not be null");
350: if (isFrozen()) {
351: throw new AopConfigException(
352: "Cannot add advisor: Configuration is frozen.");
353: }
354: if (pos > this .advisors.size()) {
355: throw new IllegalArgumentException("Illegal position "
356: + pos + " in advisor list with size "
357: + this .advisors.size());
358: }
359: this .advisors.add(pos, advisor);
360: updateAdvisorArray();
361: adviceChanged();
362: }
363:
364: /**
365: * Bring the array up to date with the list.
366: */
367: protected final void updateAdvisorArray() {
368: this .advisorArray = (Advisor[]) this .advisors
369: .toArray(new Advisor[this .advisors.size()]);
370: }
371:
372: /**
373: * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}.
374: * <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array}
375: * and {@link #adviceChanged() fire advice changed events} when making any modifications.
376: */
377: protected final List getAdvisorsInternal() {
378: return this .advisors;
379: }
380:
381: public void addAdvice(Advice advice) throws AopConfigException {
382: int pos = this .advisors.size();
383: addAdvice(pos, advice);
384: }
385:
386: /**
387: * Cannot add introductions this way unless the advice implements IntroductionInfo.
388: */
389: public void addAdvice(int pos, Advice advice)
390: throws AopConfigException {
391: Assert.notNull(advice, "Advice must not be null");
392: if (advice instanceof IntroductionInfo) {
393: // We don't need an IntroductionAdvisor for this kind of introduction:
394: // It's fully self-describing.
395: addAdvisor(pos, new DefaultIntroductionAdvisor(advice,
396: (IntroductionInfo) advice));
397: } else if (advice instanceof DynamicIntroductionAdvice) {
398: // We need an IntroductionAdvisor for this kind of introduction.
399: throw new AopConfigException(
400: "DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
401: } else {
402: addAdvisor(pos, new DefaultPointcutAdvisor(advice));
403: }
404: }
405:
406: public boolean removeAdvice(Advice advice)
407: throws AopConfigException {
408: int index = indexOf(advice);
409: if (index == -1) {
410: return false;
411: } else {
412: removeAdvisor(index);
413: return true;
414: }
415: }
416:
417: public int indexOf(Advice advice) {
418: Assert.notNull(advice, "Advice must not be null");
419: for (int i = 0; i < this .advisors.size(); i++) {
420: Advisor advisor = (Advisor) this .advisors.get(i);
421: if (advisor.getAdvice() == advice) {
422: return i;
423: }
424: }
425: return -1;
426: }
427:
428: /**
429: * Is the given advice included in any advisor within this proxy configuration?
430: * @param advice the advice to check inclusion of
431: * @return whether this advice instance is included
432: */
433: public boolean adviceIncluded(Advice advice) {
434: Assert.notNull(advice, "Advice must not be null");
435: for (int i = 0; i < this .advisors.size(); i++) {
436: Advisor advisor = (Advisor) this .advisors.get(i);
437: if (advisor.getAdvice() == advice) {
438: return true;
439: }
440: }
441: return false;
442: }
443:
444: /**
445: * Count advices of the given class.
446: * @param adviceClass the advice class to check
447: * @return the count of the interceptors of this class or subclasses
448: */
449: public int countAdvicesOfType(Class adviceClass) {
450: Assert.notNull(adviceClass, "Advice class must not be null");
451: int count = 0;
452: for (int i = 0; i < this .advisors.size(); i++) {
453: Advisor advisor = (Advisor) this .advisors.get(i);
454: if (advisor.getAdvice() != null
455: && adviceClass.isAssignableFrom(advisor.getAdvice()
456: .getClass())) {
457: count++;
458: }
459: }
460: return count;
461: }
462:
463: /**
464: * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
465: * for the given method, based on this configuration.
466: * @param method the proxied method
467: * @param targetClass the target class
468: * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
469: */
470: public List getInterceptorsAndDynamicInterceptionAdvice(
471: Method method, Class targetClass) {
472: MethodCacheKey cacheKey = new MethodCacheKey(method);
473: List cached = (List) this .methodCache.get(cacheKey);
474: if (cached == null) {
475: cached = this .advisorChainFactory
476: .getInterceptorsAndDynamicInterceptionAdvice(this ,
477: method, targetClass);
478: this .methodCache.put(cacheKey, cached);
479: }
480: return cached;
481: }
482:
483: /**
484: * Invoked when advice has changed.
485: */
486: protected void adviceChanged() {
487: synchronized (this .methodCache) {
488: this .methodCache.clear();
489: }
490: }
491:
492: /**
493: * Call this method on a new instance created by the no-arg constructor
494: * to create an independent copy of the configuration from the given object.
495: * @param other the AdvisedSupport object to copy configuration from
496: */
497: protected void copyConfigurationFrom(AdvisedSupport other) {
498: copyConfigurationFrom(other, other.targetSource, new ArrayList(
499: other.advisors));
500: }
501:
502: /**
503: * Copy the AOP configuration from the given AdvisedSupport object,
504: * but allow substitution of a fresh TargetSource and a given interceptor chain.
505: * @param other the AdvisedSupport object to take proxy configuration from
506: * @param targetSource the new TargetSource
507: * @param advisors the Advisors for the chain
508: */
509: protected void copyConfigurationFrom(AdvisedSupport other,
510: TargetSource targetSource, List advisors) {
511: copyFrom(other);
512: this .targetSource = targetSource;
513: this .advisorChainFactory = other.advisorChainFactory;
514: this .interfaces = new ArrayList(other.interfaces);
515: for (Iterator it = advisors.iterator(); it.hasNext();) {
516: Advisor advisor = (Advisor) it.next();
517: if (advisor instanceof IntroductionAdvisor) {
518: validateIntroductionAdvisor((IntroductionAdvisor) advisor);
519: }
520: Assert.notNull(advisor, "Advisor must not be null");
521: this .advisors.add(advisor);
522: }
523: updateAdvisorArray();
524: adviceChanged();
525: }
526:
527: /**
528: * Build a configuration-only copy of this AdvisedSupport,
529: * replacing the TargetSource
530: */
531: AdvisedSupport getConfigurationOnlyCopy() {
532: AdvisedSupport copy = new AdvisedSupport();
533: copy.copyFrom(this );
534: copy.targetSource = EmptyTargetSource.forClass(
535: getTargetClass(), getTargetSource().isStatic());
536: copy.advisorChainFactory = this .advisorChainFactory;
537: copy.interfaces = this .interfaces;
538: copy.advisors = this .advisors;
539: copy.updateAdvisorArray();
540: return copy;
541: }
542:
543: //---------------------------------------------------------------------
544: // Serialization support
545: //---------------------------------------------------------------------
546:
547: private void readObject(ObjectInputStream ois) throws IOException,
548: ClassNotFoundException {
549: // Rely on default serialization; just initialize state after deserialization.
550: ois.defaultReadObject();
551:
552: // Initialize transient fields.
553: initMethodCache();
554: }
555:
556: public String toProxyConfigString() {
557: return toString();
558: }
559:
560: /**
561: * For debugging/diagnostic use.
562: */
563: public String toString() {
564: StringBuffer sb = new StringBuffer(getClass().getName() + ": ");
565: sb.append(this .interfaces.size()).append(" interfaces ");
566: sb.append(ClassUtils.classNamesToString(this .interfaces))
567: .append("; ");
568: sb.append(this .advisors.size()).append(" advisors ");
569: sb.append(this .advisors).append("; ");
570: sb.append("targetSource [").append(this .targetSource).append(
571: "]; ");
572: sb.append(super .toString());
573: return sb.toString();
574: }
575:
576: /**
577: * Simple wrapper class around a Method. Used as the key when
578: * caching methods, for efficient equals and hashCode comparisons.
579: */
580: private static class MethodCacheKey {
581:
582: private final Method method;
583:
584: private final int hashCode;
585:
586: public MethodCacheKey(Method method) {
587: this .method = method;
588: this .hashCode = method.hashCode();
589: }
590:
591: public boolean equals(Object other) {
592: if (other == this ) {
593: return true;
594: }
595: MethodCacheKey otherKey = (MethodCacheKey) other;
596: return (this .method == otherKey.method);
597: }
598:
599: public int hashCode() {
600: return this.hashCode;
601: }
602: }
603:
604: }
|