001 /* 002 * (c) 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 24-Feb-2005 010 */ 011 package com.thoughtworks.proxy.toys.dispatch; 012 013 import static com.thoughtworks.proxy.toys.delegate.DelegationMode.DIRECT; 014 015 import java.io.IOException; 016 import java.io.InvalidObjectException; 017 import java.io.ObjectInputStream; 018 import java.io.ObjectOutputStream; 019 import java.lang.reflect.Method; 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.HashSet; 023 import java.util.List; 024 import java.util.Set; 025 026 import com.thoughtworks.proxy.Invoker; 027 import com.thoughtworks.proxy.ProxyFactory; 028 import com.thoughtworks.proxy.factory.InvokerReference; 029 import com.thoughtworks.proxy.factory.StandardProxyFactory; 030 import com.thoughtworks.proxy.kit.ObjectReference; 031 import com.thoughtworks.proxy.kit.ReflectionUtils; 032 import com.thoughtworks.proxy.toys.delegate.DelegatingInvoker; 033 034 035 /** 036 * Invoker that dispatches all invocations to different objects according the membership of the method. 037 * 038 * @author Jörg Schaible after idea by Rickard Öberg 039 * @since 0.2 040 */ 041 public class DispatchingInvoker implements Invoker { 042 private static final long serialVersionUID = 1L; 043 private List<Class<?>> types; 044 private Invoker[] invokers; 045 private transient Set<Method>[] methodSets; 046 private transient Method[] toStringMethods; 047 048 /** 049 * Construct a DispatchingInvoker. 050 * 051 * @param proxyFactory the {@link ProxyFactory} to use 052 * @param types the types of the generated proxy 053 * @param delegateReferences the {@link ObjectReference ObjectReferences} for the delegates 054 * @since 0.2 055 */ 056 public DispatchingInvoker( 057 final ProxyFactory proxyFactory, final Class<?>[] types, final ObjectReference<Object>[] delegateReferences) { 058 this.types = Arrays.asList(types); 059 invokers = new Invoker[types.length]; 060 toStringMethods = new Method[types.length]; 061 @SuppressWarnings("unchecked") 062 Set<Method>[] sets = new Set[types.length]; 063 methodSets = sets; 064 for (int i = 0; i < types.length; i++) { 065 for (final ObjectReference<Object> delegateReference : delegateReferences) { 066 if (types[i].isAssignableFrom(delegateReference.get().getClass())) { 067 invokers[i] = new DelegatingInvoker<Object>(proxyFactory, delegateReference, DIRECT); 068 methodSets[i] = new HashSet<Method>(Arrays.asList(types[i].getMethods())); 069 for (Method method : methodSets[i]) { 070 if (method.getName().equals("toString") && method.getParameterTypes().length == 0) { 071 toStringMethods[i] = method; 072 break; 073 } 074 } 075 break; 076 } 077 } 078 if (invokers[i] == null) { 079 throw new DispatchingException("Cannot dispatch type " + types[i].getName(), types[i]); 080 } 081 } 082 } 083 084 /** 085 * Constructor used by pure reflection serialization. 086 * 087 * @since 0.2 088 */ 089 protected DispatchingInvoker() { 090 } 091 092 public Object invoke(final Object proxy, Method method, final Object[] args) throws Throwable { 093 if (method.equals(ReflectionUtils.equals)) { 094 final Object arg = args[0]; 095 if (new StandardProxyFactory().isProxyClass(arg.getClass()) 096 && (InvokerReference.class.cast(arg)).getInvoker() instanceof DispatchingInvoker) { 097 final DispatchingInvoker invoker = DispatchingInvoker.class.cast((InvokerReference.class.cast(arg)).getInvoker()); 098 if (types.size() == invoker.types.size()) { 099 boolean isEqual = true; 100 for (int i = 0; isEqual && i < types.size(); ++i) { 101 final Class<?> type = types.get(i); 102 for (int j = 0; isEqual && j < invoker.types.size(); ++j) { 103 if (invoker.types.get(j).equals(type)) { 104 if (!invokers[i].equals(invoker.invokers[j])) { 105 isEqual = false; 106 } 107 } 108 } 109 } 110 return isEqual; 111 } 112 } 113 return Boolean.FALSE; 114 } else if (method.equals(ReflectionUtils.hashCode)) { 115 return hashCode(); 116 } else if (method.equals(ReflectionUtils.toString)) { 117 for (int i = 0; i < invokers.length; i++) { 118 Method toString = toStringMethods[i]; 119 if (toString != null && toString.getDeclaringClass().isAssignableFrom(proxy.getClass())) { 120 return invokers[i].invoke(proxy, method, args); 121 } 122 } 123 return types.toString(); 124 } else { 125 for (int i = 0; i < invokers.length; i++) { 126 if (methodSets[i].contains(method)) { 127 return invokers[i].invoke(proxy, method, args); 128 } 129 } 130 } 131 throw new RuntimeException("Cannot dispatch method " + method.getName()); 132 } 133 134 private void writeObject(final ObjectOutputStream out) throws IOException { 135 out.defaultWriteObject(); 136 @SuppressWarnings("unchecked") 137 final List<Class<?>>[] types = new List[methodSets.length]; 138 @SuppressWarnings("unchecked") 139 final List<String>[] names = new List[methodSets.length]; 140 @SuppressWarnings("unchecked") 141 final List<Class<?>[]>[] arguments = new List[methodSets.length]; 142 for (int i = 0; i < methodSets.length; i++) { 143 final Method[] methods = methodSets[i].toArray(new Method[methodSets[i].size()]); 144 types[i] = new ArrayList<Class<?>>(); 145 names[i] = new ArrayList<String>(); 146 arguments[i] = new ArrayList<Class<?>[]>(); 147 for (Method method : methods) { 148 types[i].add(method.getDeclaringClass()); 149 names[i].add(method.getName()); 150 arguments[i].add(method.getParameterTypes()); 151 } 152 } 153 out.writeObject(types); 154 out.writeObject(names); 155 out.writeObject(arguments); 156 } 157 158 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 159 in.defaultReadObject(); 160 @SuppressWarnings("unchecked") 161 final List<Class<?>>[] types = List[].class.cast(in.readObject()); 162 @SuppressWarnings("unchecked") 163 final List<String>[] names = List[].class.cast(in.readObject()); 164 @SuppressWarnings("unchecked") 165 final List<Class<?>[]>[] arguments = List[].class.cast(in.readObject()); 166 @SuppressWarnings("unchecked") 167 final Set<Method>[] set = new Set[types.length]; 168 methodSets = set; 169 toStringMethods = new Method[types.length]; 170 try { 171 for (int i = 0; i < methodSets.length; i++) { 172 methodSets[i] = new HashSet<Method>(); 173 for (int j = 0; j < types[i].size(); j++) { 174 final Class<?> type = types[i].get(j); 175 final String name = names[i].get(j); 176 final Class<?>[] argumentTypes = arguments[i].get(j); 177 final Method method = type.getMethod(name, argumentTypes); 178 methodSets[i].add(method); 179 if (name.equals("toString") && argumentTypes.length == 0) { 180 toStringMethods[i] = method; 181 } 182 } 183 } 184 } catch (final NoSuchMethodException e) { 185 throw new InvalidObjectException(e.getMessage()); 186 } 187 } 188 }