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