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 11-May-2004 010 */ 011 package com.thoughtworks.proxy.toys.multicast; 012 013 import java.lang.reflect.Array; 014 import java.lang.reflect.Method; 015 import java.util.ArrayList; 016 import java.util.List; 017 018 import com.thoughtworks.proxy.Invoker; 019 import com.thoughtworks.proxy.ProxyFactory; 020 import com.thoughtworks.proxy.kit.ReflectionUtils; 021 022 /** 023 * A {@link Invoker} implementation that multicasts calls to multiple targets. Proxies generated by this class will 024 * forward all method invocations to an array of underlying objects. The behavior is recursive, so return values will 025 * also be multicasting objects. 026 * 027 * @author Aslak Hellesøy 028 * @author Chris Stevenson 029 * @author Jörg Schaible 030 * @since 0.1 031 */ 032 public class MulticastingInvoker<T> implements Invoker { 033 private static final long serialVersionUID = 1L; 034 private static final Method multicastTargetsDirect; 035 private static final Method multicastTargetsIndirect; 036 private static final Method getTargetsInArray; 037 private static final Method getTargetsInTypedArray; 038 039 static { 040 try { 041 multicastTargetsDirect = Multicast.class.getMethod("multicastTargets", Method.class, Object[].class); 042 multicastTargetsIndirect = Multicast.class.getMethod("multicastTargets", Class.class, String.class, Object[].class); 043 getTargetsInArray = Multicast.class.getMethod("getTargetsInArray"); 044 getTargetsInTypedArray = Multicast.class.getMethod("getTargetsInArray", Class.class); 045 } catch (NoSuchMethodException e) { 046 throw new ExceptionInInitializerError(e.toString()); 047 } 048 } 049 050 private Class<?>[] types; 051 private ProxyFactory proxyFactory; 052 private Object[] targets; 053 054 /** 055 * Construct a MulticastingInvoker. 056 * 057 * @param type the implemented types 058 * @param proxyFactory the {@link ProxyFactory} to use 059 * @param targets the target instances where the proxy delegates a call 060 * @since 0.1 061 */ 062 public MulticastingInvoker(final Class<?>[] type, final ProxyFactory proxyFactory, final Object[] targets) { 063 this.types = type; 064 this.proxyFactory = proxyFactory; 065 this.targets = targets; 066 } 067 068 /** 069 * Create a proxy for this Invoker. 070 * 071 * @return the new proxy 072 * @since 0.1 073 */ 074 public T proxy() { 075 final Class<?>[] classes; 076 int i; 077 i = types.length; 078 while (--i >= 0 && types[i] != Multicast.class) { 079 } 080 if (i < 0) { 081 classes = new Class[types.length + 1]; 082 if (classes.length > 1) { 083 System.arraycopy(types, 0, classes, 1, types.length); 084 } 085 classes[0] = Multicast.class; 086 } else { 087 classes = types; 088 } 089 return proxyFactory.<T>createProxy(this, classes); 090 } 091 092 public Object invoke(final Object proxy, Method method, Object[] args) throws Throwable { 093 if (getTargetsInArray.equals(method)) { 094 return targets; 095 } else if (getTargetsInTypedArray.equals(method)) { 096 final Object[] elements = Object[].class.cast(Array.newInstance(Class.class.cast(args[0]), targets.length)); 097 System.arraycopy(targets, 0, elements, 0, targets.length); 098 return elements; 099 } else if (multicastTargetsDirect.equals(method)) { 100 method = (Method) args[0]; 101 args = (Object[]) args[1]; 102 } else if (multicastTargetsIndirect.equals(method)) { 103 final Object[] newArgs = args[2] == null ? new Object[0] : Object[].class.cast(args[2]); 104 method = ReflectionUtils.getMatchingMethod(Class.class.cast(args[0]), String.class.cast(args[1]), newArgs); 105 args = newArgs; 106 } 107 final List<Object> invocationResults = new ArrayList<Object>(); 108 for (Object target : targets) { 109 if (method.getDeclaringClass().isInstance(target)) { 110 Object result = method.invoke(target, args); 111 if (result != null) { 112 invocationResults.add(result); 113 } 114 } 115 } 116 if (invocationResults.size() == 0) { 117 return null; 118 } else if (invocationResults.size() == 1) { 119 return invocationResults.get(0); 120 } else if (method.getReturnType().equals(byte.class)) { 121 return addBytes(invocationResults.toArray()); 122 } else if (method.getReturnType().equals(char.class)) { 123 return addChars(invocationResults.toArray()); 124 } else if (method.getReturnType().equals(short.class)) { 125 return addShorts(invocationResults.toArray()); 126 } else if (method.getReturnType().equals(int.class)) { 127 return addIntegers(invocationResults.toArray()); 128 } else if (method.getReturnType().equals(long.class)) { 129 return addLongs(invocationResults.toArray()); 130 } else if (method.getReturnType().equals(float.class)) { 131 return addFloats(invocationResults.toArray()); 132 } else if (method.getReturnType().equals(double.class)) { 133 return addDoubles(invocationResults.toArray()); 134 } else if (method.getReturnType().equals(boolean.class)) { 135 return andBooleans(invocationResults.toArray()); 136 } else { 137 return Multicasting.proxy(invocationResults.toArray()).build(proxyFactory); 138 } 139 } 140 141 private static Byte addBytes(final Object[] args) { 142 byte result = 0; 143 for (Object arg : args) { 144 result += Byte.class.cast(arg); 145 } 146 return result; 147 } 148 149 private static Character addChars(final Object[] args) { 150 char result = 0; 151 for (Object arg : args) { 152 result += Character.class.cast(arg); 153 } 154 return result; 155 } 156 157 private static Short addShorts(final Object[] args) { 158 short result = 0; 159 for (Object arg : args) { 160 result += Short.class.cast(arg); 161 } 162 return result; 163 } 164 165 private static Integer addIntegers(final Object[] args) { 166 int result = 0; 167 for (Object arg : args) { 168 result += Integer.class.cast(arg); 169 } 170 return result; 171 } 172 173 private static Long addLongs(final Object[] args) { 174 long result = 0; 175 for (Object arg : args) { 176 result += Long.class.cast(arg); 177 } 178 return result; 179 } 180 181 private static Float addFloats(final Object[] args) { 182 float result = 0; 183 for (Object arg : args) { 184 result += Float.class.cast(arg); 185 } 186 return result; 187 } 188 189 private static Double addDoubles(final Object[] args) { 190 double result = 0; 191 for (Object arg : args) { 192 result += Double.class.cast(arg); 193 } 194 return result; 195 } 196 197 private static Boolean andBooleans(final Object[] args) { 198 for (Object arg : args) { 199 if (!Boolean.class.cast(arg)) { 200 return Boolean.FALSE; 201 } 202 } 203 return Boolean.TRUE; 204 } 205 }