001: /**
002: * Copyright (C) 2006 Google Inc.
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: */package com.bm.ejb3guice.inject;
016:
017: import com.bm.ejb3guice.internal.GuiceFastClass;
018: import com.bm.ejb3guice.internal.GuiceNamingPolicy;
019: import com.bm.ejb3guice.internal.Objects;
020: import com.bm.ejb3guice.internal.ReferenceCache;
021:
022: import java.lang.reflect.Constructor;
023: import java.lang.reflect.InvocationTargetException;
024: import java.lang.reflect.Method;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.List;
028: import java.util.Map;
029: import net.sf.cglib.proxy.Callback;
030: import net.sf.cglib.proxy.CallbackFilter;
031: import net.sf.cglib.proxy.Enhancer;
032: import net.sf.cglib.proxy.NoOp;
033: import net.sf.cglib.reflect.FastClass;
034: import net.sf.cglib.reflect.FastConstructor;
035: import org.aopalliance.intercept.MethodInterceptor;
036:
037: /**
038: * Proxies classes applying interceptors to methods as specified in
039: * {@link ProxyFactoryBuilder}.
040: *
041: * @author crazybob@google.com (Bob Lee)
042: */
043: class ProxyFactory implements ConstructionProxyFactory {
044:
045: final List<MethodAspect> methodAspects;
046: final ConstructionProxyFactory defaultFactory = new DefaultConstructionProxyFactory();
047:
048: ProxyFactory(List<MethodAspect> methodAspects) {
049: this .methodAspects = methodAspects;
050: }
051:
052: Map<Constructor<?>, ConstructionProxy<?>> constructionProxies = new ReferenceCache<Constructor<?>, ConstructionProxy<?>>() {
053: protected ConstructionProxy<?> create(Constructor<?> constructor) {
054: return createConstructionProxy(constructor);
055: }
056: };
057:
058: /**
059: * Gets a factory for the given type. Uses the zero-arg constructor. Wraps
060: * exceptions in {@link RuntimeException} including
061: * {@link InvocationTargetException}.
062: */
063: public <T> Provider<T> getFactory(Class<T> type)
064: throws NoSuchMethodException {
065: final ConstructionProxy<T> constructionProxy = createConstructionProxy(type
066: .getDeclaredConstructor());
067: return new Provider<T>() {
068: public T get() {
069: try {
070: return constructionProxy.newInstance();
071: } catch (InvocationTargetException e) {
072: throw new RuntimeException(e);
073: }
074: }
075: };
076: }
077:
078: <T> ConstructionProxy<T> createConstructionProxy(
079: Constructor<T> constructor) {
080: Class<T> declaringClass = constructor.getDeclaringClass();
081:
082: // Find applicable aspects. Bow out if none are applicable to this class.
083: List<MethodAspect> applicableAspects = new ArrayList<MethodAspect>();
084: for (MethodAspect methodAspect : methodAspects) {
085: if (methodAspect.matches(declaringClass)) {
086: applicableAspects.add(methodAspect);
087: }
088: }
089: if (applicableAspects.isEmpty()) {
090: return defaultFactory.get(constructor);
091: }
092:
093: // Get list of methods from cglib.
094: List<Method> methods = new ArrayList<Method>();
095: Enhancer.getMethods(declaringClass, null, methods);
096: final Map<Method, Integer> indices = new HashMap<Method, Integer>();
097:
098: // Create method/interceptor holders and record indices.
099: List<MethodInterceptorsPair> methodInterceptorsPairs = new ArrayList<MethodInterceptorsPair>();
100: for (int i = 0; i < methods.size(); i++) {
101: Method method = methods.get(i);
102: methodInterceptorsPairs.add(new MethodInterceptorsPair(
103: method));
104: indices.put(method, i);
105: }
106:
107: // Iterate over aspects and add interceptors for the methods they apply to
108: boolean anyMatched = false;
109: for (MethodAspect methodAspect : applicableAspects) {
110: for (MethodInterceptorsPair pair : methodInterceptorsPairs) {
111: if (methodAspect.matches(pair.method)) {
112: pair.addAll(methodAspect.interceptors());
113: anyMatched = true;
114: }
115: }
116: }
117: if (!anyMatched) {
118: // not test-covered
119: return defaultFactory.get(constructor);
120: }
121:
122: // Create callbacks.
123: Callback[] callbacks = new Callback[methods.size()];
124:
125: @SuppressWarnings("unchecked")
126: Class<? extends Callback>[] callbackTypes = new Class[methods
127: .size()];
128: for (int i = 0; i < methods.size(); i++) {
129: MethodInterceptorsPair pair = methodInterceptorsPairs
130: .get(i);
131: if (!pair.hasInterceptors()) {
132: callbacks[i] = NoOp.INSTANCE;
133: callbackTypes[i] = NoOp.class;
134: } else {
135: callbacks[i] = new InterceptorStackCallback(
136: pair.method, pair.interceptors);
137: callbackTypes[i] = net.sf.cglib.proxy.MethodInterceptor.class;
138: }
139: }
140:
141: // Create the proxied class.
142: Enhancer enhancer = new Enhancer();
143: enhancer.setSuperclass(declaringClass);
144: enhancer.setUseCache(false); // We do enough caching.
145: enhancer.setCallbackFilter(new CallbackFilter() {
146: public int accept(Method method) {
147: return indices.get(method);
148: }
149: });
150: enhancer.setCallbackTypes(callbackTypes);
151: enhancer.setUseFactory(false);
152: enhancer.setNamingPolicy(new GuiceNamingPolicy());
153:
154: Class<?> proxied = enhancer.createClass();
155:
156: // Store callbacks.
157: Enhancer.registerStaticCallbacks(proxied, callbacks);
158:
159: return createConstructionProxy(proxied, constructor
160: .getParameterTypes());
161: }
162:
163: /**
164: * Creates a construction proxy given a class and parameter types.
165: */
166: <T> ConstructionProxy<T> createConstructionProxy(Class<?> clazz,
167: Class[] parameterTypes) {
168: FastClass fastClass = GuiceFastClass.create(clazz);
169: final FastConstructor fastConstructor = fastClass
170: .getConstructor(parameterTypes);
171: return new ConstructionProxy<T>() {
172: @SuppressWarnings("unchecked")
173: public T newInstance(Object... arguments)
174: throws InvocationTargetException {
175: Objects.assertNoNulls(arguments);
176: return (T) fastConstructor.newInstance(arguments);
177: }
178: };
179: }
180:
181: static class MethodInterceptorsPair {
182:
183: final Method method;
184: List<MethodInterceptor> interceptors;
185:
186: public MethodInterceptorsPair(Method method) {
187: this .method = method;
188: }
189:
190: void addAll(List<MethodInterceptor> interceptors) {
191: if (this .interceptors == null) {
192: this .interceptors = new ArrayList<MethodInterceptor>();
193: }
194: this .interceptors.addAll(interceptors);
195: }
196:
197: boolean hasInterceptors() {
198: return interceptors != null;
199: }
200: }
201:
202: @SuppressWarnings("unchecked")
203: public <T> ConstructionProxy<T> get(Constructor<T> constructor) {
204: return (ConstructionProxy<T>) constructionProxies
205: .get(constructor);
206: }
207: }
|