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 }