/* * Copyright (c) 1998, 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.io; import java.io.Externalizable; import java.io.IOException; import java.io.InvalidClassException; import java.io.NotActiveException; import java.io.NotSerializableException; import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.PrivilegedAction; import javax.rmi.CORBA.Util; import org.jboss.com.sun.corba.se.impl.logging.UtilSystemException; 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.spi.logging.CORBALogDomains; import org.jboss.sun.corba.Bridge; /** * IIOPOutputStream is ... * * @author Stephen Lewallen * @since JDK1.1.6 */ public class IIOPOutputStream extends org.jboss.com.sun.corba.se.impl.io.OutputStreamHook { private UtilSystemException wrapper = UtilSystemException.get(CORBALogDomains.RPC_ENCODING); private static Bridge bridge = AccessController.doPrivileged(new PrivilegedAction<Bridge>() { public Bridge run() { return Bridge.get(); } }); private org.omg.CORBA_2_3.portable.OutputStream orbStream; private Object currentObject = null; private ObjectStreamClass currentClassDesc = null; private int recursionDepth = 0; private int simpleWriteDepth = 0; private IOException abortIOException = null; private java.util.Stack<ObjectStreamClass> classDescStack = new java.util.Stack<ObjectStreamClass>(); // Used when calling an object's writeObject method private Object[] writeObjectArgList = {this}; public IIOPOutputStream() throws java.io.IOException { super(); } // If using RMI-IIOP stream format version 2, this tells the ORB stream (which must be a ValueOutputStream) to begin // a new valuetype to contain the optional data of the writeObject method. protected void beginOptionalCustomData() { if (streamFormatVersion == 2) { org.omg.CORBA.portable.ValueOutputStream vout = (org.omg.CORBA.portable.ValueOutputStream) orbStream; vout.start_value(currentClassDesc.getRMIIIOPOptionalDataRepId()); } } public final void setOrbStream(org.omg.CORBA_2_3.portable.OutputStream os) { orbStream = os; } public final org.omg.CORBA_2_3.portable.OutputStream getOrbStream() { return orbStream; } public final void increaseRecursionDepth() { recursionDepth++; } public final int decreaseRecursionDepth() { return --recursionDepth; } /** * Override the actions of the final method "writeObject()" in ObjectOutputStream. * * @since JDK1.1.6 */ public final void writeObjectOverride(Object obj) throws IOException { writeObjectState.writeData(this); Util.writeAbstractObject(orbStream, obj); } /** * Override the actions of the final method "writeObject()" in ObjectOutputStream. * * @since JDK1.1.6 */ public final void simpleWriteObject(Object obj, byte formatVersion) /* throws IOException */ { byte oldStreamFormatVersion = streamFormatVersion; streamFormatVersion = formatVersion; Object prevObject = currentObject; ObjectStreamClass prevClassDesc = currentClassDesc; simpleWriteDepth++; try { outputObject(obj); } catch (IOException ee) { if (abortIOException == null) abortIOException = ee; } finally { /* Restore state of previous call incase this is a nested call */ streamFormatVersion = oldStreamFormatVersion; simpleWriteDepth--; currentObject = prevObject; currentClassDesc = prevClassDesc; } /* * If the recursion depth is 0, test for and clear the pending exception. If there is a pending exception throw * it. */ IOException pending = abortIOException; if (simpleWriteDepth == 0) abortIOException = null; if (pending != null) { bridge.throwException(pending); } } // Required by the superclass. ObjectStreamField[] getFieldsNoCopy() { return currentClassDesc.getFieldsNoCopy(); } /** * Override the actions of the final method "defaultWriteObject()" in ObjectOutputStream. * * @since JDK1.1.6 */ public final void defaultWriteObjectDelegate() /* throws IOException */ { try { if (currentObject == null || currentClassDesc == null) // XXX I18N, Logging needed. throw new NotActiveException("defaultWriteObjectDelegate"); ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy(); if (fields.length > 0) { outputClassFields(currentObject, currentClassDesc.forClass(), fields); } } catch (IOException ioe) { bridge.throwException(ioe); } } /** * Override the actions of the final method "enableReplaceObject()" in ObjectOutputStream. * * @since JDK1.1.6 */ public final boolean enableReplaceObjectDelegate(boolean enable) /* throws SecurityException */ { return false; } protected final void annotateClass(Class<?> cl) throws IOException { // XXX I18N, Logging needed. throw new IOException("Method annotateClass not supported"); } public final void close() throws IOException { // no op } protected final void drain() throws IOException { // no op } public final void flush() throws IOException { try { orbStream.flush(); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } protected final Object replaceObject(Object obj) throws IOException { // XXX I18N, Logging needed. throw new IOException("Method replaceObject not supported"); } /** * Reset will disregard the state of any objects already written to the stream. The state is reset to be the same as * a new ObjectOutputStream. The current point in the stream is marked as reset so the corresponding * ObjectInputStream will be reset at the same point. Objects previously written to the stream will not be refered * to as already being in the stream. They will be written to the stream again. * * @since JDK1.1 */ public final void reset() throws IOException { try { // orbStream.reset(); if (currentObject != null || currentClassDesc != null) // XXX I18N, Logging needed. throw new IOException("Illegal call to reset"); abortIOException = null; if (classDescStack == null) classDescStack = new java.util.Stack<ObjectStreamClass>(); else classDescStack.setSize(0); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void write(byte b[]) throws IOException { try { writeObjectState.writeData(this); orbStream.write_octet_array(b, 0, b.length); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void write(byte b[], int off, int len) throws IOException { try { writeObjectState.writeData(this); orbStream.write_octet_array(b, off, len); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void write(int data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_octet((byte) (data & 0xFF)); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeBoolean(boolean data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_boolean(data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeByte(int data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_octet((byte) data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeBytes(String data) throws IOException { try { writeObjectState.writeData(this); byte buf[] = data.getBytes(); orbStream.write_octet_array(buf, 0, buf.length); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeChar(int data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_wchar((char) data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeChars(String data) throws IOException { try { writeObjectState.writeData(this); char buf[] = data.toCharArray(); orbStream.write_wchar_array(buf, 0, buf.length); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeDouble(double data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_double(data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeFloat(float data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_float(data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeInt(int data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_long(data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeLong(long data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_longlong(data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } public final void writeShort(int data) throws IOException { try { writeObjectState.writeData(this); orbStream.write_short((short) data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } protected final void writeStreamHeader() throws IOException { // no op } /** * Helper method for correcting the Kestrel bug 4367783 (dealing with larger than 8-bit chars). The old behavior is * preserved in orbutil.IIOPInputStream_1_3 in order to interoperate with our legacy ORBs. */ protected void internalWriteUTF(org.omg.CORBA.portable.OutputStream stream, String data) { stream.write_wstring(data); } public final void writeUTF(String data) throws IOException { try { writeObjectState.writeData(this); internalWriteUTF(orbStream, data); } catch (Error e) { IOException ioexc = new IOException(e.getMessage()); ioexc.initCause(e); throw ioexc; } } // INTERNAL UTILITY METHODS /* * Write out the object */ private void outputObject(final Object obj) throws IOException { currentObject = obj; Class<?> currclass = obj.getClass(); /* * Get the Class descriptor for this class, Throw a NotSerializableException if there is none. */ currentClassDesc = ObjectStreamClass.lookup(currclass); if (currentClassDesc == null) { // XXX I18N, Logging needed. throw new NotSerializableException(currclass.getName()); } /* * If the object is externalizable, call writeExternal. else do Serializable processing. */ if (currentClassDesc.isExternalizable()) { // Write format version orbStream.write_octet(streamFormatVersion); Externalizable ext = (Externalizable) obj; ext.writeExternal(this); } else { /* * The object's classes should be processed from supertype to subtype Push all the clases of the current * object onto a stack. Remember the stack pointer where this set of classes is being pushed. */ int stackMark = classDescStack.size(); try { ObjectStreamClass next; while ((next = currentClassDesc.getSuperclass()) != null) { classDescStack.push(currentClassDesc); currentClassDesc = next; } /* * For currentClassDesc and all the pushed class descriptors If the class is writing its own data set * blockData = true; call the class writeObject method If not invoke either the defaultWriteObject * method. */ do { WriteObjectState oldState = writeObjectState; try { setState(NOT_IN_WRITE_OBJECT); if (currentClassDesc.hasWriteObject()) { invokeObjectWriter(currentClassDesc, obj); } else { defaultWriteObjectDelegate(); } } finally { setState(oldState); } } while (classDescStack.size() > stackMark && (currentClassDesc = classDescStack.pop()) != null); } finally { classDescStack.setSize(stackMark); } } } /* * Invoke writer. _REVISIT_ invokeObjectWriter and invokeObjectReader behave inconsistently with each other since * the reader returns a boolean...fix later */ private void invokeObjectWriter(ObjectStreamClass osc, Object obj) throws IOException { try { // Write format version orbStream.write_octet(streamFormatVersion); writeObjectState.enterWriteObject(this); // writeObject(obj, c, this); osc.writeObjectMethod.invoke(obj, writeObjectArgList); writeObjectState.exitWriteObject(this); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof IOException) throw (IOException) t; else if (t instanceof RuntimeException) throw (RuntimeException) t; else if (t instanceof Error) throw (Error) t; else throw new Error("invokeObjectWriter internal error", e); } catch (IllegalAccessException e) { // cannot happen } } void writeField(ObjectStreamField field, Object value) throws IOException { switch (field.getTypeCode()) { case 'B' : if (value == null) orbStream.write_octet((byte) 0); else orbStream.write_octet(((Byte) value).byteValue()); break; case 'C' : if (value == null) orbStream.write_wchar((char) 0); else orbStream.write_wchar(((Character) value).charValue()); break; case 'F' : if (value == null) orbStream.write_float(0); else orbStream.write_float(((Float) value).floatValue()); break; case 'D' : if (value == null) orbStream.write_double(0); else orbStream.write_double(((Double) value).doubleValue()); break; case 'I' : if (value == null) orbStream.write_long(0); else orbStream.write_long(((Integer) value).intValue()); break; case 'J' : if (value == null) orbStream.write_longlong(0); else orbStream.write_longlong(((Long) value).longValue()); break; case 'S' : if (value == null) orbStream.write_short((short) 0); else orbStream.write_short(((Short) value).shortValue()); break; case 'Z' : if (value == null) orbStream.write_boolean(false); else orbStream.write_boolean(((Boolean) value).booleanValue()); break; case '[' : case 'L' : // What to do if it's null? writeObjectField(field, value); break; default : // XXX I18N, Logging needed. throw new InvalidClassException(currentClassDesc.getName()); } } private void writeObjectField(ObjectStreamField field, Object objectValue) throws IOException { if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) { javax.rmi.CORBA.Util.writeAny(orbStream, objectValue); } else { Class<?> type = field.getType(); int callType = ValueHandlerImpl.kValueType; if (type.isInterface()) { if (java.rmi.Remote.class.isAssignableFrom(type)) { // RMI Object reference... callType = ValueHandlerImpl.kRemoteType; } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)) { // IDL Object reference... callType = ValueHandlerImpl.kRemoteType; } else if (RepositoryId.isAbstractBase(type)) { // IDL Abstract Object reference... callType = ValueHandlerImpl.kAbstractType; } else if (ObjectStreamClassCorbaExt.isAbstractInterface(type)) { callType = ValueHandlerImpl.kAbstractType; } } switch (callType) { case ValueHandlerImpl.kRemoteType : Util.writeRemoteObject(orbStream, objectValue); break; case ValueHandlerImpl.kAbstractType : Util.writeAbstractObject(orbStream, objectValue); break; case ValueHandlerImpl.kValueType : try { orbStream.write_value((java.io.Serializable) objectValue, type); } catch (ClassCastException cce) { if (objectValue instanceof java.io.Serializable) throw cce; else Utility.throwNotSerializableForCorba(objectValue.getClass().getName()); } } } } /* * Write the fields of the specified class by invoking the appropriate write* method on this class. */ private void outputClassFields(Object o, Class<?> cl, ObjectStreamField[] fields) throws IOException, InvalidClassException { for (int i = 0; i < fields.length; i++) { if (fields[i].getField() == null) // XXX I18N, Logging needed. throw new InvalidClassException(cl.getName(), "Nonexistent field " + fields[i].getName()); try { switch (fields[i].getTypeCode()) { case 'B' : byte byteValue = fields[i].getField().getByte(o); orbStream.write_octet(byteValue); break; case 'C' : char charValue = fields[i].getField().getChar(o); orbStream.write_wchar(charValue); break; case 'F' : float floatValue = fields[i].getField().getFloat(o); orbStream.write_float(floatValue); break; case 'D' : double doubleValue = fields[i].getField().getDouble(o); orbStream.write_double(doubleValue); break; case 'I' : int intValue = fields[i].getField().getInt(o); orbStream.write_long(intValue); break; case 'J' : long longValue = fields[i].getField().getLong(o); orbStream.write_longlong(longValue); break; case 'S' : short shortValue = fields[i].getField().getShort(o); orbStream.write_short(shortValue); break; case 'Z' : boolean booleanValue = fields[i].getField().getBoolean(o); orbStream.write_boolean(booleanValue); break; case '[' : case 'L' : Object objectValue = fields[i].getField().get(o); writeObjectField(fields[i], objectValue); break; default : // XXX I18N, Logging needed. throw new InvalidClassException(cl.getName()); } } catch (IllegalAccessException exc) { throw wrapper.illegalFieldAccess(exc, fields[i].getName()); } } } }