1 /*************************************************
2 * Copyright (c) Shen Li. All rights reserved. *
3 * http://joyaop.sourceforge.net *
4 * -------------------------------------------- *
5 * Distributable under LGPL license. *
6 * See terms of license at gnu.org. *
7 ************************************************/
8 package net.sf.joyaop.impl;
9
10 import net.sf.cglib.proxy.Callback;
11 import net.sf.cglib.proxy.CallbackFilter;
12 import net.sf.cglib.proxy.MethodInterceptor;
13 import net.sf.cglib.proxy.MethodProxy;
14 import net.sf.joyaop.AspectRuntimeException;
15 import net.sf.joyaop.framework.AspectizedClass;
16 import net.sf.joyaop.framework.InterceptorAspect;
17 import net.sf.joyaop.framework.MixinAspect;
18 import net.sf.joyaop.framework.RuntimeAspect;
19 import net.sf.joyaop.impl.aspect.BaseInterceptorAspectImpl;
20 import net.sf.joyaop.impl.aspect.MixinAspectImpl;
21
22 import java.io.NotSerializableException;
23 import java.io.Serializable;
24 import java.lang.reflect.Method;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30
31 /***
32 * @author Shen Li
33 */
34 public abstract class CallbackFactory {
35 public static CallbackFactory forInterceptor(AspectizedClass aspectizedClass, Method method, Set interceptorAspects) {
36 return new InterceptorCallbackFactory(aspectizedClass, method, interceptorAspects);
37 }
38
39 public static CallbackFactory forMixin(AspectizedClass aspectizedClass, MixinAspectImpl mixinAspect) {
40 return new MixinCallbackFactory(aspectizedClass, mixinAspect);
41 }
42
43 public static CallbackFactory forAspectProxy(Class[] interfaceClasses, CallbackFilter callbackFilter) {
44 return new AspectProxyCallbackFactory(interfaceClasses, callbackFilter);
45 }
46
47 public static CallbackFactory forOriginalClass() {
48 return new OriginalClassCallbackFactory();
49 }
50
51 public abstract Callback getCallback(RuntimeAspectInstanceFactory factory);
52
53 public static class InterceptorCallbackFactory extends CallbackFactory {
54 private Callback cachedCallback;
55 private List cachedInterceptorAspectInstances;
56 private Method method;
57 private Set interceptorAspects;
58 private AspectizedClass aspectizedClass;
59
60 public InterceptorCallbackFactory(AspectizedClass aspectizedClass, Method method, Set interceptorAspects) {
61 this.aspectizedClass = aspectizedClass;
62 this.method = method;
63 this.interceptorAspects = interceptorAspects;
64 boolean hasInstanceScopeInterceptor = hasInstanceScopeInterceptors();
65 RuntimeAspectInstanceFactory factory = new RuntimeAspectInstanceFactory(aspectizedClass.getOriginalClass());
66 if (!hasInstanceScopeInterceptor && !hasInstanceScopeMixin()) {
67 cachedCallback = new InvocationHandler(getInterceptorAspectInstances(factory), getTargetInstance(factory));
68 } else if (!hasInstanceScopeInterceptor) {
69 cachedInterceptorAspectInstances = getInterceptorAspectInstances(factory);
70 }
71 }
72
73 public Callback getCallback(RuntimeAspectInstanceFactory factory) {
74 if (cachedCallback != null) {
75 return cachedCallback;
76 }
77 if (cachedInterceptorAspectInstances != null) {
78 return new InvocationHandler(cachedInterceptorAspectInstances, getTargetInstance(factory));
79 }
80 return new InvocationHandler(getInterceptorAspectInstances(factory), getTargetInstance(factory));
81 }
82
83 private RuntimeInstance getTargetInstance(RuntimeAspectInstanceFactory factory) {
84 Class clazz = method.getDeclaringClass();
85 if (aspectizedClass.getOriginalClass() == clazz || Object.class == clazz) {
86 return OriginalClassInstance.instance();
87 }
88 MixinAspectImpl aspect = (MixinAspectImpl) aspectizedClass.getMixin(clazz);
89 if (aspect != null) {
90 return factory.getRuntimeAspectInstance(aspect);
91 }
92 throw new AspectRuntimeException("Can't find the target object for method " + method.getName());
93 }
94
95 private boolean hasInstanceScopeMixin() {
96 Class clazz = method.getDeclaringClass();
97 if (aspectizedClass.getOriginalClass() == clazz || Object.class == clazz) {
98 return false;
99 }
100 MixinAspect aspect = (MixinAspect) aspectizedClass.getMixin(clazz);
101 if (aspect != null) {
102 return (RuntimeAspect.INSTANCE_SCOPE.equals(aspect.getScope()));
103 }
104 throw new AspectRuntimeException("Can't find the target object for method " + method.getName());
105 }
106
107 private boolean hasInstanceScopeInterceptors() {
108 for (Iterator i = interceptorAspects.iterator(); i.hasNext();) {
109 if (RuntimeAspect.INSTANCE_SCOPE.equals(((InterceptorAspect) i.next()).getScope())) {
110 return true;
111 }
112 }
113 return false;
114 }
115
116 private List getInterceptorAspectInstances(RuntimeAspectInstanceFactory factory) {
117 List refs = new ArrayList(interceptorAspects.size());
118 for (Iterator i = interceptorAspects.iterator(); i.hasNext();) {
119 refs.add(factory.getRuntimeAspectInstance((BaseInterceptorAspectImpl) i.next()));
120 }
121 return refs;
122 }
123 }
124
125 public static class MixinCallbackFactory extends CallbackFactory {
126 private Callback cachedCallback;
127 private MixinAspectImpl aspect;
128 private AspectizedClass aspectizedClass;
129
130 public MixinCallbackFactory(AspectizedClass aspectizedClass, MixinAspectImpl aspect) {
131 this.aspect = aspect;
132 this.aspectizedClass = aspectizedClass;
133 if (!RuntimeAspect.INSTANCE_SCOPE.equals(aspect.getScope())) {
134 cachedCallback = new InvocationHandler(Collections.EMPTY_LIST,
135 new RuntimeAspectInstanceFactory(aspectizedClass.getOriginalClass()).getRuntimeAspectInstance(aspect));
136 }
137 }
138
139 public Callback getCallback(RuntimeAspectInstanceFactory factory) {
140 if (cachedCallback == null) {
141 return new InvocationHandler(Collections.EMPTY_LIST,
142 new RuntimeAspectInstanceFactory(aspectizedClass.getOriginalClass()).getRuntimeAspectInstance(aspect));
143 }
144 return cachedCallback;
145 }
146 }
147
148 public static class AspectProxyCallbackFactory extends CallbackFactory {
149 private final AspectProxyHandler callback;
150
151 public AspectProxyCallbackFactory(Class[] interfaceClasses, CallbackFilter callbackFilter) {
152 callback = new AspectProxyHandler(interfaceClasses, callbackFilter);
153 }
154
155 public Callback getCallback(RuntimeAspectInstanceFactory factory) {
156 return callback;
157 }
158
159 public Callback getCallback() {
160 return callback;
161 }
162
163 private static class AspectProxyHandler implements MethodInterceptor, Serializable {
164 private final Class[] interfacesClass;
165 private final CallbackFilter callbackFilter;
166
167 public AspectProxyHandler(Class[] interfacesClass, CallbackFilter callbackFilter) {
168 this.interfacesClass = interfacesClass;
169 this.callbackFilter = callbackFilter;
170 }
171
172 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
173 if (Serializable.class.isAssignableFrom(proxy.getClass().getSuperclass())) {
174 return new SerializableProxy(proxy, interfacesClass, callbackFilter);
175 }
176 throw new NotSerializableException(proxy.getClass().getSuperclass().getName() + " is not serializable.");
177 }
178 }
179 }
180
181 public static class OriginalClassCallbackFactory extends CallbackFactory {
182 private final Callback cachedCallback =
183 new InvocationHandler(Collections.EMPTY_LIST, OriginalClassInstance.instance());
184
185 public Callback getCallback(RuntimeAspectInstanceFactory factory) {
186 return cachedCallback;
187 }
188 }
189 }