001 /*
002 * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 *
009 * Created on 03-May-2004
010 */
011 package com.thoughtworks.proxy.factory;
012
013 import java.lang.reflect.Constructor;
014 import java.lang.reflect.Modifier;
015 import java.util.ArrayList;
016 import java.util.Arrays;
017 import java.util.Iterator;
018 import java.util.List;
019
020 import net.sf.cglib.core.DefaultNamingPolicy;
021 import net.sf.cglib.core.Predicate;
022
023 import net.sf.cglib.core.CodeGenerationException;
024 import net.sf.cglib.proxy.Enhancer;
025 import net.sf.cglib.proxy.Factory;
026 import net.sf.cglib.proxy.InvocationHandler;
027 import net.sf.cglib.proxy.Proxy;
028
029 import com.thoughtworks.proxy.Invoker;
030 import com.thoughtworks.proxy.ProxyFactory;
031 import com.thoughtworks.proxy.toys.nullobject.Null;
032
033 /**
034 * A {@link com.thoughtworks.proxy.ProxyFactory} based on <a href="http://cglib.sourceforge.net/">CGLIB</a>.
035 *
036 * @author Aslak Hellesøy
037 * @since 0.1
038 * @see com.thoughtworks.proxy.factory
039 */
040 public class CglibProxyFactory extends AbstractProxyFactory {
041 private static final long serialVersionUID = -5615928639194345818L;
042 private static final ThreadLocal<List<Class<?>>> cycleGuard = new ThreadLocal<List<Class<?>>>();
043 private static final ProxyFactory standardProxyFactory = new StandardProxyFactory();
044 private transient ForeignPackageNamingPolicy namingPolicy = new ForeignPackageNamingPolicy();
045
046 /**
047 * The native invocation handler.
048 *
049 * @since 0.1
050 */
051 static class CGLIBInvocationHandlerAdapter extends CoincidentalInvocationHandlerAdapter implements InvocationHandler {
052 private static final long serialVersionUID = 418834172207536454L;
053
054 /**
055 * Construct a CGLIBInvocationHandlerAdapter.
056 *
057 * @param invoker the wrapping invoker instance
058 * @since 0.1
059 */
060 public CGLIBInvocationHandlerAdapter(Invoker invoker) {
061 super(invoker);
062 }
063 }
064
065 /**
066 * {@inheritDoc}
067 * <p>
068 * Note: If any type the proxy instance must fulfill are all interfaces, the factory will currently create a proxy
069 * based on the JDK.
070 * </p>
071 */
072 public <T> T createProxy(final Invoker invoker, final Class<?>... types) {
073 final Class<?> type = getSingleClass(types);
074 if (type == null) {
075 // slightly faster
076 return standardProxyFactory.<T>createProxy(invoker, types);
077 }
078 if (type.isPrimitive()) {
079 throw new IllegalArgumentException("Cannot subclass primitive type");
080 }
081 final Class<?>[] interfaces = getInterfaces(types);
082 final Enhancer enhancer = new Enhancer();
083 for(;;) {
084 enhancer.setSuperclass(type);
085 enhancer.setInterfaces(interfaces);
086 enhancer.setCallback(new CGLIBInvocationHandlerAdapter(invoker));
087 try {
088 @SuppressWarnings("unchecked")
089 final T proxy = (T)enhancer.create();
090 return proxy;
091 } catch (CodeGenerationException e) { // cglib 2.0
092 final Throwable wrapper = e.getCause();
093 if (wrapper != null
094 && wrapper.getCause() instanceof SecurityException
095 && enhancer.getNamingPolicy() != namingPolicy) {
096 enhancer.setNamingPolicy(namingPolicy);
097 continue;
098 }
099 } catch (IllegalArgumentException e) { // cglib 2.0.2
100 } catch (NoSuchMethodError e) {
101 }
102 break;
103 }
104 @SuppressWarnings("unchecked")
105 final T proxy = (T)createWithConstructor(type, enhancer);
106 return proxy;
107 }
108
109 private Class<?>[] getInterfaces(final Class<?>[] types) {
110 final List<Class<?>> interfaces = new ArrayList<Class<?>>(Arrays.asList(types));
111 for (final Iterator<Class<?>> iterator = interfaces.iterator(); iterator.hasNext();) {
112 final Class<?> type = iterator.next();
113 if (!type.isInterface()) {
114 iterator.remove();
115 }
116 }
117 interfaces.add(InvokerReference.class);
118 return interfaces.toArray(new Class[interfaces.size()]);
119 }
120
121 private Class<?> getSingleClass(final Class<?>[] types) {
122 for (final Class<?> type : types) {
123 if (!type.isInterface()) {
124 return type;
125 }
126 }
127 return null;
128 }
129
130 private Object createWithConstructor(final Class<?> type, final Enhancer enhancer) {
131 final Constructor<?> constructor = getConstructor(type);
132 final Class<?>[] params = constructor.getParameterTypes();
133 final Object[] args = new Object[params.length];
134 if (cycleGuard.get() == null) {
135 cycleGuard.set(new ArrayList<Class<?>>());
136 }
137 final List<Class<?>> creating = cycleGuard.get();
138 for (int i = 0; i < args.length; i++) {
139 if (!creating.contains(params[i])) {
140 creating.add(params[i]);
141 try {
142 args[i] = Null.proxy(params[i]).build(this);
143 } finally {
144 creating.remove(params[i]);
145 }
146 } else {
147 args[i] = null;
148 }
149 }
150 return enhancer.create(params, args);
151 }
152
153 private Constructor<?> getConstructor(final Class<?> type) {
154 try {
155 return type.getConstructor(Class[].class.cast(null));
156 } catch (NoSuchMethodException e) {
157 Constructor<?>[] constructors = type.getConstructors();
158 if (constructors.length == 0) {
159 constructors = type.getDeclaredConstructors();
160 }
161 if (constructors.length == 0) {
162 throw new IllegalArgumentException("Cannot create proxy for this type without declared constructor.");
163 }
164 return constructors[0];
165 }
166 }
167
168 public boolean canProxy(final Class<?> type) {
169 return !Modifier.isFinal(type.getModifiers());
170 }
171
172 public boolean isProxyClass(final Class<?> type) {
173 return Factory.class.isAssignableFrom(type)
174 || (!type.equals(Object.class) && Proxy.isProxyClass(type))
175 || standardProxyFactory.isProxyClass(type);
176 }
177
178 private static class ForeignPackageNamingPolicy extends DefaultNamingPolicy
179 {
180 @Override
181 public String getClassName(final String prefix, final String source, final Object key, final Predicate names)
182 {
183 return getClass().getPackage().getName() + ".proxy." + super.getClassName(prefix, source, key, names);
184 }
185 }
186
187 private Object readResolve()
188 {
189 namingPolicy = new ForeignPackageNamingPolicy();
190 return this;
191 }
192 }