1 /***************************************************************************************
2 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.connectivity;
9
10 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
11 import org.codehaus.aspectwerkz.util.UuidGenerator;
12
13 import java.io.IOException;
14 import java.io.ObjectInputStream;
15 import java.io.ObjectOutputStream;
16 import java.io.Serializable;
17 import java.lang.reflect.InvocationHandler;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Proxy;
20 import java.net.InetAddress;
21 import java.net.Socket;
22 import java.util.Map;
23 import java.util.WeakHashMap;
24
25 /***
26 * This class provides a general remote proxy. It uses the Dynamic Proxy mechanism that was introduced with JDK 1.3.
27 * <p/>The client proxy sends all requests to a server via a socket connection. The server returns results in the same
28 * way. Every object that is transferred (i.e. result of method invocation) has to support the Serializable interface.
29 *
30 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
31 */
32 public class RemoteProxy implements InvocationHandler, Serializable {
33 /***
34 * The serial version uid for the class.
35 *
36 * @TODO: recalculate
37 */
38 private static final long serialVersionUID = 1L;
39
40 /***
41 * All the instances that have been wrapped by a proxy. Maps each instance to its handle.
42 */
43 private transient static Map s_instances = new WeakHashMap();
44
45 /***
46 * The server host address.
47 */
48 private final String m_address;
49
50 /***
51 * The server port.
52 */
53 private final int m_port;
54
55 /***
56 * The handle to the instance wrapped by this proxy.
57 */
58 private String m_handle = null;
59
60 /***
61 * The interface class for the wrapped instance.
62 */
63 private Class[] m_targetInterfaces = null;
64
65 /***
66 * The names of all the interfaces for the wrapped instance.
67 */
68 private String[] m_targetInterfaceNames = null;
69
70 /***
71 * The class name for the wrapped instance.
72 */
73 private String m_targetImplName = null;
74
75 /***
76 * The socket.
77 */
78 private transient Socket m_socket;
79
80 /***
81 * The input stream.
82 */
83 private transient ObjectInputStream m_in;
84
85 /***
86 * The output stream.
87 */
88 private transient ObjectOutputStream m_out;
89
90 /***
91 * The class loader to use.
92 */
93 private transient ClassLoader m_loader;
94
95 /***
96 * The client context.
97 */
98 private transient Object m_context = null;
99
100 /***
101 * The dynamic proxy instance to the wrapped instance.
102 */
103 private transient Object m_proxy = null;
104
105 /***
106 * Creates a new proxy based on the interface and class names passes to it. For client-side use. This method is
107 * never called directly.
108 *
109 * @param interfaces the class name of the interface for the object to create the proxy for
110 * @param impl the class name of the the object to create the proxy for
111 * @param address the address to connect to.
112 * @param port the port to connect to.
113 * @param context the context carrying the users principal and credentials
114 * @param loader the class loader to use
115 */
116 private RemoteProxy(final String[] interfaces,
117 final String impl,
118 final String address,
119 final int port,
120 final Object context,
121 final ClassLoader loader) {
122 if ((interfaces == null) || (interfaces.length == 0)) {
123 throw new IllegalArgumentException("at least one interface must be specified");
124 }
125 if (impl == null) {
126 throw new IllegalArgumentException("implementation class name can not be null");
127 }
128 if (address == null) {
129 throw new IllegalArgumentException("address can not be null");
130 }
131 if (port < 0) {
132 throw new IllegalArgumentException("port not valid");
133 }
134 m_targetInterfaceNames = interfaces;
135 m_targetImplName = impl;
136 m_address = address;
137 m_port = port;
138 m_context = context;
139 m_loader = loader;
140 }
141
142 /***
143 * Creates a new proxy based on the instance passed to it. For server-side use. This method is never called
144 * directly.
145 *
146 * @param targetInstance target instance to create the proxy for
147 * @param address the address to connect to.
148 * @param port the port to connect to.
149 */
150 private RemoteProxy(final Object targetInstance, final String address, final int port) {
151 if (targetInstance == null) {
152 throw new IllegalArgumentException("target instance can not be null");
153 }
154 if (address == null) {
155 throw new IllegalArgumentException("address can not be null");
156 }
157 if (port < 0) {
158 throw new IllegalArgumentException("port not valid");
159 }
160 m_targetInterfaces = targetInstance.getClass().getInterfaces();
161 m_address = address;
162 m_port = port;
163 m_handle = wrapInstance(targetInstance);
164 }
165
166 /***
167 * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
168 *
169 * @param interfaces the class name of the interface for the object to create the proxy for
170 * @param impl the class name of the the object to create the proxy for
171 * @param address the address to connect to.
172 * @param port the port to connect to.
173 * @return the new remote proxy instance
174 */
175 public static RemoteProxy createClientProxy(
176 final String[] interfaces,
177 final String impl,
178 final String address,
179 final int port) {
180 return RemoteProxy.createClientProxy(interfaces, impl, address, port, Thread.currentThread()
181 .getContextClassLoader());
182 }
183
184 /***
185 * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
186 *
187 * @param interfaces the class name of the interface for the object to create the proxy for
188 * @param impl the class name of the the object to create the proxy for
189 * @param address the address to connect to.
190 * @param port the port to connect to.
191 * @param context the context carrying the users principal and credentials
192 * @return the new remote proxy instance
193 */
194 public static RemoteProxy createClientProxy(
195 final String[] interfaces,
196 final String impl,
197 final String address,
198 final int port,
199 final Object context) {
200 return RemoteProxy.createClientProxy(interfaces, impl, address, port, context, Thread.currentThread()
201 .getContextClassLoader());
202 }
203
204 /***
205 * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
206 *
207 * @param interfaces the class name of the interface for the object to create the proxy for
208 * @param impl the class name of the the object to create the proxy for
209 * @param address the address to connect to.
210 * @param port the port to connect to.
211 * @param loader the class loader to use
212 * @return the new remote proxy instance
213 */
214 public static RemoteProxy createClientProxy(
215 final String[] interfaces,
216 final String impl,
217 final String address,
218 final int port,
219 final ClassLoader loader) {
220 return RemoteProxy.createClientProxy(interfaces, impl, address, port, null, loader);
221 }
222
223 /***
224 * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
225 *
226 * @param interfaces the class name of the interface for the object to create the proxy for
227 * @param impl the class name of the the object to create the proxy for
228 * @param address the address to connect to.
229 * @param port the port to connect to.
230 * @param ctx the context carrying the users principal and credentials
231 * @param loader the class loader to use
232 * @return the new remote proxy instance
233 */
234 public static RemoteProxy createClientProxy(
235 final String[] interfaces,
236 final String impl,
237 final String address,
238 final int port,
239 final Object context,
240 final ClassLoader loader) {
241 return new RemoteProxy(interfaces, impl, address, port, context, loader);
242 }
243
244 /***
245 * Creates a proxy to a specific <b>instance </b> in the on the server side. This proxy could then be passed to the
246 * client which can invoke method on this specific <b>instance </b>.
247 *
248 * @param the target instance to create the proxy for
249 * @param address the address to connect to.
250 * @param port the port to connect to.
251 * @return the new remote proxy instance
252 */
253 public static RemoteProxy createServerProxy(final Object targetlInstance, final String address, final int port) {
254 return new RemoteProxy(targetlInstance, address, port);
255 }
256
257 /***
258 * Look up and retrives a proxy to an object from the server.
259 *
260 * @param loader the classloader to use
261 * @return the proxy instance
262 */
263 public Object getInstance(final ClassLoader loader) {
264 m_loader = loader;
265 return getInstance();
266 }
267
268 /***
269 * Look up and retrives a proxy to an object from the server.
270 *
271 * @return the proxy instance
272 */
273 public Object getInstance() {
274 if (m_proxy != null) {
275 return m_proxy;
276 }
277 if (m_loader == null) {
278 m_loader = Thread.currentThread().getContextClassLoader();
279 }
280 try {
281 m_socket = new Socket(InetAddress.getByName(m_address), m_port);
282 m_socket.setTcpNoDelay(true);
283 m_out = new ObjectOutputStream(m_socket.getOutputStream());
284 m_in = new ObjectInputStream(m_socket.getInputStream());
285 } catch (Exception e) {
286 throw new WrappedRuntimeException(e);
287 }
288 if (m_handle == null) {
289
290 if (m_targetInterfaceNames == null) {
291 throw new IllegalStateException("interface class name can not be null");
292 }
293 if (m_targetImplName == null) {
294 throw new IllegalStateException("implementation class name can not be null");
295 }
296 try {
297
298 m_out.write(Command.CREATE);
299 m_out.writeObject(m_targetImplName);
300 m_out.flush();
301 m_handle = (String) m_in.readObject();
302 m_targetInterfaces = new Class[m_targetInterfaceNames.length];
303 for (int i = 0; i < m_targetInterfaceNames.length; i++) {
304 try {
305 m_targetInterfaces[i] = m_loader.loadClass(m_targetInterfaceNames[i]);
306 } catch (ClassNotFoundException e) {
307 throw new WrappedRuntimeException(e);
308 }
309 }
310 } catch (Exception e) {
311 throw new WrappedRuntimeException(e);
312 }
313 }
314 m_proxy = Proxy.newProxyInstance(m_loader, m_targetInterfaces, this);
315 return m_proxy;
316 }
317
318 /***
319 * This method is invoked automatically by the proxy. Should not be called directly.
320 *
321 * @param proxy the proxy instance that the method was invoked on
322 * @param method the Method instance corresponding to the interface method invoked on the proxy instance.
323 * @param args an array of objects containing the values of the arguments passed in the method invocation on the
324 * proxy instance.
325 * @return the value to return from the method invocation on the proxy instance.
326 */
327 public Object invoke(final Object proxy, final Method method, final Object[] args) {
328 try {
329 m_out.write(Command.INVOKE);
330 m_out.writeObject(m_context);
331 m_out.writeObject(m_handle);
332 m_out.writeObject(method.getName());
333 m_out.writeObject(method.getParameterTypes());
334 m_out.writeObject(args);
335 m_out.flush();
336 final Object response = m_in.readObject();
337 if (response instanceof Exception) {
338 throw (Exception) response;
339 }
340 return response;
341 } catch (Exception e) {
342 throw new WrappedRuntimeException(e);
343 }
344 }
345
346 /***
347 * Closes the proxy and the connection to the server.
348 */
349 public void close() {
350 try {
351 m_out.write(Command.CLOSE);
352 m_out.flush();
353 m_out.close();
354 m_in.close();
355 m_socket.close();
356 } catch (IOException e) {
357 throw new WrappedRuntimeException(e);
358 }
359 }
360
361 /***
362 * Returns a proxy wrapped instance by its handle.
363 *
364 * @param handle the handle
365 * @return the instance
366 */
367 public static Object getWrappedInstance(final String handle) {
368 return s_instances.get(handle);
369 }
370
371 /***
372 * Wraps a new instance and maps it to a handle.
373 *
374 * @param instance the instance to wrap
375 * @return the handle for the instance
376 */
377 public static String wrapInstance(final Object instance) {
378 final String handle = UuidGenerator.generate(instance);
379 s_instances.put(handle, instance);
380 return handle;
381 }
382 }