IIOPOutputStream.java revision 608:7e06bf1dcb09
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            int stackMark = classDescStack.size();
563            try {
564                ObjectStreamClass next;
565                while ((next = currentClassDesc.getSuperclass()) != null) {
566                    classDescStack.push(currentClassDesc);
567                    currentClassDesc = next;
568                }
569
570                /*
571                 * For currentClassDesc and all the pushed class descriptors
572                 *    If the class is writing its own data
573                 *                set blockData = true; call the class writeObject method
574                 *    If not
575                 *     invoke either the defaultWriteObject method.
576                 */
577                do {
578
579                    WriteObjectState oldState = writeObjectState;
580
581                    try {
582
583                        setState(NOT_IN_WRITE_OBJECT);
584
585                        if (currentClassDesc.hasWriteObject()) {
586                            invokeObjectWriter(currentClassDesc, obj );
587                        } else {
588                            defaultWriteObjectDelegate();
589                        }
590                    } finally {
591                        setState(oldState);
592                    }
593
594                } while (classDescStack.size() > stackMark &&
595                         (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
596            } finally {
597                classDescStack.setSize(stackMark);
598            }
599        }
600    }
601
602    /*
603     * Invoke writer.
604     * _REVISIT_ invokeObjectWriter and invokeObjectReader behave inconsistently with each other since
605     * the reader returns a boolean...fix later
606     */
607    private void invokeObjectWriter(ObjectStreamClass osc, Object obj)
608        throws IOException
609    {
610        Class c = osc.forClass() ;
611
612        try {
613
614            // Write format version
615            orbStream.write_octet(streamFormatVersion);
616
617            writeObjectState.enterWriteObject(this);
618
619            // writeObject(obj, c, this);
620            osc.writeObjectMethod.invoke( obj, writeObjectArgList ) ;
621
622            writeObjectState.exitWriteObject(this);
623
624        } catch (InvocationTargetException e) {
625            Throwable t = e.getTargetException();
626            if (t instanceof IOException)
627                throw (IOException)t;
628            else if (t instanceof RuntimeException)
629                throw (RuntimeException) t;
630            else if (t instanceof Error)
631                throw (Error) t;
632            else
633                // XXX I18N, Logging needed.
634                throw new Error("invokeObjectWriter internal error",e);
635        } catch (IllegalAccessException e) {
636            // cannot happen
637        }
638    }
639
640    void writeField(ObjectStreamField field, Object value) throws IOException {
641        switch (field.getTypeCode()) {
642            case 'B':
643                if (value == null)
644                    orbStream.write_octet((byte)0);
645                else
646                    orbStream.write_octet(((Byte)value).byteValue());
647                break;
648            case 'C':
649                if (value == null)
650                    orbStream.write_wchar((char)0);
651                else
652                    orbStream.write_wchar(((Character)value).charValue());
653                break;
654            case 'F':
655                if (value == null)
656                    orbStream.write_float((float)0);
657                else
658                    orbStream.write_float(((Float)value).floatValue());
659                break;
660            case 'D':
661                if (value == null)
662                    orbStream.write_double((double)0);
663                else
664                    orbStream.write_double(((Double)value).doubleValue());
665                break;
666            case 'I':
667                if (value == null)
668                    orbStream.write_long((int)0);
669                else
670                    orbStream.write_long(((Integer)value).intValue());
671                break;
672            case 'J':
673                if (value == null)
674                    orbStream.write_longlong((long)0);
675                else
676                    orbStream.write_longlong(((Long)value).longValue());
677                break;
678            case 'S':
679                if (value == null)
680                    orbStream.write_short((short)0);
681                else
682                    orbStream.write_short(((Short)value).shortValue());
683                break;
684            case 'Z':
685                if (value == null)
686                    orbStream.write_boolean(false);
687                else
688                    orbStream.write_boolean(((Boolean)value).booleanValue());
689                break;
690            case '[':
691            case 'L':
692                // What to do if it's null?
693                writeObjectField(field, value);
694                break;
695            default:
696                // XXX I18N, Logging needed.
697                throw new InvalidClassException(currentClassDesc.getName());
698            }
699    }
700
701    private void writeObjectField(ObjectStreamField field,
702                                  Object objectValue) throws IOException {
703
704        if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) {
705            javax.rmi.CORBA.Util.writeAny(orbStream, objectValue);
706        }
707        else {
708            Class type = field.getType();
709            int callType = ValueHandlerImpl.kValueType;
710
711            if (type.isInterface()) {
712                String className = type.getName();
713
714                if (java.rmi.Remote.class.isAssignableFrom(type)) {
715
716                    // RMI Object reference...
717
718                    callType = ValueHandlerImpl.kRemoteType;
719
720
721                } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)){
722
723                    // IDL Object reference...
724                    callType = ValueHandlerImpl.kRemoteType;
725
726                } else if (RepositoryId.isAbstractBase(type)) {
727                    // IDL Abstract Object reference...
728                    callType = ValueHandlerImpl.kAbstractType;
729                } else if (ObjectStreamClassCorbaExt.isAbstractInterface(type)) {
730                    callType = ValueHandlerImpl.kAbstractType;
731                }
732            }
733
734            switch (callType) {
735            case ValueHandlerImpl.kRemoteType:
736                Util.writeRemoteObject(orbStream, objectValue);
737                break;
738            case ValueHandlerImpl.kAbstractType:
739                Util.writeAbstractObject(orbStream, objectValue);
740                break;
741            case ValueHandlerImpl.kValueType:
742                try{
743                    orbStream.write_value((java.io.Serializable)objectValue, type);
744                }
745                catch(ClassCastException cce){
746                    if (objectValue instanceof java.io.Serializable)
747                        throw cce;
748                    else
749                        Utility.throwNotSerializableForCorba(objectValue.getClass().getName());
750                }
751            }
752        }
753    }
754
755    /* Write the fields of the specified class by invoking the appropriate
756     * write* method on this class.
757     */
758    private void outputClassFields(Object o, Class cl,
759                                   ObjectStreamField[] fields)
760        throws IOException, InvalidClassException {
761
762        for (int i = 0; i < fields.length; i++) {
763            if (fields[i].getField() == null)
764                // XXX I18N, Logging needed.
765                throw new InvalidClassException(cl.getName(),
766                                                "Nonexistent field " + fields[i].getName());
767
768            try {
769                switch (fields[i].getTypeCode()) {
770                    case 'B':
771                        byte byteValue = fields[i].getField().getByte( o ) ;
772                        orbStream.write_octet(byteValue);
773                        break;
774                    case 'C':
775                        char charValue = fields[i].getField().getChar( o ) ;
776                        orbStream.write_wchar(charValue);
777                        break;
778                    case 'F':
779                        float floatValue = fields[i].getField().getFloat( o ) ;
780                        orbStream.write_float(floatValue);
781                        break;
782                    case 'D' :
783                        double doubleValue = fields[i].getField().getDouble( o ) ;
784                        orbStream.write_double(doubleValue);
785                        break;
786                    case 'I':
787                        int intValue = fields[i].getField().getInt( o ) ;
788                        orbStream.write_long(intValue);
789                        break;
790                    case 'J':
791                        long longValue = fields[i].getField().getLong( o ) ;
792                        orbStream.write_longlong(longValue);
793                        break;
794                    case 'S':
795                        short shortValue = fields[i].getField().getShort( o ) ;
796                        orbStream.write_short(shortValue);
797                        break;
798                    case 'Z':
799                        boolean booleanValue = fields[i].getField().getBoolean( o ) ;
800                        orbStream.write_boolean(booleanValue);
801                        break;
802                    case '[':
803                    case 'L':
804                        Object objectValue = fields[i].getField().get( o ) ;
805                        writeObjectField(fields[i], objectValue);
806                        break;
807                    default:
808                        // XXX I18N, Logging needed.
809                        throw new InvalidClassException(cl.getName());
810                }
811            } catch (IllegalAccessException exc) {
812                throw wrapper.illegalFieldAccess( exc, fields[i].getName() ) ;
813            }
814        }
815    }
816}
817