/* * @(#)CDROutputStream_1_0.java 1.114 05/01/04 * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ /* * Licensed Materials - Property of IBM * RMI-IIOP v1.0 * Copyright IBM Corp. 1998 1999 All Rights Reserved * * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ package com.sun.corba.se.impl.encoding; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.rmi.Remote; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.util.Hashtable; import java.util.Stack; import javax.rmi.CORBA.Util; import javax.rmi.CORBA.ValueHandler; import javax.rmi.CORBA.ValueHandlerMultiFormat; import org.omg.CORBA.CustomMarshal; import org.omg.CORBA.DataOutputStream; import org.omg.CORBA.TypeCodePackage.BadKind; import org.omg.CORBA.SystemException; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.Object; import org.omg.CORBA.Principal; import org.omg.CORBA.TypeCode; import org.omg.CORBA.Any; import org.omg.CORBA.VM_CUSTOM; import org.omg.CORBA.VM_TRUNCATABLE; import org.omg.CORBA.VM_NONE; import org.omg.CORBA.portable.IDLEntity; import org.omg.CORBA.portable.CustomValue; import org.omg.CORBA.portable.StreamableValue; import org.omg.CORBA.portable.BoxedValueHelper; import org.omg.CORBA.portable.OutputStream; import org.omg.CORBA.portable.ValueBase; import com.sun.org.omg.CORBA.portable.ValueHelper; import com.sun.corba.se.pept.protocol.MessageMediator; import com.sun.corba.se.pept.transport.ByteBufferPool; import com.sun.corba.se.spi.ior.iiop.GIOPVersion; import com.sun.corba.se.spi.ior.IOR; import com.sun.corba.se.spi.ior.IORFactories; import com.sun.corba.se.spi.orb.ORB; import com.sun.corba.se.spi.orb.ORBVersionFactory; import com.sun.corba.se.spi.orb.ORBVersion; import com.sun.corba.se.spi.protocol.CorbaMessageMediator; import com.sun.corba.se.spi.logging.CORBALogDomains; import com.sun.corba.se.impl.encoding.ByteBufferWithInfo; import com.sun.corba.se.impl.encoding.MarshalOutputStream; import com.sun.corba.se.impl.encoding.CodeSetConversion; import com.sun.corba.se.impl.corba.TypeCodeImpl; import com.sun.corba.se.impl.orbutil.CacheTable; import com.sun.corba.se.impl.orbutil.ORBUtility; import com.sun.corba.se.impl.orbutil.RepositoryIdStrings; import com.sun.corba.se.impl.orbutil.RepositoryIdUtility; import com.sun.corba.se.impl.orbutil.RepositoryIdFactory; import com.sun.corba.se.impl.util.Utility; import com.sun.corba.se.impl.logging.ORBUtilSystemException; public class CDROutputStream_1_0 extends CDROutputStreamBase { private static final int INDIRECTION_TAG = 0xffffffff; protected boolean littleEndian; protected BufferManagerWrite bufferManagerWrite; ByteBufferWithInfo bbwi; protected ORB orb; protected ORBUtilSystemException wrapper ; protected boolean debug = false; protected int blockSizeIndex = -1; protected int blockSizePosition = 0; protected byte streamFormatVersion; private static final int DEFAULT_BUFFER_SIZE = 1024; private static final String kWriteMethod = "write"; // Codebase cache private CacheTable codebaseCache = null; // Value cache private CacheTable valueCache = null; // Repository ID cache private CacheTable repositoryIdCache = null; // Write end flag private int end_flag = 0; // Beginning with the resolution to interop issue 3526, // only enclosing chunked valuetypes are taken into account // when computing the nesting level. However, we still need // the old computation around for interoperability with our // older ORBs. private int chunkedValueNestingLevel = 0; private boolean mustChunk = false; // In block marker protected boolean inBlock = false; // Last end tag position private int end_flag_position = 0; private int end_flag_index = 0; // ValueHandler private ValueHandler valueHandler = null; // Repository ID handlers private RepositoryIdUtility repIdUtil; private RepositoryIdStrings repIdStrs; // Code set converters (created when first needed) private CodeSetConversion.CTBConverter charConverter; private CodeSetConversion.CTBConverter wcharConverter; // REVISIT - This should be re-factored so that including whether // to use pool byte buffers or not doesn't need to be known. public void init(org.omg.CORBA.ORB orb, boolean littleEndian, BufferManagerWrite bufferManager, byte streamFormatVersion, boolean usePooledByteBuffers) { // ORB must not be null. See CDROutputStream constructor. this.orb = (ORB)orb; this.wrapper = ORBUtilSystemException.get( this.orb, CORBALogDomains.RPC_ENCODING ) ; debug = this.orb.transportDebugFlag; this.littleEndian = littleEndian; this.bufferManagerWrite = bufferManager; this.bbwi = new ByteBufferWithInfo(orb, bufferManager, usePooledByteBuffers); this.streamFormatVersion = streamFormatVersion; createRepositoryIdHandlers(); } public void init(org.omg.CORBA.ORB orb, boolean littleEndian, BufferManagerWrite bufferManager, byte streamFormatVersion) { init(orb, littleEndian, bufferManager, streamFormatVersion, true); } private final void createRepositoryIdHandlers() { if (orb != null) { // Get the appropriate versions based on the ORB version. The // ORB versioning info is only in the core ORB. repIdUtil = RepositoryIdFactory.getRepIdUtility(orb); repIdStrs = RepositoryIdFactory.getRepIdStringsFactory(orb); } else { // Get the latest versions repIdUtil = RepositoryIdFactory.getRepIdUtility(); repIdStrs = RepositoryIdFactory.getRepIdStringsFactory(); } } public BufferManagerWrite getBufferManager() { return bufferManagerWrite; } public byte[] toByteArray() { byte[] it; it = new byte[bbwi.position()]; // Micro-benchmarks show ByteBuffer.get(int) out perform the bulk // ByteBuffer.get(byte[], offset, length). for (int i = 0; i < bbwi.position(); i++) it[i] = bbwi.byteBuffer.get(i); return it; } public GIOPVersion getGIOPVersion() { return GIOPVersion.V1_0; } // Called by Request and Reply message. Valid for GIOP versions >= 1.2 only. // Illegal for GIOP versions < 1.2. void setHeaderPadding(boolean headerPadding) { throw wrapper.giopVersionError(); } protected void handleSpecialChunkBegin(int requiredSize) { // No-op for GIOP 1.0 } protected void handleSpecialChunkEnd() { // No-op for GIOP 1.0 } protected final int computeAlignment(int align) { if (align > 1) { int incr = bbwi.position() & (align - 1); if (incr != 0) return align - incr; } return 0; } protected void alignAndReserve(int align, int n) { bbwi.position(bbwi.position() + computeAlignment(align)); if (bbwi.position() + n > bbwi.buflen) grow(align, n); } // // Default implementation of grow. Subclassers may override this. // Always grow the single buffer. This needs to delegate // fragmentation policy for IIOP 1.1. // protected void grow(int align, int n) { bbwi.needed = n; bufferManagerWrite.overflow(bbwi); } public final void putEndian() throws SystemException { write_boolean(littleEndian); } public final boolean littleEndian() { return littleEndian; } void freeInternalCaches() { if (codebaseCache != null) codebaseCache.done(); if (valueCache != null) valueCache.done(); if (repositoryIdCache != null) repositoryIdCache.done(); } // No such type in java public final void write_longdouble(double x) { throw wrapper.longDoubleNotImplemented( CompletionStatus.COMPLETED_MAYBE ) ; } public void write_octet(byte x) { // The 'if' stmt is commented out since we need the alignAndReserve to // be called, particularly when the first body byte is written, // to induce header padding to align the body on a 8-octet boundary, // for GIOP versions 1.2 and above. Refer to internalWriteOctetArray() // method that also has a similar change. //if (bbwi.position() + 1 > bbwi.buflen) alignAndReserve(1, 1); // REVISIT - Should just use ByteBuffer.put(byte) and let it // increment the ByteBuffer position. This is true // for all write operations in this file. bbwi.byteBuffer.put(bbwi.position(), x); bbwi.position(bbwi.position() + 1); } public final void write_boolean(boolean x) { write_octet(x? (byte)1:(byte)0); } public void write_char(char x) { CodeSetConversion.CTBConverter converter = getCharConverter(); converter.convert(x); // CORBA formal 99-10-07 15.3.1.6: "In the case of multi-byte encodings // of characters, a single instance of the char type may only // hold one octet of any multi-byte character encoding." if (converter.getNumBytes() > 1) throw wrapper.invalidSingleCharCtb(CompletionStatus.COMPLETED_MAYBE); write_octet(converter.getBytes()[0]); } // These wchar methods are only used when talking to // legacy ORBs, now. private final void writeLittleEndianWchar(char x) { bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF)); bbwi.position(bbwi.position() + 2); } private final void writeBigEndianWchar(char x) { bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)(x & 0xFF)); bbwi.position(bbwi.position() + 2); } private final void writeLittleEndianShort(short x) { bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF)); bbwi.position(bbwi.position() + 2); } private final void writeBigEndianShort(short x) { bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)(x & 0xFF)); bbwi.position(bbwi.position() + 2); } private final void writeLittleEndianLong(int x) { bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 16) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 24) & 0xFF)); bbwi.position(bbwi.position() + 4); } private final void writeBigEndianLong(int x) { bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 24) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 16) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 3, (byte)(x & 0xFF)); bbwi.position(bbwi.position() + 4); } private final void writeLittleEndianLongLong(long x) { bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 16) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 24) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 4, (byte)((x >>> 32) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 5, (byte)((x >>> 40) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 6, (byte)((x >>> 48) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 7, (byte)((x >>> 56) & 0xFF)); bbwi.position(bbwi.position() + 8); } private final void writeBigEndianLongLong(long x) { bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 56) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 48) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 40) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 32) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 4, (byte)((x >>> 24) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 5, (byte)((x >>> 16) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 6, (byte)((x >>> 8) & 0xFF)); bbwi.byteBuffer.put(bbwi.position() + 7, (byte)(x & 0xFF)); bbwi.position(bbwi.position() + 8); } public void write_wchar(char x) { // Don't allow transmission of wchar/wstring data with // foreign ORBs since it's against the spec. if (ORBUtility.isForeignORB(orb)) { throw wrapper.wcharDataInGiop10(CompletionStatus.COMPLETED_MAYBE); } // If it's one of our legacy ORBs, do what they did: alignAndReserve(2, 2); if (littleEndian) { writeLittleEndianWchar(x); } else { writeBigEndianWchar(x); } } public void write_short(short x) { alignAndReserve(2, 2); if (littleEndian) { writeLittleEndianShort(x); } else { writeBigEndianShort(x); } } public final void write_ushort(short x) { write_short(x); } public void write_long(int x) { alignAndReserve(4, 4); if (littleEndian) { writeLittleEndianLong(x); } else { writeBigEndianLong(x); } } public final void write_ulong(int x) { write_long(x); } public void write_longlong(long x) { alignAndReserve(8, 8); if (littleEndian) { writeLittleEndianLongLong(x); } else { writeBigEndianLongLong(x); } } public final void write_ulonglong(long x) { write_longlong(x); } public final void write_float(float x) { write_long(Float.floatToIntBits(x)); } public final void write_double(double x) { write_longlong(Double.doubleToLongBits(x)); } public void write_string(String value) { writeString(value); } protected int writeString(String value) { if (value == null) { throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); } CodeSetConversion.CTBConverter converter = getCharConverter(); converter.convert(value); // A string is encoded as an unsigned CORBA long for the // number of bytes to follow (including a terminating null). // There is only one octet per character in the string. int len = converter.getNumBytes() + 1; handleSpecialChunkBegin(computeAlignment(4) + 4 + len); write_long(len); int indirection = get_offset() - 4; internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes()); // Write the null ending write_octet((byte)0); handleSpecialChunkEnd(); return indirection; } public void write_wstring(String value) { if (value == null) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // Don't allow transmission of wchar/wstring data with // foreign ORBs since it's against the spec. if (ORBUtility.isForeignORB(orb)) { throw wrapper.wcharDataInGiop10(CompletionStatus.COMPLETED_MAYBE); } // When talking to our legacy ORBs, do what they did: int len = value.length() + 1; // This will only have an effect if we're already chunking handleSpecialChunkBegin(4 + (len * 2) + computeAlignment(4)); write_long(len); for (int i = 0; i < len - 1; i++) write_wchar(value.charAt(i)); // Write the null ending write_short((short)0); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } // Performs no checks and doesn't tamper with chunking void internalWriteOctetArray(byte[] value, int offset, int length) { int n = offset; // This flag forces the alignAndReserve method to be called the // first time an octet is written. This is necessary to ensure // that the body is aligned on an 8-octet boundary. Note the 'if' // condition inside the 'while' loop below. Also, refer to the // write_octet() method that has a similar change. boolean align = true; while (n < length+offset) { int avail; int bytes; int wanted; if ((bbwi.position() + 1 > bbwi.buflen) || align) { align = false; alignAndReserve(1, 1); } avail = bbwi.buflen - bbwi.position(); wanted = (length + offset) - n; bytes = (wanted < avail) ? wanted : avail; for (int i = 0; i < bytes; i++) bbwi.byteBuffer.put(bbwi.position() + i, value[n+i]); bbwi.position(bbwi.position() + bytes); n += bytes; } } public final void write_octet_array(byte b[], int offset, int length) { if ( b == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(length); internalWriteOctetArray(b, offset, length); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public void write_Principal(Principal p) { write_long(p.name().length); write_octet_array(p.name(), 0, p.name().length); } public void write_any(Any any) { if ( any == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); write_TypeCode(any.type()); any.write_value(parent); } public void write_TypeCode(TypeCode tc) { if ( tc == null ) { throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); } TypeCodeImpl tci; if (tc instanceof TypeCodeImpl) { tci = (TypeCodeImpl)tc; } else { tci = new TypeCodeImpl(orb, tc); } tci.write_value((org.omg.CORBA_2_3.portable.OutputStream)parent); } public void write_Object(org.omg.CORBA.Object ref) { if (ref == null) { IOR nullIOR = IORFactories.makeIOR( orb ) ; nullIOR.write(parent); return; } // IDL to Java formal 01-06-06 1.21.4.2 if (ref instanceof org.omg.CORBA.LocalObject) throw wrapper.writeLocalObject(CompletionStatus.COMPLETED_MAYBE); IOR ior = ORBUtility.connectAndGetIOR( orb, ref ) ; ior.write(parent); return; } // ------------ RMI related methods -------------------------- public void write_abstract_interface(java.lang.Object obj) { boolean corbaObject = false; // Assume value type. org.omg.CORBA.Object theObject = null; // Is it a CORBA.Object? if (obj != null && obj instanceof org.omg.CORBA.Object) { // Yes. theObject = (org.omg.CORBA.Object)obj; corbaObject = true; } // Write our flag... write_boolean(corbaObject); // Now write out the object... if (corbaObject) { write_Object(theObject); } else { try { write_value((java.io.Serializable)obj); } catch(ClassCastException cce) { if (obj instanceof java.io.Serializable) throw cce; else ORBUtility.throwNotSerializableForCorba(obj.getClass().getName()); } } } public void write_value(Serializable object, Class clz) { write_value(object); } private void writeWStringValue(String string) { int indirection = writeValueTag(mustChunk, true, null); // Write WStringValue's repository ID write_repositoryId(repIdStrs.getWStringValueRepId()); // Add indirection for object to indirection table updateIndirectionTable(indirection, string, string); // Write Value chunk if (mustChunk) { start_block(); end_flag--; chunkedValueNestingLevel--; } else end_flag--; write_wstring(string); if (mustChunk) end_block(); // Write end tag writeEndTag(mustChunk); } private void writeArray(Serializable array, Class clazz) { if (valueHandler == null) valueHandler = ORBUtility.createValueHandler(orb); //d11638 // Write value_tag int indirection = writeValueTag(mustChunk, true, Util.getCodebase(clazz)); // Write repository ID write_repositoryId(repIdStrs.createSequenceRepID(clazz)); // Add indirection for object to indirection table updateIndirectionTable(indirection, array, array); // Write Value chunk if (mustChunk) { start_block(); end_flag--; chunkedValueNestingLevel--; } else end_flag--; if (valueHandler instanceof ValueHandlerMultiFormat) { ValueHandlerMultiFormat vh = (ValueHandlerMultiFormat)valueHandler; vh.writeValue(parent, array, streamFormatVersion); } else valueHandler.writeValue(parent, array); if (mustChunk) end_block(); // Write end tag writeEndTag(mustChunk); } private void writeValueBase(org.omg.CORBA.portable.ValueBase object, Class clazz) { // _REVISIT_ could check to see whether chunking really needed mustChunk = true; // Write value_tag int indirection = writeValueTag(true, true, Util.getCodebase(clazz)); // Get rep id String repId = ((ValueBase)object)._truncatable_ids()[0]; // Write rep id write_repositoryId(repId); // Add indirection for object to indirection table updateIndirectionTable(indirection, object, object); // Write Value chunk start_block(); end_flag--; chunkedValueNestingLevel--; writeIDLValue(object, repId); end_block(); // Write end tag writeEndTag(true); } private void writeRMIIIOPValueType(Serializable object, Class clazz) { if (valueHandler == null) valueHandler = ORBUtility.createValueHandler(orb); //d11638 Serializable key = object; // Allow the ValueHandler to call writeReplace on // the Serializable (if the method is present) object = valueHandler.writeReplace(key); if (object == null) { // Write null tag and return write_long(0); return; } if (object != key) { if (valueCache != null && valueCache.containsKey(object)) { writeIndirection(INDIRECTION_TAG, valueCache.getVal(object)); return; } clazz = object.getClass(); } if (mustChunk || valueHandler.isCustomMarshaled(clazz)) { mustChunk = true; } // Write value_tag int indirection = writeValueTag(mustChunk, true, Util.getCodebase(clazz)); // Write rep. id write_repositoryId(repIdStrs.createForJavaType(clazz)); // Add indirection for object to indirection table updateIndirectionTable(indirection, object, key); if (mustChunk) { // Write Value chunk end_flag--; chunkedValueNestingLevel--; start_block(); } else end_flag--; if (valueHandler instanceof ValueHandlerMultiFormat) { ValueHandlerMultiFormat vh = (ValueHandlerMultiFormat)valueHandler; vh.writeValue(parent, object, streamFormatVersion); } else valueHandler.writeValue(parent, object); if (mustChunk) end_block(); // Write end tag writeEndTag(mustChunk); } public void write_value(Serializable object, String repository_id) { // Handle null references if (object == null) { // Write null tag and return write_long(0); return; } // Handle shared references if (valueCache != null && valueCache.containsKey(object)) { writeIndirection(INDIRECTION_TAG, valueCache.getVal(object)); return; } Class clazz = object.getClass(); boolean oldMustChunk = mustChunk; if (mustChunk) mustChunk = true; if (inBlock) end_block(); if (clazz.isArray()) { // Handle arrays writeArray(object, clazz); } else if (object instanceof org.omg.CORBA.portable.ValueBase) { // Handle IDL Value types writeValueBase((org.omg.CORBA.portable.ValueBase)object, clazz); } else if (shouldWriteAsIDLEntity(object)) { writeIDLEntity((IDLEntity)object); } else if (object instanceof java.lang.String) { writeWStringValue((String)object); } else if (object instanceof java.lang.Class) { writeClass(repository_id, (Class)object); } else { // RMI-IIOP value type writeRMIIIOPValueType(object, clazz); } mustChunk = oldMustChunk; // Check to see if we need to start another block for a // possible outer value if (mustChunk) start_block(); } public void write_value(Serializable object) { write_value(object, (String)null); } public void write_value(Serializable object, org.omg.CORBA.portable.BoxedValueHelper factory) { // Handle null references if (object == null) { // Write null tag and return write_long(0); return; } // Handle shared references if ((valueCache != null) && valueCache.containsKey(object)) { writeIndirection(INDIRECTION_TAG, valueCache.getVal(object)); return; } boolean oldMustChunk = mustChunk; boolean isCustom = false; if (factory instanceof ValueHelper) { short modifier; try { modifier = ((ValueHelper)factory).get_type().type_modifier(); } catch(BadKind ex) { // tk_value_box modifier = VM_NONE.value; } if (object instanceof CustomMarshal && modifier == VM_CUSTOM.value) { isCustom = true; mustChunk = true; } if (modifier == VM_TRUNCATABLE.value) mustChunk = true; } if (mustChunk) { if (inBlock) end_block(); // Write value_tag int indirection = writeValueTag(true, orb.getORBData().useRepId(), Util.getCodebase(object.getClass()) ); if (orb.getORBData().useRepId()) { write_repositoryId(factory.get_id()); } // Add indirection for object to indirection table updateIndirectionTable(indirection, object, object); // Write Value chunk start_block(); end_flag--; chunkedValueNestingLevel--; if (isCustom) ((CustomMarshal)object).marshal(parent); else factory.write_value(parent, object); end_block(); // Write end tag writeEndTag(true); } else { // Write value_tag int indirection = writeValueTag(false, orb.getORBData().useRepId(), Util.getCodebase(object.getClass()) ); if (orb.getORBData().useRepId()) { write_repositoryId(factory.get_id()); } // Add indirection for object to indirection table updateIndirectionTable(indirection, object, object); // Write Value chunk end_flag--; // no need to test for custom on the non-chunked path factory.write_value(parent, object); // Write end tag writeEndTag(false); } mustChunk = oldMustChunk; // Check to see if we need to start another block for a // possible outer value if (mustChunk) start_block(); } public int get_offset() { return bbwi.position(); } public void start_block() { if (debug) { dprint("CDROutputStream_1_0 start_block, position" + bbwi.position()); } //Move inBlock=true to after write_long since write_long might //trigger grow which will lead to erroneous behavior with a //missing blockSizeIndex. //inBlock = true; // Save space in the buffer for block size write_long(0); //Has to happen after write_long since write_long could //trigger grow which is overridden by supper classes to //depend on inBlock. inBlock = true; blockSizePosition = get_offset(); // Remember where to put the size of the endblock less 4 blockSizeIndex = bbwi.position(); if (debug) { dprint("CDROutputStream_1_0 start_block, blockSizeIndex " + blockSizeIndex); } } // Utility method which will hopefully decrease chunking complexity // by allowing us to end_block and update chunk lengths without // calling alignAndReserve. Otherwise, it's possible to get into // recursive scenarios which lose the chunking state. protected void writeLongWithoutAlign(int x) { if (littleEndian) { writeLittleEndianLong(x); } else { writeBigEndianLong(x); } } public void end_block() { if (debug) { dprint("CDROutputStream_1_0.java end_block"); } if (!inBlock) return; if (debug) { dprint("CDROutputStream_1_0.java end_block, in a block"); } inBlock = false; // Test to see if the block was of zero length // If so, remove the block instead of ending it // (This can happen if the last field written // in a value was another value) if (get_offset() == blockSizePosition) { // Need to assert that blockSizeIndex == bbwi.position()? REVISIT bbwi.position(bbwi.position() - 4); blockSizeIndex = -1; blockSizePosition = -1; return; } int oldSize = bbwi.position(); bbwi.position(blockSizeIndex - 4); writeLongWithoutAlign(oldSize - blockSizeIndex); bbwi.position(oldSize); blockSizeIndex = -1; blockSizePosition = -1; // System.out.println(" post end_block: " + get_offset() + " " + bbwi.position()); } public org.omg.CORBA.ORB orb() { return orb; } // ------------ End RMI related methods -------------------------- public final void write_boolean_array(boolean[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(length); for (int i = 0; i < length; i++) write_boolean(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_char_array(char[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(length); for (int i = 0; i < length; i++) write_char(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public void write_wchar_array(char[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(2) + (length * 2)); for (int i = 0; i < length; i++) write_wchar(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_short_array(short[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(2) + (length * 2)); for (int i = 0; i < length; i++) write_short(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_ushort_array(short[]value, int offset, int length) { write_short_array(value, offset, length); } public final void write_long_array(int[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(4) + (length * 4)); for (int i = 0; i < length; i++) write_long(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_ulong_array(int[]value, int offset, int length) { write_long_array(value, offset, length); } public final void write_longlong_array(long[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(8) + (length * 8)); for (int i = 0; i < length; i++) write_longlong(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_ulonglong_array(long[]value, int offset, int length) { write_longlong_array(value, offset, length); } public final void write_float_array(float[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(4) + (length * 4)); for (int i = 0; i < length; i++) write_float(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public final void write_double_array(double[]value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); // This will only have an effect if we're already chunking handleSpecialChunkBegin(computeAlignment(8) + (length * 8)); for (int i = 0; i < length; i++) write_double(value[offset + i]); // This will only have an effect if we're already chunking handleSpecialChunkEnd(); } public void write_string_array(String[] value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); for(int i = 0; i < length; i++) write_string(value[offset + i]); } public void write_wstring_array(String[] value, int offset, int length) { if ( value == null ) throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); for(int i = 0; i < length; i++) write_wstring(value[offset + i]); } public final void write_any_array(org.omg.CORBA.Any value[], int offset, int length) { for(int i = 0; i < length; i++) write_any(value[offset + i]); } //--------------------------------------------------------------------// // CDROutputStream state management. // public void writeTo(java.io.OutputStream s) throws java.io.IOException { byte[] tmpBuf = null; if (bbwi.byteBuffer.hasArray()) { tmpBuf = bbwi.byteBuffer.array(); } else { int size = bbwi.position(); tmpBuf = new byte[size]; // Micro-benchmarks are showing a loop of ByteBuffer.get(int) is // faster than ByteBuffer.get(byte[], offset, length) for (int i = 0; i < size; i++) tmpBuf[i] = bbwi.byteBuffer.get(i); } s.write(tmpBuf, 0, bbwi.position()); } public void writeOctetSequenceTo(org.omg.CORBA.portable.OutputStream s) { byte[] buf = null; if (bbwi.byteBuffer.hasArray()) { buf = bbwi.byteBuffer.array(); } else { int size = bbwi.position(); buf = new byte[size]; // Micro-benchmarks are showing a loop of ByteBuffer.get(int) is // faster than ByteBuffer.get(byte[], offset, length) for (int i = 0; i < size; i++) buf[i] = bbwi.byteBuffer.get(i); } s.write_long(bbwi.position()); s.write_octet_array(buf, 0, bbwi.position()); } public final int getSize() { return bbwi.position(); } public int getIndex() { return bbwi.position(); } public boolean isLittleEndian() { return littleEndian; } public void setIndex(int value) { bbwi.position(value); } public ByteBufferWithInfo getByteBufferWithInfo() { return bbwi; } public void setByteBufferWithInfo(ByteBufferWithInfo bbwi) { this.bbwi = bbwi; } public ByteBuffer getByteBuffer() { ByteBuffer result = null;; if (bbwi != null) { result = bbwi.byteBuffer; } return result; } public void setByteBuffer(ByteBuffer byteBuffer) { bbwi.byteBuffer = byteBuffer; } private final void updateIndirectionTable(int indirection, java.lang.Object object, java.lang.Object key) { // int indirection = get_offset(); if (valueCache == null) valueCache = new CacheTable(orb,true); valueCache.put(object, indirection); if (key != object) valueCache.put(key, indirection); } private final void write_repositoryId(String id) { // Use an indirection if available if (repositoryIdCache != null && repositoryIdCache.containsKey(id)) { writeIndirection(INDIRECTION_TAG, repositoryIdCache.getVal(id)); return; } // Write it as a string. Note that we have already done the // special case conversion of non-Latin-1 characters to escaped // Latin-1 sequences in RepositoryId. // It's not a good idea to cache them now that we can have // multiple code sets. int indirection = writeString(id); // Add indirection for id to indirection table if (repositoryIdCache == null) repositoryIdCache = new CacheTable(orb,true); repositoryIdCache.put(id, indirection); } private void write_codebase(String str, int pos) { if (codebaseCache != null && codebaseCache.containsKey(str)) { writeIndirection(INDIRECTION_TAG, codebaseCache.getVal(str)); } else { write_string(str); if (codebaseCache == null) codebaseCache = new CacheTable(orb,true); codebaseCache.put(str, pos); } } private final int writeValueTag(boolean chunkIt, boolean useRepId, String codebase) { int indirection = 0; if (chunkIt && !useRepId){ if (codebase == null) { write_long(repIdUtil.getStandardRMIChunkedNoRepStrId()); indirection = get_offset() - 4; } else { write_long(repIdUtil.getCodeBaseRMIChunkedNoRepStrId()); indirection = get_offset() - 4; write_codebase(codebase, get_offset()); } } else if (chunkIt && useRepId){ if (codebase == null) { write_long(repIdUtil.getStandardRMIChunkedId()); indirection = get_offset() - 4; } else { write_long(repIdUtil.getCodeBaseRMIChunkedId()); indirection = get_offset() - 4; write_codebase(codebase, get_offset()); } } else if (!chunkIt && !useRepId) { if (codebase == null) { write_long(repIdUtil.getStandardRMIUnchunkedNoRepStrId()); indirection = get_offset() - 4; } else { write_long(repIdUtil.getCodeBaseRMIUnchunkedNoRepStrId()); indirection = get_offset() - 4; write_codebase(codebase, get_offset()); } } else if (!chunkIt && useRepId) { if (codebase == null) { write_long(repIdUtil.getStandardRMIUnchunkedId()); indirection = get_offset() - 4; } else { write_long(repIdUtil.getCodeBaseRMIUnchunkedId()); indirection = get_offset() - 4; write_codebase(codebase, get_offset()); } } return indirection; } private void writeIDLValue(Serializable object, String repID) { if (object instanceof StreamableValue) { ((StreamableValue)object)._write(parent); } else if (object instanceof CustomValue) { ((CustomValue)object).marshal(parent); } else { BoxedValueHelper helper = Utility.getHelper(object.getClass(), null, repID); boolean isCustom = false; if (helper instanceof ValueHelper && object instanceof CustomMarshal) { try { if (((ValueHelper)helper).get_type().type_modifier() == VM_CUSTOM.value) isCustom = true; } catch(BadKind ex) { throw wrapper.badTypecodeForCustomValue( CompletionStatus.COMPLETED_MAYBE, ex ) ; } } if (isCustom) ((CustomMarshal)object).marshal(parent); else helper.write_value(parent, object); } } // Handles end tag compaction... private void writeEndTag(boolean chunked){ if (chunked) { if (get_offset() == end_flag_position) { if (bbwi.position() == end_flag_index) { // We are exactly at the same position and index as the // end of the last end tag. Thus, we can back up over it // and compact the tags. bbwi.position(bbwi.position() - 4); } else { // Special case in which we're at the beginning of a new // fragment, but the position is the same. We can't back up, // so we just write the new end tag without compaction. This // occurs when a value ends and calls start_block to open a // continuation chunk, but it's called at the very end of // a fragment. } } writeNestingLevel(); // Remember the last index and position. These are only used when chunking. end_flag_index = bbwi.position(); end_flag_position = get_offset(); chunkedValueNestingLevel++; } // Increment the nesting level end_flag++; } /** * Handles ORB versioning of the end tag. Should only * be called if chunking. * * If talking to our older ORBs (Standard Extension, * Kestrel, and Ladybird), write the end flag that takes * into account all enclosing valuetypes. * * If talking a newer or foreign ORB, or if the orb * instance is null, write the end flag that only takes * into account the enclosing chunked valuetypes. */ private void writeNestingLevel() { if (orb == null || ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) || ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) { write_long(chunkedValueNestingLevel); } else { write_long(end_flag); } } private void writeClass(String repository_id, Class clz) { if (repository_id == null) repository_id = repIdStrs.getClassDescValueRepId(); // Write value_tag int indirection = writeValueTag(mustChunk, true, null); updateIndirectionTable(indirection, clz, clz); write_repositoryId(repository_id); if (mustChunk) { // Write Value chunk start_block(); end_flag--; chunkedValueNestingLevel--; } else end_flag--; writeClassBody(clz); if (mustChunk) end_block(); // Write end tag writeEndTag(mustChunk); } // Pre-Merlin/J2EE 1.3 ORBs wrote the repository ID // and codebase strings in the wrong order. This handles // backwards compatibility. private void writeClassBody(Class clz) { if (orb == null || ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) || ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) { write_value(Util.getCodebase(clz)); write_value(repIdStrs.createForAnyType(clz)); } else { write_value(repIdStrs.createForAnyType(clz)); write_value(Util.getCodebase(clz)); } } // Casts and returns an Object as a Serializable // This is required for JDK 1.1 only to avoid VerifyErrors when // passing arrays as Serializable // private java.io.Serializable make_serializable(java.lang.Object object) // { // return (java.io.Serializable)object; // } private boolean shouldWriteAsIDLEntity(Serializable object) { return ((object instanceof IDLEntity) && (!(object instanceof ValueBase)) && (!(object instanceof org.omg.CORBA.Object))); } private void writeIDLEntity(IDLEntity object) { // _REVISIT_ could check to see whether chunking really needed mustChunk = true; String repository_id = repIdStrs.createForJavaType(object); Class clazz = object.getClass(); String codebase = Util.getCodebase(clazz); // Write value_tag int indirection = writeValueTag(true, true, codebase); updateIndirectionTable(indirection, object, object); // Write rep. id write_repositoryId(repository_id); // Write Value chunk end_flag--; chunkedValueNestingLevel--; start_block(); // Write the IDLEntity using reflection try { ClassLoader clazzLoader = (clazz == null ? null : clazz.getClassLoader()); final Class helperClass = Utility.loadClassForClass(clazz.getName()+"Helper", codebase, clazzLoader, clazz, clazzLoader); final Class argTypes[] = {org.omg.CORBA.portable.OutputStream.class, clazz}; // getDeclaredMethod requires RuntimePermission accessDeclaredMembers // if a different class loader is used (even though the javadoc says otherwise) Method writeMethod = null; try { writeMethod = (Method)AccessController.doPrivileged( new PrivilegedExceptionAction() { public java.lang.Object run() throws NoSuchMethodException { return helperClass.getDeclaredMethod(kWriteMethod, argTypes); } } ); } catch (PrivilegedActionException pae) { // this gets caught below throw (NoSuchMethodException)pae.getException(); } java.lang.Object args[] = {parent, object}; writeMethod.invoke(null, args); } catch (ClassNotFoundException cnfe) { throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, cnfe ) ; } catch(NoSuchMethodException nsme) { throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, nsme ) ; } catch(IllegalAccessException iae) { throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, iae ) ; } catch(InvocationTargetException ite) { throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, ite ) ; } end_block(); // Write end tag writeEndTag(true); } /* DataOutputStream methods */ public void write_Abstract (java.lang.Object value) { write_abstract_interface(value); } public void write_Value (java.io.Serializable value) { write_value(value); } // This will stay a custom add-on until the java-rtf issue is resolved. // Then it should be declared in org.omg.CORBA.portable.OutputStream. // // Pads the string representation of bigDecimal with zeros to fit the given // digits and scale before it gets written to the stream. public void write_fixed(java.math.BigDecimal bigDecimal, short digits, short scale) { String string = bigDecimal.toString(); String integerPart; String fractionPart; StringBuffer stringBuffer; // Get rid of the sign if (string.charAt(0) == '-' || string.charAt(0) == '+') { string = string.substring(1); } // Determine integer and fraction parts int dotIndex = string.indexOf('.'); if (dotIndex == -1) { integerPart = string; fractionPart = null; } else if (dotIndex == 0 ) { integerPart = null; fractionPart = string; } else { integerPart = string.substring(0, dotIndex); fractionPart = string.substring(dotIndex + 1); } // Pad both parts with zeros as necessary stringBuffer = new StringBuffer(digits); if (fractionPart != null) { stringBuffer.append(fractionPart); } while (stringBuffer.length() < scale) { stringBuffer.append('0'); } if (integerPart != null) { stringBuffer.insert(0, integerPart); } while (stringBuffer.length() < digits) { stringBuffer.insert(0, '0'); } // This string contains no sign or dot this.write_fixed(stringBuffer.toString(), bigDecimal.signum()); } // This method should be remove by the java-rtf issue. // Right now the scale and digits information of the type code is lost. public void write_fixed(java.math.BigDecimal bigDecimal) { // This string might contain sign and/or dot this.write_fixed(bigDecimal.toString(), bigDecimal.signum()); } // The string may contain a sign and dot public void write_fixed(String string, int signum) { int stringLength = string.length(); // Each octet contains (up to) two decimal digits byte doubleDigit = 0; char ch; byte digit; // First calculate the length of the string without optional sign and dot int numDigits = 0; for (int i=0; i