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