001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.connectivity;
008:
009: import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
010: import org.codehaus.aspectwerkz.util.UuidGenerator;
011:
012: import java.io.IOException;
013: import java.io.ObjectInputStream;
014: import java.io.ObjectOutputStream;
015: import java.io.Serializable;
016: import java.lang.reflect.InvocationHandler;
017: import java.lang.reflect.Method;
018: import java.lang.reflect.Proxy;
019: import java.net.InetAddress;
020: import java.net.Socket;
021: import java.util.Map;
022: import java.util.WeakHashMap;
023:
024: /**
025: * This class provides a general remote proxy. It uses the Dynamic Proxy mechanism that was introduced with JDK 1.3.
026: * <p/>The client proxy sends all requests to a server via a socket connection. The server returns results in the same
027: * way. Every object that is transferred (i.e. result of method invocation) has to support the Serializable interface.
028: *
029: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
030: */
031: public class RemoteProxy implements InvocationHandler, Serializable {
032: /**
033: * The serial version uid for the class.
034: *
035: * @TODO: recalculate
036: */
037: private static final long serialVersionUID = 1L;
038:
039: /**
040: * All the instances that have been wrapped by a proxy. Maps each instance to its handle.
041: */
042: private transient static Map s_instances = new WeakHashMap();
043:
044: /**
045: * The server host address.
046: */
047: private final String m_address;
048:
049: /**
050: * The server port.
051: */
052: private final int m_port;
053:
054: /**
055: * The handle to the instance wrapped by this proxy.
056: */
057: private String m_handle = null;
058:
059: /**
060: * The interface class for the wrapped instance.
061: */
062: private Class[] m_targetInterfaces = null;
063:
064: /**
065: * The names of all the interfaces for the wrapped instance.
066: */
067: private String[] m_targetInterfaceNames = null;
068:
069: /**
070: * The class name for the wrapped instance.
071: */
072: private String m_targetImplName = null;
073:
074: /**
075: * The socket.
076: */
077: private transient Socket m_socket;
078:
079: /**
080: * The input stream.
081: */
082: private transient ObjectInputStream m_in;
083:
084: /**
085: * The output stream.
086: */
087: private transient ObjectOutputStream m_out;
088:
089: /**
090: * The class loader to use.
091: */
092: private transient ClassLoader m_loader;
093:
094: /**
095: * The client context.
096: */
097: private transient Object m_context = null;
098:
099: /**
100: * The dynamic proxy instance to the wrapped instance.
101: */
102: private transient Object m_proxy = null;
103:
104: /**
105: * Creates a new proxy based on the interface and class names passes to it. For client-side use. This method is
106: * never called directly.
107: *
108: * @param interfaces the class name of the interface for the object to create the proxy for
109: * @param impl the class name of the the object to create the proxy for
110: * @param address the address to connect to.
111: * @param port the port to connect to.
112: * @param context the context carrying the users principal and credentials
113: * @param loader the class loader to use
114: */
115: private RemoteProxy(final String[] interfaces, final String impl,
116: final String address, final int port, final Object context,
117: final ClassLoader loader) {
118: if ((interfaces == null) || (interfaces.length == 0)) {
119: throw new IllegalArgumentException(
120: "at least one interface must be specified");
121: }
122: if (impl == null) {
123: throw new IllegalArgumentException(
124: "implementation class name can not be null");
125: }
126: if (address == null) {
127: throw new IllegalArgumentException(
128: "address can not be null");
129: }
130: if (port < 0) {
131: throw new IllegalArgumentException("port not valid");
132: }
133: m_targetInterfaceNames = interfaces;
134: m_targetImplName = impl;
135: m_address = address;
136: m_port = port;
137: m_context = context;
138: m_loader = loader;
139: }
140:
141: /**
142: * Creates a new proxy based on the instance passed to it. For server-side use. This method is never called
143: * directly.
144: *
145: * @param targetInstance target instance to create the proxy for
146: * @param address the address to connect to.
147: * @param port the port to connect to.
148: */
149: private RemoteProxy(final Object targetInstance,
150: final String address, final int port) {
151: if (targetInstance == null) {
152: throw new IllegalArgumentException(
153: "target instance can not be null");
154: }
155: if (address == null) {
156: throw new IllegalArgumentException(
157: "address can not be null");
158: }
159: if (port < 0) {
160: throw new IllegalArgumentException("port not valid");
161: }
162: m_targetInterfaces = targetInstance.getClass().getInterfaces();
163: m_address = address;
164: m_port = port;
165: m_handle = wrapInstance(targetInstance);
166: }
167:
168: /**
169: * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
170: *
171: * @param interfaces the class name of the interface for the object to create the proxy for
172: * @param impl the class name of the the object to create the proxy for
173: * @param address the address to connect to.
174: * @param port the port to connect to.
175: * @return the new remote proxy instance
176: */
177: public static RemoteProxy createClientProxy(
178: final String[] interfaces, final String impl,
179: final String address, final int port) {
180: return RemoteProxy.createClientProxy(interfaces, impl, address,
181: port, Thread.currentThread().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, final String impl,
196: final String address, final int port, final Object context) {
197: return RemoteProxy.createClientProxy(interfaces, impl, address,
198: port, context, Thread.currentThread()
199: .getContextClassLoader());
200: }
201:
202: /**
203: * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
204: *
205: * @param interfaces the class name of the interface for the object to create the proxy for
206: * @param impl the class name of the the object to create the proxy for
207: * @param address the address to connect to.
208: * @param port the port to connect to.
209: * @param loader the class loader to use
210: * @return the new remote proxy instance
211: */
212: public static RemoteProxy createClientProxy(
213: final String[] interfaces, final String impl,
214: final String address, final int port,
215: final ClassLoader loader) {
216: return RemoteProxy.createClientProxy(interfaces, impl, address,
217: port, null, loader);
218: }
219:
220: /**
221: * Creates a new proxy to a class. To be used on the client side to create a new proxy to an object.
222: *
223: * @param interfaces the class name of the interface for the object to create the proxy for
224: * @param impl the class name of the the object to create the proxy for
225: * @param address the address to connect to.
226: * @param port the port to connect to.
227: * @param ctx the context carrying the users principal and credentials
228: * @param loader the class loader to use
229: * @return the new remote proxy instance
230: */
231: public static RemoteProxy createClientProxy(
232: final String[] interfaces, final String impl,
233: final String address, final int port, final Object context,
234: final ClassLoader loader) {
235: return new RemoteProxy(interfaces, impl, address, port,
236: context, loader);
237: }
238:
239: /**
240: * Creates a proxy to a specific <b>instance </b> in the on the server side. This proxy could then be passed to the
241: * client which can invoke method on this specific <b>instance </b>.
242: *
243: * @param the target instance to create the proxy for
244: * @param address the address to connect to.
245: * @param port the port to connect to.
246: * @return the new remote proxy instance
247: */
248: public static RemoteProxy createServerProxy(
249: final Object targetlInstance, final String address,
250: final int port) {
251: return new RemoteProxy(targetlInstance, address, port);
252: }
253:
254: /**
255: * Look up and retrives a proxy to an object from the server.
256: *
257: * @param loader the classloader to use
258: * @return the proxy instance
259: */
260: public Object getInstance(final ClassLoader loader) {
261: m_loader = loader;
262: return getInstance();
263: }
264:
265: /**
266: * Look up and retrives a proxy to an object from the server.
267: *
268: * @return the proxy instance
269: */
270: public Object getInstance() {
271: if (m_proxy != null) {
272: return m_proxy;
273: }
274: if (m_loader == null) {
275: m_loader = Thread.currentThread().getContextClassLoader();
276: }
277: try {
278: m_socket = new Socket(InetAddress.getByName(m_address),
279: m_port);
280: m_socket.setTcpNoDelay(true);
281: m_out = new ObjectOutputStream(m_socket.getOutputStream());
282: m_in = new ObjectInputStream(m_socket.getInputStream());
283: } catch (Exception e) {
284: throw new WrappedRuntimeException(e);
285: }
286: if (m_handle == null) {
287: // is a client side proxy
288: if (m_targetInterfaceNames == null) {
289: throw new IllegalStateException(
290: "interface class name can not be null");
291: }
292: if (m_targetImplName == null) {
293: throw new IllegalStateException(
294: "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(
306: m_targetInterfaceNames[i], false,
307: m_loader);
308: } catch (ClassNotFoundException e) {
309: throw new WrappedRuntimeException(e);
310: }
311: }
312: } catch (Exception e) {
313: throw new WrappedRuntimeException(e);
314: }
315: }
316: m_proxy = Proxy.newProxyInstance(m_loader, m_targetInterfaces,
317: this );
318: return m_proxy;
319: }
320:
321: /**
322: * This method is invoked automatically by the proxy. Should not be called directly.
323: *
324: * @param proxy the proxy instance that the method was invoked on
325: * @param method the Method instance corresponding to the interface method invoked on the proxy instance.
326: * @param args an array of objects containing the values of the arguments passed in the method invocation on the
327: * proxy instance.
328: * @return the value to return from the method invocation on the proxy instance.
329: */
330: public Object invoke(final Object proxy, final Method method,
331: final Object[] args) {
332: try {
333: m_out.write(Command.INVOKE);
334: m_out.writeObject(m_context);
335: m_out.writeObject(m_handle);
336: m_out.writeObject(method.getName());
337: m_out.writeObject(method.getParameterTypes());
338: m_out.writeObject(args);
339: m_out.flush();
340: final Object response = m_in.readObject();
341: if (response instanceof Exception) {
342: throw (Exception) response;
343: }
344: return response;
345: } catch (Exception e) {
346: throw new WrappedRuntimeException(e);
347: }
348: }
349:
350: /**
351: * Closes the proxy and the connection to the server.
352: */
353: public void close() {
354: try {
355: m_out.write(Command.CLOSE);
356: m_out.flush();
357: m_out.close();
358: m_in.close();
359: m_socket.close();
360: } catch (IOException e) {
361: throw new WrappedRuntimeException(e);
362: }
363: }
364:
365: /**
366: * Returns a proxy wrapped instance by its handle.
367: *
368: * @param handle the handle
369: * @return the instance
370: */
371: public static Object getWrappedInstance(final String handle) {
372: return s_instances.get(handle);
373: }
374:
375: /**
376: * Wraps a new instance and maps it to a handle.
377: *
378: * @param instance the instance to wrap
379: * @return the handle for the instance
380: */
381: public static String wrapInstance(final Object instance) {
382: final String handle = UuidGenerator.generate(instance);
383: s_instances.put(handle, instance);
384: return handle;
385: }
386: }
|