001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tcspring;
006:
007: import org.aopalliance.aop.Advice;
008: import org.aopalliance.intercept.MethodInterceptor;
009: import org.apache.commons.logging.Log;
010: import org.apache.commons.logging.LogFactory;
011: import org.springframework.aop.Advisor;
012: import org.springframework.aop.AfterReturningAdvice;
013: import org.springframework.aop.IntroductionInterceptor;
014: import org.springframework.aop.MethodBeforeAdvice;
015: import org.springframework.aop.MethodMatcher;
016: import org.springframework.aop.Pointcut;
017: import org.springframework.aop.PointcutAdvisor;
018: import org.springframework.aop.ThrowsAdvice;
019: import org.springframework.aop.framework.AdvisedSupport;
020: import org.springframework.aop.framework.AopConfigException;
021: import org.springframework.aop.framework.AopProxy;
022: import org.springframework.aop.framework.ProxyFactoryBean;
023: import org.springframework.beans.factory.BeanFactory;
024:
025: import com.tc.aspectwerkz.DeploymentModel;
026: import com.tc.aspectwerkz.aspect.container.AspectFactoryManager;
027: import com.tc.aspectwerkz.definition.AspectDefinition;
028: import com.tc.aspectwerkz.definition.DocumentParser;
029: import com.tc.aspectwerkz.definition.SystemDefinition;
030: import com.tc.aspectwerkz.definition.SystemDefinitionContainer;
031: import com.tc.aspectwerkz.definition.deployer.AspectDefinitionBuilder;
032: import com.tc.aspectwerkz.exception.WrappedRuntimeException;
033: import com.tc.aspectwerkz.proxy.Proxy;
034: import com.tc.aspectwerkz.proxy.ProxyDelegationStrategy;
035: import com.tc.aspectwerkz.proxy.ProxySubclassingStrategy;
036: import com.tc.aspectwerkz.proxy.Uuid;
037: import com.tc.aspectwerkz.reflect.MethodInfo;
038: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
039: import com.tc.aspectwerkz.transform.TransformationConstants;
040: import com.tc.aspectwerkz.util.UuidGenerator;
041:
042: import java.lang.reflect.Field;
043: import java.lang.reflect.Method;
044: import java.util.HashSet;
045: import java.util.Set;
046:
047: /**
048: * Implementation of the Spring AOP proxy that is order of magnitude faster. Based on the AspectWerkz AWProxy
049: * architecture.
050: *
051: * @author Jonas Bonér
052: */
053: public class FastAopProxy implements AopProxy {
054: private final transient Log logger = LogFactory.getLog(getClass());
055:
056: private static final boolean USE_CACHE = false;
057: private static final boolean MAKE_ADVISABLE = false;
058:
059: private static final String SPRING_ASPECT_CONTAINER = SpringAspectContainer.class
060: .getName();
061:
062: private static final MethodInfo s_methodBeforeAdviceMethodInfo = JavaClassInfo
063: .getClassInfo(MethodBeforeAdvice.class).getMethods()[0];
064: private static final MethodInfo s_methodInterceptorMethodInfo = JavaClassInfo
065: .getClassInfo(MethodInterceptor.class).getMethods()[0];
066: private static final MethodInfo s_afterReturningAdviceMethodInfo = JavaClassInfo
067: .getClassInfo(AfterReturningAdvice.class).getMethods()[0];
068:
069: private static final String s_throwsAdviceMethodName = "afterThrowing";
070:
071: private final ProxyFactoryBean m_proxyFactory;
072: private final SystemDefinition m_systemDef;
073: private final ClassLoader m_loader;
074: private final Class m_targetClass;
075: private final String m_proxyName;
076: private final boolean m_isSubclassingProxy;
077: private final BeanFactory m_beanFactory;
078:
079: // private final String[] m_interceptorNames;
080:
081: public FastAopProxy(final ProxyFactoryBean proxyFactory)
082: throws AopConfigException {
083: if (proxyFactory == null) {
084: throw new AopConfigException(
085: "Cannot create AopProxy with null ProxyConfig");
086: }
087: if (proxyFactory.getAdvisors().length == 0
088: && proxyFactory.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
089: throw new AopConfigException(
090: "Cannot create AopProxy with no advisors and no target source");
091: }
092: if (proxyFactory.getTargetSource().getTargetClass() == null) {
093: throw new AopConfigException(
094: "Either an interface or a target is required for proxy creation");
095: }
096:
097: // System.out.println("### creating Fast AOP proxy for " +
098: // proxyFactory.getTargetSource().getTargetClass().getName());
099: logger.info("Creating FastProxy for "
100: + proxyFactory.getTargetSource().getTargetClass()
101: .getName());
102:
103: m_proxyFactory = proxyFactory;
104: m_targetClass = m_proxyFactory.getTargetSource()
105: .getTargetClass();
106: m_loader = m_targetClass.getClassLoader();
107: m_isSubclassingProxy = m_proxyFactory.isProxyTargetClass()
108: || m_proxyFactory.getProxiedInterfaces().length == 0;
109: m_proxyName = getProxyName();
110: m_systemDef = new SystemDefinition(m_proxyName
111: + UuidGenerator.generate(m_proxyName));
112:
113: try {
114: m_beanFactory = ((BeanFactoryAware) proxyFactory)
115: .tc$getBeanFactory();
116: } catch (Exception e) {
117: throw new WrappedRuntimeException(e);
118: }
119:
120: prepareSystemDefinition();
121: parseSpringDefinition();
122: }
123:
124: private String getProxyName() {
125: return m_targetClass.getName().replace('.', '/')
126: + (m_isSubclassingProxy ? ProxySubclassingStrategy.PROXY_SUFFIX
127: : ProxyDelegationStrategy.PROXY_SUFFIX)
128: + Long.toString(Uuid.newUuid());
129: }
130:
131: public Object getProxy() {
132: return getProxy(null);
133: }
134:
135: public Object getProxy(final ClassLoader classLoader) {
136: final Object target;
137: try {
138: target = m_proxyFactory.getTargetSource().getTarget();
139: } catch (Exception e) {
140: throw new AopConfigException(
141: "ProxyFactory is not correctly initialized - target is NULL",
142: e);
143: }
144:
145: if (m_isSubclassingProxy) {
146: // create subclassing proxy (cglib style)
147: return Proxy.newInstance(target.getClass(), USE_CACHE,
148: MAKE_ADVISABLE, m_systemDef);
149: } else {
150: // create delegating proxy (DP style)
151: final Class[] interfaces = m_proxyFactory
152: .getProxiedInterfaces();
153: final Object[] implementations = new Object[interfaces.length];
154: for (int i = 0; i < implementations.length; i++) {
155: implementations[i] = target;
156: }
157: return Proxy.newInstance(interfaces, implementations,
158: USE_CACHE, MAKE_ADVISABLE, m_systemDef);
159: }
160: }
161:
162: private void prepareSystemDefinition() {
163: DocumentParser.addVirtualAspect(m_systemDef);
164: if (!SystemDefinitionContainer.s_classLoaderSystemDefinitions
165: .containsKey(m_loader)) {
166: SystemDefinitionContainer.s_classLoaderSystemDefinitions
167: .put(m_loader, new HashSet());
168: }
169: ((Set) SystemDefinitionContainer.s_classLoaderSystemDefinitions
170: .get(m_loader)).add(m_systemDef);
171: }
172:
173: private void parseSpringDefinition() {
174: Advisor[] advisors = m_proxyFactory.getAdvisors();
175:
176: // loop over all aspects (advice/interceptors)
177: for (int i = 0; i < advisors.length; i++) {
178: Advisor advisor = advisors[i];
179: if (advisor instanceof PointcutAdvisor) {
180: PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
181:
182: final Advice aspect = pointcutAdvisor.getAdvice();
183: // FIXME BAAAAAAAD!!! support IntroductionInterceptor
184: if (aspect instanceof IntroductionInterceptor) {
185: throw new IllegalStateException(
186: "IntroductionInterceptor is currently not supported by FastProxy");
187: }
188: // FIXME BAAAAAAAD!!! support ThrowsAdvice
189: if (aspect instanceof ThrowsAdvice) {
190: throw new IllegalStateException(
191: "ThrowsAdvice is currently not supported by FastProxy");
192: }
193:
194: final Class aspectClass = aspect.getClass();
195: final Pointcut pointcut = pointcutAdvisor.getPointcut();
196: if (!pointcut.getClassFilter().matches(m_targetClass)) {
197: continue;
198: }
199:
200: // create AW version of the Spring pointcut
201: final StringBuffer pcd = new StringBuffer();
202: MethodMatcher methodMatcher = pointcut
203: .getMethodMatcher();
204: Method[] methods = m_targetClass.getDeclaredMethods();
205: boolean hasAtLeastOneMatch = false;
206: for (int j = 0; j < methods.length; j++) {
207: Method method = methods[j];
208: if (methodMatcher.matches(method, m_targetClass)) {
209: buildPointcutForMethod(pcd, method);
210: hasAtLeastOneMatch = true;
211: }
212: }
213: if (hasAtLeastOneMatch) {
214: int length = pcd.length();
215: pcd.delete(length - 3, length);
216: } else {
217: continue;
218: }
219:
220: // final AspectDefinition aspectDef = createAspectDefUsingXml(aspect, aspectClass, pcd);
221: final AspectDefinition aspectDef = buildAspectDefinition(
222: aspect, aspectClass, pcd.toString());
223:
224: initializeAspectFactory(aspect, aspectDef);
225: } else {
226: throw new IllegalStateException(
227: "introductions (mixins) are currently not supported");
228: }
229: }
230: }
231:
232: private void buildPointcutForMethod(final StringBuffer pcd,
233: final Method method) {
234: pcd.append("execution(");
235: pcd.append(method.getReturnType().getName());
236: pcd.append(" ");
237: pcd.append(m_targetClass.getName());
238: pcd.append(".");
239: pcd.append(method.getName());
240: pcd.append("(");
241: pcd.append("..");
242: pcd.append(")) || ");
243: }
244:
245: private AspectDefinition buildAspectDefinition(final Advice aspect,
246: final Class aspectClass, final String pcd) {
247: AspectDefinitionBuilder builder = new AspectDefinitionBuilder(
248: aspectClass.getName(), DeploymentModel.PER_JVM,
249: SPRING_ASPECT_CONTAINER, m_systemDef, m_loader);
250:
251: if (aspect instanceof MethodBeforeAdvice) {
252: builder.addAdvice("before", pcd.toString(),
253: s_methodBeforeAdviceMethodInfo.getName());
254: } else if (aspect instanceof MethodInterceptor) {
255: builder.addAdvice("around", pcd.toString(),
256: s_methodInterceptorMethodInfo.getName());
257: } else if (aspect instanceof AfterReturningAdvice) {
258: builder.addAdvice("after returning(java.lang.Object)", pcd
259: .toString(), s_afterReturningAdviceMethodInfo
260: .getName());
261: } else if (aspect instanceof ThrowsAdvice) {
262: builder.addAdvice("after throwing(java.lang.Throwable+)",
263: pcd.toString(), s_throwsAdviceMethodName);
264: }
265: builder.build();
266:
267: final AspectDefinition aspectDef = builder
268: .getAspectDefinition();
269: aspectDef.addParameter(SpringAspectContainer.BEAN_FACTORY_KEY,
270: m_beanFactory);
271: m_systemDef.addAspect(aspectDef);
272: return aspectDef;
273: }
274:
275: /**
276: * @param aspectDef
277: * @return the factory class name
278: */
279: private String createAspectFactory(final AspectDefinition aspectDef) {
280: String aspectFactoryClassName = AspectFactoryManager
281: .getAspectFactoryClassName(aspectDef.getClassName(),
282: aspectDef.getQualifiedName());
283: String aspectFactoryJavaName = aspectFactoryClassName.replace(
284: '/', '.');
285: AspectFactoryManager.loadAspectFactory(aspectFactoryClassName,
286: m_proxyName,
287: aspectDef.getClassName().replace('.', '/'), aspectDef
288: .getQualifiedName(), null, null, m_loader,
289: DeploymentModel.PER_JVM.toString());
290: return aspectFactoryJavaName;
291: }
292:
293: /**
294: * @param aspect
295: * @param aspectDef
296: */
297: private void initializeAspectFactory(final Advice aspect,
298: final AspectDefinition aspectDef) {
299: // create and load the factory for the aspect
300: final String factoryName = createAspectFactory(aspectDef);
301: try {
302: Class factory = m_loader.loadClass(factoryName);
303: Field aspectField = factory
304: .getDeclaredField(TransformationConstants.FACTORY_SINGLE_ASPECT_FIELD_NAME);
305: aspectField.set(null, aspect);
306: } catch (Exception e) {
307: throw new WrappedRuntimeException(e);
308: }
309: }
310: }
|