IIOPOutputStream.java revision 717:a88d571b42b6
1/*
2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * RMI-IIOP v1.0
28 * Copyright IBM Corp. 1998 1999  All Rights Reserved
29 *
30 */
31
32package com.sun.corba.se.impl.io;
33
34import org.omg.CORBA.INTERNAL;
35import org.omg.CORBA.portable.OutputStream;
36
37import java.security.AccessController ;
38import java.security.PrivilegedAction ;
39
40import java.io.IOException;
41import java.io.DataOutputStream;
42import java.io.Serializable;
43import java.io.InvalidClassException;
44import java.io.StreamCorruptedException;
45import java.io.Externalizable;
46import java.io.ObjectStreamException;
47import java.io.NotSerializableException;
48import java.io.NotActiveException;
49
50import java.lang.reflect.InvocationTargetException;
51import java.lang.reflect.Field;
52
53import java.util.Stack;
54
55import javax.rmi.CORBA.Util;
56import javax.rmi.CORBA.ValueHandlerMultiFormat;
57
58import sun.corba.Bridge ;
59
60import com.sun.corba.se.impl.io.ObjectStreamClass;
61import com.sun.corba.se.impl.util.Utility;
62import com.sun.corba.se.impl.util.RepositoryId;
63
64import com.sun.corba.se.spi.logging.CORBALogDomains ;
65import com.sun.corba.se.impl.logging.UtilSystemException ;
66
67/**
68 * IIOPOutputStream is ...
69 *
70 * @author  Stephen Lewallen
71 * @since   JDK1.1.6
72 */
73
74public class IIOPOutputStream
75    extends com.sun.corba.se.impl.io.OutputStreamHook
76{
77    private UtilSystemException wrapper = UtilSystemException.get(
78        CORBALogDomains.RPC_ENCODING ) ;
79
80    private static Bridge bridge =
81        (Bridge)AccessController.doPrivileged(
82            new PrivilegedAction() {
83                public Object run() {
84                    return Bridge.get() ;
85                }
86            }
87        ) ;
88
89    private org.omg.CORBA_2_3.portable.OutputStream orbStream;
90
91    private Object currentObject = null;
92
93    private ObjectStreamClass currentClassDesc = null;
94
95    private int recursionDepth = 0;
96
97    private int simpleWriteDepth = 0;
98
99    private IOException abortIOException = null;
100
101    private java.util.Stack classDescStack = new java.util.Stack();
102
103    // Used when calling an object's writeObject method
104    private Object[] writeObjectArgList = {this};
105
106    public IIOPOutputStream()
107        throws java.io.IOException
108   {
109        super();
110    }
111
112    // If using RMI-IIOP stream format version 2, this tells
113    // the ORB stream (which must be a ValueOutputStream) to
114    // begin a new valuetype to contain the optional data
115    // of the writeObject method.
116    protected void beginOptionalCustomData() {
117
118        if (streamFormatVersion == 2) {
119
120            org.omg.CORBA.portable.ValueOutputStream vout
121                = (org.omg.CORBA.portable.ValueOutputStream)orbStream;
122
123            vout.start_value(currentClassDesc.getRMIIIOPOptionalDataRepId());
124        }
125    }
126
127    final void setOrbStream(org.omg.CORBA_2_3.portable.OutputStream os) {
128        orbStream = os;
129    }
130
131    final org.omg.CORBA_2_3.portable.OutputStream getOrbStream() {
132        return orbStream;
133    }
134
135    final void increaseRecursionDepth(){
136        recursionDepth++;
137    }
138
139    final int decreaseRecursionDepth(){
140        return --recursionDepth;
141    }
142
143    /**
144     * Override the actions of the final method "writeObject()"
145     * in ObjectOutputStream.
146     * @since     JDK1.1.6
147     */
148    public final void writeObjectOverride(Object obj)
149        throws IOException
150    {
151        writeObjectState.writeData(this);
152
153        Util.writeAbstractObject((OutputStream)orbStream, obj);
154    }
155
156    /**
157     * Override the actions of the final method "writeObject()"
158     * in ObjectOutputStream.
159     * @since     JDK1.1.6
160     */
161    public final void simpleWriteObject(Object obj, byte formatVersion)
162    /* throws IOException */
163    {
164        byte oldStreamFormatVersion = streamFormatVersion;
165
166        streamFormatVersion = formatVersion;
167
168        Object prevObject = currentObject;
169        ObjectStreamClass prevClassDesc = currentClassDesc;
170        simpleWriteDepth++;
171
172        try {
173            // if (!checkSpecialClasses(obj) && !checkSubstitutableSpecialClasses(obj))
174            outputObject(obj);
175
176        } catch (IOException ee) {
177            if (abortIOException == null)
178                abortIOException = ee;
179        } finally {
180            /* Restore state of previous call incase this is a nested call */
181            streamFormatVersion = oldStreamFormatVersion;
182            simpleWriteDepth--;
183            currentObject = prevObject;
184            currentClassDesc = prevClassDesc;
185        }
186
187        /* If the recursion depth is 0, test for and clear the pending exception.
188         * If there is a pending exception throw it.
189         */
190        IOException pending = abortIOException;
191        if (simpleWriteDepth == 0)
192            abortIOException = null;
193        if (pending != null) {
194            bridge.throwException( pending ) ;
195        }
196    }
197
198    // Required by the superclass.
199    ObjectStreamField[] getFieldsNoCopy() {
200        return currentClassDesc.getFieldsNoCopy();
201    }
202
203    /**
204     * Override the actions of the final method "defaultWriteObject()"
205     * in ObjectOutputStream.
206     * @since     JDK1.1.6
207     */
208    public final void defaultWriteObjectDelegate()
209    /* throws IOException */
210    {
211        try {
212            if (currentObject == null || currentClassDesc == null)
213                // XXX I18N, Logging needed.
214                throw new NotActiveException("defaultWriteObjectDelegate");
215
216            ObjectStreamField[] fields =
217                currentClassDesc.getFieldsNoCopy();
218            if (fields.length > 0) {
219                outputClassFields(currentObject, currentClassDesc.forClass(),
220                                  fields);
221            }
222        } catch(IOException ioe) {
223            bridge.throwException(ioe);
224        }
225    }
226
227    /**
228     * Override the actions of the final method "enableReplaceObject()"
229     * in ObjectOutputStream.
230     * @since     JDK1.1.6
231     */
232    public final boolean enableReplaceObjectDelegate(boolean enable)
233    /* throws SecurityException */
234    {
235        return false;
236
237    }
238
239
240    protected final void annotateClass(Class<?> cl) throws IOException{
241        // XXX I18N, Logging needed.
242        throw new IOException("Method annotateClass not supported");
243    }
244
245    public final void close() throws IOException{
246        // no op
247    }
248
249    protected final void drain() throws IOException{
250        // no op
251    }
252
253    public final void flush() throws IOException{
254        try{
255            orbStream.flush();
256        } catch(Error e) {
257            IOException ioexc = new IOException(e.getMessage());
258            ioexc.initCause(e) ;
259            throw ioexc ;
260        }
261    }
262
263    protected final Object replaceObject(Object obj) throws IOException{
264        // XXX I18N, Logging needed.
265        throw new IOException("Method replaceObject not supported");
266    }
267
268    /**
269     * Reset will disregard the state of any objects already written
270     * to the stream.  The state is reset to be the same as a new
271     * ObjectOutputStream.  The current point in the stream is marked
272     * as reset so the corresponding ObjectInputStream will be reset
273     * at the same point.  Objects previously written to the stream
274     * will not be refered to as already being in the stream.  They
275     * will be written to the stream again.
276     * @since     JDK1.1
277     */
278    public final void reset() throws IOException{
279        try{
280            //orbStream.reset();
281
282            if (currentObject != null || currentClassDesc != null)
283                // XXX I18N, Logging needed.
284                throw new IOException("Illegal call to reset");
285
286            abortIOException = null;
287
288            if (classDescStack == null)
289                classDescStack = new java.util.Stack();
290            else
291                classDescStack.setSize(0);
292
293        } catch(Error e) {
294            IOException ioexc = new IOException(e.getMessage());
295            ioexc.initCause(e) ;
296            throw ioexc ;
297        }
298    }
299
300    public final void write(byte b[]) throws IOException{
301        try{
302            writeObjectState.writeData(this);
303
304            orbStream.write_octet_array(b, 0, b.length);
305        } catch(Error e) {
306            IOException ioexc = new IOException(e.getMessage());
307            ioexc.initCause(e) ;
308            throw ioexc ;
309        }
310    }
311
312    public final void write(byte b[], int off, int len) throws IOException{
313        try{
314            writeObjectState.writeData(this);
315
316            orbStream.write_octet_array(b, off, len);
317        } catch(Error e) {
318            IOException ioexc = new IOException(e.getMessage());
319            ioexc.initCause(e) ;
320            throw ioexc ;
321        }
322    }
323
324    public final void write(int data) throws IOException{
325        try{
326            writeObjectState.writeData(this);
327
328            orbStream.write_octet((byte)(data & 0xFF));
329        } catch(Error e) {
330            IOException ioexc = new IOException(e.getMessage());
331            ioexc.initCause(e) ;
332            throw ioexc ;
333        }
334    }
335
336    public final void writeBoolean(boolean data) throws IOException{
337        try{
338            writeObjectState.writeData(this);
339
340            orbStream.write_boolean(data);
341        } catch(Error e) {
342            IOException ioexc = new IOException(e.getMessage());
343            ioexc.initCause(e) ;
344            throw ioexc ;
345        }
346    }
347
348    public final void writeByte(int data) throws IOException{
349        try{
350            writeObjectState.writeData(this);
351
352            orbStream.write_octet((byte)data);
353        } catch(Error e) {
354            IOException ioexc = new IOException(e.getMessage());
355            ioexc.initCause(e) ;
356            throw ioexc ;
357        }
358    }
359
360    public final void writeBytes(String data) throws IOException{
361        try{
362            writeObjectState.writeData(this);
363
364            byte buf[] = data.getBytes();
365            orbStream.write_octet_array(buf, 0, buf.length);
366        } catch(Error e) {
367            IOException ioexc = new IOException(e.getMessage());
368            ioexc.initCause(e) ;
369            throw ioexc ;
370        }
371    }
372
373    public final void writeChar(int data) throws IOException{
374        try{
375            writeObjectState.writeData(this);
376
377            orbStream.write_wchar((char)data);
378        } catch(Error e) {
379            IOException ioexc = new IOException(e.getMessage());
380            ioexc.initCause(e) ;
381            throw ioexc ;
382        }
383    }
384
385    public final void writeChars(String data) throws IOException{
386        try{
387            writeObjectState.writeData(this);
388
389            char buf[] = data.toCharArray();
390            orbStream.write_wchar_array(buf, 0, buf.length);
391        } catch(Error e) {
392            IOException ioexc = new IOException(e.getMessage());
393            ioexc.initCause(e) ;
394            throw ioexc ;
395        }
396    }
397
398    public final void writeDouble(double data) throws IOException{
399        try{
400            writeObjectState.writeData(this);
401
402            orbStream.write_double(data);
403        } catch(Error e) {
404            IOException ioexc = new IOException(e.getMessage());
405            ioexc.initCause(e) ;
406            throw ioexc ;
407        }
408    }
409
410    public final void writeFloat(float data) throws IOException{
411        try{
412            writeObjectState.writeData(this);
413
414            orbStream.write_float(data);
415        } catch(Error e) {
416            IOException ioexc = new IOException(e.getMessage());
417            ioexc.initCause(e) ;
418            throw ioexc ;
419        }
420    }
421
422    public final void writeInt(int data) throws IOException{
423        try{
424            writeObjectState.writeData(this);
425
426            orbStream.write_long(data);
427        } catch(Error e) {
428            IOException ioexc = new IOException(e.getMessage());
429            ioexc.initCause(e) ;
430            throw ioexc ;
431        }
432    }
433
434    public final void writeLong(long data) throws IOException{
435        try{
436            writeObjectState.writeData(this);
437
438            orbStream.write_longlong(data);
439        } catch(Error e) {
440            IOException ioexc = new IOException(e.getMessage());
441            ioexc.initCause(e) ;
442            throw ioexc ;
443        }
444    }
445
446    public final void writeShort(int data) throws IOException{
447        try{
448            writeObjectState.writeData(this);
449
450            orbStream.write_short((short)data);
451        } catch(Error e) {
452            IOException ioexc = new IOException(e.getMessage());
453            ioexc.initCause(e) ;
454            throw ioexc ;
455        }
456    }
457
458    protected final void writeStreamHeader() throws IOException{
459        // no op
460    }
461
462    /**
463     * Helper method for correcting the Kestrel bug 4367783 (dealing
464     * with larger than 8-bit chars).  The old behavior is preserved
465     * in orbutil.IIOPInputStream_1_3 in order to interoperate with
466     * our legacy ORBs.
467     */
468    protected void internalWriteUTF(org.omg.CORBA.portable.OutputStream stream,
469                                    String data)
470    {
471        stream.write_wstring(data);
472    }
473
474    public final void writeUTF(String data) throws IOException{
475        try{
476            writeObjectState.writeData(this);
477
478            internalWriteUTF(orbStream, data);
479        } catch(Error e) {
480            IOException ioexc = new IOException(e.getMessage());
481            ioexc.initCause(e) ;
482            throw ioexc ;
483        }
484    }
485
486    // INTERNAL UTILITY METHODS
487    /*
488     * Check for special cases of serializing objects.
489     * These objects are not subject to replacement.
490     */
491    private boolean checkSpecialClasses(Object obj) throws IOException {
492
493        /*
494         * If this is a class, don't allow substitution
495         */
496        //if (obj instanceof Class) {
497        //    throw new IOException("Serialization of Class not supported");
498        //}
499
500        if (obj instanceof ObjectStreamClass) {
501            // XXX I18N, Logging needed.
502            throw new IOException("Serialization of ObjectStreamClass not supported");
503        }
504
505        return false;
506    }
507
508    /*
509     * Check for special cases of substitutable serializing objects.
510     * These classes are replaceable.
511     */
512    private boolean checkSubstitutableSpecialClasses(Object obj)
513        throws IOException
514    {
515        if (obj instanceof String) {
516            orbStream.write_value((java.io.Serializable)obj);
517            return true;
518        }
519
520        //if (obj.getClass().isArray()) {
521        //    outputArray(obj);
522        //    return true;
523        //}
524
525        return false;
526    }
527
528    /*
529     * Write out the object
530     */
531    private void outputObject(final Object obj) throws IOException{
532
533        currentObject = obj;
534        Class currclass = obj.getClass();
535
536        /* Get the Class descriptor for this class,
537         * Throw a NotSerializableException if there is none.
538         */
539        currentClassDesc = ObjectStreamClass.lookup(currclass);
540        if (currentClassDesc == null) {
541            // XXX I18N, Logging needed.
542            throw new NotSerializableException(currclass.getName());
543        }
544
545        /* If the object is externalizable,
546         * call writeExternal.
547         * else do Serializable processing.
548         */
549        if (currentClassDesc.isExternalizable()) {
550            // Write format version
551            orbStream.write_octet(streamFormatVersion);
552
553            Externalizable ext = (Externalizable)obj;
554            ext.writeExternal(this);
555
556        } else {
557
558            /* The object's classes should be processed from supertype to subtype
559             * Push all the clases of the current object onto a stack.
560             * Remember the stack pointer where this set of classes is being pushed.
561             */
562            if (currentClassDesc.forClass().getName().equals("java.lang.String")) {
563                    this.writeUTF((String)obj);
564                    return;
565            }
566            int stackMark = classDescStack.size();
567            try {
568                ObjectStreamClass next;
569                while ((next = currentClassDesc.getSuperclass()) != null) {
570                    classDescStack.push(currentClassDesc);
571                    currentClassDesc = next;
572                }
573
574                /*
575                 * For currentClassDesc and all the pushed class descriptors
576                 *    If the class is writing its own data
577                 *                set blockData = true; call the class writeObject method
578                 *    If not
579                 *     invoke either the defaultWriteObject method.
580                 */
581                do {
582
583                    WriteObjectState oldState = writeObjectState;
584
585                    try {
586
587                        setState(NOT_IN_WRITE_OBJECT);
588
589                        if (currentClassDesc.hasWriteObject()) {
590                            invokeObjectWriter(currentClassDesc, obj );
591                        } else {
592                            defaultWriteObjectDelegate();
593                        }
594                    } finally {
595                        setState(oldState);
596                    }
597
598                } while (classDescStack.size() > stackMark &&
599                         (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
600            } finally {
601                classDescStack.setSize(stackMark);
602            }
603        }
604    }
605
606    /*
607     * Invoke writer.
608     * _REVISIT_ invokeObjectWriter and invokeObjectReader behave inconsistently with each other since
609     * the reader returns a boolean...fix later
610     */
611    private void invokeObjectWriter(ObjectStreamClass osc, Object obj)
612        throws IOException
613    {
614        Class c = osc.forClass() ;
615
616        try {
617
618            // Write format version
619            orbStream.write_octet(streamFormatVersion);
620
621            writeObjectState.enterWriteObject(this);
622
623            // writeObject(obj, c, this);
624            osc.writeObjectMethod.invoke( obj, writeObjectArgList ) ;
625
626            writeObjectState.exitWriteObject(this);
627
628        } catch (InvocationTargetException e) {
629            Throwable t = e.getTargetException();
630            if (t instanceof IOException)
631                throw (IOException)t;
632            else if (t instanceof RuntimeException)
633                throw (RuntimeException) t;
634            else if (t instanceof Error)
635                throw (Error) t;
636            else
637                // XXX I18N, Logging needed.
638                throw new Error("invokeObjectWriter internal error",e);
639        } catch (IllegalAccessException e) {
640            // cannot happen
641        }
642    }
643
644    void writeField(ObjectStreamField field, Object value) throws IOException {
645        switch (field.getTypeCode()) {
646            case 'B':
647                if (value == null)
648                    orbStream.write_octet((byte)0);
649                else
650                    orbStream.write_octet(((Byte)value).byteValue());
651                break;
652            case 'C':
653                if (value == null)
654                    orbStream.write_wchar((char)0);
655                else
656                    orbStream.write_wchar(((Character)value).charValue());
657                break;
658            case 'F':
659                if (value == null)
660                    orbStream.write_float((float)0);
661                else
662                    orbStream.write_float(((Float)value).floatValue());
663                break;
664            case 'D':
665                if (value == null)
666                    orbStream.write_double((double)0);
667                else
668                    orbStream.write_double(((Double)value).doubleValue());
669                break;
670            case 'I':
671                if (value == null)
672                    orbStream.write_long((int)0);
673                else
674                    orbStream.write_long(((Integer)value).intValue());
675                break;
676            case 'J':
677                if (value == null)
678                    orbStream.write_longlong((long)0);
679                else
680                    orbStream.write_longlong(((Long)value).longValue());
681                break;
682            case 'S':
683                if (value == null)
684                    orbStream.write_short((short)0);
685                else
686                    orbStream.write_short(((Short)value).shortValue());
687                break;
688            case 'Z':
689                if (value == null)
690                    orbStream.write_boolean(false);
691                else
692                    orbStream.write_boolean(((Boolean)value).booleanValue());
693                break;
694            case '[':
695            case 'L':
696                // What to do if it's null?
697                writeObjectField(field, value);
698                break;
699            default:
700                // XXX I18N, Logging needed.
701                throw new InvalidClassException(currentClassDesc.getName());
702            }
703    }
704
705    private void writeObjectField(ObjectStreamField field,
706                                  Object objectValue) throws IOException {
707
708        if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) {
709            javax.rmi.CORBA.Util.writeAny(orbStream, objectValue);
710        }
711        else {
712            Class type = field.getType();
713            int callType = ValueHandlerImpl.kValueType;
714
715            if (type.isInterface()) {
716                String className = type.getName();
717
718                if (java.rmi.Remote.class.isAssignableFrom(type)) {
719
720                    // RMI Object reference...
721
722                    callType = ValueHandlerImpl.kRemoteType;
723
724
725                } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)){
726
727                    // IDL Object reference...
728                    callType = ValueHandlerImpl.kRemoteType;
729
730                } else if (RepositoryId.isAbstractBase(type)) {
731                    // IDL Abstract Object reference...
732                    callType = ValueHandlerImpl.kAbstractType;
733                } else if (ObjectStreamClassCorbaExt.isAbstractInterface(type)) {
734                    callType = ValueHandlerImpl.kAbstractType;
735                }
736            }
737
738            switch (callType) {
739            case ValueHandlerImpl.kRemoteType:
740                Util.writeRemoteObject(orbStream, objectValue);
741                break;
742            case ValueHandlerImpl.kAbstractType:
743                Util.writeAbstractObject(orbStream, objectValue);
744                break;
745            case ValueHandlerImpl.kValueType:
746                try{
747                    orbStream.write_value((java.io.Serializable)objectValue, type);
748                }
749                catch(ClassCastException cce){
750                    if (objectValue instanceof java.io.Serializable)
751                        throw cce;
752                    else
753                        Utility.throwNotSerializableForCorba(objectValue.getClass().getName());
754                }
755            }
756        }
757    }
758
759    /* Write the fields of the specified class by invoking the appropriate
760     * write* method on this class.
761     */
762    private void outputClassFields(Object o, Class cl,
763                                   ObjectStreamField[] fields)
764        throws IOException, InvalidClassException {
765
766        for (int i = 0; i < fields.length; i++) {
767            if (fields[i].getField() == null)
768                // XXX I18N, Logging needed.
769                throw new InvalidClassException(cl.getName(),
770                                                "Nonexistent field " + fields[i].getName());
771
772            try {
773                switch (fields[i].getTypeCode()) {
774                    case 'B':
775                        byte byteValue = fields[i].getField().getByte( o ) ;
776                        orbStream.write_octet(byteValue);
777                        break;
778                    case 'C':
779                        char charValue = fields[i].getField().getChar( o ) ;
780                        orbStream.write_wchar(charValue);
781                        break;
782                    case 'F':
783                        float floatValue = fields[i].getField().getFloat( o ) ;
784                        orbStream.write_float(floatValue);
785                        break;
786                    case 'D' :
787                        double doubleValue = fields[i].getField().getDouble( o ) ;
788                        orbStream.write_double(doubleValue);
789                        break;
790                    case 'I':
791                        int intValue = fields[i].getField().getInt( o ) ;
792                        orbStream.write_long(intValue);
793                        break;
794                    case 'J':
795                        long longValue = fields[i].getField().getLong( o ) ;
796                        orbStream.write_longlong(longValue);
797                        break;
798                    case 'S':
799                        short shortValue = fields[i].getField().getShort( o ) ;
800                        orbStream.write_short(shortValue);
801                        break;
802                    case 'Z':
803                        boolean booleanValue = fields[i].getField().getBoolean( o ) ;
804                        orbStream.write_boolean(booleanValue);
805                        break;
806                    case '[':
807                    case 'L':
808                        Object objectValue = fields[i].getField().get( o ) ;
809                        writeObjectField(fields[i], objectValue);
810                        break;
811                    default:
812                        // XXX I18N, Logging needed.
813                        throw new InvalidClassException(cl.getName());
814                }
815            } catch (IllegalAccessException exc) {
816                throw wrapper.illegalFieldAccess( exc, fields[i].getName() ) ;
817            }
818        }
819    }
820}
821