View Javadoc

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             // is a client side proxy
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                 // create a new instance on the server and get the handle to it in return
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 }