| /******************************************************************************* |
| * 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.common.remote; |
| /* |
| |
| |
| */ |
| |
| import java.io.*; |
| |
| import org.eclipse.jem.internal.proxy.common.CommandException; |
| /** |
| * The commands that can be passed back and forth between |
| * client and server. And other constants. |
| * |
| * - Contains helper methods for reading/writing commands. |
| */ |
| public class Commands { |
| // The commands will be written in writeByte format . |
| public final static byte |
| GET_CLASS = 1, // Get the class object, |
| VALUE = 2, // Returning a value |
| QUIT_CONNECTION = 4, // Close this connection |
| TERMINATE_SERVER = 5, // Terminate the entire server. |
| ERROR = 6, // Returning an error |
| RELEASE_OBJECT = 7, // An object is no longer needed on the client side, so |
| // it can be removed from the server id table and released. |
| GET_CLASS_RETURN = 8, // The return command from GET_CLASS |
| // Obsolete, not used anymore GET_METHOD = 9, // Return the id for a method |
| // Obsolete, not used anymore GET_CTOR = 10, // Return the id for a constructor |
| NEW_INIT_STRING = 11, // Create a new bean using the init string |
| GET_CLASS_FROM_ID = 12, // We have an ID, return the class info for this id. |
| GET_CLASS_ID_RETURN = 13, // The return command from GET_CLASS_FROM_ID |
| GET_OBJECT_DATA = 14, // We have an ID, but we don't have the info, return it. This is a |
| // corrective command only. This would happen if for some strange |
| // reason the proxy has been removed but has not been released. This |
| // really shouldn't happen except as a possible race condition between |
| // GC and returning id from the server. |
| INVOKE = 15, // Invoke a method. |
| |
| |
| // These commands are to the Master Server thread in the IDE. |
| ALIVE = 16, // Are you alive? |
| REMOTE_STARTED = 17, // Remote VM has started. |
| ATTACH_CALLBACK = 18, // Attach to a callback thread on the IDE side. The remote vm will use its socket as the callback socket. |
| // it will return boolean <code>true</code> if attach worked or <code>false</code> if it failed. |
| |
| // These are more regular commands. They were historically added after the master server thread commands, so |
| // they are shown here after them and with numbers greater than them. |
| EXPRESSION_TREE_COMMAND = 19, // An expression tree subcommand has come in. |
| INVOKE_WITH_METHOD_PASSED = 20, // Invoke where the description of the method is passed in with the command. |
| GET_ARRAY_CONTENTS = 21; // Get the first dimension contents as an array of ids and send them back. |
| |
| |
| // Callback commands |
| public final static byte |
| CALLBACK = (byte) 255, // A callback has come in. |
| CALLBACK_DONE = (byte) 254, // A callback done command, sent to the remote vm upon callback completion. |
| CALLBACK_STREAM = (byte) 253, // A callback for a byte stream has come in. |
| // This is a special callback. When this comes in a special |
| // input stream will be created that will take over control of |
| // the connection until the stream is terminated on the remote |
| // side. At this time the connection will be returned. |
| CALLBACK_STREAM_TERMINATE = (byte) 252; // A callback stream is asked to terminate early. |
| |
| // The error values from the command on the server. |
| public final static int |
| NO_ERROR = 0, // No error status. |
| UNKNOWN_COMMAND_SENT = 1, // An unknown command was sent to the server. Value is void. |
| GET_CLASS_NOT_FOUND = 2, // The class was not found in GetClass. Value is void. |
| CANNOT_EVALUATE_STRING = 3, // Evaluator couldn't evaluate the init string. Too complicated. Value is a throwable of the wrappered Init string error. |
| CLASS_CAST_EXCEPTION = 4, // The result is not assignable to the expected type. Value is void. |
| GET_METHOD_NOT_FOUND = 5, // Method requested wasn't found. Value is void. |
| THROWABLE_SENT = 6, // A Throwable is being sent back as the error, not as just data for the error. Value is the Throwable. |
| CALLBACK_RUNTIME_EXCEPTION = 7, // A runtime exception occurred during a callback. The data is the message. |
| CALLBACK_NOT_REGISTERED = 8, |
| MAX_ERROR_CODE = CALLBACK_NOT_REGISTERED; // This is just the max code. Not actually sent. Used as a flag. |
| |
| // Predefined standard id's for standard classes/objects. Both sides will assume these id's have been assigned |
| // to these classes/types/objects |
| public final static int |
| NOT_AN_ID = -1, // This value means it is not an id. It is never a valid id. |
| VOID_TYPE = 0, |
| BOOLEAN_TYPE = 1, |
| BOOLEAN_CLASS = 2, |
| INTEGER_TYPE = 3, |
| INTEGER_CLASS = 4, |
| BYTE_TYPE = 5, |
| BYTE_CLASS = 6, |
| CHARACTER_TYPE = 7, |
| CHARACTER_CLASS = 8, |
| DOUBLE_TYPE = 9, |
| DOUBLE_CLASS = 10, |
| FLOAT_TYPE = 11, |
| FLOAT_CLASS = 12, |
| SHORT_TYPE = 13, |
| SHORT_CLASS = 14, |
| LONG_TYPE = 15, |
| LONG_CLASS = 16, |
| STRING_CLASS = 17, |
| BIG_DECIMAL_CLASS = 18, |
| BIG_INTEGER_CLASS = 19, |
| NUMBER_CLASS = 20, |
| THROWABLE_CLASS = 21, |
| CLASS_CLASS = 22, |
| OBJECT_CLASS = 23, |
| ACCESSIBLEOBJECT_CLASS = 24, |
| METHOD_CLASS = 25, |
| FIELD_CLASS = 26, |
| CONSTRUCTOR_CLASS = 27, |
| GET_METHOD_ID = 28, // Class.getMethod(...) predefined id. |
| IVMSERVER_CLASS = 29, // IVMServer.class |
| ICALLBACK_CLASS = 30, // ICallback.class |
| REMOTESERVER_ID = 31, // id of RemoteVMServerThread instance. |
| REMOTEVMSERVER_CLASS = 32, // RemoteVMServer.class |
| INITIALIZECALLBACK_METHOD_ID = 33, // ICallback.initializeCallback method. |
| THREAD_CLASS = 34, |
| EXPRESSIONPROCESSERCONTROLLER_CLASS = 35, |
| FIRST_FREE_ID = 36; |
| |
| // The type flags written in writeByte format |
| public final static byte |
| VOID = VOID_TYPE, // null - nothing follows |
| BYTE = BYTE_TYPE, // byte - writeByte |
| L_BYTE = BYTE_CLASS, // java.lang.Byte - writeByte |
| CHAR = CHARACTER_TYPE, // char - writeChar |
| L_CHAR = CHARACTER_CLASS, // java.lang.Character - writeChar |
| DOUBLE = DOUBLE_TYPE, // double - writeDouble |
| L_DOUBLE = DOUBLE_CLASS, // java.lang.Double - writeDouble |
| FLOAT = FLOAT_TYPE, // float - writeFloat |
| L_FLOAT = FLOAT_CLASS, // java.lang.Float - writeFloat |
| INT = INTEGER_TYPE, // int - writeInt |
| L_INT = INTEGER_CLASS, // java.lang.Integer - writeInt |
| LONG = LONG_TYPE, // long - writeLong |
| L_LONG = LONG_CLASS, // java.lang.Long - writeLong |
| SHORT = SHORT_TYPE, // short - writeShort |
| L_SHORT = SHORT_CLASS, // java.lang.Short - writeShort |
| BOOL = BOOLEAN_TYPE, // boolean - writeBoolean |
| L_BOOL = BOOLEAN_CLASS, // java.lang.Boolean - writeBoolean |
| STRING = STRING_CLASS, // java.lang.String - writeUTF |
| OBJECT = OBJECT_CLASS, // Object - special, see below (Object can be used to return an array (except if the array contains any object_ids, that has a special type) |
| OBJECT_ID = 50, // Object identity key - writeInt |
| NEW_OBJECT_ID = 51, // New Object identity (this is a new object that didn't exist before) |
| THROW = 52, // An exception occured. The value is a throwable, it is of the same format as NEW_OBJECT_ID. |
| ARRAY_IDS = 53, // An array of values, where there are at least one ID in the array. If there were no |
| // ID's (i.e. all just values), then use OBJECT type intead and have it written as |
| // writeObject. |
| FLAG = 54; // The value is a flag int. If this is allowed on a read, the anInt field will contain the flag value. |
| |
| |
| |
| // Unless specified below, the commands are one byte long. |
| // Also, unless specified below, the commands do not return a confirmation response. |
| // |
| // NOTE: VERY IMPORTANT, after every command, flush() should be used so that the |
| // the data is immediately sent to the server. |
| // |
| // n means int (e.g. 1) |
| // nb means byte (e.g. 1b) |
| // 'x' means Unicode char (i.e. writeChar()) |
| // "xxx" means UTF8 string (i.e. writeUTF) |
| // bool means a one byte boolean value |
| // |
| // The commas aren't actually written, they are used as separaters in the comments below |
| // |
| // GET_CLASS: 1b, "classname" |
| // Will return on the output stream GET_CLASS_RETURN command: |
| // 8b, n1, bool1, bool2, "superclassname" |
| // The "n1" is the class id. |
| // The bool1 is whether this class is an interface (true if it is). |
| // The bool2 is whether this class is abstract (true if it is). |
| // The "superclassname" is the class name of the super class (0 length if no superclass) |
| // If the class is not found, then it will return an error with a value for the error. |
| // |
| // GET_CLASS_FROM_ID: 12b, n |
| // Where "n" is the class id. |
| // Will return on the output stream GET_CLASS_ID_RETURN command: |
| // 13b, "classname", bool1, bool2, "superclassname" |
| // The bool1 is whether this class is an interface (true if it is). |
| // The bool2 is whether this class is abstract (true if it is). |
| // |
| // VALUE: 2b, tb, value |
| // Where tb is the type in byte, and value is the appropriate value shown in |
| // table above. |
| // OBJECT_ID: 50b, n |
| // Where "n" is the object id. |
| // NEW_OBJECT_ID: 51b, n1, n2 |
| // Where "n1" is class ObjectID of the object that the object_id ("n2") is made of. |
| // OBJECT: 19b, n, writeObject |
| // Where "n" is the classObjectID of the class of the type of the object. |
| // NOTE: Object should be used only very rarely. Identity is lost, i.e. |
| // a copy is made each time and it can't be referenced back on the remote |
| // VM. |
| // ARRAY_IDS: 52b, id, n, [tb, value, ...] |
| // This is a very special array. It contains at least one ID. Therefor all of the |
| // First level entries are value objects. |
| // "id" is the id of the component type of the array(e.g. id for Object, or if multi-dimensional String[] (this will produce String[][]). |
| // "n" is the number of entries in the array. Followed by the values, one of the |
| // values could be an ARRAY_IDS too. The reading/writing of these are special because |
| // there is a callback mechanism to process the individual entries. This is so that |
| // temp arrays of ValueObjects won't need to be created to handle this, so it can |
| // go directly from the array to/from the stream. |
| // |
| // RELEASE_OBJECT: 7b, n |
| // Where the n is the object id to release. There is no confirmation to read back. |
| // |
| // ERROR: 6b, n, tb, ... |
| // n is the error code for this error. |
| // tb is a type flag, followed by the value. The value is dependent upon |
| // the command that this is error is from. If a THROW, then the THROW is ALWAYS a new |
| // ID, it can never be an existing id. |
| // |
| // |
| // TO_BEAN_STRING: 9b, n |
| // Where n is the object id to produce the bean string for. |
| // It will return a VALUE command where the type is String. |
| // |
| // NEW_INSTANCE: 10b, n |
| // Where n is the class object id of the class to create a new instance of using the default ctor. |
| // It will return either a VALUE command containing the new value (of type OBJECT_ID/NEW_OBJECT_ID if not |
| // one of the constant types with the true classID in it) or an ERROR command. (The ERROR could |
| // be a THROW type). If the object created is not assignable to the type passed in, then |
| // an ERROR is returned with CLASS_CAST_EXCEPTION flag. |
| // |
| // NEW_INIT_STRING: 11b, n, "initstring" |
| // Where n is the class object id of the class this initstring is supposed to create for. |
| // It will return either a VALUE command containing the new value (of type OBJECT_ID/NEW_OBJECT_ID if not |
| // one of the constant types with the true classID in it) or an ERROR command. (The ERROR could |
| // be a THROW type). The error could also be CANNOT_EVALUATE_STRING. This means that the string was too |
| // complicated for the evaluator and needs to be compiled and tried again. (TBD) |
| // If the object created is not assignable to the type passed in, then |
| // an ERROR is returned with CLASS_CAST_EXCEPTION flag. |
| // |
| // GET_OBJECT_DATA: 14b, n |
| // Where n is the id of the object being requested. It will return a NEW_OBJECT_ID value with the info. |
| // |
| // GET_METHOD: 9b, classId, "methodName", n1, [n2]... |
| // Where classID is the id of the class the method should be found in. |
| // Where n1 is the number of parm types following, and n2 is replicated that many times, |
| // each entry is the id of class for the parm type. (0 is valid which means there are no parms). |
| // The return will be a VALUE command containing the OBJECT_ID of the method. |
| // |
| // GET_CTOR: 10b, classId, n1, [n2]... |
| // Where classID is the id of the class the method should be found in. |
| // Where n1 is the number of parm types following, and n2 is replicated that many times, |
| // each entry is the id of class for the parm type. (0 is valid which means there are no parms). |
| // The return will be a VALUE command containing the OBJECT_ID of the method. |
| // |
| // GET_FIELD: |
| // |
| // GET_CTOR: |
| // |
| // INVOKE: 15b, n1, tb, value1, value2 |
| // Where "n1" is the id of the method to invoke. |
| // tb, value1 is the value of who to invoke against (it is usually an OBJECT_ID for tb) |
| // value2 is an ARRAY_IDS type or an OBJECT array of values if all constants. |
| // What is returned is a VALUE command containing the return value, (the value will be null (VOID) if |
| // there is no return type (i.e. the method was void). So null can be returned either if the value |
| // was null or if the return type was void. |
| // |
| // EXPRESSION_TREE_COMMAND: 20b, n, b |
| // Receiving an expression tree subcommand. Where "n" is a unique id number of the |
| // expression being processed. Where "b" is byte code, defined in ExpressionCommands, that |
| // determines the type of expression tree commands. |
| // There can be more data following, but it is read by the |
| // ExpressionProcesserController, not by the connection. See the controller for the subcommands. |
| // |
| // The id number is the id of the expression being processed. This allows more than one expression |
| // to be processed at a time from this connection. |
| // |
| // @see ExpressionCommands |
| // @see ExpressionProcessController |
| // |
| // INVOKE_WITH_METHOD_PASSED: 20b, classId, "methodName", value0, tb, value1, value2 |
| // Where classID is the id of the class the method should be found in. |
| // value0 is an ARRAY_IDS type for the type of the parms, or null type for no parms. |
| // tb, value1 is the value of who to invoke against (it is usually an OBJECT_ID for tb) |
| // value2 is an ARRAY_IDS type or an OBJECT array of values if all constants. |
| // What is returned is a VALUE command containing the return value, (the value will be null (VOID) if |
| // there is no return type (i.e. the method was void). So null can be returned either if the value |
| // was null or if the return type was void. |
| // |
| // GET_ARRAY_CONTENTS: 21b, arrayId |
| // Where arrayID is the id of the array to get the contents of. What is returned is a value command |
| // containing an array of ids of the first dimension contents. |
| // |
| // Callback commands: |
| // |
| // CALLBACK: 255b, n1, n2, value1 |
| // Where |
| // "n1" is the id of callback type (these are registered with the callback server) |
| // "n2" is the msgId for the callback (These are entirely callback dependent and are maintained by the callback developer) |
| // value1 is an ARRAY_IDS type or an OBJECT array of values if all constants. These are |
| // parms to send to the callback msg. |
| // It will return a CALLBACK_DONE. |
| // |
| // CALLBACK_DONE: 254b, value command. |
| // What comes back is a value command (i.e. Commands.VALUE followed by value). This allows |
| // ERRORS to be sent back too. |
| // |
| // CALLBACK_STREAM: 253b, n1, n2 |
| // Where |
| // "n1" is the id of callback type (these are registered with the callback server) |
| // "n2" is the msgId for the callback (These are entirely callback dependent and are maintained by the callback developer) |
| // It will create a CallbackInputStream and notify the registered callback that the |
| // stream is available. It will send a callback_done when it has accepted the request |
| // but before it notifies the registered callback with the stream. This lets the remote |
| // vm know that it can start sending data. |
| |
| |
| // To the MasterServer socket: |
| // The MasterServer socket will expect input in DataInputStream format, and DataOutputStream for return. |
| // The socket will be short-lived. It will be for one transaction only. Each request will return a new socket. |
| // |
| // ALIVE: 16b, n1 |
| // Where |
| // "n1" is the id of the registry this is asking to test for aliveness |
| // Will return bool, where false if registry is not alive, true if it is alive. |
| // REMOTE_STARTED: 17b, n1, n2 |
| // Where |
| // "n1" is the id of the registry this is telling that it is started |
| // "n2" is the serversocket port number of the server socket in this remote vm. |
| // Will return bool, where false if registry is not alive, true if it is alive. If false, then terminate the server because nothing to talk to. |
| // GET_CALLBACK_PORT: 18b, n1 |
| // Where |
| // "n1" is the id of the registry this is asking for the callback server port. |
| // Will return int, where the value is the callback server port number. -1 if there is no callback server port. |
| |
| /** |
| * This class is the return from a read value. It contains the |
| * type of the value and the value itself. Since primitives can be |
| * returned also, there is a slot for each one and the type should |
| * be checked to see which one is set. |
| * |
| * Also, if the type is OBJECT, then the anObject has the object in it, AND |
| * the classID field has the object_id of the class of the object so that the |
| * appropriate beantypeproxy can be found to use that object. Also, only |
| * IREMConstantBeanTypeProxies can be of type OBJECT. That is because those |
| * are the only ones that know how to take the value object and interpret it. |
| * |
| * If the type is OBJECT_ID or NEW_OBJECT_ID, then the objectID field will be set with |
| * the id. |
| * If the type is NEW_OBJECT_ID, then the classID field will |
| * have the class objectID of the class of the object for which object_id proxies. |
| * |
| * THROW is treated like NEW_OBJECT_ID in what fields are set since it is a new object. |
| * |
| * Note: so as not to create unnecessary objects, if the Object type of the primitive is being |
| * sent, then the primitive field will be set instead, though the type |
| * will still be the Object type (i.e. if type = L_BYTE, the aByte will |
| * have the value in it). |
| * |
| * Note: Also flags can be send back. The type will be FLAG and the anInt field will be the |
| * flag value. This is used to indicate special things that aren't values. Most useful in |
| * arrays where one of the entries is not a value. This can only be used if readValue |
| * is passed a flag indicating flags are valid, otherwise it will be treated as not valie. |
| */ |
| public static class ValueObject implements Cloneable { |
| public byte type; // Same as the types above |
| public byte aByte; |
| public char aChar; |
| public double aDouble; |
| public float aFloat; |
| public int anInt; |
| public short aShort; |
| public long aLong; |
| public boolean aBool; |
| public int objectID; // The object id for either OBJECT_ID or NEW_OBJECT_ID. |
| public int classID; // The class object id of the value in Object if the type is Object |
| public Object anObject; // String also will be in here |
| |
| public ValueObject() { |
| type = VOID; |
| } |
| |
| public Object clone() { |
| try { |
| return super.clone(); |
| } catch (CloneNotSupportedException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Return whether the value stored here is a primitive. |
| * |
| * @return <code>true</code> if value is a primitive type. |
| * |
| * @since 1.0.0 |
| */ |
| public boolean isPrimitive() { |
| return getPrimitiveType().isPrimitive(); |
| } |
| |
| /** |
| * Get the primitive type of the value. |
| * @return The primitive type, or if not primitive, it returns simply <code>Object.class</code>. |
| * |
| * @since 1.0.0 |
| */ |
| public Class getPrimitiveType() { |
| switch (type) { |
| case BYTE: |
| return Byte.TYPE; |
| case CHAR: |
| return Character.TYPE; |
| case DOUBLE: |
| return Double.TYPE; |
| case FLOAT: |
| return Float.TYPE; |
| case INT: |
| return Integer.TYPE; |
| case SHORT: |
| return Short.TYPE; |
| case LONG: |
| return Long.TYPE; |
| case BOOL: |
| return Boolean.TYPE; |
| default: |
| return Object.class; |
| } |
| |
| } |
| |
| /** |
| * Get the type as one of the valid Commands.Types. VOID, BYTE, L_BYTE, etc. |
| * @return |
| * |
| * @since 1.1.0 |
| */ |
| public int getType() { |
| return type; |
| } |
| |
| /** |
| * Special getter to get the type as an Object, this is used by invoke for example. |
| */ |
| public Object getAsObject() { |
| switch (type) { |
| case VOID: |
| return null; |
| case BYTE: |
| case L_BYTE: |
| return new Byte(aByte); |
| case CHAR: |
| case L_CHAR: |
| return new Character(aChar); |
| case DOUBLE: |
| case L_DOUBLE: |
| return new Double(aDouble); |
| case FLOAT: |
| case L_FLOAT: |
| return new Float(aFloat); |
| case INT: |
| case L_INT: |
| return new Integer(anInt); |
| case SHORT: |
| case L_SHORT: |
| return new Short(aShort); |
| case LONG: |
| case L_LONG: |
| return new Long(aLong); |
| case BOOL: |
| case L_BOOL: |
| return aBool ? Boolean.TRUE : Boolean.FALSE; |
| case STRING: |
| return anObject; |
| case OBJECT: |
| return anObject; |
| |
| default: |
| return null; // Can't handle others. Those need to be checked before calling. |
| } |
| } |
| |
| /** |
| * Special setter to set the value depending upon the type. |
| */ |
| public void setAsObject(Object value, int valueClassID) { |
| switch (valueClassID) { |
| case VOID: |
| set(); |
| break; |
| case BYTE_CLASS: |
| set((Byte) value); |
| break; |
| case CHARACTER_CLASS: |
| set((Character) value); |
| break; |
| case DOUBLE_CLASS: |
| set((Double) value); |
| break; |
| case FLOAT_CLASS: |
| set((Float) value); |
| break; |
| case INTEGER_CLASS: |
| set((Integer) value); |
| break; |
| case SHORT_CLASS: |
| set((Short) value); |
| break; |
| case LONG_CLASS: |
| set((Long) value); |
| break; |
| case BOOLEAN_CLASS: |
| set((Boolean) value); |
| break; |
| case STRING_CLASS: |
| set((String) value); |
| break; |
| default: |
| set(value, valueClassID); |
| break; |
| } |
| } |
| |
| public void set() { |
| type = VOID; |
| anObject = null; |
| } |
| |
| public void setFlag(int flag) { |
| type = FLAG; |
| anInt = flag; |
| } |
| |
| public void set(byte value) { |
| type = BYTE; |
| aByte = value; |
| anObject = null; |
| } |
| public void set(Byte value) { |
| if (value != null) { |
| type = L_BYTE; |
| aByte = value.byteValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(char value) { |
| type = CHAR; |
| aChar = value; |
| anObject = null; |
| } |
| public void set(Character value) { |
| if (value != null) { |
| type = L_CHAR; |
| aChar = value.charValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(double value) { |
| type = DOUBLE; |
| aDouble = value; |
| anObject = null; |
| } |
| public void set(Double value) { |
| if (value != null) { |
| type = L_DOUBLE; |
| aDouble = value.doubleValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(float value) { |
| type = FLOAT; |
| aFloat = value; |
| anObject = null; |
| } |
| public void set(Float value) { |
| if (value != null) { |
| type = L_FLOAT; |
| aFloat = value.floatValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(int value) { |
| type = INT; |
| anInt = value; |
| anObject = null; |
| } |
| public void set(Integer value) { |
| if (value != null) { |
| type = L_INT; |
| anInt = value.intValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(short value) { |
| type = SHORT; |
| aShort = value; |
| anObject = null; |
| } |
| public void set(Short value) { |
| if (value != null) { |
| type = L_SHORT; |
| aShort = value.shortValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(long value) { |
| type = LONG; |
| aLong = value; |
| anObject = null; |
| } |
| public void set(Long value) { |
| type = L_LONG; |
| aLong = value.longValue(); |
| anObject = null; |
| } |
| public void set(boolean value) { |
| type = BOOL; |
| aBool = value; |
| anObject = null; |
| } |
| public void set(Boolean value) { |
| if (value != null) { |
| type = L_BOOL; |
| aBool = value.booleanValue(); |
| anObject = null; |
| } else |
| set(); |
| } |
| public void set(String value) { |
| if (value != null) { |
| type = STRING; |
| anObject = value; |
| } else |
| set(); |
| } |
| public void set(Object value, int classObjectID) { |
| if (value != null) { |
| type = OBJECT; |
| classID = classObjectID; |
| anObject = value; |
| } else |
| set(); |
| } |
| public void setObjectID(int value) { |
| type = OBJECT_ID; |
| objectID = value; |
| anObject = null; |
| } |
| |
| // Use this if the object is an array containing IDs. The retriever |
| // will be used to get the next value to write to the stream. |
| public void setArrayIDS(ValueRetrieve retriever, int arraySize, int componentType) { |
| type = ARRAY_IDS; |
| classID = componentType; |
| anInt = arraySize; |
| anObject = retriever; |
| } |
| |
| |
| // Use this if this is a new object so that we can get the correct class type. |
| public void setObjectID(int value, int classObjectID) { |
| type = NEW_OBJECT_ID; |
| objectID = value; |
| classID = classObjectID; |
| anObject = null; |
| } |
| |
| // Use this to indicate an exception occured. |
| public void setException(int throwID, int throwClassID) { |
| type = THROW; |
| objectID = throwID; |
| classID = throwClassID; |
| anObject = null; |
| } |
| } |
| |
| /************************ |
| * Helpful commands. |
| * - If a command throws any exception except CommandErrorException, or |
| * UnexpectedCommandException with recoverable true, then the connection is in a bad state |
| * and needs to be closed. |
| ************************/ |
| |
| /** |
| * Use this to read a value (inputstream should be pointing to the type byte as the next byte to read). |
| * The primitive fields of "value" will not be changed if they are not the |
| * type of the value being read. However, anObject will be set to null. |
| */ |
| |
| /** |
| * Error flags for UnexpectedCommandExceptions that can be thrown. |
| */ |
| public static final Object UNKNOWN_READ_TYPE = "UNKNOWN_READ_TYPE"; // The read type byte was not a valid type //$NON-NLS-1$ |
| public static final Object UNKNOWN_WRITE_TYPE = "UNKNOWN_WRITE_TYPE"; // The write type byte was not a valid type //$NON-NLS-1$ |
| public static final Object TYPE_INVALID_FOR_COMMAND = "TYPE_INVALID_FOR_COMMAND"; // The data type read is not valid for this command //$NON-NLS-1$ |
| public static final Object UNKNOWN_COMMAND = "UNKNOWN_COMMAND"; // The command flag is unknown //$NON-NLS-1$ |
| public static final Object SOME_UNEXPECTED_EXCEPTION = "SOME_UNEXPECTED_EXCEPTION"; // There was some kind of exception that wasn't expected. The data will be the exception. //$NON-NLS-1$ |
| public static final Object TOO_MANY_BYTES = "TOO_MANY_BYTES"; // Too many bytes were sent on a writeBytes. It was //$NON-NLS-1$ |
| // more than could be read into the buffer. The data will be the size sent. |
| |
| /** |
| * Read a value from the stream into the value object. It will not allow values of type FLAG. |
| * |
| * @param is |
| * @param value |
| * @param allowFlag |
| * @return the value object sent in. This allows <code>value = Commands.readValue(is, new Commands.ValueObject());</code> |
| * @throws CommandException |
| * |
| * |
| * @since 1.0.0 |
| */ |
| public static ValueObject readValue(DataInputStream is, ValueObject value) throws CommandException { |
| readValue(is, value, false); |
| return value; |
| } |
| |
| /** |
| * Read a value from the stream into the value object. It will allow values of type FLAG if allowFlag is true. |
| * @param is |
| * @param value |
| * @param allowFlag <code>true</code> if values of type flag are allow. |
| * @throws CommandException |
| * |
| * @since 1.1.0 |
| */ |
| public static void readValue(DataInputStream is, ValueObject value, boolean allowFlag) throws CommandException { |
| try { |
| value.anObject = null; |
| value.type = is.readByte(); |
| switch (value.type) { |
| case BYTE: |
| case L_BYTE: |
| value.aByte = is.readByte(); |
| break; |
| case CHAR: |
| case L_CHAR: |
| value.aChar = is.readChar(); |
| break; |
| case DOUBLE: |
| case L_DOUBLE: |
| value.aDouble = is.readDouble(); |
| break; |
| case FLOAT: |
| case L_FLOAT: |
| value.aFloat = is.readFloat(); |
| break; |
| case INT: |
| case L_INT: |
| value.anInt = is.readInt(); |
| break; |
| case OBJECT_ID: |
| value.objectID = is.readInt(); |
| break; |
| case NEW_OBJECT_ID: |
| value.classID = is.readInt(); |
| value.objectID = is.readInt(); |
| break; |
| case THROW: |
| value.classID = is.readInt(); |
| value.objectID = is.readInt(); |
| break; |
| case SHORT: |
| case L_SHORT: |
| value.aShort = is.readShort(); |
| break; |
| case LONG: |
| case L_LONG: |
| value.aLong = is.readLong(); |
| break; |
| case BOOL: |
| case L_BOOL: |
| value.aBool = is.readBoolean(); |
| break; |
| case STRING: |
| value.anObject = readStringData(is); |
| break; |
| case OBJECT: |
| value.classID = is.readInt(); // Read the class id |
| ObjectInputStream oi = new ObjectInputStream(is); |
| value.anObject = oi.readObject(); // Read the object itself |
| oi = null; // Don't close it, that would close the stream itself. |
| break; |
| case ARRAY_IDS: |
| // The header for an array of ids. |
| value.classID = is.readInt(); // The component type of the array |
| value.anInt = is.readInt(); // The size of the array. |
| // At this point, it is the responsibility of the caller to use readArray to read in the array. |
| break; |
| case VOID: |
| break; |
| case FLAG: |
| if (allowFlag) { |
| value.anInt = is.readInt(); |
| break; |
| } |
| // Flags not allowed, so drop into default. |
| default: |
| throw new UnexpectedCommandException(UNKNOWN_READ_TYPE, false, new Byte(value.type)); |
| } |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /** |
| * Special interface used to read back arrays. It will be called when |
| */ |
| public static interface ValueSender { |
| /** |
| * This is called for each entry from the array. It is assumed that the ValueSender has |
| * the array that is being built. |
| * @param value |
| * |
| * @since 1.1.0 |
| */ |
| public void sendValue(ValueObject value); |
| |
| /** |
| * This is called when an ARRAY_IDS is found within the reading of the array (i.e. nested arrays) |
| * It is asking for a new ValueSender to use while this nested array. The arrayValue contains |
| * the valueobject for the array header (i.e. the class id of the array and the number of elements). |
| * It is the responsibility of the ValueSender to store this array in the array that is being built. |
| * @param arrayValue |
| * @return |
| * |
| * @since 1.1.0 |
| */ |
| public ValueSender nestedArray(ValueObject arrayValue); |
| |
| /** |
| * Called to initialize the sender with the given array header. This is not always called, each usage |
| * knows whether it can be called or not. For example the implementation of nestedArray may not need to call initialize. |
| * @param arrayHeader |
| * |
| * @since 1.1.0 |
| */ |
| public void initialize(ValueObject arrayHeader); |
| |
| } |
| |
| /* |
| * NOTE: It is important that on the IDE side that this is called within a transaction. |
| * If not, there could be some corruption if proxy cleanup occurs in the middle. |
| */ |
| public static void readArray(DataInputStream is, int arraySize, ValueSender valueSender, ValueObject value, boolean allowFlag) throws CommandException { |
| // Anything exception other than a CommandException, we will try to flush the input so that |
| // it can continue with the next command and not close the connection. |
| RuntimeException exception = null; |
| for (int i=0; i<arraySize; i++) { |
| readValue(is, value, allowFlag); |
| if (exception == null) |
| try { |
| if (value.type != ARRAY_IDS) |
| valueSender.sendValue(value); |
| else { |
| // We have a nested array. |
| ValueSender nestedSender = null; |
| try { |
| nestedSender = valueSender.nestedArray(value); |
| } catch (RuntimeException e) { |
| // We still need to read in the array to flush. Create |
| // a dummy sender that accepts everything sent to it. |
| exception = e; |
| nestedSender = new ValueSender() { |
| public void sendValue(ValueObject value) { |
| } |
| public ValueSender nestedArray(ValueObject arrayValue) { |
| return this; |
| } |
| public void initialize(ValueObject arrayHeader) { |
| } |
| }; |
| } |
| readArray(is, value.anInt, nestedSender, value, allowFlag); |
| if (exception != null) |
| throw exception; // An exception ocurred in new sender request. |
| } |
| } catch (RuntimeException e) { |
| // We want to flush the queue, so save the exception for later. |
| exception = e; |
| } |
| } |
| if (exception != null) |
| throw exception; |
| } |
| |
| |
| /** |
| * Special interface to handle writing the ARRAY_IDS type. |
| * An instance of this object will be in the valueObject sent to writeValue when the type of the value |
| * is ARRAY_IDS. Then write value will know to call this interface to write out the values. |
| * |
| * @since 1.1.0 |
| */ |
| public static interface ValueRetrieve { |
| /** |
| * Returns the next value object to send. It will be called the number of times that the size of |
| * the array was set to be send. |
| * @return The value object to send. |
| * @throws EOFException |
| * |
| * @since 1.1.0 |
| */ |
| public ValueObject nextValue() throws EOFException; |
| } |
| |
| public static void writeValue(DataOutputStream os, ValueObject value, boolean asValueCommand) throws CommandException { |
| writeValue(os, value, asValueCommand, asValueCommand ? true : false); |
| } |
| |
| public static void writeValue(DataOutputStream os, ValueObject value, boolean asValueCommand, boolean flush) throws CommandException { |
| try { |
| if (asValueCommand) |
| os.writeByte(VALUE); |
| switch (value.type) { |
| case BYTE: |
| case L_BYTE: |
| os.writeByte(value.type); |
| os.writeByte(value.aByte); |
| break; |
| case CHAR: |
| case L_CHAR: |
| os.writeByte(value.type); |
| os.writeChar(value.aChar); |
| break; |
| case DOUBLE: |
| case L_DOUBLE: |
| os.writeByte(value.type); |
| os.writeDouble(value.aDouble); |
| break; |
| case FLOAT: |
| case L_FLOAT: |
| os.writeByte(value.type); |
| os.writeFloat(value.aFloat); |
| break; |
| case INT: |
| case L_INT: |
| os.writeByte(value.type); |
| os.writeInt(value.anInt); |
| break; |
| case OBJECT_ID: |
| os.writeByte(value.type); |
| os.writeInt(value.objectID); |
| break; |
| case NEW_OBJECT_ID: |
| os.writeByte(value.type); |
| os.writeInt(value.classID); |
| os.writeInt(value.objectID); |
| break; |
| case THROW: |
| os.writeByte(value.type); |
| os.writeInt(value.classID); |
| os.writeInt(value.objectID); |
| break; |
| case SHORT: |
| case L_SHORT: |
| os.writeByte(value.type); |
| os.writeShort(value.aShort); |
| break; |
| case LONG: |
| case L_LONG: |
| os.writeByte(value.type); |
| os.writeLong(value.aLong); |
| break; |
| case BOOL: |
| case L_BOOL: |
| os.writeByte(value.type); |
| os.writeBoolean(value.aBool); |
| break; |
| case STRING: |
| os.writeByte(value.type); |
| sendStringData(os, (String) value.anObject); |
| break; |
| case OBJECT: |
| os.writeByte(value.type); |
| os.writeInt(value.classID); // Write the class ID. |
| ObjectOutputStream oos = new ObjectOutputStream(os); |
| oos.writeObject(value.anObject); |
| oos.flush(); |
| oos = null; // Don't close it, that would close the stream itself. |
| break; |
| case ARRAY_IDS: |
| // We are writing out an array with ID's in it. The fields of the vale object will be: |
| // classID: The class id of the component type of the array. |
| // anObject: Contains the ValueRetriever to get the next value. |
| os.writeByte(ARRAY_IDS); |
| os.writeInt(value.classID); |
| os.writeInt(value.anInt); // The size of the array. |
| // Now comes the kludgy part, writing the values. |
| ValueRetrieve retriever = (ValueRetrieve) value.anObject; |
| int len = value.anInt; |
| while (len-- > 0) |
| writeValue(os, retriever.nextValue(), false); |
| break; |
| case VOID: |
| os.writeByte(value.type); |
| break; |
| case FLAG: |
| os.writeByte(FLAG); |
| os.writeInt(value.anInt); |
| break; |
| default: |
| os.writeByte(VOID); |
| throw new UnexpectedCommandException(UNKNOWN_WRITE_TYPE, true, value); |
| } |
| if (flush) |
| os.flush(); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /** |
| * For reading a large number of bytes. This is a value type, not a command. The command |
| * needs to be handled separately. It returns the number of bytes read. -1 if there |
| * is no more data to send and the stream should closed. If read something but not all, |
| * then just what it could read will be returned. The next read will return -1 for EOF. |
| * |
| * It will read from the format: |
| * int - number of bytes to read (retrieved from the stream). |
| * bytes - the actual bytes. |
| * |
| * Note: A command exception will be thrown if the number of bytes to read |
| * is larger than the size of the byte array. |
| */ |
| public static int readBytes(DataInputStream is, byte[] bytes) throws CommandException { |
| try { |
| int bytesToRead = -1; |
| try { |
| bytesToRead = is.readInt(); |
| } catch (EOFException e) { |
| } |
| if (bytesToRead == -1) |
| return -1; |
| if (bytesToRead > bytes.length) |
| throw new UnexpectedCommandException(TOO_MANY_BYTES, false, new Integer(bytesToRead)); |
| int start = 0; |
| int toRead = bytesToRead; |
| while (toRead > 0) { |
| int bytesRead = is.read(bytes, start, toRead); |
| if (bytesRead == -1) |
| return bytesToRead != toRead ? bytesToRead-toRead : -1; // Actual number read, or if none read, then EOF |
| start+=bytesRead; |
| toRead-=bytesRead; |
| } |
| return bytesToRead; |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /** |
| * For writing a large number of bytes. This is a value type, not a command. The command |
| * needs to be handled separately. |
| * |
| * It will write it in the format: |
| * int - number of bytes |
| * bytes - the actual bytes. |
| * |
| */ |
| public static void writeBytes(DataOutputStream os, byte[] bytes, int bytesToWrite) throws CommandException { |
| try { |
| os.writeInt(bytesToWrite); |
| os.write(bytes, 0, bytesToWrite); |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /************************ |
| * Send command helpers |
| ************************/ |
| |
| public static void sendQuitCommand(DataOutputStream os) throws IOException { |
| os.writeByte(QUIT_CONNECTION); |
| os.flush(); |
| |
| } |
| |
| public static void sendTerminateCommand(DataOutputStream os) throws IOException { |
| os.writeByte(TERMINATE_SERVER); |
| os.flush(); |
| } |
| |
| |
| public static void releaseObjectCommand(DataOutputStream os, int id) throws IOException { |
| os.writeByte(Commands.RELEASE_OBJECT); |
| os.writeInt(id); |
| os.flush(); |
| } |
| |
| /** |
| * Send a callback request. The value is to be send separately. |
| */ |
| public static void sendCallbackCommand(DataOutputStream os, int callbackID, int msgID) throws CommandException { |
| try { |
| os.writeByte(CALLBACK); |
| os.writeInt(callbackID); |
| os.writeInt(msgID); |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendCallbackDoneCommand(DataOutputStream os, ValueObject value, int errorCode) throws CommandException { |
| try { |
| os.writeByte(CALLBACK_DONE); |
| if (errorCode == NO_ERROR) { |
| writeValue(os, value, true); |
| os.flush(); |
| } else |
| sendErrorCommand(os, errorCode, value); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /** |
| * Send a start callback stream request. The data will be written separately. |
| * There will not be a callback done command. It will return as soon as the command |
| * is sent. |
| */ |
| public static void sendCallbackStreamCommand(DataOutputStream os, int callbackID, int msgID) throws CommandException { |
| try { |
| os.writeByte(CALLBACK_STREAM); |
| os.writeInt(callbackID); |
| os.writeInt(msgID); |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| protected static final byte STRING_NOT_CHUNKED = 0; |
| protected static final byte STRING_CHUNKED = 1; |
| protected static final byte MORE_CHUNKS = 2; |
| protected static final byte LAST_CHUNK = 3; |
| protected static final int CHUNK_SIZE = 65000/3; |
| |
| /** |
| * Send string data. This is for general string data. It makes sure that if the string is too big (there is a UTF-8 limit) |
| * that it will send it in chunks. Use the corresponding <code>readStringData</code> to read such data in. |
| * @param os |
| * @param string |
| * @throws IOException |
| * |
| * @since 1.0.0 |
| */ |
| public static void sendStringData(DataOutputStream os, String string) throws IOException { |
| // UTF-8 can take up to three bytes for each char. To be on safe side we will |
| // not send a string larger than 65K/3 as one chunk. |
| if (string.length() <= CHUNK_SIZE) { |
| // Less than the limit, send byte to indicate not chunked. |
| os.writeByte(STRING_NOT_CHUNKED); |
| os.writeUTF(string); |
| } else { |
| // Over limit, need to chunk it. |
| // send byte to indicate chunked, then send true length so that other side knows how big to create. |
| os.writeByte(STRING_CHUNKED); |
| os.writeInt(string.length()); |
| // Now send first chunk |
| for(int i=0; i<string.length(); i+=CHUNK_SIZE) { |
| int endIndex = i+CHUNK_SIZE; |
| if (i == 0) { |
| // The first chunk is just written as is. We know endIndex will be ok because we wouldn't get here unless LARGER than chunksize. |
| os.writeUTF(string.substring(i, endIndex)); |
| } else { |
| // Following chunks have either byte MORE_CHUNKS or LAST_CHUNK |
| if (endIndex >= string.length()) { |
| // This is last chunk. |
| os.writeByte(LAST_CHUNK); |
| os.writeUTF(string.substring(i)); |
| } else { |
| // This is an intermediate chunk. |
| os.writeByte(MORE_CHUNKS); |
| os.writeUTF(string.substring(i, endIndex)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Read a string that was sent using the sendStringData command. |
| * @param in |
| * @return |
| * @throws IOException |
| * |
| * @since 1.0.0 |
| */ |
| public static String readStringData(DataInputStream is) throws IOException { |
| byte chunked = is.readByte(); |
| if (chunked == STRING_NOT_CHUNKED) |
| return is.readUTF(); // Not chunk, just read it. |
| else { |
| // It is chunked. |
| int totalLength = is.readInt(); // Get the total length. |
| StringBuffer sbr = new StringBuffer(totalLength); |
| while(true) { |
| sbr.append(is.readUTF()); |
| if (chunked != LAST_CHUNK) |
| chunked = is.readByte(); |
| else |
| break; |
| } |
| return sbr.toString(); |
| } |
| } |
| |
| /** |
| * Read back command, expecting either a VALUE or an ERROR. You can request that |
| * it be of a specific type (if any type can be accepted then enter -1 for the type). |
| */ |
| public static final byte NO_TYPE_CHECK = -1; |
| public static void readBackValue(DataInputStream is, ValueObject value, byte expectedType) throws CommandException { |
| try { |
| byte v = is.readByte(); |
| switch (v) { |
| case VALUE: |
| readValue(is, value); |
| if (expectedType != NO_TYPE_CHECK && |
| !(expectedType == value.type || (expectedType == Commands.OBJECT_ID && value.type == NEW_OBJECT_ID))) |
| throw new UnexpectedCommandException(TYPE_INVALID_FOR_COMMAND, true, value); |
| break; |
| case ERROR: |
| int code = is.readInt(); |
| readValue(is, value); |
| throw new CommandErrorException(code, value); |
| default: |
| throw new UnexpectedCommandException(UNKNOWN_COMMAND, false, new Byte(v)); |
| } |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| |
| /** |
| * GetClass command returns a GetClassReturn object. |
| */ |
| public static class GetClassReturn { |
| public int classID; |
| public boolean isInterface; |
| public boolean isAbstract; |
| public String superClassname; |
| } |
| |
| public static GetClassReturn sendGetClassCommand(DataOutputStream os, DataInputStream is, String className) throws CommandException { |
| try { |
| os.writeByte(GET_CLASS); |
| os.writeUTF(className); |
| os.flush(); |
| |
| GetClassReturn ret = new GetClassReturn(); |
| byte v = is.readByte(); |
| switch (v) { |
| case GET_CLASS_RETURN: |
| ret.classID = is.readInt(); // Get the new class id. |
| ret.isInterface = is.readBoolean(); // Get the isInterface flag |
| ret.isAbstract = is.readBoolean(); // Get the isAbstract flag. |
| ret.superClassname = is.readUTF(); // Get the super class name. |
| return ret; |
| case ERROR: |
| int code = is.readInt(); |
| ValueObject value = new ValueObject(); |
| readValue(is, value); |
| throw new CommandErrorException(code, value); |
| default: |
| throw new UnexpectedCommandException(UNKNOWN_COMMAND, false, new Byte(v)); |
| } |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| /** |
| * GetClassFromID command returns a GetClassIDReturn object. |
| */ |
| public static class GetClassIDReturn { |
| public String className; |
| public boolean isInterface; |
| public boolean isAbstract; |
| public String superClassname; |
| } |
| |
| public static GetClassIDReturn sendGetClassFromIDCommand(DataOutputStream os, DataInputStream is, int classID) throws CommandException { |
| try { |
| os.writeByte(GET_CLASS_FROM_ID); |
| os.writeInt(classID); |
| os.flush(); |
| |
| GetClassIDReturn ret = new GetClassIDReturn(); |
| byte v = is.readByte(); |
| switch (v) { |
| case GET_CLASS_ID_RETURN: |
| ret.className = is.readUTF(); // Get the new class name. |
| ret.isInterface = is.readBoolean(); // Get the isInterface flag |
| ret.isAbstract = is.readBoolean(); // Get the isAbstract flag. |
| ret.superClassname = is.readUTF(); // Get the super class name. |
| return ret; |
| case ERROR: |
| int code = is.readInt(); |
| ValueObject value = new ValueObject(); |
| readValue(is, value); |
| throw new CommandErrorException(code, value); |
| default: |
| throw new UnexpectedCommandException(UNKNOWN_COMMAND, false, new Byte(v)); |
| } |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendGetObjectData(DataOutputStream os, DataInputStream is, int objectID, ValueObject valueReturn) throws CommandException { |
| try { |
| os.writeByte(GET_OBJECT_DATA); |
| os.writeInt(objectID); |
| os.flush(); |
| readBackValue(is, valueReturn, NEW_OBJECT_ID); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendErrorCommand(DataOutputStream os, int code, ValueObject errorValue) throws CommandException { |
| try { |
| os.writeByte(ERROR); |
| os.writeInt(code); |
| writeValue(os, errorValue, false); |
| os.flush(); |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendNewInstance(DataOutputStream os, DataInputStream is, int classId, String initializationString, ValueObject newValueReturn) throws CommandException { |
| try { |
| os.writeByte(NEW_INIT_STRING); |
| os.writeInt(classId); |
| os.writeUTF(initializationString); |
| os.flush(); |
| readBackValue(is, newValueReturn, NO_TYPE_CHECK); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| |
| public static void sendInvokeMethodCommand(DataOutputStream os, DataInputStream is, int methodID, ValueObject invokeOn, ValueObject parms, ValueObject valueReturn) throws CommandException { |
| try { |
| os.writeByte(INVOKE); |
| os.writeInt(methodID); |
| writeValue(os, invokeOn, false); |
| writeValue(os, parms, false); |
| os.flush(); |
| readBackValue(is, valueReturn, NO_TYPE_CHECK); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendInvokeMethodCommand(DataOutputStream os, DataInputStream is, ValueObject classType, String methodName, ValueObject parmTypes, ValueObject invokeOn, ValueObject parms, ValueObject valueReturn) throws CommandException { |
| try { |
| os.writeByte(INVOKE_WITH_METHOD_PASSED); |
| writeValue(os, classType, false); |
| os.writeUTF(methodName); |
| writeValue(os, parmTypes, false); |
| writeValue(os, invokeOn, false); |
| writeValue(os, parms, false); |
| os.flush(); |
| readBackValue(is, valueReturn, NO_TYPE_CHECK); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| public static void sendGetArrayContentsCommand(DataOutputStream os, DataInputStream is, int arrayID, ValueObject valueReturn) throws CommandException { |
| try { |
| os.writeByte(GET_ARRAY_CONTENTS); |
| os.writeInt(arrayID); |
| os.flush(); |
| readBackValue(is, valueReturn, NO_TYPE_CHECK); |
| } catch (CommandException e) { |
| // rethrow this exception since we want these to go on out. |
| throw e; |
| } catch (Exception e) { |
| // Wrapper this one. |
| throw new UnexpectedExceptionCommandException(false, e); |
| } |
| } |
| |
| private Commands() { |
| // Never intended to be instantiated. |
| } |
| } |