1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.caleigo.toolkit.tunnel;
20
21
22 import java.lang.reflect.*;
23 import java.util.*;
24
25 import org.caleigo.toolkit.log.*;
26
27 /***
28 *
29 * @author Mattias Hagstrand
30 * @version 1.00
31 *
32 *//*
33 *
34 * WHEN WHO WHY & WHAT
35 * -----------------------------------------------------------------------------
36 * 2002-07-12 Mattias Hagstrand Creation
37 */
38 public class ProxyHandler implements IMessageConsumer
39 {
40
41 protected int mCurrentProxyID;
42 protected ITunnel mTunnel;
43 protected Hashtable mProxyObjects;
44 protected Hashtable mRemoteInvocationHandlers;
45
46
47 public ProxyHandler(ITunnel tunnel)
48 {
49 mTunnel = tunnel;
50 mProxyObjects = new Hashtable();
51 mRemoteInvocationHandlers = new Hashtable();
52 mTunnel.addMessageConsumer(this);
53 }
54
55
56
57 /*** Creates a proxy for all interfaces that the provided object and its
58 * superclasses implement. Invocations on methods in the interfaces will be
59 * forwarded to the provided object.
60 */
61 public Object createProxy(Object obj)
62 {
63 if (!(obj instanceof IDistributable))
64 throw new IllegalArgumentException("Object must be an IDistributable");
65
66 List proxyInterfaces = new ArrayList();
67 Class currentClass = obj.getClass();
68 while (currentClass != null)
69 {
70 Class[] interfaces = currentClass.getInterfaces();
71 for (int i = 0; i < interfaces.length; i++)
72 proxyInterfaces.add(interfaces[i]);
73 currentClass = currentClass.getSuperclass();
74 }
75 Class[] proxyInterfaceClasses = (Class[]) proxyInterfaces.toArray(new Class[0]);
76 Log.print(this, "Creating proxy for interfaces:");
77 for (int i = 0; i < proxyInterfaceClasses.length; i++)
78 Log.print(this, proxyInterfaceClasses[i].getName());
79 int proxyID = this.generateProxyID();
80 Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
81 proxyInterfaceClasses,
82 new DynamicRemoteProxy(proxyID, this, proxyInterfaceClasses));
83 mProxyObjects.put(new Integer(proxyID), obj);
84 return proxy;
85 }
86
87 /*** Creates a proxy for the provided interfaces.
88 *
89 * @param handler object that will handle invocations on the created proxy.
90 * The handler is allowed to be <code>null</code> in which
91 * case invocation handlers must be registered for the
92 * interfaces.
93 */
94 public Object createProxy(Class[] interfaceClasses, Object handler)
95 {
96 if (interfaceClasses.length == 0)
97 throw new IllegalArgumentException("No interface classes found");
98
99 Log.print(this, "Creating proxy for interfaces:");
100 for (int i = 0; i < interfaceClasses.length; i++)
101 Log.print(this, interfaceClasses[i].getName());
102 int proxyID = this.generateProxyID();
103 Object proxy = Proxy.newProxyInstance(interfaceClasses[0].getClassLoader(),
104 interfaceClasses,
105 new DynamicRemoteProxy(proxyID, this, interfaceClasses));
106 if (handler != null)
107 mProxyObjects.put(new Integer(proxyID), handler);
108 return proxy;
109 }
110
111 public void finishProxy(Object proxy)
112 {
113 Log.print(this, "Proxy finished");
114 mProxyObjects.remove(new Integer(((DynamicRemoteProxy) Proxy.getInvocationHandler(proxy)).getID()));
115 }
116
117 /*** Register an object that handles remote invokations for the provided
118 * interface.
119 */
120 public void registerRemoteInvocationHandler(Class interfaceClass, Object handler)
121 {
122 if (!interfaceClass.isInterface())
123 throw new IllegalArgumentException("The interface class must be an interface");
124
125 if (!interfaceClass.isInstance(handler))
126 throw new IllegalArgumentException("Handler must be an instance of the interface class");
127
128 mRemoteInvocationHandlers.put(interfaceClass.getName(), handler);
129 }
130
131
132 public ITunnel getTunnel()
133 {
134 return mTunnel;
135 }
136
137
138
139 /***
140 * Returns <code>true</code> if this IMessageConsumer accepts the message.
141 * This method is allways called before <code>consumeMessage</code> for any
142 * given message.
143 */
144 public boolean acceptsMessage(Object message)
145 {
146 return (message instanceof DynamicRemoteProxy.IProxyInvocationMessage ||
147 message instanceof DynamicRemoteProxy.IProxyCleanUpMessage);
148 }
149
150 /***
151 * Tells the IMessageConsumer to consume a message.
152 */
153 public void consumeMessage(Object message)
154 {
155 Log.print(this, "Clean up message received");
156 mProxyObjects.remove(new Integer(((DynamicRemoteProxy.IProxyCleanUpMessage) message).getProxyID()));
157 }
158
159 /***
160 * Tells the IMessageConsumer to consume a message and returns a new message
161 * that will be delivered to the sender of the originial message.
162 */
163 public Object answerMessage(Object message)
164 {
165 Log.print(this, "answerMessage");
166
167 String[] interfaceClassNames = ((DynamicRemoteProxy.IProxyInvocationMessage) message).getInterfaceClassNames();
168 String methodName = ((DynamicRemoteProxy.IProxyInvocationMessage) message).getMethodName();
169 Method method = null;
170 Object target = null;
171
172
173 Object[] arguments = ((DynamicRemoteProxy.IProxyInvocationMessage) message).getArguments();
174 if (arguments != null)
175 for (int i = 0; i < arguments.length; i++)
176 if (arguments[i] != null && Proxy.isProxyClass(arguments[i].getClass()))
177 ((DynamicRemoteProxy) Proxy.getInvocationHandler(arguments[i])).setProxyHandler(this);
178
179
180 target = mProxyObjects.get(new Integer(((DynamicRemoteProxy.IProxyInvocationMessage) message).getProxyID()));
181 if (target != null)
182 method = this.findMatchingMethod(target.getClass(), methodName, arguments);
183
184
185
186 for (int i = 0; i < interfaceClassNames.length && method == null; i++)
187 if (mRemoteInvocationHandlers.get(interfaceClassNames[i]) != null)
188 try
189 {
190 method = this.findMatchingMethod(Class.forName(interfaceClassNames[i]), methodName, arguments);
191 if (method != null)
192 target = mRemoteInvocationHandlers.get(interfaceClassNames[i]);
193 }
194 catch (ClassNotFoundException e)
195 {
196 Log.printError(this, "Couldn't find class", e);
197 }
198
199 try
200 {
201
202 Log.print(this, "Invoking method...");
203 Object returnValue = method.invoke(target, arguments);
204 Log.print(this, "done!");
205 if (returnValue instanceof IDistributable)
206 returnValue = this.createProxy(returnValue);
207 return new DynamicRemoteProxy.DefaultProxyReturnMessage(returnValue);
208 }
209 catch (InvocationTargetException targetException)
210 {
211 Log.printError(this, "Couldn't invoke target method", targetException);
212 return new DynamicRemoteProxy.DefaultProxyExceptionMessage(targetException.getTargetException().getClass(), targetException.getTargetException().getMessage());
213 }
214 catch (Throwable t)
215 {
216 Log.printError(this, "Unknown error", t);
217 return new DynamicRemoteProxy.DefaultProxyExceptionMessage(t.getClass(), t.getMessage());
218 }
219 }
220
221
222 protected synchronized int generateProxyID()
223 {
224 if (mCurrentProxyID < Integer.MAX_VALUE)
225 mCurrentProxyID++;
226 else
227 mCurrentProxyID = 1;
228
229 return mCurrentProxyID;
230 }
231
232 protected Method findMatchingMethod(Class targetClass, String methodName, Object[] arguments)
233 {
234 Method method = null;
235
236 try
237 {
238 Method[] methods = targetClass.getMethods();
239 for (int m = 0; m < methods.length && method == null; m++)
240 {
241 if (methods[m].getName().compareTo(methodName) == 0)
242 {
243
244 Class[] paramClasses = methods[m].getParameterTypes();
245 if (arguments == null && paramClasses.length > 0 ||
246 arguments != null && paramClasses.length != arguments.length)
247
248 continue;
249
250
251 boolean match = true;
252 for (int c = 0; c < paramClasses.length && match; c++)
253 if (arguments[c] != null && !paramClasses[c].isInstance(arguments[c]))
254 match = false;
255 if (match)
256 method = methods[m];
257 }
258 }
259 }
260 catch (Exception e)
261 {
262 }
263
264 return method;
265 }
266 }