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 }