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(final String[] interfaces,
    176                                                 final String impl,
    177                                                 final String address,
    178                                                 final int port) {
    179         return RemoteProxy.createClientProxy(
    180                 interfaces, impl, address, port, Thread.currentThread()
    181                                                  .getContextClassLoader()
    182         );
    183     }
    184
    185     /***
    186      * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
    187      *
    188      * @param interfaces the class name of the interface for the object to create the proxy for
    189      * @param impl       the class name of the the object to create the proxy for
    190      * @param address    the address to connect to.
    191      * @param port       the port to connect to.
    192      * @param context    the context carrying the users principal and credentials
    193      * @return the new remote proxy instance
    194      */
    195     public static RemoteProxy createClientProxy(final String[] interfaces,
    196                                                 final String impl,
    197                                                 final String address,
    198                                                 final int port,
    199                                                 final Object context) {
    200         return RemoteProxy.createClientProxy(
    201                 interfaces, impl, address, port, context, Thread.currentThread()
    202                                                           .getContextClassLoader()
    203         );
    204     }
    205
    206     /***
    207      * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
    208      *
    209      * @param interfaces the class name of the interface for the object to create the proxy for
    210      * @param impl       the class name of the the object to create the proxy for
    211      * @param address    the address to connect to.
    212      * @param port       the port to connect to.
    213      * @param loader     the class loader to use
    214      * @return the new remote proxy instance
    215      */
    216     public static RemoteProxy createClientProxy(final String[] interfaces,
    217                                                 final String impl,
    218                                                 final String address,
    219                                                 final int port,
    220                                                 final ClassLoader loader) {
    221         return RemoteProxy.createClientProxy(interfaces, impl, address, port, null, loader);
    222     }
    223
    224     /***
    225      * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
    226      *
    227      * @param interfaces the class name of the interface for the object to create the proxy for
    228      * @param impl       the class name of the the object to create the proxy for
    229      * @param address    the address to connect to.
    230      * @param port       the port to connect to.
    231      * @param ctx        the context carrying the users principal and credentials
    232      * @param loader     the class loader to use
    233      * @return the new remote proxy instance
    234      */
    235     public static RemoteProxy createClientProxy(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] = Class.forName(m_targetInterfaceNames[i], false, m_loader);
    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 }