package org.jacorb.orb; /* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.giop.GIOPConnection; import org.jacorb.orb.typecode.DelegatingTypeCodeWriter; import org.jacorb.orb.typecode.TypeCodeCompactor; import org.jacorb.util.ObjectUtil; import org.jacorb.util.ValueHandler; import org.omg.CORBA.BAD_PARAM; import org.omg.CORBA.DATA_CONVERSION; import org.omg.CORBA.INTERNAL; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.NO_IMPLEMENT; import org.omg.CORBA.StringValueHelper; import org.omg.CORBA.TCKind; import org.omg.CORBA.WStringValueHelper; import org.omg.CORBA.TypeCodePackage.BadKind; import org.omg.CORBA.TypeCodePackage.Bounds; import org.omg.CORBA.portable.BoxedValueHelper; import org.omg.CORBA.portable.ValueOutputStream; import org.omg.IOP.IOR; import org.omg.IOP.IORHelper; import org.omg.IOP.TaggedProfile; /** * @author Gerald Brose * * A stream for CDR marshalling. * */ public class CDROutputStream extends org.omg.CORBA_2_3.portable.OutputStream implements CodeSet.OutputBuffer, ValueOutputStream { private final static IOR null_ior = new IOR("", new TaggedProfile[0]); /** needed for alignment purposes */ private int index; /** the current write position in the buffer */ private int pos; private final IBufferManager bufMgr; protected byte[] buffer; private boolean closed; /* character encoding code sets for char and wchar, default ISO8859_1 */ private CodeSet codeSet; private CodeSet codeSetW; private int encaps_start = -1; /** * <code>encaps_stack</code> is used to store encapsulations. Do NOT * access this variable directly. It is initialized on demand. Use the * method {@link #getEncapsStack() getEncapsStack()} */ private ArrayDeque<EncapsInfo> encaps_stack; /** * <code>valueMap</code> is used to maps all value objects that have * already been written to this stream to their position within the * buffer. The position is stored as a java.lang.Integer. Do NOT access * this variable directly. It is initialised on demand. Use the method * {@link #getValueMap() getValueMap()} */ private Map valueMap; /** * <code>repIdMap</code> is used to map all repository ids that have already * been written to this stream to their position within the buffer. The * position is stored as a java.lang.Integer. Do NOT access this variable * directly. It is initialised on demand. Use the method * {@link #getRepIdMap() getRepIdMap()} */ private Map repIdMap; /** * <code>codebaseMap</code> is used to maps all codebase strings that have * already been written to this stream to their position within the buffer. * The position is stored as a java.lang.Integer. Do NOT access this variable * directly. It is initialised on demand. Use the method * {@link #getCodebaseMap() getCodebaseMap()} */ private Map codebaseMap; /** * This Map is basically a one-entry map pool to be used in * write_TypeCode() as the repeatedTCMap. */ private Map repeatedTCMap; /** * This Map is basically a one-entry map pool to be used in * write_TypeCode() as the recursiveTCMap. */ private Map recursiveTCMap; /** Remembers the starting position of the current chunk. */ private int chunk_size_tag_pos = -1; // -1 means we're not within a chunk private int chunk_size_tag_index; private int chunk_octets_pos; /** Nesting level of chunked valuetypes */ private int valueNestingLevel = 0; /** Nesting level of calls write_value_internal */ private int writeValueNestingLevel = 0; /** True if write_value_internal called writeReplace */ private boolean writeReplaceCalled = false; protected final org.jacorb.orb.ORBSingleton orb; protected int giop_minor = 2; /** The chunking flag is either 0 (no chunking) or 0x00000008 (chunking), to be bitwise or'ed into value tags. */ private int chunkingFlag = 0; /** * <code>mutator</code> is a pluggable IOR mutator. */ private IORMutator mutator; private boolean isMutatorEnabled; /** * <code>codesetEnabled</code> denotes whether codeset marshalling is enabled. */ private boolean codesetEnabled; /** configurable properties */ private boolean useBOM = false; private boolean chunkCustomRmiValuetypes = false; private boolean useIndirection = true; private boolean nullStringEncoding; // by default stream version is 1 for GIOP v1.2 messages private byte maxStreamFormatVersion = ValueHandler.STREAM_FORMAT_VERSION_1; private final TypeCodeCompactor typeCodeCompactor; private final static DelegatingTypeCodeWriter typeCodeWriter = new DelegatingTypeCodeWriter(); /** * size selecting c'tor * @param orb must be a JacORB ORB * @param bufferSize -1 to fetch the default buffer size, * value > 0 to specify a specific size */ public CDROutputStream(org.omg.CORBA.ORB orb, int bufferSize) { super(); if (! (orb instanceof ORBSingleton)) { throw new BAD_PARAM("don't pass in a non JacORB ORB"); } this.orb = (ORBSingleton) orb; bufMgr = this.orb.getBufferManager(); typeCodeCompactor = this.orb.getTypeCodeCompactor(); try { configure(((ORBSingleton)orb).getConfiguration()); } catch(ConfigurationException e) { throw new INTERNAL(e.getMessage()); } if (bufferSize == -1) { buffer = bufMgr.getPreferredMemoryBuffer(); } else { buffer = bufMgr.getBuffer(bufferSize); } } /** * OutputStreams created using this constructor * are used also for in memory marshaling, but do use the * ORB's output buffer manager */ public CDROutputStream(final org.omg.CORBA.ORB orb) { this(orb, -1); } /** * OutputStreams created using the empty constructor are used for * in memory marshaling. A stream created with this c'tor is not explicitly * configured, i.e. it will use default configuration only */ public CDROutputStream() { this(org.omg.CORBA.ORBSingleton.init()); } @Override public org.omg.CORBA.ORB orb() { return orb; } /** * This stream is self-configuring, i.e. configure() is private * and only called from the constructor */ private void configure(Configuration configuration) throws ConfigurationException { codesetEnabled = configuration.getAttributeAsBoolean ("jacorb.codeset", true); useBOM = configuration.getAttributeAsBoolean("jacorb.use_bom",false); chunkCustomRmiValuetypes = configuration.getAttributeAsBoolean("jacorb.interop.chunk_custom_rmi_valuetypes", false); useIndirection = !( configuration.getAttributeAsBoolean("jacorb.interop.indirection_encoding_disable", false)); nullStringEncoding = configuration.getAttributeAsBoolean("jacorb.interop.null_string_encoding", false); mutator = (IORMutator) configuration.getAttributeAsObject("jacorb.iormutator"); isMutatorEnabled = (mutator != null); maxStreamFormatVersion = (byte) configuration.getAttributeAsInteger("jacorb.interop.maximum_stream_format_version", 1); codeSet = orb.getTCSDefault(); codeSetW = orb.getTCSWDefault(); } /** * <code>getEncapsStack</code> is used to initialize encaps_stack * on demand. * * @return a <code>ArrayDeque</code> value */ private ArrayDeque<EncapsInfo> getEncapsStack() { if (encaps_stack == null) { encaps_stack = new ArrayDeque<EncapsInfo>(); } return encaps_stack; } /** * Gets the Map that is used to detect reference sharing when * marshaling valuetype instances. * * @return a <code>Map</code> value */ private Map<Serializable, Integer> getValueMap() { if (valueMap == null) { valueMap = new IdentityHashMap<Serializable, Integer>(); } return valueMap; } /** * Gets the Map that is used to implement indirections for RepositoryIDs. * * @return a <code>Map</code> value */ private Map<String, Integer> getRepIdMap() { if (repIdMap == null) { repIdMap = new HashMap<String, Integer>(); } return repIdMap; } /** * Gets the Map that is used to implement indirections for Codebase * specifications. * * @return a <code>Map</code> value */ private Map<String, Integer> getCodebaseMap() { if (codebaseMap == null) { codebaseMap = new HashMap<String, Integer>(); } return codebaseMap; } /** * write the contents of this CDR stream to the output stream * called by, e.g. GIOPConnection to write directly to the * wire. */ public void write( OutputStream out, int start, int length ) throws IOException { if( start < start + length ) { out.write( buffer, start, length ); } } public void setCodeSets(CodeSet codeSet, CodeSet codeSetWide) { this.codeSet = codeSet; this.codeSetW = codeSetWide; } public void setGIOPMinor(final int giop_minor) { this.giop_minor = giop_minor; } public int getGIOPMinor() { return giop_minor; } @Override public void close() { // Don't need to call super.close as super is noop. if( closed ) { return; } bufMgr.returnBuffer( buffer, true ); buffer = null; closed = true; } /** * This version of check does both array length checking and * data type alignment. It is a convenience method. */ public final void check(final int i, final int align) { final int remainder = align - (index % align); check (i + remainder); if (remainder != align) { // Clear padding. Allowing for possible buffer end. final int blp = buffer.length - pos; final int topad = (blp <= 8 ? blp : 8); Arrays.fill(buffer, pos, pos + topad, (byte)0); index += remainder; pos += remainder; } } /** * check whether the current buffer is big enough to receive * i more bytes. If it isn't, get a bigger buffer. */ private final void check(final int i) { final int requiredSize = pos + i + 2; if (buffer == null || requiredSize > buffer.length) { final byte[] new_buf = bufMgr.getExpandedBuffer(requiredSize); if (buffer != null) { System.arraycopy(buffer, 0, new_buf, 0, pos); } // Change buffer size so return the old one. bufMgr.returnBuffer (buffer, true); buffer = new_buf; } } private final static void _write4int (final byte[] buf, final int _pos, final int value) { buf[_pos] = (byte)((value >> 24) & 0xFF); buf[_pos+1] = (byte)((value >> 16) & 0xFF); buf[_pos+2] = (byte)((value >> 8) & 0xFF); buf[_pos+3] = (byte) (value & 0xFF); } /** * Start a CDR encapsulation. All subsequent writes * will place data in the encapsulation until * endEncapsulation is called. This will write * the size of the encapsulation. */ public final void beginEncapsulation() { // align to the next four byte boundary // as a preparation for writing the size // integer (which we don't know before the // encapsulation is closed) check(8,4); // leave 4 bytes for the encaps. size that // is to be written later pos += 4; index += 4; /* Because encapsulations can be nested, we need to remember the beginnning of the enclosing encapsulation (or -1 if we are in the outermost encapsulation) Also, remember the current index and the indirection maps because we need to restore these when closing the encapsulation */ getEncapsStack().push ( new EncapsInfo(index, encaps_start, getValueMap(), getRepIdMap(), getCodebaseMap()) ); // set up new indirection maps for this encapsulation valueMap = new IdentityHashMap(); repIdMap = new HashMap(); codebaseMap = new HashMap(); // the start of this encapsulation encaps_start = pos; beginEncapsulatedArray(); } /** * Can be used locally for data type conversions * without preceeding call to beginEncapsulation, i.e. * without a leading long that indicates the size. */ public final void beginEncapsulatedArray() { /* set the index for alignment to 0, i.e. align relative to the beginning of the encapsulation */ index = 0; // byte_order flag set to FALSE buffer[pos++] = 0; index++; } /** * Terminate the encapsulation by writing its length * to its beginning. */ public final void endEncapsulation() { if( encaps_start == -1 ) { throw new MARSHAL("Too many end-of-encapsulations"); } if( encaps_stack == null ) { throw new MARSHAL("Internal Error - closeEncapsulation failed"); } // determine the size of this encapsulation int encaps_size = pos - encaps_start; // insert the size integer into the appropriate place buffer[encaps_start -4 ] = (byte)((encaps_size >>> 24) & 0xFF); buffer[encaps_start -3 ] = (byte)((encaps_size >>> 16) & 0xFF); buffer[encaps_start -2 ] = (byte)((encaps_size >>> 8) & 0xFF); buffer[encaps_start -1 ] = (byte)(encaps_size & 0xFF); /* restore index and encaps_start information and indirection maps */ EncapsInfo ei = getEncapsStack().pop(); encaps_start = ei.start; index = ei.index + encaps_size; valueMap = ei.valueMap; repIdMap = ei.repIdMap; codebaseMap = ei.codebaseMap; } public byte[] getBufferCopy() { final ByteArrayOutputStream bos = new ByteArrayOutputStream(size()); try { write( bos, 0, size()); } catch( IOException e ) { throw new INTERNAL("should not happen: " + e.toString()); } return bos.toByteArray(); } public int size() { return pos; } public final void skip(final int step) { pos += step; index += step; } public final void reduceSize(final int amount) { pos -= amount; } /** * Add <tt>amount</tt> empty space */ public final void increaseSize(final int amount) { check( amount ); pos += amount; } /** * Replace existing buffer by a new one and reset indices. * * @param b the new buffer */ public void setBuffer(final byte[] b) { bufMgr.returnBuffer( buffer, true ); buffer = b; pos = 0; index = 0; } /** * Give up the buffer to the caller and mark the stream closed. It is * up to the caller to eventually return the buffer to the manager. */ public byte[] releaseBuffer () { byte [] retn = buffer; buffer = null; close (); return retn; } /************************************************** * The following operations are from OutputStream * **************************************************/ @Override public org.omg.CORBA.portable.InputStream create_input_stream() { final byte[] result = new byte[index]; System.arraycopy(buffer, 0, result, 0, result.length); return new CDRInputStream(orb, result); } @Override public final void write_any(final org.omg.CORBA.Any value) { if( value == null ) { throw new MARSHAL("Cannot marshall null value."); } write_TypeCode( value.type() ); value.write_value( this ) ; } @Override public final void write_boolean(final boolean value) { check(1); if( value ) { buffer[pos++] = 1; } else { buffer[pos++] = 0; } index++; } @Override public final void write_boolean_array (final boolean[] value, final int offset, final int length) { if (value != null ) { //no alignment necessary check(length); for( int i = offset; i < offset+length; i++ ) { if( value[i] ) { buffer[pos++] = 1; } else { buffer[pos++] = 0; } } index += length; } } /** * <code>write_char</code> writes a character to the output stream. If * codeset translation is active then it will use String and an encoding to * get the bytes. It can then do a test for whether to throw DATA_CONVERSION. * * @param c a <code>char</code> value */ @Override public final void write_char (final char c) { // According to 15.3.1.6 of CORBA 3.0 'a single instance of the char type // may only hold one octet of any multi-byte character encoding.' // Therefore we ensure that we are in the single byte range i.e. // less than 0xFF or \377. if (c > '\377') { throw new DATA_CONVERSION ("Char " + c + " out of range"); } check( 1 ); buffer[pos++] = (byte)c; index++; } @Override public final void write_char_array (final char[] value, final int offset, final int length) { if( value == null ) { throw new MARSHAL("Cannot marshall null array."); } else if ( offset + length > value.length || length < 0 || offset < 0 ) { throw new MARSHAL ("Cannot marshall as indices for array are out bounds."); } check( length ); for (int i=offset; i < length+offset; i++) { if (value[i] > '\377') { throw new DATA_CONVERSION ("Char " + value[i] + " out of range"); } buffer[pos++] = (byte)value[i]; } index+=length; } /** * <code>write_string</code> writes a string to the output stream. It is * optimised for whether it is writing a blank string or for whether codeset * translation is active. * * @param value a <code>String</code> value */ @Override public final void write_string(final String value) { if( value == null ) { if (nullStringEncoding) { write_long(0); return; } else { throw new MARSHAL("Cannot marshall null string."); } } final int valueLength = value.length(); // size leaves room for ulong, plus the string itself (one or more // bytes per char in the string, depending on the codeset), plus the // terminating NUL char int size; // sizePosition is the position in the buffer for the size to be // written. int sizePosition; if (valueLength == 0) { // Blank string; just write size and terminator. Don't need to // align here as these methods will do it for us. write_long( 1 ); write_octet( (byte)0 ); } else { if (codesetEnabled) { // in the worst case (UTF-8) a string char might take up to 4 bytes size = 4 + ( 4 * valueLength ) + 1; } else { // just one byte per string char size = 4 + valueLength + 1; } check(size, 4); sizePosition = pos; pos += 4; index += 4; if (codesetEnabled) { codeSet.write_string(this, value, false, false, giop_minor); } else { // If not using codesets then use the deprecated getBytes for speed. value.getBytes(0, valueLength, buffer, pos); index += valueLength; pos += valueLength; } buffer[ pos++ ] = (byte) 0; //terminating NUL char index ++; // Now write the size back in. size = pos - (sizePosition + 4); // compute translated size _write4int( buffer, sizePosition, size); } } @Override public final void write_wchar(final char c) { check(6); // maximum is UTF-16 handling non-BPM character with BOM // alignment/check must happen prior to calling. codeSetW.write_char( this, c, codeSetW.write_bom( useBOM ), true, giop_minor ); } @Override public void write_byte( byte b ) { buffer[pos++] = b; index ++; } @Override public final void write_wchar_array (final char[] value, final int offset, final int length) { if( value == null ) { throw new MARSHAL("Null References"); } check( length * 3 ); for( int i = offset; i < offset+length; i++) { write_wchar( value[i] ); } } @Override public final void write_wstring(final String s) { if( s == null ) { throw new MARSHAL("Null References"); } //size ulong + no of bytes per char (max 3 if UTF-8) + //terminating NUL check( 4 + s.length() * 3 + 3, 4); int startPos = pos; // store position for length indicator pos += 4; index += 4; // reserve for length indicator //the byte order marker if( this.giop_minor == 2 && codeSetW.write_bom( useBOM ) && s.length() > 0) { //big endian encoding buffer[ pos++ ] = (byte) 0xFE; buffer[ pos++ ] = (byte) 0xFF; index += 2; } // write characters in current wide encoding, add null terminator for( int i = 0; i < s.length(); i++ ) { codeSetW.write_char( this, s.charAt(i), false, false, this.giop_minor ); } int str_size; if (this.giop_minor >= 2) { //size in bytes (without the size ulong) str_size = pos - startPos - 4; } else { //terminating NUL char codeSetW.write_char( this, (char)0, false, false, giop_minor ); str_size = codeSetW.get_wstring_size( s, startPos, pos ); } // write length indicator _write4int( buffer, startPos, str_size ); } @Override public final void write_double(final double value) { write_longlong (Double.doubleToLongBits (value)); } @Override public final void write_double_array (final double[] value, final int offset, final int length) { //if nothing has to be written, return, and especially DON'T //ALIGN if( length == 0 ) { return; } /* align to 8 byte boundary */ check(7 + length*8, 8); if( value != null ) { for( int i = offset; i < offset+length; i++ ) { long d = Double.doubleToLongBits(value[i]); buffer[pos] = (byte)((d >>> 56) & 0xFF); buffer[pos+1] = (byte)((d >>> 48) & 0xFF); buffer[pos+2] = (byte)((d >>> 40) & 0xFF); buffer[pos+3] = (byte)((d >>> 32) & 0xFF); buffer[pos+4] = (byte)((d >>> 24) & 0xFF); buffer[pos+5] = (byte)((d >>> 16) & 0xFF); buffer[pos+6] = (byte)((d >>> 8) & 0xFF); buffer[pos+7] = (byte) (d & 0xFF); pos += 8; } index += 8*length; } } public void write_fixed(BigDecimal value, short digits, short scale) { String v = value.unscaledValue().toString(); byte [] representation; int b, c; // Strip off any leading '-' from value to encode if (v.startsWith ("-")) { v = v.substring (1); } if( (v.length() %2) == 0) { representation = new byte[ v.length()/2 +1]; representation[0] = 0x00; for( int i = 0; i < v.length(); i++ ) { c = Character.digit(v.charAt(i), 10); b = representation[(1 + i)/2] << 4; b |= c; representation[(1 + i)/2] = (byte)b; } } else { representation = new byte[ (v.length()+1) /2]; for( int i = 0; i < v.length(); i++ ) { c = Character.digit(v.charAt(i), 10); b = representation[i/2] << 4; b |= c; representation[i/2] = (byte)b; } } b = representation[representation.length-1] << 4; representation[representation.length-1] = (byte)((value.signum() < 0 )? (b | 0xD) : (b | 0xC)); check(representation.length); System.arraycopy(representation,0,buffer,pos,representation.length); index += representation.length; pos += representation.length; } /** * @deprecated */ @Override @Deprecated public void write_fixed(final java.math.BigDecimal value) { write_fixed(value, (short)-1, (short)-1); } @Override public final void write_float(final float value) { write_long(Float.floatToIntBits(value)); } @Override public final void write_float_array (final float[] value, final int offset, final int length) { //if nothing has to be written, return, and especially DON'T //ALIGN if( length == 0 ) { return; } /* align to 4 byte boundary */ check(3 + length*4,4); if( value != null ) { for( int i = offset; i < offset+length; i++ ) { _write4int(buffer,pos, Float.floatToIntBits( value[i] )); pos += 4; } index += 4*length; } } @Override public final void write_long(final int value) { check(7,4); _write4int(buffer,pos,value); pos += 4; index += 4; } @Override public final void write_long_array (final int[] value, final int offset, final int length) { //if nothing has to be written, return, and especially DON'T //ALIGN if( length == 0 ) { return; } /* align to 4 byte boundary */ check(3 + length*4,4); final int remainder = 4 - (index % 4); if (remainder != 4) { index += remainder; pos+=remainder; } if( value != null ) { for( int i = offset; i < offset+length; i++ ) { _write4int(buffer,pos,value[i]); pos += 4; } index += 4*length; } } @Override public final void write_longlong(final long value) { check(15,8); buffer[pos] = (byte)((value >>> 56) & 0xFF); buffer[pos+1] = (byte)((value >>> 48) & 0xFF); buffer[pos+2] = (byte)((value >>> 40) & 0xFF); buffer[pos+3] = (byte)((value >>> 32) & 0xFF); buffer[pos+4] = (byte)((value >>> 24) & 0xFF); buffer[pos+5] = (byte)((value >>> 16) & 0xFF); buffer[pos+6] = (byte)((value >>> 8) & 0xFF); buffer[pos+7] = (byte)(value & 0xFF); index += 8; pos += 8; } @Override public final void write_longlong_array (final long[] value, final int offset, final int length) { //if nothing has to be written, return, and especially DON'T //ALIGN if( length == 0 ) { return; } check(7 + length*8,8); if( value != null ) { for( int i = offset; i < offset+length; i++ ) { buffer[pos] = (byte)((value[i] >>> 56) & 0xFF); buffer[pos+1] = (byte)((value[i] >>> 48) & 0xFF); buffer[pos+2] = (byte)((value[i] >>> 40) & 0xFF); buffer[pos+3] = (byte)((value[i] >>> 32) & 0xFF); buffer[pos+4] = (byte)((value[i] >>> 24) & 0xFF); buffer[pos+5] = (byte)((value[i] >>> 16) & 0xFF); buffer[pos+6] = (byte)((value[i] >>> 8) & 0xFF); buffer[pos+7] = (byte) (value[i] & 0xFF); pos += 8; } index += 8*length; } } @Override public void write_Object(final org.omg.CORBA.Object value) { if( value == null ) { IORHelper.write(this, null_ior ); } else { if( value instanceof org.omg.CORBA.LocalObject ) { throw new MARSHAL("Attempt to serialize a locality-constrained object."); } org.omg.CORBA.portable.ObjectImpl obj = (org.omg.CORBA.portable.ObjectImpl)value; IOR intermediary = ((Delegate)obj._get_delegate()).getIOR(); if (isMutatorEnabled) { intermediary = mutator.mutateOutgoing (intermediary); } IORHelper.write(this, intermediary); } } ////////////////////////////////////////////// NEW! public void write_IOR(final IOR ior) { if( ior == null ) { IORHelper.write(this, null_ior ); } else { IORHelper.write(this, ior); } } ////////////////////////////////////////////// NEW! @Override public final void write_octet(final byte value) { check(1); index++; buffer[pos++] = value; } @Override public final void write_octet_array( final byte[] value, final int offset, final int length) { if( value != null ) { check(length); System.arraycopy(value,offset,buffer,pos,length); index += length; pos += length; } } @Override public final void write_Principal(final org.omg.CORBA.Principal value) { throw new NO_IMPLEMENT ("Principal deprecated"); } @Override public final void write_short(final short value) { check(3,2); buffer[pos++] = (byte)((value >> 8) & 0xFF); buffer[pos++] = (byte)( value & 0xFF); index += 2; } @Override public final void write_short_array (final short[] value, final int offset, final int length) { //if nothing has to be written, return, and especially DON'T //ALIGN if( length == 0 ) { return; } /* align to 2-byte boundary */ check(2*length + 3); final int remainder = 2 - (index % 2); if (remainder != 2) { index += remainder; pos+=remainder; } if( value != null ) { for( int i = offset; i < offset+length; i++ ) { buffer[pos++] = (byte)((value[i] >> 8) & 0xFF ); buffer[pos++] = (byte)( value[i] & 0xFF ); } index += 2*length; } } @Override public final void write_TypeCode (org.omg.CORBA.TypeCode typeCode) { if (typeCode == null) { throw new BAD_PARAM("TypeCode is null"); } typeCode = typeCodeCompactor.getCompactTypeCode(typeCode); if (repeatedTCMap == null) { repeatedTCMap = new HashMap(); } if (recursiveTCMap == null) { recursiveTCMap = new HashMap(); } try { write_TypeCode(typeCode, recursiveTCMap, repeatedTCMap); } finally { repeatedTCMap.clear(); recursiveTCMap.clear(); } } public final void write_TypeCode(final org.omg.CORBA.TypeCode typeCode, final Map recursiveTCMap, final Map repeatedTCMap) { typeCodeWriter.writeTypeCode(typeCode, this, recursiveTCMap, repeatedTCMap); } @Override public final void write_ulong(final int value) { write_long (value); } @Override public final void write_ulong_array (final int[] value, final int offset, final int length) { write_long_array (value, offset, length); } @Override public final void write_ulonglong(final long value) { write_longlong (value); } @Override public final void write_ulonglong_array (final long[] value, final int offset, final int length) { write_longlong_array (value, offset, length); } @Override public final void write_ushort(final short value) { write_short (value); } @Override public final void write_ushort_array (final short[] value, final int offset, final int length) { write_short_array (value, offset, length); } /** * Reads a value of the type indicated by <code>tc</code> from the * InputStream <code>in</code> and remarshals it to this CDROutputStream. * Called from Any. */ public final void write_value ( final org.omg.CORBA.TypeCode typeCode, final org.omg.CORBA.portable.InputStream input ) { if (typeCode == null) { throw new BAD_PARAM("TypeCode is null"); } int kind = typeCode.kind().value(); try { switch (kind) { case TCKind._tk_null: // 0 // fallthrough case TCKind._tk_void: // 1 { break; } case TCKind._tk_short: // 2 { write_short( input.read_short()); break; } case TCKind._tk_long: // 3 { write_long( input.read_long()); break; } case TCKind._tk_ushort: // 4 { write_ushort(input.read_ushort()); break; } case TCKind._tk_ulong: // 5 { write_ulong( input.read_ulong()); break; } case TCKind._tk_float: // 6 { write_float( input.read_float()); break; } case TCKind._tk_double: // 7 { write_double(input.read_double()); break; } case TCKind._tk_boolean: // 8 { write_boolean( input.read_boolean()); break; } case TCKind._tk_char: // 9 { write_char( input.read_char()); break; } case TCKind._tk_octet: // 10 { write_octet( input.read_octet()); break; } case TCKind._tk_any: // 11 { write_any( input.read_any()); break; } case TCKind._tk_TypeCode: // 12 { write_TypeCode(input.read_TypeCode()); break; } case TCKind._tk_Principal: // 13 { throw new NO_IMPLEMENT ("Principal deprecated"); } case TCKind._tk_objref: // 14 { write_Object( input.read_Object()); break; } case TCKind._tk_struct: // 15 { for( int i = 0; i < typeCode.member_count(); i++) { write_value( typeCode.member_type(i), input ); } break; } case TCKind._tk_union: // 16 { org.omg.CORBA.TypeCode disc = typeCode.discriminator_type(); disc = TypeCode.originalType(disc); int def_idx = typeCode.default_index(); int member_idx = -1; switch( disc.kind().value() ) { case TCKind._tk_short: // 2 { short s = input.read_short(); write_short(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_short()) { member_idx = i; break; } } } break; } case TCKind._tk_long: // 3 { int s = input.read_long(); write_long(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_long()) { member_idx = i; break; } } } break; } case TCKind._tk_ushort: // 4 { short s = input.read_ushort(); write_ushort(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_ushort()) { member_idx = i; break; } } } break; } case TCKind._tk_ulong: // 5 { int s = input.read_ulong(); write_ulong(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_ulong()) { member_idx = i; break; } } } break; } case TCKind._tk_float: // 6 // fallthrough case TCKind._tk_double: // 7 { throw new MARSHAL( "Invalid union discriminator type: " + disc); } case TCKind._tk_boolean: // 8 { boolean s = input.read_boolean(); write_boolean(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_boolean()) { member_idx = i; break; } } } break; } case TCKind._tk_char: // 9 { char s = input.read_char(); write_char(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_char()) { member_idx = i; break; } } } break; } case TCKind._tk_octet: // 10 case TCKind._tk_any: // 11 case TCKind._tk_TypeCode: // 12 case TCKind._tk_Principal: // 13 case TCKind._tk_objref: // 14 case TCKind._tk_struct: // 15 case TCKind._tk_union: // 16 { throw new MARSHAL( "Invalid union discriminator type: " + disc); } case TCKind._tk_enum: // 17 { int s = input.read_long(); write_long(s); for( int i = 0 ; i < typeCode.member_count(); i++ ) { if( i != def_idx) { int label = typeCode.member_label(i).create_input_stream().read_long(); /* we have to use the any's input stream because enums are not inserted as longs */ if( s == label) { member_idx = i; break; } } } break; } case TCKind._tk_string: // 18 case TCKind._tk_sequence: // 19 case TCKind._tk_array: // 20 case TCKind._tk_alias: // 21 case TCKind._tk_except: // 22 { throw new MARSHAL( "Invalid union discriminator type: " + disc); } case TCKind._tk_longlong: // 23 { long s = input.read_longlong(); write_longlong(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_longlong()) { member_idx = i; break; } } } break; } case TCKind._tk_ulonglong: // 24 { long s = input.read_ulonglong(); write_ulonglong(s); for(int i = 0 ; i < typeCode.member_count() ; i++) { if(i != def_idx) { if(s == typeCode.member_label(i).extract_ulonglong()) { member_idx = i; break; } } } break; } default: { throw new MARSHAL("Invalid union discriminator type: " + disc); } } // write the member or default value, if any // (if the union has no explicit default but the // the case labels do not cover the range of // possible discriminator values, there may be // several "implicit defaults" without associated // union values. if( member_idx != -1 ) { write_value( typeCode.member_type( member_idx ), input ); } else if( def_idx != -1 ) { write_value( typeCode.member_type( def_idx ), input ); } break; } case TCKind._tk_enum: // 17 { write_long( input.read_long() ); break; } case TCKind._tk_string: // 18 { write_string( input.read_string()); break; } case TCKind._tk_sequence: // 19 { int len = input.read_long(); write_long(len); org.omg.CORBA.TypeCode content_tc = typeCode.content_type(); for( int i = 0; i < len; i++ ) { write_value(content_tc, input); } break; } case TCKind._tk_array: // 20 { int length = typeCode.length(); if( typeCode.content_type().kind().value() == TCKind._tk_octet ) { check( length ); input.read_octet_array( buffer, pos, length); index+= length; pos += length; } else { for( int i = 0; i < length; i++ ) { write_value( typeCode.content_type(), input ); } } break; } case TCKind._tk_alias: // 21 { write_value( typeCode.content_type(), input ); break; } case TCKind._tk_except: // 22 { write_string( input.read_string()); for (int i = 0; i < typeCode.member_count(); i++) { write_value(typeCode.member_type(i), input); } break; } case TCKind._tk_longlong: // 23 { write_longlong(input.read_longlong()); break; } case TCKind._tk_ulonglong: // 24 { write_ulonglong( input.read_ulonglong()); break; } case TCKind._tk_longdouble: // 25 { throw new org.omg.CORBA.BAD_TYPECODE( "type longdouble not supported in java"); } case TCKind._tk_wchar: // 26 { write_wchar( input.read_wchar()); break; } case TCKind._tk_wstring: // 27 { write_wstring( input.read_wstring()); break; } case TCKind._tk_fixed: // 28 { final short digits = typeCode.fixed_digits(); final short scale = typeCode.fixed_scale(); final BigDecimal value; if (input instanceof CDRInputStream) { value = ((CDRInputStream)input).read_fixed(digits, scale); } else { // TODO can we remove this? mixed usage orb classes from different vendors ... value = input.read_fixed(); } write_fixed(value, digits, scale); break; } case TCKind._tk_value: // 29 { final Serializable val = ((org.omg.CORBA_2_3.portable.InputStream)input).read_value(); write_value(val, typeCode.id()); break; } case TCKind._tk_value_box: { String id = typeCode.id(); org.omg.CORBA.portable.BoxedValueHelper helper = ((org.jacorb.orb.ORB)orb()).getBoxedValueHelper(id); if (helper == null) { throw new MARSHAL("No BoxedValueHelper for id " + id); } java.io.Serializable value = ((org.omg.CORBA_2_3.portable.InputStream)input).read_value(helper); write_value (value, helper); break; } default: { throw new MARSHAL("Cannot handle TypeCode with kind " + kind); } } } catch (BadKind ex) { throw new MARSHAL ("When processing TypeCode with kind: " + kind + " caught " + ex); } catch (Bounds ex) { throw new MARSHAL ("When processing TypeCode with kind: " + kind + " caught " + ex); } } /** * Writes the serialized state of `value' to this stream. */ @Override public void write_value(final java.io.Serializable value) { if (!write_special_value (value)) { write_value_internal (value, ValueHandler.getRMIRepositoryID (value.getClass())); } } @Override public void write_value(final java.io.Serializable value, final org.omg.CORBA.portable.BoxedValueHelper factory) { if (!write_special_value (value)) { write_previous_chunk_size(); check(7,4); getValueMap().put (value, Integer.valueOf(size())); if (((value instanceof org.omg.CORBA.portable.IDLEntity) || isSimpleString(value, factory))) { write_long (0x7fffff00 | chunkingFlag); } else { // repository id is required for RMI: types write_long (0x7fffff02 | chunkingFlag); final String repId = factory.get_id(); write_repository_id (repId); } start_chunk(); factory.write_value (this, value); end_chunk(); } } /** * check if the specified value may be marshalled as a simple string (length, value) * or it should be encoded as a valuetype. */ private boolean isSimpleString(Serializable value, BoxedValueHelper factory) { if (!(value instanceof String)) { return false; } if (factory.get_id().equals(StringValueHelper.id())) { return false; } if (factory.get_id().equals(WStringValueHelper.id())) { return false; } return true; } @Override public void write_value(final java.io.Serializable value, final java.lang.Class clazz) { if (!write_special_value (value)) { final Class _clazz = value.getClass(); final String repId = ValueHandler.getRMIRepositoryID(_clazz); if (_clazz == clazz && !repId.startsWith("RMI:")) { // the repository id is required for "RMI:" valuetypes write_value_internal (value, null); } else if (clazz.isInstance (value)) { write_value_internal (value, repId); } else { throw new BAD_PARAM(); } } } @Override public void write_value(final java.io.Serializable value, final String repository_id) { if (!write_special_value (value)) { write_value_internal (value, repository_id); } } /** * If `value' is null, or has already been written to this stream, * then this method writes that information to the stream and returns * true, otherwise does nothing and returns false. */ private boolean write_special_value(final java.io.Serializable value) { if (value == null) { // null tag write_long (0x00000000); return true; } Integer index = getValueMap().get (value); if (index != null) { // value has already been written -- make an indirection write_long (0xffffffff); write_long (index.intValue() - size()); return true; } return false; } /** * Writes `repository_id' to this stream, perhaps via indirection. */ private void write_repository_id(final String repository_id) { Integer _index = getRepIdMap().get (repository_id); if ( _index == null) { // a new repository id -- write it // first make sure the pos we're about to remember is // a correctly aligned one, i.e., the actual writing position int remainder = 4 - (index % 4); if ( remainder != 4 ) { index += remainder; pos += remainder; } getRepIdMap().put (repository_id, Integer.valueOf(size())); write_string (repository_id); } else { // a previously written repository id -- make an indirection write_long (0xffffffff); write_long (_index.intValue() - size()); } } /** * Writes `codebase' to this stream, perhaps via indirection. */ private void write_codebase(final String codebase) { Integer _index = null; if (codebaseMap == null) { codebaseMap = new HashMap(); } else { _index = getCodebaseMap().get (codebase); } if ( _index == null) { // a new codebase -- write it# // first make sure the pos we're about to remember is // a correctly aligned one int remainder = 4 - (index % 4); if ( remainder != 4 ) { index += remainder; pos += remainder; } getCodebaseMap().put (codebase, Integer.valueOf(size())); write_string (codebase); } else { // a previously written codebase -- make an indirection write_long (0xffffffff); write_long (_index.intValue() - size()); } } /** * Writes to this stream a value header with the specified `repository_id' * and no codebase string. */ private void write_value_header(final String[] repository_ids) { if (repository_ids != null) { if( repository_ids.length > 1 ) { // truncatable value type, must use chunking! chunkingFlag = 0x00000008; write_long (0x7fffff06 | chunkingFlag); write_long( repository_ids.length ); for( int i = 0; i < repository_ids.length; i++ ) { write_repository_id (repository_ids[i]); } } else { write_long (0x7fffff02 | chunkingFlag); write_repository_id (repository_ids[0]); } } else { write_long (0x7fffff00 | chunkingFlag); } } /** * Writes to this stream a value header with the specified `repository_id'. * and `codebase' string. */ private void write_value_header(final String[] repository_ids, final String codebase) { if (codebase != null) { if ( repository_ids != null ) { if( repository_ids.length > 1 ) { // truncatable value type, must use chunking! chunkingFlag = 0x00000008; write_long (0x7fffff07 | chunkingFlag); write_codebase(codebase); write_long( repository_ids.length ); for( int i = 0; i < repository_ids.length; i++ ) { write_repository_id (repository_ids[i]); } } else { write_long (0x7fffff03 | chunkingFlag); write_codebase(codebase); write_repository_id (repository_ids[0]); } } else { write_long (0x7fffff01 | chunkingFlag); write_codebase(codebase); } } else { write_value_header (repository_ids); } } /** * This method does the actual work of writing `value' to this * stream. If `repository_id' is non-null, then it is used as * the type information for `value' (possibly via indirection). * If `repository_id' is null, `value' is written without * type information. * Note: This method does not check for the special cases covered * by write_special_value(). */ private void write_value_internal(final java.io.Serializable value, final String repository_id) { write_previous_chunk_size(); check(7,4); getValueMap().put(value, Integer.valueOf(size())); if (value.getClass() == String.class) { // special handling for strings required according to spec String[] repository_ids = (repository_id == null) ? null : new String[]{ repository_id }; write_value_header(repository_ids); start_chunk(); write_wstring((String)value); end_chunk(); } else if (value.getClass() == Class.class) { String[] repository_ids = new String[] { ValueHandler.getRMIRepositoryID(javax.rmi.CORBA.ClassDesc.class) }; write_value_header(repository_ids); start_chunk(); write_value(ValueHandler.getCodebase((Class)value)); write_value(ValueHandler.getRMIRepositoryID((Class)value)); end_chunk(); } else if (value instanceof org.omg.CORBA.portable.StreamableValue) { org.omg.CORBA.portable.StreamableValue streamable = (org.omg.CORBA.portable.StreamableValue)value; write_value_header( streamable._truncatable_ids() ); start_chunk(); ((org.omg.CORBA.portable.StreamableValue)value)._write(this); end_chunk(); } else if (value instanceof org.omg.CORBA.portable.CustomValue ) { org.omg.CORBA.DataOutputStream outputStream = new DataOutputStream( this ); write_value_header ( ((org.omg.CORBA.portable.CustomValue )value )._truncatable_ids() ); ( ( org.omg.CORBA.portable.CustomValue ) value ).marshal( outputStream ); } else { String[] repository_ids = (repository_id == null) ? null : new String[]{ repository_id }; Class clazz = value.getClass(); String codebase = ValueHandler.getCodebase(clazz); if (value instanceof org.omg.CORBA.portable.IDLEntity) { java.lang.reflect.Method writeMethod = null; if (clazz != org.omg.CORBA.Any.class) { String helperClassName = clazz.getName() + "Helper"; try { Class helperClass = (clazz.getClassLoader() != null) ? clazz.getClassLoader().loadClass(helperClassName) : ObjectUtil.classForName(helperClassName); Class[] paramTypes = { org.omg.CORBA.portable.OutputStream.class, clazz }; writeMethod = helperClass.getMethod("write", paramTypes); } catch (ClassNotFoundException e) { throw new MARSHAL("Error loading class " + helperClassName + ": " + e); } catch (NoSuchMethodException e) { throw new MARSHAL("No write method in helper class " + helperClassName + ": " + e); } } write_value_header( repository_ids, codebase ); start_chunk(); if (writeMethod == null) { write_any((org.omg.CORBA.Any)value); } else { try { writeMethod.invoke(null, new Object[] { this, value }); } catch (IllegalAccessException e) { throw new MARSHAL("Internal error: " + e); } catch (java.lang.reflect.InvocationTargetException e) { throw new MARSHAL("Exception marshaling IDLEntity: " + e.getTargetException()); } } end_chunk(); } else { try { writeValueNestingLevel++; if (chunkCustomRmiValuetypes && ValueHandler.isCustomMarshaled(clazz)) { chunkingFlag = 0x00000008; } Serializable newValue = value; if (!writeReplaceCalled) { // writeReplace must be called only once for this value newValue = ValueHandler.writeReplace(value); writeReplaceCalled = true; // won't call it again } if (newValue != value) { // recompute codebase and/or repositoryID as might have changed String new_rep_id = ValueHandler.getRMIRepositoryID(newValue.getClass()); repository_ids = (new_rep_id == null) ? null : new String []{new_rep_id}; clazz = newValue.getClass(); codebase = ValueHandler.getCodebase(clazz); } write_value_header( repository_ids, codebase ); start_chunk(); if (newValue != value) { // look at the new value Integer index = getValueMap().get(newValue); if (index != null) { // previously marshaled value -- make an indirection write_long (0xffffffff); write_long (index.intValue() - size()); } else if (newValue instanceof org.omg.CORBA.Object) { write_Object((org.omg.CORBA.Object)newValue); } else { ValueHandler.writeValue(this, newValue); } } else { // Skip writeReplace call // (writeReplace was already called for this value) ValueHandler.writeValue(this, value); } end_chunk(); } finally { if (--writeValueNestingLevel == 0) { writeReplaceCalled = false; } } } } } /** * start a new chunk, end any previously started chunk (no nesting!) */ private void start_chunk() { if (chunkingFlag > 0) { write_previous_chunk_size(); valueNestingLevel++; skip_chunk_size_tag(); } } private void end_chunk() { if (chunkingFlag > 0) { write_previous_chunk_size(); write_long(-valueNestingLevel); if ( --valueNestingLevel == 0 ) { // ending chunk for outermost value chunkingFlag = 0; } else { // start continuation chunk for outer value skip_chunk_size_tag(); } } } /** * writes the chunk size to the header of the previous chunk */ private void write_previous_chunk_size() { if (chunk_size_tag_pos != -1) { if (pos == chunk_octets_pos) { // empty chunk: erase chunk size tag pos = chunk_size_tag_pos; // the tag will be overwritten index = chunk_size_tag_index; // by subsequent data } else { // go to the beginning of the chunk and write the size tag rewrite_long(chunk_size_tag_pos, chunk_size_tag_index, pos - chunk_octets_pos); } chunk_size_tag_pos = -1; // no chunk is currently open } } private void skip_chunk_size_tag() { // remember where we are right now, chunk_size_tag_pos = pos; chunk_size_tag_index = index; // insert four bytes here as a place-holder write_long(0); // need to go back later and write the actual size // remember starting position of chunk data chunk_octets_pos = pos; } /** * Writes a CORBA long value to (write_pos, write_index) without clearing * the buffer padding. In the case of a non-sequential write, clearing * buffer positions after the data just written is likely to erase data * previously written. */ private final void rewrite_long(int write_pos, int write_index, final int value) { final int align = 4; int remainder = align - (write_index % align); if (remainder != align) { write_pos += remainder; } _write4int(buffer, write_pos, value); } /** * Writes an abstract interface to this stream. The abstract interface is * written as a union with a boolean discriminator, which is true if the * union contains a CORBA object reference, or false if the union contains * a value. */ @Override public void write_abstract_interface(final java.lang.Object object) { if (object instanceof org.omg.CORBA.Object) { write_boolean(true); write_Object((org.omg.CORBA.Object)object); } else { write_boolean(false); write_value((java.io.Serializable)object); } } /** * <code>start_value</code> implements ValueOutputStream JavaToIDL mapping 02-01-12. * It should end any currently open chunk, write a valuetype header for a nested custom valuetype * (with a null codebase and the specified repository ID), and increment the valuetype nesting depth. * * @param rep_id */ @Override public void start_value (String rep_id) { write_value_header (new String[] {rep_id}); start_chunk(); } /** * <code>end_value</code> implements ValueOutputStream JavaToIDL mapping 02-01-12. * It should end any currently open chunk, write the end tag for the nested custom valuetype, * and decrement the valuetype nesting depth. */ @Override public void end_value () { end_chunk(); } public byte getMaximumStreamFormatVersion () { return maxStreamFormatVersion; } /** * <code>updateMutatorConnection</code> is an accessor that updates the * ior mutator. * * By making callers pass in a GIOPConnection not a transport this allows * callers to not have to call getTransport which would require a synchronized * lock. Therefore if the mutator has not been enabled this is effectively a * NOP. * * @param connection an <code>org.omg.ETF.Connection</code> value */ public void updateMutatorConnection(GIOPConnection connection) { if (isMutatorEnabled) { mutator.updateConnection (connection.getTransport()); } } public int get_pos() { return pos; } public boolean isIndirectionEnabled() { return useIndirection; } }