| /* Vio.java -- Value type IO operations. |
| Copyright (C) 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.CORBA.CDR; |
| |
| import gnu.CORBA.Minor; |
| import gnu.CORBA.ObjectCreator; |
| |
| import org.omg.CORBA.CustomMarshal; |
| import org.omg.CORBA.DataInputStream; |
| import org.omg.CORBA.DataOutputStream; |
| import org.omg.CORBA.MARSHAL; |
| import org.omg.CORBA.NO_IMPLEMENT; |
| import org.omg.CORBA.StringSeqHelper; |
| import org.omg.CORBA.StringValueHelper; |
| import org.omg.CORBA.SystemException; |
| import org.omg.CORBA.WStringValueHelper; |
| import org.omg.CORBA.portable.BoxedValueHelper; |
| import org.omg.CORBA.portable.InputStream; |
| import org.omg.CORBA.portable.OutputStream; |
| import org.omg.CORBA.portable.Streamable; |
| import org.omg.CORBA.portable.ValueFactory; |
| |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Modifier; |
| import java.util.StringTokenizer; |
| |
| import javax.rmi.CORBA.Util; |
| import javax.rmi.CORBA.ValueHandler; |
| |
| /** |
| * A specialised class for reading and writing the value types. |
| * |
| * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) |
| */ |
| public abstract class Vio |
| { |
| /** |
| * If true, wrap value type data into chunks. This decrease the performance, |
| * and is not required for interoperability with jdk 1.5, but is left in the |
| * implementation as the optional mode for solving possible interoperability |
| * problems with non-Sun CORBA implementations. |
| * |
| * The current implementation would accept both single chunk or multiple |
| * chunks, but will always send a single chunk (if true) or unchunked data (if |
| * false). |
| */ |
| public static boolean USE_CHUNKING = false; |
| |
| /** |
| * The first field in the value record. The last octet may contain additional |
| * flags (vf_CODEBASE, vf_ID and vf_MULTIPLE_IDS). The tag value is different |
| * for the indirections (vt_INDIRECTION) and nulls (vt_NULL). |
| */ |
| public static final int vt_VALUE_TAG = 0x7fffff00; |
| |
| /** |
| * The value tag flag, indicating that the codebase URL is present in the |
| * value tag record. |
| */ |
| public static final int vf_CODEBASE = 0x1; |
| |
| /** |
| * The value tag flag, indicating that a single repository id is present in |
| * the value tag record. |
| */ |
| public static final int vf_ID = 0x2; |
| |
| /** |
| * The value tag flag, indicating, that there are multiple repository ids |
| * present in the record. If this flag is set, the flag vf_ID must also be |
| * set, resulting the value of the least significant byte 0x6. |
| */ |
| public static final int vf_MULTIPLE_IDS = 0x4; |
| |
| /** |
| * The value tag flag, indicating the presence of chunking. Each chunk is |
| * preceeded by a positive int, indicating the number of bytes in the chunk. A |
| * sequence of chunks is terminated by a non positive int. |
| */ |
| public static final int vf_CHUNKING = 0x8; |
| |
| /** |
| * The indirection tag value. Such tag must be followed by the CORBA long, |
| * indicating the offset in the CORBA message, where the indirected |
| * information is present. This offset is assumed zero at the position where |
| * the mentioned CORBA long starts and can refer both forward (positive |
| * values) and backward (negative values). |
| */ |
| public static final int vt_INDIRECTION = 0xffffffff; |
| |
| /** |
| * This tag value means that the value object being transferred is equal to |
| * null. |
| */ |
| public static final int vt_NULL = 0x0; |
| |
| /** |
| * The size of CORBA long (java int). |
| */ |
| static final int INT_SIZE = 4; |
| |
| /** |
| * The String value helper (one instance is sufficient). |
| */ |
| public static final WStringValueHelper m_StringValueHelper = new WStringValueHelper(); |
| |
| /** |
| * An instance of the value handler. |
| */ |
| static ValueHandler handler = Util.createValueHandler(); |
| |
| /** |
| * Read the value base from the given input stream. Determines the required |
| * class from the repository id. This includes operations that are not |
| * required when an unitialised instance or at least class of the value type |
| * is known. Hence it may be faster to use the alternative methods, |
| * read(InputStream, Class) or read(InputStream, Serializable). |
| * |
| * @param input a stream to read from. |
| * @param repository_id a repository id of the object being read, may be null. |
| * |
| * @return the loaded value. |
| * |
| * @throws MARSHAL if the reading has failed due any reason. |
| */ |
| public static Serializable read(InputStream input) |
| { |
| return read(input, (String) null); |
| } |
| |
| /** |
| * Read the value base from the given input stream. Determines the required |
| * class from the repository id. This includes operations that are not |
| * required when an unitialised instance or at least class of the value type |
| * is known. Hence it may be faster to use the alternative methods, |
| * read(InputStream, Class) or read(InputStream, Serializable). |
| * |
| * @param an_input a stream to read from. |
| * @param repository_id a repository id of the object being read, may be null. |
| * |
| * @return the loaded value. |
| * |
| * @throws MARSHAL if the reading has failed due any reason. |
| */ |
| public static Serializable read(InputStream input, String repository_id) |
| { |
| try |
| { |
| final int position = getCurrentPosition(input); |
| // We may need to jump back if the value is read via value factory. |
| input.mark(512); |
| |
| int value_tag = input.read_long(); |
| checkTag(value_tag); |
| |
| String codebase = null; |
| String[] ids = null; |
| String id = repository_id; |
| |
| // Check for the agreed null value. |
| if (value_tag == vt_NULL) |
| return null; |
| else if (value_tag == vt_INDIRECTION) |
| return readIndirection(input); |
| else |
| { |
| // Read the value. |
| if ((value_tag & vf_CODEBASE) != 0) |
| { |
| // The codebase is present. The codebase is a space |
| // separated list of URLs from where the implementing |
| // code can be downloaded. |
| codebase = read_string(input); |
| } |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0) |
| { |
| // Multiple supported repository ids are present. |
| ids = read_string_array(input); |
| } |
| else if ((value_tag & vf_ID) != 0) |
| { |
| // Single supported repository id is present. |
| id = read_string(input); |
| } |
| } |
| |
| BoxedValueHelper helper = getHelper(null, id); |
| // The existing implementing object. |
| java.lang.Object ox = null; |
| |
| if (helper != null) |
| ox = null; // Helper will care about the instantiating. |
| else if (id.equals(WStringValueHelper.id())) |
| helper = m_StringValueHelper; |
| else |
| ox = createInstance(id, ids, codebase); |
| return (Serializable) read_instance(input, position, ox, value_tag, |
| helper, id, ids, codebase); |
| } |
| catch (Exception ex) |
| { |
| MARSHAL m = new MARSHAL(); |
| m.minor = Minor.Value; |
| m.initCause(ex); |
| throw m; |
| } |
| } |
| |
| /** |
| * Read the value base from the given input stream when the value base class |
| * is available. Hence there is no need to guess it from the repository id. |
| * |
| * @param input a stream to read from. |
| * @param value_class the class of the value being read. |
| * |
| * @return the loaded value. |
| * |
| * @throws MARSHAL if the reading has failed due any reason. |
| */ |
| public static Serializable read(InputStream input, Class value_class) |
| { |
| final int position = getCurrentPosition(input); |
| |
| String id = null; |
| String[] ids = null; |
| String codebase = null; |
| |
| try |
| { |
| int value_tag = input.read_long(); |
| checkTag(value_tag); |
| |
| // Check for the agreed null value. |
| if (value_tag == vt_NULL) |
| return null; |
| else if (value_tag == vt_INDIRECTION) |
| return readIndirection(input); |
| else |
| { |
| // Read the value. |
| if ((value_tag & vf_CODEBASE) != 0) |
| { |
| // The codebase is present. |
| codebase = read_string(input); |
| } |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0) |
| { |
| // Multiple supported repository ids are present. |
| ids = read_string_array(input); |
| } |
| else if ((value_tag & vf_ID) != 0) |
| { |
| // Single supported repository id is present. |
| id = read_string(input); |
| } |
| } |
| |
| BoxedValueHelper vHelper = id != null ? getHelper(value_class, id) |
| : getHelper(value_class, ids); |
| |
| java.lang.Object ox; |
| |
| if (vHelper == null) |
| { |
| try |
| { |
| ox = createInstance(id, ids, codebase); |
| } |
| catch (Exception e) |
| { |
| ox = null; |
| } |
| |
| if (ox != null) |
| { |
| if (value_class != null |
| && !value_class.isAssignableFrom(ox.getClass())) |
| { |
| MARSHAL m = new MARSHAL(ox.getClass() + " is not a " |
| + value_class.getName()); |
| m.minor = Minor.ClassCast; |
| throw m; |
| } |
| } |
| } |
| else |
| ox = null; |
| |
| ox = read_instance(input, position, ox, value_tag, vHelper, id, ids, |
| codebase); |
| return (Serializable) ox; |
| } |
| catch (MARSHAL m) |
| { |
| throw m; |
| } |
| catch (SystemException sysEx) |
| { |
| // OK. |
| throw sysEx; |
| } |
| catch (Exception ex) |
| { |
| MARSHAL m = new MARSHAL("Cant read " + value_class); |
| m.minor = Minor.Value; |
| m.initCause(ex); |
| throw m; |
| } |
| } |
| |
| /** |
| * Read the value base from the given input stream when the unitialised |
| * instance is available. Hence there is no need to guess the class from the |
| * repository id and then to instantiate an instance. |
| * |
| * @param input a stream to read from. |
| * |
| * @param value_instance an pre-created instance of the value. If the helper |
| * is not null, this parameter is ignored an should be null. |
| * |
| * @param helper a helper to create an instance and read the object- specific |
| * part of the record. If the value_instance is used instead, this parameter |
| * should be null. |
| * |
| * @return the loaded value. |
| * |
| * @throws MARSHAL if the reading has failed due any reason. |
| */ |
| public static Object read(InputStream input, Object value_instance, |
| BoxedValueHelper helper) |
| { |
| final int position = getCurrentPosition(input); |
| |
| String id = null; |
| String[] ids = null; |
| String codebase = null; |
| |
| try |
| { |
| int value_tag = input.read_long(); |
| checkTag(value_tag); |
| |
| // Check for the agreed null value. |
| if (value_tag == vt_NULL) |
| return null; |
| else if (value_tag == vt_INDIRECTION) |
| return readIndirection(input); |
| else |
| { |
| // Read the value. |
| if ((value_tag & vf_CODEBASE) != 0) |
| { |
| // The codebase is present. |
| codebase = read_string(input); |
| } |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0) |
| { |
| // Multiple supported repository ids are present. |
| ids = read_string_array(input); |
| } |
| else if ((value_tag & vf_ID) != 0) |
| { |
| // Single supported repository id is present. |
| id = read_string(input); |
| } |
| } |
| |
| Class value_class = value_instance == null ? null |
| : value_instance.getClass(); |
| |
| if (helper == null) |
| helper = id != null ? getHelper(value_class, id) : getHelper( |
| value_class, ids); |
| |
| value_instance = read_instance(input, position, value_instance, |
| value_tag, helper, id, ids, codebase); |
| return value_instance; |
| } |
| catch (Exception ex) |
| { |
| MARSHAL m = new MARSHAL(); |
| m.minor = Minor.Value; |
| m.initCause(ex); |
| throw m; |
| } |
| } |
| |
| /** |
| * Read using provided boxed value helper. This method expects the full value |
| * type header, followed by contents, that are delegated to the provided |
| * helper. It handles null. |
| * |
| * @param input the stream to read from. |
| * @param helper the helper that reads the type-specific part of the content. |
| * |
| * @return the value, created by the helper, or null if the header indicates |
| * that null was previously written. |
| */ |
| public static Serializable read(InputStream input, BoxedValueHelper helper) |
| { |
| return (Serializable) read(input, null, helper); |
| } |
| |
| /** |
| * Fill in the instance fields by the data from the input stream. The method |
| * assumes that the value header, if any, is already behind. The information |
| * from the stream is stored into the passed ox parameter. |
| * |
| * @param input an input stream to read from. |
| * |
| * @param value a pre-instantiated value type object, must be either |
| * Streamable or CustomMarshal. If the helper is used, this parameter is |
| * ignored and should be null. |
| * |
| * @param value_tag the tag that must be read previously. |
| * @param helper the helper for read object specific part; may be null to read |
| * in using other methods. |
| * |
| * @return the value that was read. |
| */ |
| static Object read_instance(InputStream input, final int position, |
| Object value, int value_tag, BoxedValueHelper helper, String id, |
| String[] ids, String codebase) |
| { |
| if (helper != m_StringValueHelper && id != null) |
| if (id.equals(StringValueHelper.id())) |
| { |
| value = null; |
| helper = m_StringValueHelper; |
| } |
| |
| try |
| { |
| if ((value_tag & vf_CHUNKING) != 0) |
| { |
| BufferedCdrOutput output = createBuffer(input, 1024); |
| // Read the current (not a nested one) value in this spec case. |
| readNestedValue(value_tag, input, output, -1); |
| BufferredCdrInput ci = new BufferredCdrInput(output.buffer.getBuffer()); |
| ci.setRunTime(output.getRunTime()); |
| |
| input = new HeadlessInput(ci, input); |
| } |
| else |
| { |
| if (input instanceof BufferredCdrInput) |
| { |
| // Highly probable case. |
| input = new HeadlessInput((BufferredCdrInput) input, null); |
| } |
| else if (input instanceof HeadlessInput) |
| { |
| // There is no need to instantiate one more HeadlessInput |
| // as we can just reset. |
| ((HeadlessInput) input).subsequentCalls = false; |
| } |
| else |
| { |
| BufferedCdrOutput bout = new BufferedCdrOutput(); |
| int c; |
| while ((c = input.read()) >= 0) |
| bout.write((byte) c); |
| input = new HeadlessInput( |
| (BufferredCdrInput) bout.create_input_stream(), input); |
| } |
| } |
| } |
| catch (IOException ex) |
| { |
| MARSHAL m = new MARSHAL("Unable to read chunks"); |
| m.minor = Minor.Value; |
| m.initCause(ex); |
| throw m; |
| } |
| |
| return readValue(input, position, value, helper, id, ids, codebase); |
| } |
| |
| /** |
| * Create a buffer, inheriting critical settings from the passed input stream. |
| */ |
| private static BufferedCdrOutput createBuffer(InputStream input, int proposed_size) |
| { |
| BufferedCdrOutput bout; |
| bout = new BufferedCdrOutput(2 * proposed_size + 256); |
| |
| if (input instanceof BufferredCdrInput) |
| { |
| BufferredCdrInput in = (BufferredCdrInput) input; |
| bout.setBigEndian(in.isBigEndian()); |
| } |
| |
| if (input instanceof gnuValueStream) |
| bout.setRunTime(((gnuValueStream) input).getRunTime()); |
| else |
| bout.setRunTime(new gnuRuntime(null, null)); |
| return bout; |
| } |
| |
| /** |
| * Read the chunked nested value from the given input stream, transferring the |
| * contents to the given output stream. |
| * |
| * @param value_tag the value tag of the value being read. |
| * @param input the input stream from where the remainder of the nested value |
| * must be read. |
| * @param output the output stream where the unchunked nested value must be |
| * copied. |
| * |
| * @return the tag that ended the nested value. |
| */ |
| public static int readNestedValue(int value_tag, InputStream input, |
| BufferedCdrOutput output, int level) |
| throws IOException |
| { |
| String id = null; |
| if (level < -1) |
| { |
| // For the first level, this information is already behind. |
| output.write_long(value_tag - vf_CHUNKING); |
| |
| // The nested value should be aways chunked. |
| if ((value_tag & vf_CHUNKING) == 0) |
| { |
| MARSHAL m = new MARSHAL("readNestedValue: must be chunked"); |
| m.minor = Minor.Chunks; |
| throw m; |
| } |
| else if (value_tag == vt_NULL) |
| { |
| MARSHAL m = new MARSHAL("readNestedValue: nul"); |
| m.minor = Minor.Chunks; |
| throw m; |
| } |
| else if (value_tag == vt_INDIRECTION) |
| { |
| MARSHAL m = new MARSHAL("readNestedValue: indirection"); |
| m.minor = Minor.Chunks; |
| throw m; |
| } |
| else |
| { |
| // Read the value. |
| if ((value_tag & vf_CODEBASE) != 0) |
| { |
| String codebase = read_string(input); |
| write_string(output, codebase); |
| } |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0) |
| { |
| // Multiple supported repository ids are present. |
| String[] ids = read_string_array(input); |
| id = ids[0]; |
| write_string_array(output, ids); |
| } |
| else if ((value_tag & vf_ID) != 0) |
| { |
| id = read_string(input); |
| write_string(output, id); |
| } |
| } |
| } |
| |
| int n = -1; |
| |
| // Read all chunks. |
| int chunk_size; |
| |
| byte[] r = null; |
| |
| while (true) |
| { |
| // Read the size of the next chunk or it may also be the |
| // header of the nested value. |
| chunk_size = input.read_long(); |
| |
| // End of chunk terminator. |
| if (chunk_size < 0 && chunk_size >= level) |
| return chunk_size; |
| else if (chunk_size >= 0x7FFFFF00) |
| { |
| int onInput = getCurrentPosition(input) - 4; |
| int onOutput = output.getPosition(); |
| output.getRunTime().redirect(onInput, onOutput); |
| // Value over 0x7FFFFF00 indicates that the nested value |
| // starts here. Read the nested value, storing it into the output. |
| // First parameter is actually the value tag. |
| chunk_size = readNestedValue(chunk_size, input, output, level - 1); |
| if (chunk_size < 0 && chunk_size >= level) |
| return chunk_size; |
| } |
| else |
| { |
| // The chunk follows. |
| if (r == null || r.length < chunk_size) |
| r = new byte[chunk_size + 256]; |
| |
| n = 0; |
| reading: while (n < chunk_size) |
| n += input.read(r, n, chunk_size - n); |
| output.write(r, 0, n); |
| } |
| } |
| } |
| |
| /** |
| * Read the value (the header must be behind). |
| */ |
| public static Serializable readValue(InputStream input, final int position, |
| Object value, BoxedValueHelper helper, String id, String[] ids, |
| String codebase) |
| { |
| gnuRuntime g; |
| gnuValueStream c = ((gnuValueStream) input); |
| if (c.getRunTime() == null) |
| { |
| g = new gnuRuntime(codebase, value); |
| c.setRunTime(g); |
| } |
| else |
| { |
| g = c.getRunTime(); |
| g.addCodeBase(codebase); |
| g.target = (Serializable) value; |
| } |
| if (value != null) |
| g.objectWritten(value, position); |
| |
| if (input instanceof HeadlessInput) |
| ((HeadlessInput) input).subsequentCalls = false; |
| |
| boolean ok = true; |
| |
| // The user-defined io operations are implemented. |
| if (value instanceof CustomMarshal) |
| { |
| CustomMarshal marsh = (CustomMarshal) value; |
| marsh.unmarshal((DataInputStream) input); |
| } |
| else |
| // The IDL-generated io operations are implemented. |
| if (value instanceof Streamable) |
| { |
| ((Streamable) value)._read(input); |
| } |
| else if (helper != null) |
| { |
| // If helper is non-null the value should normally be null. |
| value = helper.read_value(input); |
| g.objectWritten(value, position); |
| } |
| else |
| { |
| ok = false; |
| ValueFactory factory = null; |
| org.omg.CORBA_2_3.ORB orb = (org.omg.CORBA_2_3.ORB) input.orb(); |
| |
| if (id != null) |
| factory = orb.lookup_value_factory(id); |
| |
| if (factory == null && ids != null) |
| { |
| for (int i = 0; i < ids.length && factory == null; i++) |
| { |
| factory = orb.lookup_value_factory(ids[i]); |
| } |
| } |
| |
| if (factory != null) |
| { |
| value = factory.read_value((org.omg.CORBA_2_3.portable.InputStream) input); |
| ok = true; |
| } |
| } |
| |
| if (!ok && value instanceof Serializable) |
| // Delegate to ValueHandler |
| { |
| if (ids != null && ids.length > 0) |
| id = ids[0]; |
| |
| value = handler.readValue(input, position, value.getClass(), id, g); |
| ok = true; |
| } |
| |
| if (!ok) |
| { |
| if (value != null) |
| { |
| MARSHAL m = new MARSHAL(value.getClass().getName() |
| + " must be Streamable, CustomMarshal or Serializable"); |
| m.minor = Minor.UnsupportedValue; |
| throw m; |
| } |
| else |
| { |
| MARSHAL m = new MARSHAL("Unable to instantiate " + id + ":" + list(ids) |
| + " helper " + helper); |
| m.minor = Minor.UnsupportedValue; |
| throw m; |
| } |
| } |
| else |
| return (Serializable) value; |
| } |
| |
| /** |
| * Conveniency method to list ids in exception reports. |
| */ |
| static String list(String[] s) |
| { |
| if (s == null) |
| return "null"; |
| else |
| { |
| StringBuffer b = new StringBuffer("{"); |
| for (int i = 0; i < s.length; i++) |
| { |
| b.append(s[i]); |
| b.append(" "); |
| } |
| b.append("}"); |
| return b.toString(); |
| } |
| } |
| |
| /** |
| * Write the value base into the given stream. |
| * |
| * @param output a stream to write to. |
| * |
| * @param value a value type object, must be either Streamable or |
| * CustomMarshal. |
| * |
| * @throws MARSHAL if the writing failed due any reason. |
| */ |
| public static void write(OutputStream output, Serializable value) |
| { |
| // Write null if this is a null value. |
| if (value == null) |
| output.write_long(vt_NULL); |
| else if (value instanceof String) |
| write(output, value, m_StringValueHelper); |
| else |
| write(output, value, value.getClass()); |
| } |
| |
| /** |
| * Write the value base into the given stream, stating that it is an instance |
| * of the given class. |
| * |
| * @param output a stream to write to. |
| * |
| * @param value a value to write. |
| * |
| * @throws MARSHAL if the writing failed due any reason. |
| */ |
| public static void write(OutputStream output, Serializable value, |
| Class substitute) |
| { |
| // Write null if this is a null value. |
| if (value == null) |
| output.write_long(vt_NULL); |
| else if (value instanceof String || substitute == String.class) |
| writeString(output, value); |
| else |
| { |
| String vId = ObjectCreator.getRepositoryId(value.getClass()); |
| if (substitute == null || value.getClass().equals(substitute)) |
| write_instance(output, value, vId, getHelper(value.getClass(), vId)); |
| else |
| { |
| String vC = ObjectCreator.getRepositoryId(substitute); |
| String[] ids = new String[] { vId, vC }; |
| BoxedValueHelper h = getHelper(substitute.getClass(), ids); |
| // If the helper is available, it is also responsible for |
| // providing the repository Id. Otherwise, write both |
| // ids. |
| if (h == null) |
| write_instance(output, value, ids, null); |
| else |
| write_instance(output, value, h.get_id(), null); |
| } |
| } |
| } |
| |
| /** |
| * Write the value base into the given stream, supplementing it with an array |
| * of the provided repository ids plus the repository id, derived from the |
| * passed value. |
| * |
| * @param output a stream to write to. |
| * |
| * @param value a value to write. |
| * |
| * @throws MARSHAL if the writing failed due any reason. |
| */ |
| public static void write(OutputStream output, Serializable value, |
| String[] multiple_ids) |
| { |
| // Write null if this is a null value. |
| if (value == null) |
| output.write_long(vt_NULL); |
| else |
| { |
| String[] ids = new String[multiple_ids.length + 1]; |
| ids[0] = ObjectCreator.getRepositoryId(value.getClass()); |
| System.arraycopy(multiple_ids, 0, ids, 1, multiple_ids.length); |
| BoxedValueHelper h = getHelper(value.getClass(), ids); |
| write_instance(output, value, ids, h); |
| } |
| } |
| |
| /** |
| * Write value when its repository Id is explicitly given. Only this Id is |
| * written, the type of value is not taken into consideration. |
| * |
| * @param output an output stream to write into. |
| * @param value a value to write. |
| * @param id a value repository id. |
| */ |
| public static void write(OutputStream output, Serializable value, String id) |
| { |
| if (value == null) |
| output.write_long(vt_NULL); |
| else |
| write_instance(output, value, id, getHelper(value.getClass(), id)); |
| } |
| |
| /** |
| * Write standard value type header, followed by contents, produced by the |
| * boxed value helper. |
| * |
| * @param output the stream to write to. |
| * @param value the value to write, can be null. |
| * @param helper the helper that writes the value content if it is not null |
| * (must be provided for this method). |
| */ |
| public static void write(OutputStream output, Serializable value, |
| BoxedValueHelper helper) |
| { |
| if (helper == null) |
| throw new AssertionError("Helper must be provided"); |
| if (value == null) |
| output.write_long(vt_NULL); |
| else |
| write_instance(output, value, helper.get_id(), helper); |
| } |
| |
| /** |
| * Write the parameter that is surely a string and not null. |
| */ |
| private static void writeString(OutputStream output, Serializable string) |
| { |
| write_instance(output, string, m_StringValueHelper.get_id(), |
| m_StringValueHelper); |
| } |
| |
| /** |
| * Write value when its repository Id is explicitly given. Does not handle |
| * null. |
| * |
| * @param output an output stream to write into. |
| * @param value a value to write. |
| * @param id a value repository id (can be either single string or string |
| * array). |
| * @param helper a helper, writing object - specifical part. Can be null if |
| * the value should be written using other methods. |
| */ |
| static void write_instance(OutputStream output, Serializable value, |
| Object ids, BoxedValueHelper helper) |
| { |
| gnuValueStream rout = null; |
| gnuRuntime runtime = null; |
| |
| try |
| { |
| if (output instanceof gnuValueStream) |
| { |
| int position; |
| rout = (gnuValueStream) output; |
| runtime = rout.getRunTime(); |
| |
| if (runtime == null) |
| { |
| runtime = new gnuRuntime(null, value); |
| rout.setRunTime(runtime); |
| rout.getRunTime().objectWritten(value, |
| position = rout.getPosition()); |
| } |
| else if (runtime.target == value) |
| { |
| if (!writeSelf(output, value)) |
| throw new InternalError("Recursive helper call for " |
| + value.getClass().getName()); |
| return; |
| } |
| else |
| { |
| position = runtime.isWrittenAt(value); |
| if (position >= 0) |
| { |
| // The object was already written. |
| output.write_long(vt_INDIRECTION); |
| output.write_long(position - rout.getPosition()); |
| // Replacing object write data by indirection reference. |
| return; |
| } |
| else |
| { |
| runtime.objectWritten(value, position = rout.getPosition()); |
| } |
| } |
| } |
| |
| int value_tag = vt_VALUE_TAG; |
| |
| if (ids instanceof String) |
| value_tag |= vf_ID; |
| else if (ids instanceof String[]) |
| // OMG standard requires to set both flags. |
| value_tag |= vf_MULTIPLE_IDS | vf_ID; |
| |
| int chunkSizeLocation; |
| |
| OutputStream outObj; |
| |
| if (USE_CHUNKING) |
| { |
| // Wrap the value being written into one chunk (makes sense only for |
| // compatibility reasons). |
| outObj = output; |
| value_tag |= vf_CHUNKING; |
| } |
| else |
| outObj = output; |
| |
| output.write_long(value_tag); |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0) |
| write_string_array(output, (String[]) ids); |
| else if ((value_tag & vf_ID) != 0) |
| write_string(output, (String) ids); |
| |
| if (USE_CHUNKING) |
| { |
| // So far, write 0x55555555 instead of the chunk size (alignment may |
| // take place). |
| output.write_long(0x55555555); |
| // If the chunking is involved, the chunk size must be written here. |
| chunkSizeLocation = rout.getPosition() - INT_SIZE; |
| } |
| else |
| // Not in use for this case. |
| chunkSizeLocation = -1; |
| |
| writeValue(outObj, value, helper); |
| |
| if (USE_CHUNKING) |
| { |
| // Write the chunk size where the place for it was reserved. |
| int chunkSize = rout.getPosition() - chunkSizeLocation - INT_SIZE; |
| int current = rout.getPosition(); |
| rout.seek(chunkSizeLocation); |
| output.write_long(chunkSize); |
| rout.seek(current); |
| |
| // The end of record marker. |
| output.write_long(-1); |
| } |
| } |
| finally |
| { |
| if (runtime != null) |
| runtime.target = null; |
| } |
| } |
| |
| /** |
| * Write value (after header). |
| */ |
| static void writeValue(OutputStream output, Serializable value, |
| BoxedValueHelper helper) |
| { |
| ((gnuValueStream) output).getRunTime().target = value; |
| if (helper != null) |
| helper.write_value(output, value); |
| else if (!writeSelf(output, value)) |
| { |
| // Try to find helper via class loader. |
| boolean ok = false; |
| |
| if (!ok) |
| { |
| if (output instanceof BufferedCdrOutput) |
| { |
| BufferedCdrOutput b = (BufferedCdrOutput) output; |
| if (b.runtime == null) |
| b.runtime = new gnuRuntime(null, value); |
| } |
| |
| handler.writeValue(output, value); |
| } |
| } |
| } |
| |
| /** |
| * Try to write value supposing that it implements self-streamable interfaces. |
| * Return false if it does not or true on success. |
| */ |
| static boolean writeSelf(OutputStream output, Serializable value) |
| { |
| // User defined write method is present. |
| if (value instanceof CustomMarshal) |
| { |
| ((CustomMarshal) value).marshal((DataOutputStream) output); |
| return true; |
| } |
| else if (value instanceof Streamable) |
| { |
| ((Streamable) value)._write(output); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Read the indirection data and return the object that was already written to |
| * this stream. |
| * |
| * @param an_input the input stream, must be BufferredCdrInput. |
| */ |
| static Serializable readIndirection(InputStream an_input) |
| { |
| if (!(an_input instanceof gnuValueStream)) |
| throw new NO_IMPLEMENT(gnuValueStream.class.getName() |
| + " expected as parameter"); |
| |
| gnuValueStream in = (gnuValueStream) an_input; |
| |
| int current_pos = in.getPosition(); |
| |
| int offset = an_input.read_long(); |
| if (offset > -INT_SIZE) |
| { |
| MARSHAL m = new MARSHAL("Indirection tag refers to " + offset |
| + " (must be less than -" + INT_SIZE + ")"); |
| m.minor = Minor.Offset; |
| throw m; |
| } |
| |
| int stored_at = current_pos + offset; |
| |
| if (in.getRunTime() == null) |
| { |
| MARSHAL m = new MARSHAL(stored_at + " offset " + offset + ": not written"); |
| m.minor = Minor.Value; |
| throw m; |
| } |
| |
| return (Serializable) in.getRunTime().isObjectWrittenAt(stored_at, offset); |
| } |
| |
| /** |
| * Check the passed value tag for correctness. |
| * |
| * @param value_tag a tag to check, must be between 0x7fffff00 and 0x7fffffff |
| * |
| * @throws MARSHAL if the tag is outside this interval. |
| */ |
| static void checkTag(int value_tag) |
| { |
| if ((value_tag < 0x7fffff00 || value_tag > 0x7fffffff) |
| && value_tag != vt_NULL && value_tag != vt_INDIRECTION) |
| { |
| MARSHAL m = new MARSHAL("Invalid value record, unsupported header tag: " |
| + value_tag + " (0x" + Integer.toHexString(value_tag) + ")"); |
| m.minor = Minor.ValueHeaderTag; |
| throw m; |
| } |
| |
| if ((value_tag & vf_MULTIPLE_IDS) != 0 && (value_tag & vf_ID) == 0) |
| { |
| MARSHAL m = new MARSHAL("Invalid value record header flag combination (0x" |
| + Integer.toHexString(value_tag) + ")"); |
| m.minor = Minor.ValueHeaderFlags; |
| throw m; |
| } |
| } |
| |
| /** |
| * Throw MARSHAL. |
| */ |
| static void throwIt(String msg, String id1, String id2, Throwable e) |
| throws MARSHAL |
| { |
| MARSHAL m = new MARSHAL(msg + ":'" + id1 + "' versus '" + id2 + "'"); |
| if (e != null) |
| m.initCause(e); |
| m.minor = Minor.Value; |
| throw m; |
| } |
| |
| /** |
| * Load class by name and create the instance. |
| */ |
| static Object createInstance(String id, String[] ids, String codebase) |
| { |
| Object o = null; |
| |
| if (id != null) |
| o = _createInstance(id, codebase); |
| |
| if (ids != null) |
| for (int i = 0; i < ids.length && o == null; i++) |
| o = _createInstance(ids[i], codebase); |
| return o; |
| } |
| |
| static Object _createInstance(String id, String codebase) |
| { |
| if (id == null) |
| return null; |
| if (id.equals(StringValueHelper.id())) |
| return ""; |
| StringTokenizer st = new StringTokenizer(id, ":"); |
| |
| String prefix = st.nextToken(); |
| if (prefix.equalsIgnoreCase("IDL")) |
| return ObjectCreator.Idl2Object(id); |
| else if (prefix.equalsIgnoreCase("RMI")) |
| { |
| String className = st.nextToken(); |
| String hashCode = st.nextToken(); |
| String sid = null; |
| if (st.hasMoreElements()) |
| sid = st.nextToken(); |
| |
| try |
| { |
| Class objectClass = Util.loadClass(className, codebase, |
| Vio.class.getClassLoader()); |
| |
| String rid = ObjectCreator.getRepositoryId(objectClass); |
| |
| if (!rid.equals(id)) |
| { |
| // If direct string comparison fails, compare by meaning. |
| StringTokenizer st2 = new StringTokenizer(rid, ":"); |
| if (!st2.nextToken().equals("RMI")) |
| throw new InternalError("RMI format expected: '" + rid + "'"); |
| if (!st2.nextToken().equals(className)) |
| throwIt("Class name mismatch", id, rid, null); |
| |
| try |
| { |
| long h1 = Long.parseLong(hashCode, 16); |
| long h2 = Long.parseLong(st2.nextToken(), 16); |
| if (h1 != h2) |
| throwIt("Hashcode mismatch", id, rid, null); |
| |
| if (sid != null && st2.hasMoreTokens()) |
| { |
| long s1 = Long.parseLong(hashCode, 16); |
| long s2 = Long.parseLong(st2.nextToken(), 16); |
| if (s1 != s2) |
| throwIt("serialVersionUID mismatch", id, rid, null); |
| } |
| } |
| catch (NumberFormatException e) |
| { |
| throwIt("Invalid hashcode or svuid format: ", id, rid, e); |
| } |
| } |
| |
| // Low - level instantiation required here. |
| return instantiateAnyWay(objectClass); |
| } |
| catch (Exception ex) |
| { |
| MARSHAL m = new MARSHAL("Unable to instantiate " + id); |
| m.minor = Minor.Instantiation; |
| m.initCause(ex); |
| throw m; |
| } |
| } |
| else |
| throw new NO_IMPLEMENT("Unsupported prefix " + prefix + ":"); |
| } |
| |
| /** |
| * Read string, expecting the probable indirection. |
| */ |
| static String read_string(InputStream input) |
| { |
| gnuValueStream g = (gnuValueStream) input; |
| int previous = g.getPosition(); |
| int l = input.read_long(); |
| if (l != vt_INDIRECTION) |
| { |
| g.seek(previous); |
| String s = input.read_string(); |
| if (g.getRunTime() == null) |
| g.setRunTime(new gnuRuntime(null, null)); |
| g.getRunTime().singleIdWritten(s, previous); |
| return s; |
| } |
| else |
| { |
| gnuRuntime r = g.getRunTime(); |
| int base = g.getPosition(); |
| int delta = input.read_long(); |
| if (r == null) |
| { |
| previous = g.getPosition(); |
| g.seek(base + delta); |
| String indir = input.read_string(); |
| g.seek(previous); |
| return indir; |
| } |
| else |
| { |
| return (String) r.isObjectWrittenAt(base + delta, delta); |
| } |
| } |
| } |
| |
| /** |
| * Read string array, expecting the probable indirection. |
| */ |
| static String[] read_string_array(InputStream input) |
| { |
| gnuValueStream g = (gnuValueStream) input; |
| int previous = g.getPosition(); |
| int l = input.read_long(); |
| if (l != vt_INDIRECTION) |
| { |
| g.seek(previous); |
| String[] s = StringSeqHelper.read(input); |
| if (g.getRunTime() == null) |
| g.setRunTime(new gnuRuntime(null, null)); |
| g.getRunTime().objectWritten(s, previous); |
| return s; |
| } |
| else |
| { |
| gnuRuntime r = g.getRunTime(); |
| int base = g.getPosition(); |
| int delta = input.read_long(); |
| if (r == null) |
| { |
| previous = g.getPosition(); |
| g.seek(base + delta); |
| String[] indir = StringSeqHelper.read(input); |
| g.seek(previous); |
| return indir; |
| } |
| else |
| { |
| return (String[]) r.isObjectWrittenAt(base + delta, delta); |
| } |
| } |
| } |
| |
| /** |
| * Write repository Id, probably shared. |
| */ |
| static void write_string(OutputStream output, String id) |
| { |
| if (output instanceof gnuValueStream) |
| { |
| gnuValueStream b = (gnuValueStream) output; |
| if (b != null) |
| { |
| int written = b.getRunTime().idWrittenAt(id); |
| if (written >= 0) |
| { |
| // Reuse existing id record. |
| output.write_long(vt_INDIRECTION); |
| int p = b.getPosition(); |
| output.write_long(written - p); |
| } |
| else |
| { |
| b.getRunTime().singleIdWritten(id, b.getPosition()); |
| output.write_string(id); |
| } |
| } |
| } |
| else |
| output.write_string(id); |
| } |
| |
| /** |
| * Write repository Id, probably shared. |
| */ |
| static void write_string_array(OutputStream output, String[] ids) |
| { |
| if (output instanceof gnuValueStream) |
| { |
| gnuValueStream b = (gnuValueStream) output; |
| if (b != null) |
| { |
| int written = b.getRunTime().idWrittenAt(ids); |
| if (written >= 0) |
| { |
| // Reuse existing id record. |
| output.write_long(vt_INDIRECTION); |
| int p = b.getPosition(); |
| output.write_long(written - p); |
| } |
| else |
| { |
| b.getRunTime().multipleIdsWritten(ids, b.getPosition()); |
| StringSeqHelper.write(output, ids); |
| } |
| } |
| } |
| else |
| StringSeqHelper.write(output, ids); |
| } |
| |
| /** |
| * Get the helper that could write the given object, or null if no pre-defined |
| * helper available for this object. |
| */ |
| public static BoxedValueHelper getHelper(Class x, Object ids) |
| { |
| if (x != null && x.equals(String.class)) |
| return m_StringValueHelper; |
| else if (x != null && x.isArray()) |
| return new ArrayValueHelper(x); |
| else if (ids instanceof String) |
| return locateHelper((String) ids); |
| else if (ids instanceof String[]) |
| { |
| String[] ia = (String[]) ids; |
| BoxedValueHelper h; |
| for (int i = 0; i < ia.length; i++) |
| { |
| h = locateHelper(ia[i]); |
| if (h != null) |
| return h; |
| } |
| return null; |
| } |
| else |
| return null; |
| } |
| |
| /** |
| * Get the helper that could write the given object, or null if no pre-defined |
| * helper available for this object. |
| */ |
| public static BoxedValueHelper getHelper(Class x, String id) |
| { |
| if (x != null && x.equals(String.class)) |
| return m_StringValueHelper; |
| else if (x != null && x.isArray()) |
| return new ArrayValueHelper(x); |
| else |
| return locateHelper(id); |
| } |
| |
| /** |
| * Try to locate helper from the repository id. |
| */ |
| static BoxedValueHelper locateHelper(String id) |
| { |
| if (id != null) |
| { |
| if (id.equals(m_StringValueHelper.get_id())) |
| return m_StringValueHelper; |
| else |
| // Try to locate helper for IDL type. |
| if (id.startsWith("IDL:")) |
| { |
| try |
| { |
| Class helperClass = ObjectCreator.findHelper(id); |
| if (BoxedValueHelper.class.isAssignableFrom(helperClass)) |
| return (BoxedValueHelper) helperClass.newInstance(); |
| else if (helperClass != null) |
| return new IDLTypeHelper(helperClass); |
| else |
| return null; |
| } |
| catch (Exception ex) |
| { |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the current position. |
| */ |
| static int getCurrentPosition(InputStream x) |
| { |
| if (x instanceof gnuValueStream) |
| return ((gnuValueStream) x).getPosition(); |
| else |
| return 0; |
| } |
| |
| /** |
| * Instantiate an instance of this class anyway; also in the case when it has |
| * no parameterless or any other constructor. The fields will be assigned |
| * while reading the class from the stream. |
| * |
| * @param clazz a class for that the instance should be instantiated. |
| */ |
| public static Object instantiateAnyWay(Class clazz) |
| throws Exception |
| { |
| Class first_nonserial = clazz; |
| |
| while (Serializable.class.isAssignableFrom(first_nonserial) |
| || Modifier.isAbstract(first_nonserial.getModifiers())) |
| first_nonserial = first_nonserial.getSuperclass(); |
| |
| final Class local_constructor_class = first_nonserial; |
| |
| Constructor constructor = local_constructor_class.getDeclaredConstructor(new Class[0]); |
| |
| return VMVio.allocateObject(clazz, constructor.getDeclaringClass(), |
| constructor); |
| } |
| } |