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(); } }
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 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; }
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; } }
/** * 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; }