/******************************************************************************* * Copyright (c) 2001, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jem.internal.proxy.vm.remote; /* */ import java.io.*; import java.lang.reflect.Array; import java.net.Socket; import java.net.SocketException; import java.util.*; import org.eclipse.jem.internal.proxy.common.CommandException; import org.eclipse.jem.internal.proxy.common.remote.*; import org.eclipse.jem.internal.proxy.initParser.*; import org.eclipse.jem.internal.proxy.initParser.tree.NoExpressionValueException; /** * This handles one connection. */ public class ConnectionHandler { protected Socket socket; final protected RemoteVMServerThread server; final protected Thread connectionThread; protected DataInputStream in; protected DataOutputStream out; // Kludge: Bug in Linux 1.3.xxx of JVM. Closing a socket while the socket is being read/accept will not interrupt the // wait. Need to timeout to the socket read/accept before the socket close will be noticed. This has been fixed // in Linux 1.4. So on Linux 1.3 need to put timeouts in on those sockets that can be separately closed while reading/accepting. static boolean LINUX_1_3 = "linux".equalsIgnoreCase(System.getProperty("os.name")) && System.getProperty("java.version","").startsWith("1.3"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ static { try{ Class.forName("java.awt.Dialog"); } catch (ExceptionInInitializerError e){} catch (LinkageError e){} catch (ClassNotFoundException e) {} try{ Class.forName("sun.awt.windows.WToolkit"); } catch (ExceptionInInitializerError e){} catch (LinkageError e){} catch (ClassNotFoundException e) {} try{ Class.forName("java.awt.Component"); } catch (ExceptionInInitializerError e){} catch (LinkageError e){} catch (ClassNotFoundException e) {} } /** * This may be running as a callback, in that case there is no connectionThread. */ public ConnectionHandler(Socket sc, RemoteVMServerThread svr, Thread connectionThread) { socket = sc; server = svr; this.connectionThread = connectionThread; Integer bufSize = Integer.getInteger("proxyvm.bufsize"); //$NON-NLS-1$ if (bufSize == null) bufSize = new Integer(5000); try { out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), bufSize.intValue())); in = new DataInputStream(new BufferedInputStream(socket.getInputStream(), bufSize.intValue())); } catch (IOException e) { } } /** * Did this construct correctly. * This is needed because an exception could be thrown in the ctor and * that's not a good thing to do. */ public boolean isConnected() { return socket != null; } /** * Request the thread to close down. */ public void close() { if (socket != null) { try { Socket s = socket; socket = null; s.close(); } catch (Exception e) { } } } // Map of ID to expression processers that are currently being processed. private Map expressionProcessors = new HashMap(); /** * Process and loop until told to stop. A callback_done will stop * the loop and will return a result. Otherwise null is returned. */ public Object run() throws CommandException { Object result = null; boolean shutdown = false; boolean closeWhenDone = true; Commands.ValueObject valueObject = new Commands.ValueObject(); // Working value object so not continually recreated. InvokableValueSender valueSender = new InvokableValueSender(); // Working valuesender so not continually recreated. try { boolean doLoop = true; /** * Note: In the cases below you will see a lot of finally clauses that null variables out. * This is because this is a long running loop, and variables declared within blocks are not * garbage collected until the method is terminated, so these variables once set would never * be GC'd. The nulling at the end of the case makes sure that any of those objects set are * now available for garbage collection when necessary. */ while(doLoop && isConnected()) { byte cmd = 0; try { if (LINUX_1_3) socket.setSoTimeout(1000); // Linux 1.3 bug, see comment on LINUX_1_3 cmd = in.readByte(); if (LINUX_1_3 && isConnected()) socket.setSoTimeout(0); // Linux 1.3 bug, see comment on LINUX_1_3 } catch (InterruptedIOException e) { continue; // Timeout, try again } switch (cmd) { case Commands.QUIT_CONNECTION: doLoop = false; break; // Close this connection case Commands.TERMINATE_SERVER: doLoop = false; shutdown = true; // Shutdown everything break; case Commands.GET_CLASS: String className = in.readUTF(); Class aClass = null; Class superClass = null; String superClassName = null; boolean added = false; try { aClass = Class.forName(className); // Turns out using JNI format for array type will work fine. added = server.getIdentityID(aClass, valueObject); boolean isInterface = aClass.isInterface(); boolean isAbstract = java.lang.reflect.Modifier.isAbstract(aClass.getModifiers()); superClass = aClass.getSuperclass(); superClassName = (superClass != null) ? superClass.getName() : ""; //$NON-NLS-1$ out.writeByte(Commands.GET_CLASS_RETURN); out.writeInt(valueObject.objectID); out.writeBoolean(isInterface); out.writeBoolean(isAbstract); out.writeUTF(superClassName); out.flush(); } catch (ClassNotFoundException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.GET_CLASS_NOT_FOUND, valueObject); } catch (ExceptionInInitializerError e) { sendException(e.getException(), valueObject, out); } catch (LinkageError e) { sendException(e, valueObject, out); } catch (Throwable e) { // Something bad, did we add a class? If we did remove it from the table. if (added) server.removeObject(server.getObject(valueObject.objectID)); throw e; } finally { // clear out for GC to work. className = null; aClass = null; superClass = null; superClassName = null; valueObject.set(); } break; case Commands.GET_CLASS_FROM_ID: int classID = in.readInt(); try { aClass = (Class) server.getObject(classID); boolean isInterface = aClass.isInterface(); boolean isAbstract = java.lang.reflect.Modifier.isAbstract(aClass.getModifiers()); superClass = aClass.getSuperclass(); superClassName = (superClass != null) ? superClass.getName() : ""; //$NON-NLS-1$ out.writeByte(Commands.GET_CLASS_ID_RETURN); out.writeUTF(aClass.getName()); out.writeBoolean(isInterface); out.writeBoolean(isAbstract); out.writeUTF(superClassName); out.flush(); } catch (ClassCastException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); } finally { // clear out for GC to work. aClass = null; superClass = null; superClassName = null; valueObject.set(); } break; case Commands.GET_OBJECT_DATA: int objectID = in.readInt(); Object anObject = null; try { anObject = server.getObject(objectID); valueObject.setObjectID(objectID, server.getIdentityID(anObject.getClass())); Commands.writeValue(out, valueObject, true); } catch (ClassCastException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); } finally { anObject = null; // Clear out for GC to work valueObject.set(); } break; case Commands.RELEASE_OBJECT: int id = in.readInt(); server.removeObject(server.getObject(id)); break; case Commands.NEW_INIT_STRING: classID = in.readInt(); // ID Of class to do new upon. String initString = in.readUTF(); // The init string. Object newValue = null; Class theClass = null; try { theClass = (Class) server.getObject(classID); if (theClass == null) { // The class wasn't found. So imply ClassNotFound exception. throw new ClassNotFoundException(); } InitializationStringParser parser = null; try { parser = InitializationStringParser.createParser(initString); newValue = parser.evaluate(); boolean primitive = parser.isPrimitive(); // If expected class is Void.TYPE, that means don't test the type of the result // to verify if correct type, just return what it really is. if (theClass != Void.TYPE && primitive != theClass.isPrimitive()) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command. } if (primitive) { try { // Need to do special tests for compatibility and assignment. sendObject(newValue, classID != Commands.VOID_TYPE ? classID : server.getIdentityID(parser.getExpectedType()), valueObject, out, true); // This will make sure it goes out as the correct primitive type } catch (ClassCastException e) { // The returned type is not of the correct type for what is expected. valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command } } else { if (newValue != null) { // Test to see if they are compatible. (Null can be assigned to any object, // so that is why it was tested out above). // If expected class is Void.TYPE, that means don't test the type of the result // to verify if correct type, just return what it really is. if (theClass != Void.TYPE && !theClass.isInstance(newValue)) { // The returned type is not of the correct type for what is expected. valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command } } sendObject(newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Send out as an object. } } catch (InitializationStringEvaluationException e) { if (e instanceof EvaluationException) { // Want to return the real exception. sendException(e.getOriginalException(), valueObject, out); } else { // Couldn't be evaluated, return an error for this. setExceptionIntoValue(e.getOriginalException(), valueObject); Commands.sendErrorCommand(out, Commands.CANNOT_EVALUATE_STRING, valueObject); } } finally { parser = null; // Clear out for GC to work } } catch (Throwable e) { sendException(e, valueObject, out); } finally { // Clear out for GC to work initString = null; theClass = null; newValue = null; valueObject.set(); } break; case Commands.INVOKE: Object target = null; Object[] parms = null; Class returnType = null; java.lang.reflect.Method aMethod = null; try { int methodID = in.readInt(); // ID of method to invoke aMethod = (java.lang.reflect.Method) server.getObject(methodID); // Method to invoke Commands.readValue(in, valueObject); target = getInvokableObject(valueObject); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parms = (Object[]) valueSender.getArray(); } else { // It is all objects or null, so it should be an Object[] or null. If not, then this is an error. parms = (Object[]) valueObject.anObject; } if (!aMethod.isAccessible()) aMethod.setAccessible(true); // We will allow all to occur. Let access control be handled by IDE and compiler. newValue = aMethod.invoke(target, parms); returnType = aMethod.getReturnType(); if (returnType.isPrimitive()) { int returnTypeID = server.getIdentityID(returnType); // Need to tell sendObject the correct primitive type. sendObject(newValue, returnTypeID, valueObject, out, true); } else { sendObject(newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Just send the object back. sendObject knows how to iterpret the type } } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. These should end the thread. } catch (java.lang.reflect.InvocationTargetException e) { // This is a wrappered exception. Return the wrappered one so it looks like // it was the real one. (Sometimes the method being invoked is on a java.lang.reflect.Constructor.newInstance, // which in turn is an InvocationTargetException, so we will go until we don't have an InvocationTargetException. Throwable t = e; do { t = ((java.lang.reflect.InvocationTargetException) t).getTargetException(); } while (t instanceof java.lang.reflect.InvocationTargetException); sendException(t, valueObject, out); } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { // Clear out for GC to work valueObject.set(); parms = null; target = null; aMethod = null; returnType = null; newValue = null; valueSender.clear(); } break; case Commands.INVOKE_WITH_METHOD_PASSED: aClass = null; String methodName = null; Class[] parmTypes = null; target = null; parms = null; returnType = null; aMethod = null; try { Commands.readValue(in, valueObject); aClass = (Class) getInvokableObject(valueObject); // The class that has the method. methodName = in.readUTF(); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parmTypes = (Class[]) valueSender.getArray(); } else { // It null, so it should be an null. If not, then this is an error. parmTypes = null; } aMethod = aClass.getMethod(methodName, parmTypes); // Now we get the info for the invocation of the method and execute it. Commands.readValue(in, valueObject); target = getInvokableObject(valueObject); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parms = (Object[]) valueSender.getArray(); } else { // It is all objects or null, so it should be an Object[] or null. If not, then this is an error. parms = (Object[]) valueObject.anObject; } if (!aMethod.isAccessible()) aMethod.setAccessible(true); // We will allow all to occur. Let access control be handled by IDE and compiler. newValue = aMethod.invoke(target, parms); returnType = aMethod.getReturnType(); if (returnType.isPrimitive()) { int returnTypeID = server.getIdentityID(returnType); // Need to tell sendObject the correct primitive type. sendObject(newValue, returnTypeID, valueObject, out, true); } else { sendObject(newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Just send the object back. sendObject knows how to iterpret the type } } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. These should end the thread. } catch (java.lang.reflect.InvocationTargetException e) { // This is a wrappered exception. Return the wrappered one so it looks like // it was the real one. (Sometimes the method being invoked is on a java.lang.reflect.Constructor.newInstance, // which in turn is an InvocationTargetException, so we will go until we don't have an InvocationTargetException. Throwable t = e; do { t = ((java.lang.reflect.InvocationTargetException) t).getTargetException(); } while (t instanceof java.lang.reflect.InvocationTargetException); sendException(t, valueObject, out); } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { aClass = null; methodName = null; parmTypes = null; // Clear out for GC to work valueObject.set(); parms = null; target = null; aMethod = null; returnType = null; newValue = null; valueSender.clear(); } break; case Commands.GET_ARRAY_CONTENTS: try { target = server.getObject(in.readInt()); // Array to get the ids for. valueObject.setArrayIDS(new ArrayContentsRetriever(target), Array.getLength(target), Commands.OBJECT_CLASS); Commands.writeValue(out, valueObject, true); // Write it back as a value command. } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. These should end the thread. } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { target = null; valueObject.set(); } break; case Commands.CALLBACK_DONE: try { if (connectionThread != null) { valueObject.set(); Commands.sendErrorCommand(out, Commands.UNKNOWN_COMMAND_SENT, valueObject); } else { try { Commands.readBackValue(in, valueObject, Commands.NO_TYPE_CHECK); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); result = valueSender.getArray(); } else { result = getInvokableObject(valueObject); } doLoop = false; // We need to terminate and return result closeWhenDone = false; // Don't close, we will continue. } catch (CommandErrorException e) { // There was an command error on the other side. This means // connection still good, but don't continue the callback processing. doLoop = false; // We need to terminate and return result closeWhenDone = false; // Don't close, we will continue. throw e; // Let it go on out. } } } finally { valueObject.set(); valueSender.clear(); } break; case Commands.ERROR: try { // Got an error command. Don't know what to do but read the // value and simply print it out. Commands.readValue(in, valueObject); result = getInvokableObject(valueObject); System.out.println("Error sent to server: Result=" + result); //$NON-NLS-1$ } finally { valueObject.set(); } break; case Commands.EXPRESSION_TREE_COMMAND: try { processExpressionCommand(valueObject, valueSender); } finally { valueObject.set(); valueSender.clear(); } break; default: // Unknown command. We don't know how long it is, so we need to shut the connection down. System.err.println("Error: Invalid cmd send to server: Cmd=" + cmd); //$NON-NLS-1$ doLoop = false; closeWhenDone = true; break; } } } catch (EOFException e) { // This is ok. It means that the connection on the other side was terminated. // So just accept this and go down. } catch (CommandException e) { throw e; } catch (SocketException e) { if (socket != null) throw new UnexpectedExceptionCommandException(false, e); // socket null means a valid close request } catch (Throwable e) { e.printStackTrace(); } finally { if (closeWhenDone) { try { for (Iterator itr = expressionProcessors.values().iterator(); itr.hasNext();) { ExpressionProcesserController exp = (ExpressionProcesserController) itr.next(); exp.close(); } } finally { expressionProcessors.clear(); } if (in != null) try { in.close(); } catch (Exception e) { } in = null; if (out != null) try { out.close(); } catch (Exception e) { } out = null; close(); } } if (closeWhenDone && connectionThread != null) server.removeConnectionThread(connectionThread); if (shutdown) server.requestShutdown(); return result; } // A retriever is what handles the array get contents. private class ArrayContentsRetriever implements Commands.ValueRetrieve { int index=0; Object array; int componentType; Commands.ValueObject worker = new Commands.ValueObject(); public ArrayContentsRetriever(Object anArray) { array = anArray; if (anArray.getClass().getComponentType().isPrimitive()) { componentType = server.getIdentityID(anArray.getClass().getComponentType()); } else componentType = NOT_A_PRIMITIVE; } public Commands.ValueObject nextValue() { fillInValue(Array.get(array, index++), componentType, worker); return worker; } }; private void processExpressionCommand(Commands.ValueObject valueObject, InvokableValueSender valueSender) throws IOException, CommandException { Integer expressionID = new Integer(in.readInt()); byte cmdType = in.readByte(); ExpressionProcesserController exp = null; switch (cmdType) { case ExpressionCommands.START_EXPRESSION_TREE_PROCESSING: byte trace = in.readByte(); if (trace == ExpressionCommands.TRACE_DEFAULT) exp = new ExpressionProcesserController(server, this); else exp = new ExpressionProcesserController(server, this, trace == ExpressionCommands.TRACE_ON); expressionProcessors.put(expressionID, exp); break; case ExpressionCommands.TRANSFER_EXPRESSION_REQUEST: exp = (ExpressionProcesserController) expressionProcessors.remove(expressionID); sendObject(exp, NOT_A_PRIMITIVE, valueObject, out, true); break; case ExpressionCommands.RESUME_EXPRESSION_REQUEST: Commands.readValue(in, valueObject); exp = (ExpressionProcesserController) getInvokableObject(valueObject); expressionProcessors.put(expressionID, exp); break; case ExpressionCommands.PUSH_EXPRESSION: exp = (ExpressionProcesserController) expressionProcessors.get(expressionID); exp.process(in); break; case ExpressionCommands.SYNC_REQUEST: boolean haveProxies = in.readBoolean(); // See if we expression proxy ids that need to be resolved if (haveProxies) { Commands.readValue(in, valueObject); valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); } exp = (ExpressionProcesserController) expressionProcessors.get(expressionID); if (haveProxies) sendExpressionProxyResolutions(valueObject, (int[]) valueSender.getArray(), exp); if (exp.noErrors()) { valueObject.set(true); // Mark that all is good. Commands.writeValue(out, valueObject, true, true); } else { processExpressionError(exp, valueObject); } break; case ExpressionCommands.PULL_VALUE_REQUEST: haveProxies = in.readBoolean(); // See if we expression proxy ids that need to be resolved if (haveProxies) { Commands.readValue(in, valueObject); valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); } exp = (ExpressionProcesserController) expressionProcessors.get(expressionID); if (haveProxies) sendExpressionProxyResolutions(valueObject, (int[]) valueSender.getArray(), exp); if (exp.noErrors()) { try { Object[] pulledValue = exp.pullValue(); // Send back the command code for pull value. Don't flush. We will flush when all done. if (((Class) pulledValue[1]).isPrimitive()) { int returnTypeID = server.getIdentityID(pulledValue[1]); // Need to tell sendObject the correct primitive type. sendObject(pulledValue[0], returnTypeID, valueObject, out, true, true); } else { sendObject(pulledValue[0], NOT_A_PRIMITIVE, valueObject, out, true, true); // Just send the object back. sendObject knows how to iterpret the type } } catch (NoExpressionValueException e) { sendNoValueErrorCommand(exp, valueObject); } } else processExpressionError(exp, valueObject); break; case ExpressionCommands.END_EXPRESSION_TREE_PROCESSING: exp = (ExpressionProcesserController) expressionProcessors.remove(expressionID); exp.close(); break; } } /* * @param is * @param exp * * @since 1.1.0 */ private void sendExpressionProxyResolutions(Commands.ValueObject valueObject, final int[] proxyIDs, final ExpressionProcesserController exp) throws CommandException { class ExpressionProxyRetriever implements Commands.ValueRetrieve { int index = 0; Object[] proxyResolution = new Object[2]; Commands.ValueObject worker = new Commands.ValueObject(); public Commands.ValueObject nextValue() { int proxyID = proxyIDs[index++]; if (exp.pullExpressionProxyValue(proxyID, proxyResolution)) { if (proxyResolution[1] != Void.TYPE) { if (((Class) proxyResolution[1]).isPrimitive()) { int returnTypeID = server.getIdentityID(proxyResolution[1]); // Need to tell worker the correct primitive type. fillInValue(proxyResolution[0], returnTypeID, worker); } else { fillInValue(proxyResolution[0], NOT_A_PRIMITIVE, worker); } } else worker.setFlag(ExpressionCommands.EXPRESSIONPROXY_VOIDTYPE); // It was resolved, but to not set due to void type of expression. } else worker.setFlag(ExpressionCommands.EXPRESSIONPROXY_NOTRESOLVED); // It wasn't resolved. return worker; } }; valueObject.setArrayIDS(new ExpressionProxyRetriever(), proxyIDs.length, Commands.OBJECT_CLASS); Commands.writeValue(out, valueObject, true, false); // Write it back as a value command. } private void processExpressionError(ExpressionProcesserController exp, Commands.ValueObject valueObject) throws CommandException { if (exp.isNoExpressionValue()) { sendNoValueErrorCommand(exp, valueObject); } else sendException(exp.getErrorThrowable(), valueObject, out); } /* * @param exp * @param valueObject * @throws CommandException * * @since 1.1.0 */ private void sendNoValueErrorCommand(ExpressionProcesserController exp, Commands.ValueObject valueObject) throws CommandException { setExceptionIntoValue(exp.getErrorThrowable(), valueObject); Commands.sendErrorCommand(out, ExpressionCommands.EXPRESSION_NOEXPRESSIONVALUE_EXCEPTION, valueObject); } protected static final int NOT_A_PRIMITIVE = Commands.NOT_AN_ID; protected static final int SEND_AS_IS = -2; // This means sends as an object not as an id. public static final int CLASS_ADDED = 1; public static final int OBJECT_ADDED = 2; public int fillInValue(Object object, int primitiveTypeID, Commands.ValueObject valueObject) { int added = 0; if (object == null) { valueObject.set(); } else { int classID = 0; if (primitiveTypeID != NOT_A_PRIMITIVE && primitiveTypeID != SEND_AS_IS) { classID = primitiveTypeID; // The object is really supposed to be a primitive of this type switch (classID) { case Commands.BOOLEAN_TYPE: valueObject.set(((Boolean) object).booleanValue()); break; case Commands.INTEGER_TYPE: if (object instanceof Character) valueObject.set((int) ((Character) object).charValue()); // Because char can be widened to an int else valueObject.set(((Number) object).intValue()); break; case Commands.BYTE_TYPE: valueObject.set(((Number) object).byteValue()); break; case Commands.CHARACTER_TYPE: valueObject.set(((Character) object).charValue()); break; case Commands.DOUBLE_TYPE: if (object instanceof Character) valueObject.set((double) ((Character) object).charValue()); // Because char can be widened to a double else valueObject.set(((Number) object).doubleValue()); break; case Commands.FLOAT_TYPE: if (object instanceof Character) valueObject.set((float) ((Character) object).charValue()); // Because char can be widened to a float else valueObject.set(((Number) object).floatValue()); break; case Commands.SHORT_TYPE: valueObject.set(((Number) object).shortValue()); break; case Commands.LONG_TYPE: if (object instanceof Character) valueObject.set((long) ((Character) object).charValue()); // Because char can be widened to a long else valueObject.set(((Number) object).longValue()); break; } } else { // It's not a primitive. boolean addObject = false, addClass = false; Class objClass = object.getClass(); classID = server.getIdentityID(objClass); if (classID == RemoteVMServerThread.ID_NOT_FOUND) { Commands.ValueObject classValue = new Commands.ValueObject(); addClass = server.getIdentityID(objClass, classValue); if (addClass) added |= CLASS_ADDED; classID = classValue.objectID; } switch (classID) { case Commands.BYTE_CLASS: valueObject.set((Byte) object); break; case Commands.CHARACTER_CLASS: valueObject.set((Character) object); break; case Commands.DOUBLE_CLASS: valueObject.set((Double) object); break; case Commands.FLOAT_CLASS: valueObject.set((Float) object); break; case Commands.INTEGER_CLASS: valueObject.set((Integer) object); break; case Commands.LONG_CLASS: valueObject.set((Long) object); break; case Commands.SHORT_CLASS: valueObject.set((Short) object); break; case Commands.BOOLEAN_CLASS: valueObject.set((Boolean) object); break; case Commands.STRING_CLASS: valueObject.set((String) object); break; case Commands.BIG_DECIMAL_CLASS: valueObject.set(object, Commands.BIG_DECIMAL_CLASS); break; case Commands.BIG_INTEGER_CLASS: valueObject.set(object, Commands.BIG_INTEGER_CLASS); break; default: if (primitiveTypeID != SEND_AS_IS) { addObject = server.getIdentityID(object, valueObject); if (addObject) { added |= OBJECT_ADDED; valueObject.setObjectID(valueObject.objectID, classID); } else valueObject.setObjectID(valueObject.objectID); } else valueObject.set(object, classID); break; } } } return added; } public void sendObject(Object object, int primitiveTypeID, Commands.ValueObject valueObject, DataOutputStream out, boolean writeAsCommand) throws CommandException { sendObject(object, primitiveTypeID, valueObject, out, writeAsCommand, writeAsCommand); } public void sendObject(Object object, int primitiveTypeID, Commands.ValueObject valueObject, DataOutputStream out, boolean writeAsCommand, boolean flush) throws CommandException { int added = fillInValue(object, primitiveTypeID, valueObject); boolean sent = false; try { Commands.writeValue(out, valueObject, writeAsCommand, flush); // Write it back as a value command. sent = true; } finally { if (!sent) { // Ending due to some problem, so clean up any added objects from the id table. if ((added & OBJECT_ADDED) != 0) server.removeObject(valueObject.objectID); if ((added & CLASS_ADDED) != 0) server.removeObject(valueObject.classID); } } } public void sendException(Throwable e, Commands.ValueObject value, DataOutputStream out) throws CommandException { // Exception ocurred, return a value command with the exception within it. setExceptionIntoValue(e, value); Commands.sendErrorCommand(out, Commands.THROWABLE_SENT, value); } public void setExceptionIntoValue(Throwable e, Commands.ValueObject value) { server.getIdentityID(e, value); // It should always create it so we don't need to know. int eID = value.objectID; Class eClass = e.getClass(); server.getIdentityID(eClass, value); int eClassID = value.objectID; value.setException(eID, eClassID); } /** * From the valueObject, get an Object that is invokable (i.e. can be the target of an invoke, or one of its parms). * If it is an array type, the a ValueSender is returned. The invoker needs to then cast this * to a ValueSender and call the readArray routine. */ public Object getInvokableObject(final Commands.ValueObject value) { switch (value.type) { case Commands.NEW_OBJECT_ID: case Commands.OBJECT_ID: // These need to have access to the server to get the real object return server.getObject(value.objectID); default: // These have all the info needed within the value object itself, so ask it. return value.getAsObject(); } } // Helper class for getting an array. private class InvokableValueSender implements Commands.ValueSender { int index = 0; Object array; public InvokableValueSender() { } public InvokableValueSender(Commands.ValueObject arrayHeader) { initialize(arrayHeader); } public void initialize(Commands.ValueObject arrayHeader) { index = 0; Class arrayType = (Class) server.getObject(arrayHeader.classID); array = java.lang.reflect.Array.newInstance(arrayType, arrayHeader.anInt); } public void clear() { array = null; index = 0; } public Object getArray() { return array; } // A new value is being sent to the array public void sendValue(Commands.ValueObject value) { java.lang.reflect.Array.set(array, index++, getInvokableObject(value)); // add it to the array } // The next entry is an array too! public Commands.ValueSender nestedArray(Commands.ValueObject arrayHeader) { InvokableValueSender sender = new InvokableValueSender(arrayHeader); // Take the newly created array and put it into the current array. java.lang.reflect.Array.set(array, index++, sender.getArray()); return sender; } } }