/* * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * Licensed Materials - Property of IBM * RMI-IIOP v1.0 * Copyright IBM Corp. 1998 1999 All Rights Reserved * */ package org.jboss.com.sun.corba.se.impl.encoding; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.rmi.CORBA.Util; import javax.rmi.CORBA.ValueHandler; import javax.rmi.CORBA.ValueHandlerMultiFormat; import org.jboss.com.sun.corba.se.impl.corba.TypeCodeImpl; import org.jboss.com.sun.corba.se.impl.logging.ORBUtilSystemException; import org.jboss.com.sun.corba.se.impl.orbutil.CacheTable; import org.jboss.com.sun.corba.se.impl.orbutil.ORBUtility; import org.jboss.com.sun.corba.se.impl.orbutil.RepositoryIdFactory; import org.jboss.com.sun.corba.se.impl.orbutil.RepositoryIdStrings; import org.jboss.com.sun.corba.se.impl.orbutil.RepositoryIdUtility; import org.jboss.com.sun.corba.se.impl.util.Utility; import org.jboss.com.sun.corba.se.pept.protocol.MessageMediator; import org.jboss.com.sun.corba.se.pept.transport.ByteBufferPool; import org.jboss.com.sun.corba.se.spi.ior.IOR; import org.jboss.com.sun.corba.se.spi.ior.IORFactories; import org.jboss.com.sun.corba.se.spi.ior.iiop.GIOPVersion; import org.jboss.com.sun.corba.se.spi.logging.CORBALogDomains; import org.jboss.com.sun.corba.se.spi.orb.ORB; import org.jboss.com.sun.corba.se.spi.orb.ORBVersionFactory; import org.jboss.com.sun.org.omg.CORBA.portable.ValueHelper; import org.omg.CORBA.Any; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.CustomMarshal; import org.omg.CORBA.Principal; import org.omg.CORBA.SystemException; import org.omg.CORBA.TypeCode; import org.omg.CORBA.VM_CUSTOM; import org.omg.CORBA.VM_NONE; import org.omg.CORBA.VM_TRUNCATABLE; import org.omg.CORBA.TypeCodePackage.BadKind; import org.omg.CORBA.portable.BoxedValueHelper; import org.omg.CORBA.portable.CustomValue; import org.omg.CORBA.portable.IDLEntity; import org.omg.CORBA.portable.StreamableValue; import org.omg.CORBA.portable.ValueBase; @SuppressWarnings("deprecation") 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 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(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(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 = 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 String) { writeWStringValue((String) object); } else if (object instanceof 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, Object object, 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)); } } 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 = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() { public Method run() throws NoSuchMethodException { return helperClass.getDeclaredMethod(kWriteMethod, argTypes); } }); } catch (PrivilegedActionException pae) { // this gets caught below throw (NoSuchMethodException) pae.getException(); } 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(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 < stringLength; i++) { ch = string.charAt(i); if (ch == '-' || ch == '+' || ch == '.') continue; numDigits++; } for (int i = 0; i < stringLength; i++) { ch = string.charAt(i); if (ch == '-' || ch == '+' || ch == '.') continue; digit = (byte) Character.digit(ch, 10); if (digit == -1) { throw wrapper.badDigitInFixed(CompletionStatus.COMPLETED_MAYBE); } // If the fixed type has an odd number of decimal digits, then the representation begins with the first // (most significant) digit. Otherwise, this first half-octet is all zero, and the first digit is in the // second half-octet. if (numDigits % 2 == 0) { doubleDigit |= digit; this.write_octet(doubleDigit); doubleDigit = 0; } else { doubleDigit |= (digit << 4); } numDigits--; } // The sign configuration, in the last half-octet of the representation, is 0xD for negative numbers and 0xC for // positive and zero values if (signum == -1) { doubleDigit |= 0xd; } else { doubleDigit |= 0xc; } this.write_octet(doubleDigit); } private final static String _id = "IDL:omg.org/CORBA/DataOutputStream:1.0"; private final static String[] _ids = {_id}; public String[] _truncatable_ids() { if (_ids == null) return null; return _ids.clone(); } /* for debugging */ public void printBuffer() { CDROutputStream_1_0.printBuffer(this.bbwi); } public static void printBuffer(ByteBufferWithInfo bbwi) { System.out.println("+++++++ Output Buffer ++++++++"); System.out.println(); System.out.println("Current position: " + bbwi.position()); System.out.println("Total length : " + bbwi.buflen); System.out.println(); char[] charBuf = new char[16]; try { for (int i = 0; i < bbwi.position(); i += 16) { int j = 0; // For every 16 bytes, there is one line of output. First, the hex output of the 16 bytes with each byte // separated by a space. while (j < 16 && j + i < bbwi.position()) { int k = bbwi.byteBuffer.get(i + j); if (k < 0) k = 256 + k; String hex = Integer.toHexString(k); if (hex.length() == 1) hex = "0" + hex; System.out.print(hex + " "); j++; } // Add any extra spaces to align the text column in case we didn't end at 16 while (j < 16) { System.out.print(" "); j++; } // Now output the ASCII equivalents. Non-ASCII characters are shown as periods. int x = 0; while (x < 16 && x + i < bbwi.position()) { if (ORBUtility.isPrintable((char) bbwi.byteBuffer.get(i + x))) charBuf[x] = (char) bbwi.byteBuffer.get(i + x); else charBuf[x] = '.'; x++; } System.out.println(new String(charBuf, 0, x)); } } catch (Throwable t) { t.printStackTrace(); } System.out.println("++++++++++++++++++++++++++++++"); } public void writeIndirection(int tag, int posIndirectedTo) { // Must ensure that there are no chunks between the tag and the actual indirection value. This isn't talked // about in the spec, but seems to cause headaches in our code. At the very least, this method isolates the // indirection code that was duplicated so often. handleSpecialChunkBegin(computeAlignment(4) + 8); // write indirection tag write_long(tag); // write indirection // Use parent.getRealIndex() so that it can be overridden by TypeCodeOutputStreams /* * System.out.println("CDROutputStream_1_0 writing indirection pos " + posIndirectedTo + " - real index " + * parent.getRealIndex(get_offset()) + " = " + (posIndirectedTo - parent.getRealIndex(get_offset()))); */ write_long(posIndirectedTo - parent.getRealIndex(get_offset())); handleSpecialChunkEnd(); } protected CodeSetConversion.CTBConverter getCharConverter() { if (charConverter == null) charConverter = parent.createCharCTBConverter(); return charConverter; } protected CodeSetConversion.CTBConverter getWCharConverter() { if (wcharConverter == null) wcharConverter = parent.createWCharCTBConverter(); return wcharConverter; } protected void dprint(String msg) { if (debug) ORBUtility.dprint(this, msg); } void alignOnBoundary(int octetBoundary) { alignAndReserve(octetBoundary, 0); } public void start_value(String rep_id) { if (debug) { dprint("start_value w/ rep id " + rep_id + " called at pos " + get_offset() + " position " + bbwi.position()); } if (inBlock) end_block(); // Write value_tag writeValueTag(true, true, null); // Write rep. id write_repositoryId(rep_id); // Write Value chunk end_flag--; chunkedValueNestingLevel--; // Make sure to chunk the custom data start_block(); } public void end_value() { if (debug) { dprint("end_value called at pos " + get_offset() + " position " + bbwi.position()); } end_block(); writeEndTag(true); // Check to see if we need to start another block for a possible outer value. Since we're in the stream format 2 // custom type contained by another custom type, mustChunk should always be true. // // Here's why we need to open a continuation chunk: // // We need to enclose the default data of the next subclass down in chunks. There won't be an end tag separating // the superclass optional data and the subclass's default data. if (debug) { dprint("mustChunk is " + mustChunk); } if (mustChunk) { start_block(); } } public void close() throws IOException { // tell BufferManagerWrite to release any ByteBuffers getBufferManager().close(); // It's possible bbwi.byteBuffer is shared between this OutputStream and an InputStream. Thus, we check if the // Input/Output streams are using the same ByteBuffer. If they sharing the same ByteBuffer we need to ensure // only one of those ByteBuffers are released to the ByteBufferPool. if (getByteBufferWithInfo() != null && getByteBuffer() != null) { int bbHash = System.identityHashCode(bbwi.byteBuffer); MessageMediator messageMediator = parent.getMessageMediator(); if (messageMediator != null) { CDRInputObject inputObj = (CDRInputObject) messageMediator.getInputObject(); if (inputObj != null) { ByteBuffer inputBb = inputObj.getByteBuffer(); int iBbHash = 0; if (inputBb != null) { iBbHash = System.identityHashCode(inputBb); if (bbHash == iBbHash) // shared? { // Set InputStream's ByteBuffer and bbwi to null so its ByteBuffer cannot be released to the pool inputObj.setByteBuffer(null); inputObj.setByteBufferWithInfo(null); } } } } // release this stream's ByteBuffer to the pool ByteBufferPool byteBufferPool = orb.getByteBufferPool(); if (debug) { // print address of ByteBuffer being released int bbAddress = System.identityHashCode(bbwi.byteBuffer); StringBuffer sb = new StringBuffer(80); sb.append(".close - releasing ByteBuffer id ("); sb.append(bbAddress).append(") to ByteBufferPool."); String msg = sb.toString(); dprint(msg); } byteBufferPool.releaseByteBuffer(getByteBuffer()); bbwi.byteBuffer = null; bbwi = null; } } }