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&oslash;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    }