package org.archstudio.ljm; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.util.Hashtable; @SuppressWarnings({ "unchecked", "rawtypes" }) public class LJMServer extends Thread { public static final int LJM_STAT_ERROR = 1000; public static final int LJM_STAT_DONE = 2000; public static final int LJM_STAT_RETVAL = 3000; public static final int LJM_STAT_EXCEPTION = 4000; protected ServerSocket socket; protected Hashtable serverObjects = new Hashtable(); public LJMServer() throws IOException { //Create a socket on any available port this(0); } public LJMServer(int port) throws IOException { socket = new ServerSocket(port); start(); } public int getPort() { return socket.getLocalPort(); } public void deploy(String objectName, Object o) { serverObjects.put(objectName, o); } public Object getDeployedObject(String objectName) { return serverObjects.get(objectName); } public void undeploy(String objectName) { serverObjects.remove(objectName); } @Override public void destroy() { try { socket.close(); } catch (IOException e) { } } @Override public void run() { LJMConnectionHandler connHandler = null; try { while (true) { Socket slaveSocket = socket.accept(); connHandler = new LJMConnectionHandler(slaveSocket); } } catch (IOException e) { //e.printStackTrace(); return; } finally { if (connHandler != null) { connHandler.close(); connHandler = null; } } } //Protocol is: //1. Read a boolean whether we want to continue or not (true = continue, false = don't) //If the boolean was false, we close the connection and return. //2. Read the object name (java.lang.String) //3. Read the method name (java.lang.String) //4. Read the parameter class list (java.lang.Class[]) //5. Read the parameter value list (java.lang.Object[]) //Then we do our thing. No matter what happens, an integer //gets written to the wire: //LJM_STAT_ERROR (a network, object lookup, or whatever error happened) //LJM_STAT_DONE (the method ran and returned void) //LJM_STAT_RETVAL (the method ran and returned a value //LJM_STAT_EXCEPTION (the method ran and threw an exception) //If LJM_STAT_ERROR happened, then an LJMException is written to the wire //If LJM_STAT_DONE happened, then that's the end of the interaction. //If LJM_STAT_RETVAL happened then we write the return value to the wire and //that's the end of the interaction //If LJM_STAT_EXCEPTION happened, then we write the exception to the wire and //that's the end of the interaction. //At the end of the interaction, we go back to reading the next interaction. class LJMConnectionHandler extends Thread { protected Socket slaveSocket; protected InputStream is; protected OutputStream os; protected ObjectInputStream ois; protected ObjectOutputStream oos; public LJMConnectionHandler(Socket slaveSocket) { //System.out.println("Creating new LJMConnectionHandler!"); //new Throwable().printStackTrace(); int priority = Thread.NORM_PRIORITY; this.setPriority(priority); try { this.slaveSocket = slaveSocket; is = new BufferedInputStream(slaveSocket.getInputStream()); ois = new ObjectInputStream(is); os = new BufferedOutputStream(slaveSocket.getOutputStream()); oos = new ObjectOutputStream(os); // Note: ObjectOutputStream sends an initial short across the stream // we have to flush the stream so that this short is sent and the client // does not hang waiting for it. oos.flush(); } catch (IOException e) { e.printStackTrace(); close(); return; } start(); } public void close() { try { if (is != null) { is.close(); } } catch (IOException ignored) { } try { if (ois != null) { ois.close(); } } catch (IOException ignored2) { } try { if (os != null) { os.close(); } } catch (IOException ignored3) { } try { if (oos != null) { oos.close(); } } catch (IOException ignored4) { } try { if (slaveSocket != null) { slaveSocket.close(); } } catch (IOException ignored5) { } } @Override public void run() { while (true) { //System.err.println("Processing call: "); boolean cont; try { cont = ois.readBoolean(); //System.err.println("Continuing: "+cont); } catch (ClassCastException cce1) { cce1.printStackTrace(); try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Protocol error.", cce1)); oos.flush(); close(); } catch (IOException writeException) { close(); return; } return; } catch (IOException ioe1) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Protocol error.")); oos.flush(); close(); } catch (IOException writeException) { close(); return; } return; } if (!cont) { close(); return; } String objectName; String methodName; Class[] paramClasses; Object[] paramValues; try { objectName = (String) ois.readObject(); //System.err.println("objectName: "+objectName); methodName = (String) ois.readObject(); //System.err.println("methodName: "+methodName); String[] paramClassNames = (String[]) ois.readObject(); //System.err.println("paramClassNames: "+Arrays.asList(paramClassNames)); paramClasses = ClassArrayEncoder.stringArrayToClassArray(paramClassNames); paramValues = (Object[]) ois.readObject(); //System.err.println("paramClassValues: "+Arrays.asList(paramValues)); } catch (ClassCastException cce) { cce.printStackTrace(); try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Protocol error.")); oos.flush(); } catch (IOException writeException) { close(); return; } close(); return; } catch (IOException ioe) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Protocol error.")); oos.flush(); } catch (IOException writeException) { close(); return; } close(); return; } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Can't find class:" + cnfe.toString())); oos.flush(); } catch (IOException writeException) { close(); return; } close(); return; } //System.out.println("ObjectName= " + objectName); Object targetObject = serverObjects.get(objectName); //System.err.println("targetObject: "+targetObject); if (targetObject == null) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Can't locate object.", true)); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } Class targetObjectClass = targetObject.getClass(); //System.err.println("targetObjectClass: "+targetObjectClass); Method targetMethod = null; try { targetMethod = targetObjectClass.getMethod(methodName, paramClasses); //System.err.println("targetMethod: "+targetMethod); } catch (NoSuchMethodException nsme) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Method does not exist.")); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } //System.out.println("Performing method invocation on true object: " + targetMethod); Object retVal; try { //System.err.println("retVal: ?"); retVal = targetMethod.invoke(targetObject, paramValues); //System.err.println("retVal: "+retVal); if (targetMethod.getReturnType().equals(void.class) || targetMethod.getReturnType().equals(Void.class)) { try { oos.writeInt(LJM_STAT_DONE); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } else { if (retVal != null && !(retVal instanceof java.io.Serializable)) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Return type (" + retVal.getClass().getName() + ") is not serializable.")); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } else { try { oos.writeInt(LJM_STAT_RETVAL); oos.writeObject(retVal); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } } } catch (IllegalAccessException iae) { iae.printStackTrace(); try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Illegal access exception invoking method.", iae)); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } catch (InvocationTargetException ite) { Throwable t = ite.getTargetException(); //System.out.println("An exception occurred performing the invocation!"); t.printStackTrace(); if (!(t instanceof java.io.Serializable)) { try { oos.writeInt(LJM_STAT_ERROR); oos.writeObject(new LJMException("Exception type is not serializable.", ite)); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } else { try { oos.writeInt(LJM_STAT_EXCEPTION); oos.writeObject(t); oos.flush(); } catch (IOException writeException) { close(); return; } continue; } } } } } }