| /* |
| * Copyright (c) 1998, 2013, 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 com.sun.corba.se.impl.io; |
| |
| import org.omg.CORBA.portable.OutputStream; |
| |
| import java.security.AccessController ; |
| import java.security.PrivilegedAction ; |
| |
| import java.io.IOException; |
| import java.io.InvalidClassException; |
| import java.io.Externalizable; |
| import java.io.NotSerializableException; |
| import java.io.NotActiveException; |
| |
| import java.lang.reflect.InvocationTargetException; |
| |
| import javax.rmi.CORBA.Util; |
| |
| import sun.corba.Bridge ; |
| |
| 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 |
| * @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()); |
| } |
| } |
| |
| final void setOrbStream(org.omg.CORBA_2_3.portable.OutputStream os) { |
| orbStream = os; |
| } |
| |
| final org.omg.CORBA_2_3.portable.OutputStream getOrbStream() { |
| return orbStream; |
| } |
| |
| final void increaseRecursionDepth(){ |
| recursionDepth++; |
| } |
| |
| 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. |
| */ |
| if (currentClassDesc.forClass().getName().equals("java.lang.String")) { |
| this.writeUTF((String)obj); |
| return; |
| } |
| 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.invokeWriteObject( obj, this ) ; |
| |
| 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); |
| } |
| } |
| |
| 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 { |
| |
| for (int i = 0; i < fields.length; i++) { |
| if (fields[i].getField() == null) |
| throw new InvalidClassException(cl.getName(), |
| "Nonexistent field " + fields[i].getName()); |
| switch (fields[i].getTypeCode()) { |
| case 'B': |
| byte byteValue = bridge.getByte(o, fields[i].getFieldID()) ; |
| orbStream.write_octet(byteValue); |
| break; |
| case 'C': |
| char charValue = bridge.getChar(o, fields[i].getFieldID()) ; |
| orbStream.write_wchar(charValue); |
| break; |
| case 'F': |
| float floatValue = bridge.getFloat(o, fields[i].getFieldID()) ; |
| orbStream.write_float(floatValue); |
| break; |
| case 'D' : |
| double doubleValue = bridge.getDouble(o, fields[i].getFieldID()) ; |
| orbStream.write_double(doubleValue); |
| break; |
| case 'I': |
| int intValue = bridge.getInt(o, fields[i].getFieldID()) ; |
| orbStream.write_long(intValue); |
| break; |
| case 'J': |
| long longValue = bridge.getLong(o, fields[i].getFieldID()) ; |
| orbStream.write_longlong(longValue); |
| break; |
| case 'S': |
| short shortValue = bridge.getShort(o, fields[i].getFieldID()) ; |
| orbStream.write_short(shortValue); |
| break; |
| case 'Z': |
| boolean booleanValue = bridge.getBoolean(o, fields[i].getFieldID()) ; |
| orbStream.write_boolean(booleanValue); |
| break; |
| case '[': |
| case 'L': |
| Object objectValue = bridge.getObject(o, fields[i].getFieldID()) ; |
| writeObjectField(fields[i], objectValue); |
| break; |
| default: |
| throw new InvalidClassException(cl.getName()); |
| } |
| } |
| } |
| } |