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 */
011package com.thoughtworks.proxy.factory;
012
013import java.lang.reflect.Constructor;
014import java.lang.reflect.Modifier;
015import java.util.ArrayList;
016import java.util.Arrays;
017import java.util.Iterator;
018import java.util.List;
019
020import net.sf.cglib.core.DefaultNamingPolicy;
021import net.sf.cglib.core.Predicate;
022
023import net.sf.cglib.core.CodeGenerationException;
024import net.sf.cglib.proxy.Enhancer;
025import net.sf.cglib.proxy.Factory;
026import net.sf.cglib.proxy.InvocationHandler;
027import net.sf.cglib.proxy.Proxy;
028
029import com.thoughtworks.proxy.Invoker;
030import com.thoughtworks.proxy.ProxyFactory;
031import 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&oslash;y
037 * @since 0.1
038 * @see com.thoughtworks.proxy.factory
039 */
040public 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}