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