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.support;
018:
019: import java.util.Map;
020: import java.util.WeakHashMap;
021:
022: import org.aopalliance.intercept.MethodInvocation;
023:
024: import org.springframework.aop.DynamicIntroductionAdvice;
025: import org.springframework.aop.IntroductionInterceptor;
026: import org.springframework.aop.ProxyMethodInvocation;
027:
028: /**
029: * Convenient implementation of the
030: * {@link org.springframework.aop.IntroductionInterceptor} interface.
031: *
032: * <p>This differs from {@link DelegatingIntroductionInterceptor} in that a single
033: * instance of this class can be used to advise multiple target objects, and each target
034: * object will have its <i>own</i> delegate (whereas DelegatingIntroductionInterceptor
035: * shares the same delegate, and hence the same state across all targets).
036: *
037: * <p>The <code>suppressInterface</code> method can be used to suppress interfaces
038: * implemented by the delegate class but which should not be introduced to the
039: * owning AOP proxy.
040: *
041: * <p>An instance of this class is serializable if the delegates are.
042: *
043: * <p><i>Note: There are some implementation similarities between this class and
044: * {@link DelegatingIntroductionInterceptor} that suggest a possible refactoring
045: * to extract a common ancestor class in the future.</i>
046: *
047: * @author Adrian Colyer
048: * @author Juergen Hoeller
049: * @since 2.0
050: * @see #suppressInterface
051: * @see DelegatingIntroductionInterceptor
052: */
053: public class DelegatePerTargetObjectIntroductionInterceptor extends
054: IntroductionInfoSupport implements IntroductionInterceptor {
055:
056: /**
057: * Hold weak references to keys as we don't want to interfere with garbage collection..
058: */
059: private Map delegateMap = new WeakHashMap();
060:
061: private Class defaultImplType;
062:
063: private Class interfaceType;
064:
065: public DelegatePerTargetObjectIntroductionInterceptor(
066: Class defaultImplType, Class interfaceType) {
067: this .defaultImplType = defaultImplType;
068: this .interfaceType = interfaceType;
069: // cCeate a new delegate now (but don't store it in the map).
070: // We do this for two reasons:
071: // 1) to fail early if there is a problem instantiating delegates
072: // 2) to populate the interface map once and once only
073: Object delegate = createNewDelegate();
074: implementInterfacesOnObject(delegate);
075: suppressInterface(IntroductionInterceptor.class);
076: suppressInterface(DynamicIntroductionAdvice.class);
077: }
078:
079: /**
080: * Subclasses may need to override this if they want to perform custom
081: * behaviour in around advice. However, subclasses should invoke this
082: * method, which handles introduced interfaces and forwarding to the target.
083: */
084: public Object invoke(MethodInvocation mi) throws Throwable {
085: if (isMethodOnIntroducedInterface(mi)) {
086: Object delegate = getIntroductionDelegateFor(mi.getThis());
087:
088: // Using the following method rather than direct reflection,
089: // we get correct handling of InvocationTargetException
090: // if the introduced method throws an exception.
091: Object retVal = AopUtils.invokeJoinpointUsingReflection(
092: delegate, mi.getMethod(), mi.getArguments());
093:
094: // Massage return value if possible: if the delegate returned itself,
095: // we really want to return the proxy.
096: if (retVal == delegate
097: && mi instanceof ProxyMethodInvocation) {
098: retVal = ((ProxyMethodInvocation) mi).getProxy();
099: }
100: return retVal;
101: }
102:
103: return doProceed(mi);
104: }
105:
106: /**
107: * Proceed with the supplied {@link org.aopalliance.intercept.MethodInterceptor}.
108: * Subclasses can override this method to intercept method invocations on the
109: * target object which is useful when an introduction needs to monitor the object
110: * that it is introduced into. This method is <strong>never</strong> called for
111: * {@link MethodInvocation MethodInvocations} on the introduced interfaces.
112: */
113: protected Object doProceed(MethodInvocation mi) throws Throwable {
114: // If we get here, just pass the invocation on.
115: return mi.proceed();
116: }
117:
118: private Object getIntroductionDelegateFor(Object targetObject) {
119: synchronized (this .delegateMap) {
120: if (this .delegateMap.containsKey(targetObject)) {
121: return this .delegateMap.get(targetObject);
122: } else {
123: Object delegate = createNewDelegate();
124: this .delegateMap.put(targetObject, delegate);
125: return delegate;
126: }
127: }
128: }
129:
130: private Object createNewDelegate() {
131: try {
132: return this .defaultImplType.newInstance();
133: } catch (Throwable ex) {
134: throw new IllegalArgumentException(
135: "Cannot create default implementation for '"
136: + this .interfaceType.getName()
137: + "' mixin ("
138: + this .defaultImplType.getName() + "): "
139: + ex);
140: }
141: }
142:
143: }
|