/* * 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.math.BigDecimal; import java.net.MalformedURLException; import java.nio.ByteBuffer; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.rmi.CORBA.Tie; import javax.rmi.CORBA.ValueHandler; import org.jboss.com.sun.corba.se.impl.corba.CORBAObjectImpl; import org.jboss.com.sun.corba.se.impl.corba.PrincipalImpl; import org.jboss.com.sun.corba.se.impl.corba.TypeCodeImpl; import org.jboss.com.sun.corba.se.impl.logging.OMGSystemException; 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.RepositoryIdInterface; 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.RepositoryId; 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.corba.se.spi.presentation.rmi.PresentationDefaults; import org.jboss.com.sun.corba.se.spi.presentation.rmi.PresentationManager; import org.jboss.com.sun.corba.se.spi.presentation.rmi.StubAdapter; import org.jboss.com.sun.corba.se.spi.protocol.CorbaClientDelegate; import org.jboss.com.sun.org.omg.CORBA.portable.ValueHelper; import org.jboss.com.sun.org.omg.SendingContext.CodeBase; import org.omg.CORBA.Any; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.CustomMarshal; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.Principal; import org.omg.CORBA.SystemException; import org.omg.CORBA.TCKind; import org.omg.CORBA.TypeCode; 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.IndirectionException; import org.omg.CORBA.portable.StreamableValue; import org.omg.CORBA.portable.ValueBase; import org.omg.CORBA.portable.ValueFactory; @SuppressWarnings("deprecation") public class CDRInputStream_1_0 extends CDRInputStreamBase implements RestorableInputStream { private static final String kReadMethod = "read"; private static final int maxBlockLength = 0x7fffff00; protected BufferManagerRead bufferManagerRead; protected ByteBufferWithInfo bbwi; // Set to the ORB's transportDebugFlag value. This value is // used if the ORB is null. private boolean debug = false; protected boolean littleEndian; protected ORB orb; protected ORBUtilSystemException wrapper; protected OMGSystemException omgWrapper; protected ValueHandler valueHandler = null; // Value cache private CacheTable valueCache = null; // Repository ID cache private CacheTable repositoryIdCache = null; // codebase cache private CacheTable codebaseCache = null; // Current Class Stack (repository Ids of current class being read) private Stack currentStack = null; // Length of current chunk, or a large positive number if not in a chunk protected int blockLength = maxBlockLength; // Read end flag (value nesting depth) protected int end_flag = 0; // Beginning with the resolution to interop issue 3526 (4328?), 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; // Flag used to determine whether blocksize was zero // private int checkForNullBlock = -1; // In block flag // private boolean inBlock = false; // Indicates whether we are inside a value // private boolean outerValueDone = true; // Int used by read_value(Serializable) that is set by this class // before calling ValueFactory.read_value protected int valueIndirection = 0; // Int set by readStringOrIndirection to communicate the actual // offset of the string length field back to the caller protected int stringIndirection = 0; // Flag indicating whether we are unmarshalling a chunked value protected boolean isChunked = false; // Repository ID handlers private RepositoryIdUtility repIdUtil; private RepositoryIdStrings repIdStrs; // Code set converters (created when first needed) private CodeSetConversion.BTCConverter charConverter; private CodeSetConversion.BTCConverter wcharConverter; // RMI-IIOP stream format version 2 case in which we know that there is no more optional data available. If the // Serializable's readObject method tries to read anything, we must throw a MARSHAL with the special minor code so // that the ValueHandler can give the correct exception to readObject. The state is cleared when the ValueHandler // calls end_value after the readObject method exits. private boolean specialNoOptionalDataState = false; // Template method public CDRInputStreamBase dup() { CDRInputStreamBase result = null; try { result = this.getClass().newInstance(); } catch (Exception e) { throw wrapper.couldNotDuplicateCdrInputStream(e); } result.init(this.orb, this.bbwi.byteBuffer, this.bbwi.buflen, this.littleEndian, this.bufferManagerRead); ((CDRInputStream_1_0) result).bbwi.position(this.bbwi.position()); // To ensure we keep bbwi.byteBuffer.limit in sync with bbwi.buflen. ((CDRInputStream_1_0) result).bbwi.byteBuffer.limit(this.bbwi.buflen); return result; } /** * NOTE: size passed to init means buffer size */ public void init(org.omg.CORBA.ORB orb, ByteBuffer byteBuffer, int size, boolean littleEndian, BufferManagerRead bufferManager) { this.orb = (ORB) orb; this.wrapper = ORBUtilSystemException.get((ORB) orb, CORBALogDomains.RPC_ENCODING); this.omgWrapper = OMGSystemException.get((ORB) orb, CORBALogDomains.RPC_ENCODING); this.littleEndian = littleEndian; this.bufferManagerRead = bufferManager; this.bbwi = new ByteBufferWithInfo(orb, byteBuffer, 0); this.bbwi.buflen = size; this.bbwi.byteBuffer.limit(bbwi.buflen); this.markAndResetHandler = bufferManagerRead.getMarkAndResetHandler(); debug = ((ORB) orb).transportDebugFlag; } // See description in CDRInputStream void performORBVersionSpecificInit() { createRepositoryIdHandlers(); } private final void createRepositoryIdHandlers() { repIdUtil = RepositoryIdFactory.getRepIdUtility(orb); repIdStrs = RepositoryIdFactory.getRepIdStringsFactory(orb); } 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 final int computeAlignment(int index, int align) { if (align > 1) { int incr = index & (align - 1); if (incr != 0) return align - incr; } return 0; } public int getSize() { return bbwi.position(); } protected void checkBlockLength(int align, int dataSize) { // Since chunks can end at arbitrary points (though not within primitive CDR types, arrays of primitives, // strings, wstrings, or indirections), we must check here for termination of the current chunk. if (!isChunked) return; // RMI-IIOP stream format version 2 case in which we know that there is no more optional data available. If the // Serializable's readObject method tries to read anything, must throw a MARSHAL exception with the special // minor code so that the ValueHandler can give the correct exception to readObject. The state is cleared when // the ValueHandler calls end_value after the readObject method exits. if (specialNoOptionalDataState) { throw omgWrapper.rmiiiopOptionalDataIncompatible1(); } boolean checkForEndTag = false; // Are we at the end of the current chunk? If so, try to interpret the next long as a chunk length. (It has to // be either a chunk length, end tag, or valuetag.) If it isn't a chunk length, blockLength will remain set to // maxBlockLength. if (blockLength == get_offset()) { blockLength = maxBlockLength; start_block(); // What's next is either a valuetag or an end tag. If it's a valuetag, we're probably being called as part // of the process to read the valuetag. If it's an end tag, then there isn't enough data left in this // valuetype to read! if (blockLength == maxBlockLength) checkForEndTag = true; } else if (blockLength < get_offset()) { // Are we already past the end of the current chunk? This is always an error. throw wrapper.chunkOverflow(); } // If what's next on the wire isn't a chunk length or what we want to read (which can't be split across chunks) // won't fit in the current chunk, throw this exception. This probably means that we're in an // RMI-IIOPrializable's readObject method or a custom marshaled IDL type is reading too much/in an incorrect // order int requiredNumBytes = computeAlignment(bbwi.position(), align) + dataSize; if (blockLength != maxBlockLength && blockLength < get_offset() + requiredNumBytes) { throw omgWrapper.rmiiiopOptionalDataIncompatible2(); } // REVISIT - We should look at using the built in advancement of using ByteBuffer.get() rather than explicitly // advancing the ByteBuffer's position. This is true for anywhere we are incrementing the ByteBuffer's position. if (checkForEndTag) { bbwi.position(bbwi.position() - 4); // It was an end tag, so there wasn't enough data left in the valuetype's encoding on the wire to read what // we wanted (nextLong < 0) throw omgWrapper.rmiiiopOptionalDataIncompatible3(); } } protected void alignAndCheck(int align, int n) { checkBlockLength(align, n); // WARNING: Must compute real alignment after calling checkBlockLength since it may move the position int alignResult = computeAlignment(bbwi.position(), align); bbwi.position(bbwi.position() + alignResult); if (bbwi.position() + n > bbwi.buflen) grow(align, n); } // This can be overridden.... protected void grow(int align, int n) { bbwi.needed = n; bbwi = bufferManagerRead.underflow(bbwi); } // Marshal primitives. public final void consumeEndian() { littleEndian = read_boolean(); } // No such type in java public final double read_longdouble() { throw wrapper.longDoubleNotImplemented(CompletionStatus.COMPLETED_MAYBE); } public final boolean read_boolean() { return (read_octet() != 0); } public final char read_char() { alignAndCheck(1, 1); return getConvertedChars(1, getCharConverter())[0]; } public char read_wchar() { // 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 we're talking to one of our legacy ORBs, do what they did: int b1, b2; alignAndCheck(2, 2); if (littleEndian) { b2 = bbwi.byteBuffer.get(bbwi.position()) & 0x00FF; bbwi.position(bbwi.position() + 1); b1 = bbwi.byteBuffer.get(bbwi.position()) & 0x00FF; bbwi.position(bbwi.position() + 1); } else { b1 = bbwi.byteBuffer.get(bbwi.position()) & 0x00FF; bbwi.position(bbwi.position() + 1); b2 = bbwi.byteBuffer.get(bbwi.position()) & 0x00FF; bbwi.position(bbwi.position() + 1); } return (char) ((b1 << 8) + (b2 << 0)); } public final byte read_octet() { alignAndCheck(1, 1); byte b = bbwi.byteBuffer.get(bbwi.position()); bbwi.position(bbwi.position() + 1); return b; } public final short read_short() { int b1, b2; alignAndCheck(2, 2); if (littleEndian) { b2 = (bbwi.byteBuffer.get(bbwi.position()) << 0) & 0x000000FF; bbwi.position(bbwi.position() + 1); b1 = (bbwi.byteBuffer.get(bbwi.position()) << 8) & 0x0000FF00; bbwi.position(bbwi.position() + 1); } else { b1 = (bbwi.byteBuffer.get(bbwi.position()) << 8) & 0x0000FF00; bbwi.position(bbwi.position() + 1); b2 = (bbwi.byteBuffer.get(bbwi.position()) << 0) & 0x000000FF; bbwi.position(bbwi.position() + 1); } return (short) (b1 | b2); } public final short read_ushort() { return read_short(); } public final int read_long() { int b1, b2, b3, b4; alignAndCheck(4, 4); int bufPos = bbwi.position(); if (littleEndian) { b4 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b3 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b2 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b1 = bbwi.byteBuffer.get(bufPos++) & 0xFF; } else { b1 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b2 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b3 = bbwi.byteBuffer.get(bufPos++) & 0xFF; b4 = bbwi.byteBuffer.get(bufPos++) & 0xFF; } bbwi.position(bufPos); return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; } public final int read_ulong() { return read_long(); } public final long read_longlong() { long i1, i2; alignAndCheck(8, 8); if (littleEndian) { i2 = read_long() & 0xFFFFFFFFL; i1 = (long) read_long() << 32; } else { i1 = (long) read_long() << 32; i2 = read_long() & 0xFFFFFFFFL; } return (i1 | i2); } public final long read_ulonglong() { return read_longlong(); } public final float read_float() { return Float.intBitsToFloat(read_long()); } public final double read_double() { return Double.longBitsToDouble(read_longlong()); } protected final void checkForNegativeLength(int length) { if (length < 0) throw wrapper.negativeStringLength(CompletionStatus.COMPLETED_MAYBE, new Integer(length)); } protected final String readStringOrIndirection(boolean allowIndirection) { int len = read_long(); // Check for indirection if (allowIndirection) { if (len == 0xffffffff) return null; else stringIndirection = get_offset() - 4; } checkForNegativeLength(len); if (orb != null && ORBUtility.isLegacyORB(orb)) return legacyReadString(len); else return internalReadString(len); } private final String internalReadString(int len) { // Workaround for ORBs which send string lengths of zero to mean empty string. // // IMPORTANT: Do not replace 'new String("")' with "", it may result in a Serialization bug (See // serialization.zerolengthstring) and bug id: 4728756 for details if (len == 0) return new String(""); char[] result = getConvertedChars(len - 1, getCharConverter()); // Skip over the 1 byte null read_octet(); return new String(result, 0, getCharConverter().getNumChars()); } private final String legacyReadString(int len) { // Workaround for ORBs which send string lengths of zero to mean empty string. // // IMPORTANT: Do not replace 'new String("")' with "", it may result in a Serialization bug (See // serialization.zerolengthstring) and bug id: 4728756 for details if (len == 0) return new String(""); len--; char[] c = new char[len]; int n = 0; while (n < len) { int avail; int bytes; int wanted; avail = bbwi.buflen - bbwi.position(); if (avail <= 0) { grow(1, 1); avail = bbwi.buflen - bbwi.position(); } wanted = len - n; bytes = (wanted < avail) ? wanted : avail; // Microbenchmarks are showing a loop of ByteBuffer.get(int) being faster than ByteBuffer.get(byte[], int, // int). for (int i = 0; i < bytes; i++) { c[n + i] = (char) (bbwi.byteBuffer.get(bbwi.position() + i) & 0xFF); } bbwi.position(bbwi.position() + bytes); n += bytes; } // Skip past terminating null byte if (bbwi.position() + 1 > bbwi.buflen) alignAndCheck(1, 1); bbwi.position(bbwi.position() + 1); return new String(c); } public final String read_string() { return readStringOrIndirection(false); } public String read_wstring() { // 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); } int len = read_long(); // Workaround for ORBs which send string lengths of zero to mean empty string. // // IMPORTANT: Do not replace 'new String("")' with "", it may result in a Serialization bug (See // serialization.zerolengthstring) and bug id: 4728756 for details if (len == 0) return new String(""); checkForNegativeLength(len); len--; char[] c = new char[len]; for (int i = 0; i < len; i++) c[i] = read_wchar(); // skip the two null terminator bytes read_wchar(); // bbwi.position(bbwi.position() + 2); return new String(c); } public final void read_octet_array(byte[] b, int offset, int length) { if (b == null) throw wrapper.nullParam(); // Must call alignAndCheck at least once to ensure we aren't at the end of a chunk. Of course, we should only // call it if we actually need to read something, otherwise we might end up with an exception at the end of the // stream. if (length == 0) return; alignAndCheck(1, 1); int n = offset; while (n < length + offset) { int avail; int bytes; int wanted; avail = bbwi.buflen - bbwi.position(); if (avail <= 0) { grow(1, 1); avail = bbwi.buflen - bbwi.position(); } wanted = (length + offset) - n; bytes = (wanted < avail) ? wanted : avail; // Microbenchmarks are showing a loop of ByteBuffer.get(int) being faster than ByteBuffer.get(byte[], int, // int). for (int i = 0; i < bytes; i++) { b[n + i] = bbwi.byteBuffer.get(bbwi.position() + i); } bbwi.position(bbwi.position() + bytes); n += bytes; } } public Principal read_Principal() { int len = read_long(); byte[] pvalue = new byte[len]; read_octet_array(pvalue, 0, len); Principal p = new PrincipalImpl(); p.name(pvalue); return p; } public TypeCode read_TypeCode() { TypeCodeImpl tc = new TypeCodeImpl(orb); tc.read_value(parent); return tc; } public Any read_any() { Any any = orb.create_any(); TypeCodeImpl tc = new TypeCodeImpl(orb); // read off the typecode // REVISIT We could avoid this try-catch if we could peek the typecode kind off this stream and see if it is a // tk_value. Looking at the code we know that for tk_value the Any.read_value() below ignores the tc argument // anyway (except for the kind field). But still we would need to make sure that the whole typecode, including // encapsulations, is read off. try { tc.read_value(parent); } catch (MARSHAL ex) { if (tc.kind().value() != TCKind._tk_value) throw ex; // We can be sure that the whole typecode encapsulation has been read off. dprintThrowable(ex); } // read off the value of the any any.read_value(parent, tc); return any; } public org.omg.CORBA.Object read_Object() { return read_Object(null); } // ------------ RMI related methods -------------------------- // IDL to Java ptc-00-01-08 1.21.4.1 // // The clz argument to read_Object can be either a stub Class or the // "Class object for the RMI/IDL interface type that is statically expected." This functions as follows: // 1. If clz==null, just use the repository ID from the stub // 2. If clz is a stub class, just use it as a static factory. clz is a stub class if StubAdapter.isStubClass(clz). // In addition, clz is a IDL stub class if IDLEntity.class.isAssignableFrom( clz ). // 3. If clz is an interface, use it to create the appropriate stub factory. public org.omg.CORBA.Object read_Object(Class<?> clz) { // In any case, we must first read the IOR. IOR ior = IORFactories.makeIOR(parent); if (ior.isNil()) return null; PresentationManager.StubFactoryFactory sff = ORB.getStubFactoryFactory(); String codeBase = ior.getProfile().getCodebase(); PresentationManager.StubFactory stubFactory = null; if (clz == null) { RepositoryId rid = RepositoryId.cache.getId(ior.getTypeId()); String className = rid.getClassName(); boolean isIDLInterface = rid.isIDLType(); if (className == null || className.equals("")) stubFactory = null; else try { stubFactory = sff.createStubFactory(className, isIDLInterface, codeBase, null, (ClassLoader) null); } catch (Exception exc) { // Could not create stubFactory, so use null. // XXX stubFactory handling is still too complex: Can we resolve the stubFactory question once in a // single place? stubFactory = null; } } else if (StubAdapter.isStubClass(clz)) { stubFactory = PresentationDefaults.makeStaticStubFactory(clz); } else { // clz is an interface class boolean isIDL = IDLEntity.class.isAssignableFrom(clz); stubFactory = sff.createStubFactory(clz.getName(), isIDL, codeBase, clz, clz.getClassLoader()); } return internalIORToObject(ior, stubFactory, orb); } /* * This is used as a general utility (e.g., the PortableInterceptor implementation uses it. If stubFactory is null, * the ior's IIOPProfile must support getServant. */ public static org.omg.CORBA.Object internalIORToObject(IOR ior, PresentationManager.StubFactory stubFactory, ORB orb) { ORBUtilSystemException wrapper = ORBUtilSystemException.get(orb, CORBALogDomains.RPC_ENCODING); Object servant = ior.getProfile().getServant(); if (servant != null) { if (servant instanceof Tie) { String codebase = ior.getProfile().getCodebase(); org.omg.CORBA.Object objref = (org.omg.CORBA.Object) Utility.loadStub((Tie) servant, stubFactory, codebase, false); // If we managed to load a stub, return it, otherwise we must fail... if (objref != null) { return objref; } else { throw wrapper.readObjectException(); } } else if (servant instanceof org.omg.CORBA.Object) { if (!(servant instanceof org.omg.CORBA.portable.InvokeHandler)) { return (org.omg.CORBA.Object) servant; } } else throw wrapper.badServantReadObject(); } CorbaClientDelegate del = ORBUtility.makeClientDelegate(ior); org.omg.CORBA.Object objref = null; try { objref = stubFactory.makeStub(); } catch (Throwable e) { wrapper.stubCreateError(e); if (e instanceof ThreadDeath) { throw (ThreadDeath) e; } // Return the "default" stub... objref = new CORBAObjectImpl(); } StubAdapter.setDelegate(objref, del); return objref; } public Object read_abstract_interface() { return read_abstract_interface(null); } public Object read_abstract_interface(Class<?> clz) { boolean object = read_boolean(); if (object) { return read_Object(clz); } else { return read_value(); } } public Serializable read_value() { return read_value((Class<?>) null); } private Serializable handleIndirection() { int indirection = read_long() + get_offset() - 4; if (valueCache != null && valueCache.containsVal(indirection)) { java.io.Serializable cachedValue = (java.io.Serializable) valueCache.getKey(indirection); return cachedValue; } else { // In RMI-IIOP the ValueHandler will recognize this exception and use the provided indirection value to // lookup a possible indirection to an object currently on the deserialization stack. throw new IndirectionException(indirection); } } private String readRepositoryIds(int valueTag, Class<?> expectedType, String expectedTypeRepId) { return readRepositoryIds(valueTag, expectedType, expectedTypeRepId, null); } /** * Examines the valuetag to see how many (if any) repository IDs are present on the wire. If no repository ID * information is on the wire but the expectedType or expectedTypeRepId is known, it will return one of those * (favoring the expectedType's repId). Failing that, it uses the supplied BoxedValueHelper to obtain the repository * ID, as a last resort. */ private String readRepositoryIds(int valueTag, Class<?> expectedType, String expectedTypeRepId, BoxedValueHelper factory) { switch (repIdUtil.getTypeInfo(valueTag)) { case RepositoryIdUtility.NO_TYPE_INFO : // Throw an exception if we have no repository ID info and no expectedType to work with. Otherwise, how // would we know what to unmarshal? if (expectedType == null) { if (expectedTypeRepId != null) { return expectedTypeRepId; } else if (factory != null) { return factory.get_id(); } else { throw wrapper.expectedTypeNullAndNoRepId(CompletionStatus.COMPLETED_MAYBE); } } return repIdStrs.createForAnyType(expectedType); case RepositoryIdUtility.SINGLE_REP_TYPE_INFO : return read_repositoryId(); case RepositoryIdUtility.PARTIAL_LIST_TYPE_INFO : return read_repositoryIds(); default : throw wrapper.badValueTag(CompletionStatus.COMPLETED_MAYBE, Integer.toHexString(valueTag)); } } public Serializable read_value(Class<?> expectedType) { // Read value tag int vType = readValueTag(); // Is value null? if (vType == 0) return null; // Is this an indirection to a previously read valuetype? if (vType == 0xffffffff) return handleIndirection(); // Save where this valuetype started so we can put it in the indirection valueCache later int indirection = get_offset() - 4; // Need to save this special marker variable to restore its value during recursion boolean saveIsChunked = isChunked; isChunked = repIdUtil.isChunkedEncoding(vType); Object value = null; String codebase_URL = null; if (repIdUtil.isCodeBasePresent(vType)) { codebase_URL = read_codebase_URL(); } // Read repository id(s) String repositoryIDString = readRepositoryIds(vType, expectedType, null); // If isChunked was determined to be true based on the valuetag, this will read a chunk length start_block(); // Remember that end_flag keeps track of all nested valuetypes and is used for older ORBs end_flag--; if (isChunked) chunkedValueNestingLevel--; if (repositoryIDString.equals(repIdStrs.getWStringValueRepId())) { value = read_wstring(); } else if (repositoryIDString.equals(repIdStrs.getClassDescValueRepId())) { // read in the class whether with the old ClassDesc or the new one value = readClass(); } else { Class<?> valueClass = expectedType; // By this point, either the expectedType or repositoryIDString is guaranteed to be non-null. if (expectedType == null || !repositoryIDString.equals(repIdStrs.createForAnyType(expectedType))) { valueClass = getClassFromString(repositoryIDString, codebase_URL, expectedType); } if (valueClass == null) { // No point attempting to use value handler below, since the class information is not available. throw wrapper.couldNotFindClass(CompletionStatus.COMPLETED_MAYBE, new ClassNotFoundException()); } if (valueClass != null && org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(valueClass)) { value = readIDLValue(indirection, repositoryIDString, valueClass, codebase_URL); } else { // Must be some form of RMI-IIOP valuetype try { if (valueHandler == null) valueHandler = ORBUtility.createValueHandler(orb); value = valueHandler.readValue(parent, indirection, valueClass, repositoryIDString, getCodeBase()); } catch (SystemException sysEx) { // Just rethrow any CORBA system exceptions that come out of the ValueHandler throw sysEx; } catch (Exception ex) { throw wrapper.valuehandlerReadException(CompletionStatus.COMPLETED_MAYBE, ex); } catch (Error e) { throw wrapper.valuehandlerReadError(CompletionStatus.COMPLETED_MAYBE, e); } } } // Skip any remaining chunks until we get to an end tag or a valuetag. If we see a valuetag, that means there // was another valuetype in the sender's version of this class that we need to skip over. handleEndOfValue(); // Read and process the end tag if we're chunking. Assumes that we're at the position of the end tag // (handleEndOfValue should assure this) readEndTag(); // Cache the valuetype that we read if (valueCache == null) valueCache = new CacheTable(orb, false); valueCache.put(value, indirection); // Allow for possible continuation chunk. If we're a nested valuetype inside of a chunked valuetype, and that // enclosing valuetype has more data to write, it will need to have this new chunk begin after we wrote our end // tag. isChunked = saveIsChunked; start_block(); return (java.io.Serializable) value; } public Serializable read_value(BoxedValueHelper factory) { // Read value tag int vType = readValueTag(); if (vType == 0) return null; // value is null else if (vType == 0xffffffff) { // Indirection tag int indirection = read_long() + get_offset() - 4; if (valueCache != null && valueCache.containsVal(indirection)) { java.io.Serializable cachedValue = (java.io.Serializable) valueCache.getKey(indirection); return cachedValue; } else { throw new IndirectionException(indirection); } } else { int indirection = get_offset() - 4; // end_block(); boolean saveIsChunked = isChunked; isChunked = repIdUtil.isChunkedEncoding(vType); Object value = null; String codebase_URL = null; if (repIdUtil.isCodeBasePresent(vType)) { codebase_URL = read_codebase_URL(); } // Read repository id String repositoryIDString = readRepositoryIds(vType, null, null, factory); // Compare rep. ids to see if we should use passed helper if (!repositoryIDString.equals(factory.get_id())) factory = Utility.getHelper(null, codebase_URL, repositoryIDString); start_block(); end_flag--; if (isChunked) chunkedValueNestingLevel--; if (factory instanceof ValueHelper) { value = readIDLValueWithHelper((ValueHelper) factory, indirection); } else { valueIndirection = indirection; // for callback value = factory.read_value(parent); } handleEndOfValue(); readEndTag(); // Put into valueCache if (valueCache == null) valueCache = new CacheTable(orb, false); valueCache.put(value, indirection); // allow for possible continuation chunk isChunked = saveIsChunked; start_block(); return (java.io.Serializable) value; } } private boolean isCustomType(ValueHelper helper) { try { TypeCode tc = helper.get_type(); int kind = tc.kind().value(); if (kind == TCKind._tk_value) { return (tc.type_modifier() == org.omg.CORBA.VM_CUSTOM.value); } } catch (BadKind ex) { throw wrapper.badKind(ex); } return false; } // This method is actually called indirectly by read_value(String repositoryId). Therefore, it is not a truly // independent read call that handles header information itself. public java.io.Serializable read_value(java.io.Serializable value) { // Put into valueCache using valueIndirection if (valueCache == null) valueCache = new CacheTable(orb, false); valueCache.put(value, valueIndirection); if (value instanceof StreamableValue) ((StreamableValue) value)._read(parent); else if (value instanceof CustomValue) ((CustomValue) value).unmarshal(parent); return value; } public java.io.Serializable read_value(String repositoryId) { // if (inBlock) // end_block(); // Read value tag int vType = readValueTag(); if (vType == 0) return null; // value is null else if (vType == 0xffffffff) { // Indirection tag int indirection = read_long() + get_offset() - 4; if (valueCache != null && valueCache.containsVal(indirection)) { java.io.Serializable cachedValue = (java.io.Serializable) valueCache.getKey(indirection); return cachedValue; } else { throw new IndirectionException(indirection); } } else { int indirection = get_offset() - 4; // end_block(); boolean saveIsChunked = isChunked; isChunked = repIdUtil.isChunkedEncoding(vType); Object value = null; String codebase_URL = null; if (repIdUtil.isCodeBasePresent(vType)) { codebase_URL = read_codebase_URL(); } // Read repository id String repositoryIDString = readRepositoryIds(vType, null, repositoryId); ValueFactory factory = Utility.getFactory(null, codebase_URL, orb, repositoryIDString); start_block(); end_flag--; if (isChunked) chunkedValueNestingLevel--; valueIndirection = indirection; // for callback value = factory.read_value(parent); handleEndOfValue(); readEndTag(); // Put into valueCache if (valueCache == null) valueCache = new CacheTable(orb, false); valueCache.put(value, indirection); // allow for possible continuation chunk isChunked = saveIsChunked; start_block(); return (java.io.Serializable) value; } } private Class<?> readClass() { String codebases = null, classRepId = null; if (orb == null || ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) || ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) { codebases = (String) read_value(String.class); classRepId = (String) read_value(String.class); } else { // Pre-Merlin/J2EE 1.3 ORBs wrote the repository ID and codebase strings in the wrong order. classRepId = (String) read_value(String.class); codebases = (String) read_value(String.class); } if (debug) { dprint("readClass codebases: " + codebases + " rep Id: " + classRepId); } Class<?> cl = null; RepositoryIdInterface repositoryID = repIdStrs.getFromString(classRepId); try { cl = repositoryID.getClassFromType(codebases); } catch (ClassNotFoundException cnfe) { throw wrapper.cnfeReadClass(CompletionStatus.COMPLETED_MAYBE, cnfe, repositoryID.getClassName()); } catch (MalformedURLException me) { throw wrapper.malformedUrl(CompletionStatus.COMPLETED_MAYBE, me, repositoryID.getClassName(), codebases); } return cl; } private Object readIDLValueWithHelper(ValueHelper helper, int indirection) { // look for two-argument static read method Method readMethod; try { Class<?> argTypes[] = {org.omg.CORBA.portable.InputStream.class, helper.get_class()}; readMethod = helper.getClass().getDeclaredMethod(kReadMethod, argTypes); } catch (NoSuchMethodException nsme) { // must be boxed value helper Object result = helper.read_value(parent); return result; } // found two-argument read method, so must be non-boxed value... create a blank instance Object val = null; try { val = helper.get_class().newInstance(); } catch (InstantiationException ie) { throw wrapper.couldNotInstantiateHelper(ie, helper.get_class()); } catch (IllegalAccessException iae) { // Value's constructor is protected or private // // So, use the helper to read the value. // // NOTE : This means that in this particular case a recursive ref would fail. return helper.read_value(parent); } // add blank instance to cache table if (valueCache == null) valueCache = new CacheTable(orb, false); valueCache.put(val, indirection); // if custom type, call unmarshal method if (val instanceof CustomMarshal && isCustomType(helper)) { ((CustomMarshal) val).unmarshal(parent); return val; } // call two-argument read method using reflection try { Object args[] = {parent, val}; readMethod.invoke(helper, args); return val; } catch (IllegalAccessException iae2) { throw wrapper.couldNotInvokeHelperReadMethod(iae2, helper.get_class()); } catch (InvocationTargetException ite) { throw wrapper.couldNotInvokeHelperReadMethod(ite, helper.get_class()); } } private Object readBoxedIDLEntity(Class<?> clazz, String codebase) { Class<?> cls = null; try { ClassLoader clazzLoader = (clazz == null ? null : clazz.getClassLoader()); cls = Utility.loadClassForClass(clazz.getName() + "Helper", codebase, clazzLoader, clazz, clazzLoader); final Class<?> helperClass = cls; final Class<?> argTypes[] = {org.omg.CORBA.portable.InputStream.class}; // getDeclaredMethod requires RuntimePermission accessDeclaredMembers if a different class loader is used // (even though the javadoc says otherwise) Method readMethod = null; try { readMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() { public Method run() throws NoSuchMethodException { return helperClass.getDeclaredMethod(kReadMethod, argTypes); } }); } catch (PrivilegedActionException pae) { // this gets caught below throw (NoSuchMethodException) pae.getException(); } Object args[] = {parent}; return readMethod.invoke(null, args); } catch (ClassNotFoundException cnfe) { throw wrapper.couldNotInvokeHelperReadMethod(cnfe, cls); } catch (NoSuchMethodException nsme) { throw wrapper.couldNotInvokeHelperReadMethod(nsme, cls); } catch (IllegalAccessException iae) { throw wrapper.couldNotInvokeHelperReadMethod(iae, cls); } catch (InvocationTargetException ite) { throw wrapper.couldNotInvokeHelperReadMethod(ite, cls); } } private Object readIDLValue(int indirection, String repId, Class<?> clazz, String codebase) { ValueFactory factory; // Always try to find a ValueFactory first, as required by the spec. // There are some complications here in the IDL 3.0 mapping (see 1.13.8), but basically we must always be able // to override the DefaultFactory or Helper mappings that are also used. This appears to be the case even in the // boxed value cases. The original code only did the lookup in the case of class implementing either // StreamableValue or CustomValue, but abstract valuetypes only implement ValueBase, and really require the use // of the repId to find a factory (including the DefaultFactory). try { // use new-style OBV support (factory object) factory = Utility.getFactory(clazz, codebase, orb, repId); } catch (MARSHAL marshal) { // XXX log marshal at one of the INFO levels // Could not get a factory, so try alternatives if (!StreamableValue.class.isAssignableFrom(clazz) && !CustomValue.class.isAssignableFrom(clazz) && ValueBase.class.isAssignableFrom(clazz)) { // use old-style OBV support (helper object) BoxedValueHelper helper = Utility.getHelper(clazz, codebase, repId); if (helper instanceof ValueHelper) return readIDLValueWithHelper((ValueHelper) helper, indirection); else return helper.read_value(parent); } else { // must be a boxed IDLEntity, so make a reflective call to the // helper's static read method... return readBoxedIDLEntity(clazz, codebase); } } // If there was no error in getting the factory, use it. valueIndirection = indirection; // for callback return factory.read_value(parent); } /** * End tags are only written for chunked valuetypes. * * Before Merlin, our ORBs wrote end tags which took into account all enclosing valuetypes. This was changed by an * interop resolution (see details around chunkedValueNestingLevel) to only include enclosing chunked types. * * ORB versioning and end tag compaction are handled here. */ private void readEndTag() { if (isChunked) { // Read the end tag int anEndTag = read_long(); // End tags should always be negative, and the outermost enclosing chunked valuetype should have a -1 end // tag. // // handleEndOfValue should have assured that we were at the end tag position! if (anEndTag >= 0) { throw wrapper.positiveEndTag(CompletionStatus.COMPLETED_MAYBE, new Integer(anEndTag), new Integer( get_offset() - 4)); } // If the ORB is null, or if we're sure we're talking to a foreign ORB, Merlin, or something more recent, we // use the updated end tag computation, and are more strenuous about the values. if (orb == null || ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) || ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) { // If the end tag we read was less than what we were expecting, then the sender must think it's sent // more enclosing chunked valuetypes than we have. Throw an exception. if (anEndTag < chunkedValueNestingLevel) throw wrapper.unexpectedEnclosingValuetype(CompletionStatus.COMPLETED_MAYBE, new Integer(anEndTag), new Integer(chunkedValueNestingLevel)); // If the end tag is bigger than what we expected, but still negative, then the sender has done some end // tag compaction. We back up the stream 4 bytes so that the next time readEndTag is called, it will get // down here again. Even with fragmentation, we'll always be able to do this. if (anEndTag != chunkedValueNestingLevel) { bbwi.position(bbwi.position() - 4); } } else { // When talking to Kestrel or Ladybird, we use our old end tag rules and are less strict. If the end tag // isn't what we expected, we back up, assuming compaction. if (anEndTag != end_flag) { bbwi.position(bbwi.position() - 4); } } // This only keeps track of the enclosing chunked valuetypes chunkedValueNestingLevel++; } // This keeps track of all enclosing valuetypes end_flag++; } protected int get_offset() { return bbwi.position(); } private void start_block() { // if (outerValueDone) if (!isChunked) return; // if called from alignAndCheck, need to reset blockLength to avoid an infinite recursion loop on read_long() // call blockLength = maxBlockLength; blockLength = read_long(); // Must remember where we began the chunk to calculate how far along we are. See notes above about // chunkBeginPos. if (blockLength > 0 && blockLength < maxBlockLength) { blockLength += get_offset(); // _REVISIT_ unsafe, should use a Java long // inBlock = true; } else { // System.out.println("start_block snooped a " + Integer.toHexString(blockLength)); // not a chunk length field blockLength = maxBlockLength; bbwi.position(bbwi.position() - 4); } } // Makes sure that if we were reading a chunked value, we end up at the right place in the stream, no matter how // little the unmarshalling code read. // // After calling this method, if we are chunking, we should be in position to read the end tag. private void handleEndOfValue() { // If we're not chunking, we don't have to worry about skipping remaining chunks or finding end tags if (!isChunked) return; // Skip any remaining chunks while (blockLength != maxBlockLength) { end_block(); start_block(); } // Now look for the end tag // This is a little wasteful since we're reading this long up to 3 times in the worst cases (once in // start_block, once here, and once in readEndTag // // Peek next long int nextLong = read_long(); bbwi.position(bbwi.position() - 4); // We did find an end tag, so we're done. readEndTag should take care of making sure it's the correct end tag, // etc. Remember that since end tags, chunk lengths, and valuetags have non overlapping ranges, we can tell by // the value what the longs are. if (nextLong < 0) return; if (nextLong == 0 || nextLong >= maxBlockLength) { // A custom marshaled valuetype left extra data on the wire, and that data had another nested value inside // of it. We've just read the value tag or null of that nested value. // // In an attempt to get by it, we'll try to call read_value() to get the nested value off of the wire. // Afterwards, we must call handleEndOfValue recursively to read any further chunks that the containing // valuetype might still have after the nested value. read_value(); handleEndOfValue(); } else { // This probably means that the code to skip chunks has an error, and ended up setting blockLength to // something other than maxBlockLength even though we weren't starting a new chunk. throw wrapper.couldNotSkipBytes(CompletionStatus.COMPLETED_MAYBE, new Integer(nextLong), new Integer( get_offset())); } } private void end_block() { // if in a chunk, check for underflow or overflow if (blockLength != maxBlockLength) { if (blockLength == get_offset()) { // Chunk ended correctly blockLength = maxBlockLength; } else { // Skip over anything left by bad unmarshaling code (ex: a buggy custom unmarshaler). See // handleEndOfValue. if (blockLength > get_offset()) { skipToOffset(blockLength); } else { throw wrapper.badChunkLength(new Integer(blockLength), new Integer(get_offset())); } } } } private int readValueTag() { // outerValueDone = false; return read_long(); } public org.omg.CORBA.ORB orb() { return orb; } // ------------ End RMI related methods -------------------------- public final void read_boolean_array(boolean[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_boolean(); } } public final void read_char_array(char[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_char(); } } public final void read_wchar_array(char[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_wchar(); } } public final void read_short_array(short[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_short(); } } public final void read_ushort_array(short[] value, int offset, int length) { read_short_array(value, offset, length); } public final void read_long_array(int[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_long(); } } public final void read_ulong_array(int[] value, int offset, int length) { read_long_array(value, offset, length); } public final void read_longlong_array(long[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_longlong(); } } public final void read_ulonglong_array(long[] value, int offset, int length) { read_longlong_array(value, offset, length); } public final void read_float_array(float[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_float(); } } public final void read_double_array(double[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_double(); } } public final void read_any_array(org.omg.CORBA.Any[] value, int offset, int length) { for (int i = 0; i < length; i++) { value[i + offset] = read_any(); } } // -------------------------------------------------------------------- // // CDRInputStream state management. // -------------------------------------------------------------------- // /** * Are we at the end of the input stream? */ // public final boolean isAtEnd() { // return bbwi.position() == bbwi.buflen; // } // public int available() throws IOException { // return bbwi.buflen - bbwi.position(); // } private String read_repositoryIds() { // Read # of repository ids int numRepIds = read_long(); if (numRepIds == 0xffffffff) { int indirection = read_long() + get_offset() - 4; if (repositoryIdCache != null && repositoryIdCache.containsOrderedVal(indirection)) return (String) repositoryIdCache.getKey(indirection); else throw wrapper.unableToLocateRepIdArray(new Integer(indirection)); } else { // read first array element and store it as an indirection to the whole array int indirection = get_offset(); String repID = read_repositoryId(); if (repositoryIdCache == null) repositoryIdCache = new CacheTable(orb, false); repositoryIdCache.put(repID, indirection); // read and ignore the subsequent array elements, but put them in the indirection table in case there are // later indirections back to them for (int i = 1; i < numRepIds; i++) { read_repositoryId(); } return repID; } } private final String read_repositoryId() { String result = readStringOrIndirection(true); if (result == null) { // Indirection int indirection = read_long() + get_offset() - 4; if (repositoryIdCache != null && repositoryIdCache.containsOrderedVal(indirection)) return (String) repositoryIdCache.getKey(indirection); else throw wrapper.badRepIdIndirection(CompletionStatus.COMPLETED_MAYBE, new Integer(bbwi.position())); } else { if (repositoryIdCache == null) repositoryIdCache = new CacheTable(orb, false); repositoryIdCache.put(result, stringIndirection); } return result; } private final String read_codebase_URL() { String result = readStringOrIndirection(true); if (result == null) { // Indirection int indirection = read_long() + get_offset() - 4; if (codebaseCache != null && codebaseCache.containsVal(indirection)) return (String) codebaseCache.getKey(indirection); else throw wrapper.badCodebaseIndirection(CompletionStatus.COMPLETED_MAYBE, new Integer(bbwi.position())); } else { if (codebaseCache == null) codebaseCache = new CacheTable(orb, false); codebaseCache.put(result, stringIndirection); } return result; } /* DataInputStream methods */ public Object read_Abstract() { return read_abstract_interface(); } public java.io.Serializable read_Value() { return read_value(); } public void read_any_array(org.omg.CORBA.AnySeqHolder seq, int offset, int length) { read_any_array(seq.value, offset, length); } public void read_boolean_array(org.omg.CORBA.BooleanSeqHolder seq, int offset, int length) { read_boolean_array(seq.value, offset, length); } public void read_char_array(org.omg.CORBA.CharSeqHolder seq, int offset, int length) { read_char_array(seq.value, offset, length); } public void read_wchar_array(org.omg.CORBA.WCharSeqHolder seq, int offset, int length) { read_wchar_array(seq.value, offset, length); } public void read_octet_array(org.omg.CORBA.OctetSeqHolder seq, int offset, int length) { read_octet_array(seq.value, offset, length); } public void read_short_array(org.omg.CORBA.ShortSeqHolder seq, int offset, int length) { read_short_array(seq.value, offset, length); } public void read_ushort_array(org.omg.CORBA.UShortSeqHolder seq, int offset, int length) { read_ushort_array(seq.value, offset, length); } public void read_long_array(org.omg.CORBA.LongSeqHolder seq, int offset, int length) { read_long_array(seq.value, offset, length); } public void read_ulong_array(org.omg.CORBA.ULongSeqHolder seq, int offset, int length) { read_ulong_array(seq.value, offset, length); } public void read_ulonglong_array(org.omg.CORBA.ULongLongSeqHolder seq, int offset, int length) { read_ulonglong_array(seq.value, offset, length); } public void read_longlong_array(org.omg.CORBA.LongLongSeqHolder seq, int offset, int length) { read_longlong_array(seq.value, offset, length); } public void read_float_array(org.omg.CORBA.FloatSeqHolder seq, int offset, int length) { read_float_array(seq.value, offset, length); } public void read_double_array(org.omg.CORBA.DoubleSeqHolder seq, int offset, int length) { read_double_array(seq.value, offset, length); } public java.math.BigDecimal read_fixed(short digits, short scale) { // digits isn't really needed here StringBuffer buffer = read_fixed_buffer(); if (digits != buffer.length()) throw wrapper.badFixed(new Integer(digits), new Integer(buffer.length())); buffer.insert(digits - scale, '.'); return new BigDecimal(buffer.toString()); } // This method is unable to yield the correct scale. public java.math.BigDecimal read_fixed() { return new BigDecimal(read_fixed_buffer().toString()); } // Each octet contains (up to) two decimal digits. 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. The sign configuration, in the last half-octet of the // representation, is 0xD for negative numbers and 0xC for positive and zero values. private StringBuffer read_fixed_buffer() { StringBuffer buffer = new StringBuffer(64); byte doubleDigit; int firstDigit; int secondDigit; boolean wroteFirstDigit = false; boolean more = true; while (more) { doubleDigit = this.read_octet(); firstDigit = ((doubleDigit & 0xf0) >> 4); secondDigit = (doubleDigit & 0x0f); if (wroteFirstDigit || firstDigit != 0) { buffer.append(Character.forDigit(firstDigit, 10)); wroteFirstDigit = true; } if (secondDigit == 12) { // positive number or zero if (!wroteFirstDigit) { // zero return new StringBuffer("0.0"); } else { // positive number - done } more = false; } else if (secondDigit == 13) { // negative number buffer.insert(0, '-'); more = false; } else { buffer.append(Character.forDigit(secondDigit, 10)); wroteFirstDigit = true; } } return buffer; } private final static String _id = "IDL:omg.org/CORBA/DataInputStream: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() { CDRInputStream_1_0.printBuffer(this.bbwi); } public static void printBuffer(ByteBufferWithInfo bbwi) { System.out.println("----- Input Buffer -----"); System.out.println(); System.out.println("Current position: " + bbwi.position()); System.out.println("Total length : " + bbwi.buflen); System.out.println(); try { char[] charBuf = new char[16]; for (int i = 0; i < bbwi.buflen; 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.buflen) { 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.buflen) { 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 ByteBuffer getByteBuffer() { ByteBuffer result = null; if (bbwi != null) { result = bbwi.byteBuffer; } return result; } public int getBufferLength() { return bbwi.buflen; } public void setBufferLength(int value) { bbwi.buflen = value; bbwi.byteBuffer.limit(bbwi.buflen); } public void setByteBufferWithInfo(ByteBufferWithInfo bbwi) { this.bbwi = bbwi; } public void setByteBuffer(ByteBuffer byteBuffer) { bbwi.byteBuffer = byteBuffer; } public int getIndex() { return bbwi.position(); } public void setIndex(int value) { bbwi.position(value); } public boolean isLittleEndian() { return littleEndian; } public void orb(org.omg.CORBA.ORB orb) { this.orb = (ORB) orb; } public BufferManagerRead getBufferManager() { return bufferManagerRead; } private void skipToOffset(int offset) { // Number of bytes to skip int len = offset - get_offset(); int n = 0; while (n < len) { int avail; int bytes; int wanted; avail = bbwi.buflen - bbwi.position(); if (avail <= 0) { grow(1, 1); avail = bbwi.buflen - bbwi.position(); } wanted = len - n; bytes = (wanted < avail) ? wanted : avail; bbwi.position(bbwi.position() + bytes); n += bytes; } } // Mark and reset ------------------------------------------------- protected MarkAndResetHandler markAndResetHandler = null; protected class StreamMemento { // These are the fields that may change after marking the stream position, so we need to save them. private int blockLength_; private int end_flag_; private int chunkedValueNestingLevel_; private int valueIndirection_; private int stringIndirection_; private boolean isChunked_; private javax.rmi.CORBA.ValueHandler valueHandler_; private ByteBufferWithInfo bbwi_; private boolean specialNoOptionalDataState_; public StreamMemento() { blockLength_ = blockLength; end_flag_ = end_flag; chunkedValueNestingLevel_ = chunkedValueNestingLevel; valueIndirection_ = valueIndirection; stringIndirection_ = stringIndirection; isChunked_ = isChunked; valueHandler_ = valueHandler; specialNoOptionalDataState_ = specialNoOptionalDataState; bbwi_ = new ByteBufferWithInfo(bbwi); } } public Object createStreamMemento() { return new StreamMemento(); } public void restoreInternalState(Object streamMemento) { StreamMemento mem = (StreamMemento) streamMemento; blockLength = mem.blockLength_; end_flag = mem.end_flag_; chunkedValueNestingLevel = mem.chunkedValueNestingLevel_; valueIndirection = mem.valueIndirection_; stringIndirection = mem.stringIndirection_; isChunked = mem.isChunked_; valueHandler = mem.valueHandler_; specialNoOptionalDataState = mem.specialNoOptionalDataState_; bbwi = mem.bbwi_; } public int getPosition() { return get_offset(); } public void mark(int readlimit) { markAndResetHandler.mark(this); } public void reset() { markAndResetHandler.reset(); } // ---------------------------------- end Mark and Reset // Provides a hook so subclasses of CDRInputStream can provide a CodeBase. This ultimately allows us to grab a // Connection instance in IIOPInputStream, the only subclass where this is actually used. CodeBase getCodeBase() { return parent.getCodeBase(); } /** * Attempts to find the class described by the given repository ID string and expected type. The first attempt is to * find the class locally, falling back on the URL that came with the value. The second attempt is to use a URL from * the remote CodeBase. */ private Class<?> getClassFromString(String repositoryIDString, String codebaseURL, Class<?> expectedType) { RepositoryIdInterface repositoryID = repIdStrs.getFromString(repositoryIDString); try { try { // First try to load the class locally, then use the provided URL (if it isn't null) return repositoryID.getClassFromType(expectedType, codebaseURL); } catch (ClassNotFoundException cnfeOuter) { try { if (getCodeBase() == null) { return null; // class cannot be loaded remotely. } // Get a URL from the remote CodeBase and retry codebaseURL = getCodeBase().implementation(repositoryIDString); // Don't bother trying to find it locally again if we got a null URL if (codebaseURL == null) return null; return repositoryID.getClassFromType(expectedType, codebaseURL); } catch (ClassNotFoundException cnfeInner) { dprintThrowable(cnfeInner); // Failed to load the class return null; } } } catch (MalformedURLException mue) { // Always report a bad URL throw wrapper.malformedUrl(CompletionStatus.COMPLETED_MAYBE, mue, repositoryIDString, codebaseURL); } } // Utility method used to get chars from bytes char[] getConvertedChars(int numBytes, CodeSetConversion.BTCConverter converter) { // REVISIT - Look at CodeSetConversion.BTCConverter to see if it can work with an NIO ByteBuffer. We should // avoid getting the bytes into an array if possible. // To be honest, I doubt this saves much real time if (bbwi.buflen - bbwi.position() >= numBytes) { // If the entire string is in this buffer, just convert directly from the bbwi rather than allocating and // copying. byte[] tmpBuf; if (bbwi.byteBuffer.hasArray()) { tmpBuf = bbwi.byteBuffer.array(); } else { tmpBuf = new byte[bbwi.buflen]; // Microbenchmarks are showing a loop of ByteBuffer.get(int) being faster than ByteBuffer.get(byte[], // int, int). for (int i = 0; i < bbwi.buflen; i++) tmpBuf[i] = bbwi.byteBuffer.get(i); } char[] result = converter.getChars(tmpBuf, bbwi.position(), numBytes); bbwi.position(bbwi.position() + numBytes); return result; } else { // Stretches across buffers. Unless we provide an incremental conversion interface, allocate and copy the // bytes. byte[] bytes = new byte[numBytes]; read_octet_array(bytes, 0, bytes.length); return converter.getChars(bytes, 0, numBytes); } } protected CodeSetConversion.BTCConverter getCharConverter() { if (charConverter == null) charConverter = parent.createCharBTCConverter(); return charConverter; } protected CodeSetConversion.BTCConverter getWCharConverter() { if (wcharConverter == null) wcharConverter = parent.createWCharBTCConverter(); return wcharConverter; } protected void dprintThrowable(Throwable t) { if (debug && t != null) t.printStackTrace(); } protected void dprint(String msg) { if (debug) { ORBUtility.dprint(this, msg); } } /** * Aligns the current position on the given octet boundary if there are enough bytes available to do so. Otherwise, * it just returns. This is used for some (but not all) GIOP 1.2 message headers. */ void alignOnBoundary(int octetBoundary) { int needed = computeAlignment(bbwi.position(), octetBoundary); if (bbwi.position() + needed <= bbwi.buflen) { bbwi.position(bbwi.position() + needed); } } public void resetCodeSetConverters() { charConverter = null; wcharConverter = null; } public void start_value() { // Read value tag int vType = readValueTag(); if (vType == 0) { // Stream needs to go into a state where it throws standard exception until end_value is called. This means // the sender didn't send any custom data. If the reader here tries to read more, we need to throw an // exception before reading beyond where we're supposed to specialNoOptionalDataState = true; return; } if (vType == 0xffffffff) { // One should never indirect to a custom wrapper throw wrapper.customWrapperIndirection(CompletionStatus.COMPLETED_MAYBE); } if (repIdUtil.isCodeBasePresent(vType)) { throw wrapper.customWrapperWithCodebase(CompletionStatus.COMPLETED_MAYBE); } if (repIdUtil.getTypeInfo(vType) != RepositoryIdUtility.SINGLE_REP_TYPE_INFO) { throw wrapper.customWrapperNotSingleRepid(CompletionStatus.COMPLETED_MAYBE); } // REVISIT - Could verify repository ID even though it isn't used elsewhere read_repositoryId(); // Note: isChunked should be true here. Should have been set to true in the containing value's read_value // method. start_block(); end_flag--; chunkedValueNestingLevel--; } public void end_value() { if (specialNoOptionalDataState) { specialNoOptionalDataState = false; return; } handleEndOfValue(); readEndTag(); // Note that isChunked should still be true here. If the containing valuetype is the highest chunked value, it // will get set to false at the end of read_value. // allow for possible continuation chunk start_block(); } public void close() throws IOException { // tell BufferManagerRead to release any ByteBuffers getBufferManager().close(bbwi); // It's possible bbwi.byteBuffer is shared between this InputStream and an OutputStream. 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 (bbwi != null && getByteBuffer() != null) { int bbHash = System.identityHashCode(bbwi.byteBuffer); MessageMediator messageMediator = parent.getMessageMediator(); if (messageMediator != null) { CDROutputObject outputObj = (CDROutputObject) messageMediator.getOutputObject(); if (outputObj != null) { ByteBuffer outputBb = outputObj.getByteBuffer(); int oBbHash = 0; if (outputBb != null) { oBbHash = System.identityHashCode(outputBb); if (bbHash == oBbHash) // shared? { // Set OutputStream's ByteBuffer and bbwi to null so its ByteBuffer cannot be released to the pool outputObj.setByteBuffer(null); outputObj.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(bbwi.byteBuffer); bbwi.byteBuffer = null; bbwi = null; } } }