/* * @(#)IIOPOutputStream.java 1.51 04/05/05 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ /* * Licensed Materials - Property of IBM * RMI-IIOP v1.0 * Copyright IBM Corp. 1998 1999 All Rights Reserved * * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ package com.sun.corba.se.impl.io; import org.omg.CORBA.INTERNAL; import org.omg.CORBA.portable.OutputStream; import java.security.AccessController ; import java.security.PrivilegedAction ; import java.io.IOException; import java.io.DataOutputStream; import java.io.Serializable; import java.io.InvalidClassException; import java.io.StreamCorruptedException; import java.io.Externalizable; import java.io.ObjectStreamException; import java.io.NotSerializableException; import java.io.NotActiveException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Field; import java.util.Stack; import javax.rmi.CORBA.Util; import javax.rmi.CORBA.ValueHandlerMultiFormat; import sun.corba.Bridge ; import com.sun.corba.se.impl.io.ObjectStreamClass; import com.sun.corba.se.impl.util.Utility; import com.sun.corba.se.impl.util.RepositoryId; import com.sun.corba.se.spi.logging.CORBALogDomains ; import com.sun.corba.se.impl.logging.UtilSystemException ; /** * IIOPOutputStream is ... * * @author Stephen Lewallen * @version 0.01, 4/6/98 * @since JDK1.1.6 */ public class IIOPOutputStream extends com.sun.corba.se.impl.io.OutputStreamHook { private UtilSystemException wrapper = UtilSystemException.get( CORBALogDomains.RPC_ENCODING ) ; private static Bridge bridge = (Bridge)AccessController.doPrivileged( new PrivilegedAction() { public Object 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 classDescStack = new java.util.Stack(); // 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((OutputStream)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 { // if (!checkSpecialClasses(obj) && !checkSubstitutableSpecialClasses(obj)) 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(); 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 /* * Check for special cases of serializing objects. * These objects are not subject to replacement. */ private boolean checkSpecialClasses(Object obj) throws IOException { /* * If this is a class, don't allow substitution */ //if (obj instanceof Class) { // throw new IOException("Serialization of Class not supported"); //} if (obj instanceof ObjectStreamClass) { // XXX I18N, Logging needed. throw new IOException("Serialization of ObjectStreamClass not supported"); } return false; } /* * Check for special cases of substitutable serializing objects. * These classes are replaceable. */ private boolean checkSubstitutableSpecialClasses(Object obj) throws IOException { if (obj instanceof String) { orbStream.write_value((java.io.Serializable)obj); return true; } //if (obj.getClass().isArray()) { // outputArray(obj); // return true; //} return false; } /* * 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 = (ObjectStreamClass)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 { Class c = osc.forClass() ; 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 // XXX I18N, Logging needed. 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((float)0); else orbStream.write_float(((Float)value).floatValue()); break; case 'D': if (value == null) orbStream.write_double((double)0); else orbStream.write_double(((Double)value).doubleValue()); break; case 'I': if (value == null) orbStream.write_long((int)0); else orbStream.write_long(((Integer)value).intValue()); break; case 'J': if (value == null) orbStream.write_longlong((long)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()) { String className = type.getName(); 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() ) ; } } } }