/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.util.reflect;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import xxl.core.io.CounterInputStream;
import xxl.core.io.CounterOutputStream;
import xxl.core.util.WrappingRuntimeException;
/**
* This class provides a general Proxy that can be used
* with every interface. It uses the reflection 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 has to support the
* serialisable Interface.
* <p>
* This is a very useful class in a distributed environment.
* <p>
* The example with no parameters makes some communications
* on the local host. If you pass 2 or more parameters, the
* example works in client/server mode and makes the connections
* over the network. The possible parameters are:
* <ol>
* <li>port number</li>
* <li>'client' or 'server'</li>
* <li>only in 'client'-mode: name of the server (e.g. localhost or ip-address</li>
* </ol>
*/
public class SocketProxy implements InvocationHandler {
/** Socket to use */
private Socket socket;
/** InputStream of the socket */
private ObjectInputStream in;
/** OutputStream of the socket */
private ObjectOutputStream out;
/**
* Produces dynamically a SocketProxy for the specified interface and object and
* returns it.
*
* @param address Address where to find the original object
* @param port Port to reach the original object
* @param interfaceName name of the interface
* @return returns the dynamic proxy (in [0] and an access object
* of class SocketProxy (for shuting down the server).
*/
public static Object[] newClientInstance(InetAddress address, int port, String interfaceName) {
try {
Class c = Class.forName(interfaceName);
SocketProxy sp = new SocketProxy(address,port);
return new Object[] {
Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { c },
sp
),
sp
};
}
catch (ClassNotFoundException e) {
return null;
}
}
/**
* Starts a server object and starts listening for client access.
*
* @param port Port to reach the original object
* @param origObject object that can be called from a client.
* @param newThread running in a new thread or in the same (blocking until termination
* of the server).
* @param ps PrintStream for output of statistical information (e.g. System.out). If out==null, then
* no statistical data is collected and printed.
*/
public static void newServerInstance(final int port, final Object origObject, boolean newThread, final PrintStream ps) {
Thread thread = new Thread() {
public void run() {
try {
ServerSocket s = new ServerSocket(port);
Socket socket = s.accept();
// The following line is very important if you have
// only one sending task and small packets.
socket.setTcpNoDelay(true);
InputStream inRaw = socket.getInputStream();
OutputStream outRaw = socket.getOutputStream();
if (ps!=null) {
inRaw = new CounterInputStream(inRaw);
outRaw = new CounterOutputStream(outRaw);
}
ObjectInputStream inServer = new ObjectInputStream(inRaw);
ObjectOutputStream outServer = new ObjectOutputStream(outRaw);
System.out.println("Connect successful");
Object args[],o;
Class parTypes[];
String methodName;
Class cl;
Method meth;
while (inServer.read()!=0) {
methodName = (String) inServer.readObject();
parTypes = (Class[]) inServer.readObject();
args = (Object[]) inServer.readObject();
cl = origObject.getClass();
meth = cl.getMethod(methodName,parTypes);
o = meth.invoke(origObject,args);
outServer.writeObject(o);
outServer.flush();
}
inServer.close();
outServer.close();
socket.close();
s.close();
if (ps!=null) {
CounterInputStream iC = (CounterInputStream) inRaw;
CounterOutputStream oC = (CounterOutputStream) outRaw;
ps.println(iC.toString());
ps.println(oC.toString());
}
}
// don't know what to do with exceptions!
catch (Exception e) {
e.printStackTrace();
}
/* Exceptions that can occur:
catch (IOException e) {}
catch (ClassNotFoundException e) {}
catch (NoSuchMethodException e) {}
catch (IllegalAccessException e) {}
catch (InvocationTargetException e) {}
*/
// Server is terminating
}
};
if (newThread)
thread.start();
else
thread.run();
}
/**
* Creates a new socket proxy and connects it to the specified port number at
* the specified IP address.
*
* @param address the IP address.
* @param port the port number.
*/
private SocketProxy(InetAddress address, int port) {
try {
this.socket = new Socket(address,port);
// The following line is very important if you have
// only one sending task and small packets.
socket.setTcpNoDelay(true);
this.out = new ObjectOutputStream(socket.getOutputStream());
this.in = new ObjectInputStream(socket.getInputStream());
}
catch (IOException e) {
throw new WrappingRuntimeException(e);
}
}
/**
* Frees the resources used and sends a shutdown message to the server
* object.
*/
public void shutdown() {
try {
out.write(0);
out.flush();
out.close();
in.close();
socket.close();
}
catch (IOException e) {
throw new WrappingRuntimeException(e);
}
}
/**
* This method is invoked automatically by the proxy. It is
* unusual to call this method directly.
*
* @param proxy the proxy instance that the method was invoked on
* @param meth the Method instance corresponding to the interface method
* invoked on the proxy instance. The declaring class of the Method
* object will be the interface that the method was declared in, which
* may be a superinterface of the proxy interface that the proxy class
* inherits the method through.
* @param args an array of objects containing the values of the arguments passed
* in the method invocation on the proxy instance, or null if interface
* method takes no arguments. Arguments of primitive types are wrapped in
* instances of the appropriate primitive wrapper class, such as
* java.lang.Integer or java.lang.Boolean.
* @return the value to return from the method invocation on the proxy instance.
* If the declared return type of the interface method is a primitive type,
* then the value returned by this method must be an instance of the
* corresponding primitive wrapper class; otherwise, it must be a type
* assignable to the declared return type. If the value returned by this
* method is null and the interface method's return type is primitive, then
* a NullPointerException will be thrown by the method invocation on the
* proxy instance. If the value returned by this method is otherwise not
* compatible with the interface method's declared return type as described
* above, a ClassCastException will be thrown by the method invocation on
* the proxy instance.
* @throws Throwable
*/
public Object invoke(Object proxy, Method meth, Object[] args) throws Throwable {
try {
out.write(1);
out.writeObject(meth.getName());
out.writeObject(meth.getParameterTypes());
out.writeObject(args);
out.flush();
return in.readObject();
}
catch (Exception e) {
throw new WrappingRuntimeException(e);
}
}
}