/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.connectivity; import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; import org.codehaus.aspectwerkz.util.UuidGenerator; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetAddress; import java.net.Socket; import java.util.Map; import java.util.WeakHashMap; /** * This class provides a general remote proxy. It uses the Dynamic Proxy mechanism that was introduced with JDK 1.3. * <p/>The client proxy sends all requests to a server via a socket connection. The server returns results in the same * way. Every object that is transferred (i.e. result of method invocation) has to support the Serializable interface. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> */ public class RemoteProxy implements InvocationHandler, Serializable { /** * The serial version uid for the class. * * @TODO: recalculate */ private static final long serialVersionUID = 1L; /** * All the instances that have been wrapped by a proxy. Maps each instance to its handle. */ private transient static Map s_instances = new WeakHashMap(); /** * The server host address. */ private final String m_address; /** * The server port. */ private final int m_port; /** * The handle to the instance wrapped by this proxy. */ private String m_handle = null; /** * The interface class for the wrapped instance. */ private Class[] m_targetInterfaces = null; /** * The names of all the interfaces for the wrapped instance. */ private String[] m_targetInterfaceNames = null; /** * The class name for the wrapped instance. */ private String m_targetImplName = null; /** * The socket. */ private transient Socket m_socket; /** * The input stream. */ private transient ObjectInputStream m_in; /** * The output stream. */ private transient ObjectOutputStream m_out; /** * The class loader to use. */ private transient ClassLoader m_loader; /** * The client context. */ private transient Object m_context = null; /** * The dynamic proxy instance to the wrapped instance. */ private transient Object m_proxy = null; /** * Creates a new proxy based on the interface and class names passes to it. For client-side use. This method is * never called directly. * * @param interfaces the class name of the interface for the object to create the proxy for * @param impl the class name of the the object to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @param context the context carrying the users principal and credentials * @param loader the class loader to use */ private RemoteProxy(final String[] interfaces, final String impl, final String address, final int port, final Object context, final ClassLoader loader) { if ((interfaces == null) || (interfaces.length == 0)) { throw new IllegalArgumentException("at least one interface must be specified"); } if (impl == null) { throw new IllegalArgumentException("implementation class name can not be null"); } if (address == null) { throw new IllegalArgumentException("address can not be null"); } if (port < 0) { throw new IllegalArgumentException("port not valid"); } m_targetInterfaceNames = interfaces; m_targetImplName = impl; m_address = address; m_port = port; m_context = context; m_loader = loader; } /** * Creates a new proxy based on the instance passed to it. For server-side use. This method is never called * directly. * * @param targetInstance target instance to create the proxy for * @param address the address to connect to. * @param port the port to connect to. */ private RemoteProxy(final Object targetInstance, final String address, final int port) { if (targetInstance == null) { throw new IllegalArgumentException("target instance can not be null"); } if (address == null) { throw new IllegalArgumentException("address can not be null"); } if (port < 0) { throw new IllegalArgumentException("port not valid"); } m_targetInterfaces = targetInstance.getClass().getInterfaces(); m_address = address; m_port = port; m_handle = wrapInstance(targetInstance); } /** * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object. * * @param interfaces the class name of the interface for the object to create the proxy for * @param impl the class name of the the object to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @return the new remote proxy instance */ public static RemoteProxy createClientProxy(final String[] interfaces, final String impl, final String address, final int port) { return RemoteProxy.createClientProxy( interfaces, impl, address, port, Thread.currentThread() .getContextClassLoader() ); } /** * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object. * * @param interfaces the class name of the interface for the object to create the proxy for * @param impl the class name of the the object to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @param context the context carrying the users principal and credentials * @return the new remote proxy instance */ public static RemoteProxy createClientProxy(final String[] interfaces, final String impl, final String address, final int port, final Object context) { return RemoteProxy.createClientProxy( interfaces, impl, address, port, context, Thread.currentThread() .getContextClassLoader() ); } /** * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object. * * @param interfaces the class name of the interface for the object to create the proxy for * @param impl the class name of the the object to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @param loader the class loader to use * @return the new remote proxy instance */ public static RemoteProxy createClientProxy(final String[] interfaces, final String impl, final String address, final int port, final ClassLoader loader) { return RemoteProxy.createClientProxy(interfaces, impl, address, port, null, loader); } /** * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object. * * @param interfaces the class name of the interface for the object to create the proxy for * @param impl the class name of the the object to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @param ctx the context carrying the users principal and credentials * @param loader the class loader to use * @return the new remote proxy instance */ public static RemoteProxy createClientProxy(final String[] interfaces, final String impl, final String address, final int port, final Object context, final ClassLoader loader) { return new RemoteProxy(interfaces, impl, address, port, context, loader); } /** * Creates a proxy to a specific <b>instance </b> in the on the server side. This proxy could then be passed to the * client which can invoke method on this specific <b>instance </b>. * * @param the target instance to create the proxy for * @param address the address to connect to. * @param port the port to connect to. * @return the new remote proxy instance */ public static RemoteProxy createServerProxy(final Object targetlInstance, final String address, final int port) { return new RemoteProxy(targetlInstance, address, port); } /** * Look up and retrives a proxy to an object from the server. * * @param loader the classloader to use * @return the proxy instance */ public Object getInstance(final ClassLoader loader) { m_loader = loader; return getInstance(); } /** * Look up and retrives a proxy to an object from the server. * * @return the proxy instance */ public Object getInstance() { if (m_proxy != null) { return m_proxy; } if (m_loader == null) { m_loader = Thread.currentThread().getContextClassLoader(); } try { m_socket = new Socket(InetAddress.getByName(m_address), m_port); m_socket.setTcpNoDelay(true); m_out = new ObjectOutputStream(m_socket.getOutputStream()); m_in = new ObjectInputStream(m_socket.getInputStream()); } catch (Exception e) { throw new WrappedRuntimeException(e); } if (m_handle == null) { // is a client side proxy if (m_targetInterfaceNames == null) { throw new IllegalStateException("interface class name can not be null"); } if (m_targetImplName == null) { throw new IllegalStateException("implementation class name can not be null"); } try { // create a new instance on the server and get the handle to it in return m_out.write(Command.CREATE); m_out.writeObject(m_targetImplName); m_out.flush(); m_handle = (String) m_in.readObject(); m_targetInterfaces = new Class[m_targetInterfaceNames.length]; for (int i = 0; i < m_targetInterfaceNames.length; i++) { try { m_targetInterfaces[i] = Class.forName(m_targetInterfaceNames[i], false, m_loader); } catch (ClassNotFoundException e) { throw new WrappedRuntimeException(e); } } } catch (Exception e) { throw new WrappedRuntimeException(e); } } m_proxy = Proxy.newProxyInstance(m_loader, m_targetInterfaces, this); return m_proxy; } /** * This method is invoked automatically by the proxy. Should not be called directly. * * @param proxy the proxy instance that the method was invoked on * @param method the Method instance corresponding to the interface method invoked on the proxy instance. * @param args an array of objects containing the values of the arguments passed in the method invocation on the * proxy instance. * @return the value to return from the method invocation on the proxy instance. */ public Object invoke(final Object proxy, final Method method, final Object[] args) { try { m_out.write(Command.INVOKE); m_out.writeObject(m_context); m_out.writeObject(m_handle); m_out.writeObject(method.getName()); m_out.writeObject(method.getParameterTypes()); m_out.writeObject(args); m_out.flush(); final Object response = m_in.readObject(); if (response instanceof Exception) { throw (Exception) response; } return response; } catch (Exception e) { throw new WrappedRuntimeException(e); } } /** * Closes the proxy and the connection to the server. */ public void close() { try { m_out.write(Command.CLOSE); m_out.flush(); m_out.close(); m_in.close(); m_socket.close(); } catch (IOException e) { throw new WrappedRuntimeException(e); } } /** * Returns a proxy wrapped instance by its handle. * * @param handle the handle * @return the instance */ public static Object getWrappedInstance(final String handle) { return s_instances.get(handle); } /** * Wraps a new instance and maps it to a handle. * * @param instance the instance to wrap * @return the handle for the instance */ public static String wrapInstance(final Object instance) { final String handle = UuidGenerator.generate(instance); s_instances.put(handle, instance); return handle; } }