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