blob: c13d92faac3c2be6ff5c2aae8809cd68f67165d1 [file] [log] [blame]
/*******************************************************************************
* 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;
/*
* $RCSfile: ConnectionHandler.java,v $
* $Revision: 1.15 $ $Date: 2005/08/24 20:39:08 $
*/
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$
/**
* 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 = (Object[]) 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 length;
int componentType;
Commands.ValueObject worker = new Commands.ValueObject();
public ArrayContentsRetriever(Object anArray) {
array = anArray;
length = Array.getLength(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;
}
}
}