IDLJavaSerializationOutputStream.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 2004, 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 */
25package com.sun.corba.se.impl.encoding;
26
27import java.io.IOException;
28import java.io.ObjectOutputStream;
29import java.io.ByteArrayOutputStream;
30
31import java.nio.ByteBuffer;
32
33import com.sun.corba.se.spi.orb.ORB;
34import com.sun.corba.se.spi.ior.IOR;
35import com.sun.corba.se.spi.ior.IORFactories;
36import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
37import com.sun.corba.se.spi.logging.CORBALogDomains;
38import com.sun.corba.se.spi.presentation.rmi.StubAdapter;
39
40import com.sun.corba.se.impl.util.Utility;
41import com.sun.corba.se.impl.orbutil.ORBConstants;
42import com.sun.corba.se.impl.orbutil.ORBUtility;
43import com.sun.corba.se.impl.corba.TypeCodeImpl;
44import com.sun.corba.se.impl.logging.ORBUtilSystemException;
45import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;
46
47import org.omg.CORBA.Any;
48import org.omg.CORBA.TypeCode;
49import org.omg.CORBA.Principal;
50import org.omg.CORBA.CompletionStatus;
51
52/**
53 * Implementation class that uses Java serialization for output streams.
54 * This assumes a GIOP version 1.2 message format.
55 *
56 * This class uses a ByteArrayOutputStream as the underlying buffer. The
57 * first 16 bytes are direct writes into the underlying buffer. This allows
58 * [GIOPHeader (12 bytes) + requestID (4 bytes)] to be written as bytes.
59 * Subsequent write operations on this output stream object uses
60 * ObjectOutputStream class to write into the buffer. This allows marshaling
61 * complex types and graphs using the ObjectOutputStream implementation.
62 *
63 * Note, this class assumes a GIOP 1.2 style header. Note, we expect that the
64 * first 16 bytes are written only using the write_octet, write_long or
65 * write_ulong method calls.
66 *
67 * @author Ram Jeyaraman
68 */
69final class IDLJavaSerializationOutputStream extends CDROutputStreamBase {
70
71    private ORB orb;
72    private byte encodingVersion;
73    private ObjectOutputStream os;
74    private _ByteArrayOutputStream bos;
75    private BufferManagerWrite bufferManager;
76
77    // [GIOPHeader(12) + requestID(4)] bytes
78    private final int directWriteLength = Message.GIOPMessageHeaderLength + 4;
79
80    protected ORBUtilSystemException wrapper;
81
82    class _ByteArrayOutputStream extends ByteArrayOutputStream {
83
84        _ByteArrayOutputStream(int initialSize) {
85            super(initialSize);
86        }
87
88        byte[] getByteArray() {
89            return this.buf;
90        }
91    }
92
93    class MarshalObjectOutputStream extends ObjectOutputStream {
94
95        ORB orb;
96
97        MarshalObjectOutputStream(java.io.OutputStream out, ORB orb)
98                throws IOException {
99
100            super(out);
101            this.orb = orb;
102            java.security.AccessController.doPrivileged(
103                new java.security.PrivilegedAction() {
104                    public Object run() {
105                        // needs SerializablePermission("enableSubstitution")
106                        enableReplaceObject(true);
107                        return null;
108                    }
109                }
110            );
111        }
112
113        /**
114         * Checks for objects that are instances of java.rmi.Remote
115         * that need to be serialized as proxy (Stub) objects.
116         */
117        protected final Object replaceObject(Object obj) throws IOException {
118            try {
119                if ((obj instanceof java.rmi.Remote) &&
120                        !(StubAdapter.isStub(obj))) {
121                    return Utility.autoConnect(obj, orb, true);
122                }
123            } catch (Exception e) {
124                IOException ie = new IOException("replaceObject failed");
125                ie.initCause(e);
126                throw ie;
127            }
128            return obj;
129        }
130    }
131
132    public IDLJavaSerializationOutputStream(byte encodingVersion) {
133        super();
134        this.encodingVersion = encodingVersion;
135    }
136
137    public void init(org.omg.CORBA.ORB orb, boolean littleEndian,
138                     BufferManagerWrite bufferManager,
139                     byte streamFormatVersion,
140                     boolean usePooledByteBuffers) {
141        this.orb = (ORB) orb;
142        this.bufferManager = bufferManager;
143        wrapper = ORBUtilSystemException.get((ORB) orb,
144                                             CORBALogDomains.RPC_ENCODING);
145        bos =
146            new _ByteArrayOutputStream(ORBConstants.GIOP_DEFAULT_BUFFER_SIZE);
147    }
148
149    // Called from read_octet or read_long or read_ulong method.
150    private void initObjectOutputStream() {
151        //System.out.print(" os ");
152        if (os != null) {
153            throw wrapper.javaStreamInitFailed();
154        }
155        try {
156            os = new MarshalObjectOutputStream(bos, orb);
157        } catch (Exception e) {
158            throw wrapper.javaStreamInitFailed(e);
159        }
160    }
161
162    // org.omg.CORBA.portable.OutputStream
163
164    // Primitive types.
165
166    public final void write_boolean(boolean value) {
167        try {
168            os.writeBoolean(value);
169        } catch (Exception e) {
170            throw wrapper.javaSerializationException(e, "write_boolean");
171        }
172    }
173
174    public final void write_char(char value) {
175        try {
176            os.writeChar(value);
177        } catch (Exception e) {
178            throw wrapper.javaSerializationException(e, "write_char");
179        }
180    }
181
182    public final void write_wchar(char value) {
183        this.write_char(value);
184    }
185
186    public final void write_octet(byte value) {
187
188        // check if size < [ GIOPHeader(12) + requestID(4)] bytes
189        if (bos.size() < directWriteLength) {
190            bos.write(value); // direct write.
191            if (bos.size() == directWriteLength) {
192                initObjectOutputStream();
193            }
194            return;
195        }
196
197        try {
198            os.writeByte(value);
199        } catch (Exception e) {
200            throw wrapper.javaSerializationException(e, "write_octet");
201        }
202    }
203
204    public final void write_short(short value) {
205        try {
206            os.writeShort(value);
207        } catch (Exception e) {
208            throw wrapper.javaSerializationException(e, "write_short");
209        }
210    }
211
212    public final void write_ushort(short value) {
213        this.write_short(value);
214    }
215
216    public final void write_long(int value) {
217
218        // check if size < [ GIOPHeader(12) + requestID(4)] bytes
219        if (bos.size() < directWriteLength) {
220
221            // Use big endian (network byte order). This is fixed.
222            // Both the writer and reader use the same byte order.
223            bos.write((byte)((value >>> 24) & 0xFF));
224            bos.write((byte)((value >>> 16) & 0xFF));
225            bos.write((byte)((value >>> 8) & 0xFF));
226            bos.write((byte)((value >>> 0) & 0xFF));
227
228            if (bos.size() == directWriteLength) {
229                initObjectOutputStream();
230            } else if (bos.size() > directWriteLength) {
231                // Cannot happen. All direct writes are contained
232                // within the first 16 bytes.
233                wrapper.javaSerializationException("write_long");
234            }
235            return;
236        }
237
238        try {
239            os.writeInt(value);
240        } catch (Exception e) {
241            throw wrapper.javaSerializationException(e, "write_long");
242        }
243    }
244
245    public final void write_ulong(int value) {
246        this.write_long(value);
247    }
248
249    public final void write_longlong(long value) {
250        try {
251            os.writeLong(value);
252        } catch (Exception e) {
253            throw wrapper.javaSerializationException(e, "write_longlong");
254        }
255    }
256
257    public final void write_ulonglong(long value) {
258        this.write_longlong(value);
259    }
260
261    public final void write_float(float value) {
262        try {
263            os.writeFloat(value);
264        } catch (Exception e) {
265            throw wrapper.javaSerializationException(e, "write_float");
266        }
267    }
268
269    public final void write_double(double value) {
270        try {
271            os.writeDouble(value);
272        } catch (Exception e) {
273            throw wrapper.javaSerializationException(e, "write_double");
274        }
275    }
276
277    // String types.
278
279    public final void write_string(String value) {
280        try {
281            os.writeUTF(value);
282        } catch (Exception e) {
283            throw wrapper.javaSerializationException(e, "write_string");
284        }
285    }
286
287    public final void write_wstring(String value) {
288        try {
289            os.writeObject(value);
290        } catch (Exception e) {
291            throw wrapper.javaSerializationException(e, "write_wstring");
292        }
293    }
294
295    // Array types.
296
297    public final void write_boolean_array(boolean[] value,
298                                          int offset, int length) {
299        for (int i = 0; i < length; i++) {
300            write_boolean(value[offset + i]);
301        }
302    }
303
304    public final void write_char_array(char[] value, int offset, int length) {
305        for (int i = 0; i < length; i++) {
306            write_char(value[offset + i]);
307        }
308    }
309
310    public final void write_wchar_array(char[] value, int offset, int length) {
311        write_char_array(value, offset, length);
312    }
313
314    public final void write_octet_array(byte[] value, int offset, int length) {
315        try {
316            os.write(value, offset, length);
317        } catch (Exception e) {
318            throw wrapper.javaSerializationException(e, "write_octet_array");
319        }
320    }
321
322    public final void write_short_array(short[] value,
323                                        int offset, int length) {
324        for (int i = 0; i < length; i++) {
325            write_short(value[offset + i]);
326        }
327    }
328
329    public final void write_ushort_array(short[] value,
330                                         int offset, int length){
331        write_short_array(value, offset, length);
332    }
333
334    public final void write_long_array(int[] value, int offset, int length) {
335        for (int i = 0; i < length; i++) {
336            write_long(value[offset + i]);
337        }
338    }
339
340    public final void write_ulong_array(int[] value, int offset, int length) {
341        write_long_array(value, offset, length);
342    }
343
344    public final void write_longlong_array(long[] value,
345                                           int offset, int length) {
346        for (int i = 0; i < length; i++) {
347            write_longlong(value[offset + i]);
348        }
349    }
350
351    public final void write_ulonglong_array(long[] value,
352                                            int offset,int length) {
353        write_longlong_array(value, offset, length);
354    }
355
356    public final void write_float_array(float[] value,
357                                        int offset, int length) {
358        for (int i = 0; i < length; i++) {
359            write_float(value[offset + i]);
360        }
361    }
362
363    public final void write_double_array(double[] value,
364                                         int offset, int length) {
365        for (int i = 0; i < length; i++) {
366            write_double(value[offset + i]);
367        }
368    }
369
370    // Complex types (objects and graphs).
371
372    public final void write_Object(org.omg.CORBA.Object value) {
373        if (value == null) {
374            IOR nullIOR = IORFactories.makeIOR(orb);
375            nullIOR.write(parent);
376            return;
377        }
378        // IDL to Java formal 01-06-06 1.21.4.2
379        if (value instanceof org.omg.CORBA.LocalObject) {
380            throw wrapper.writeLocalObject(CompletionStatus.COMPLETED_MAYBE);
381        }
382        IOR ior = ORBUtility.connectAndGetIOR(orb, value);
383        ior.write(parent);
384        return;
385    }
386
387    public final void write_TypeCode(TypeCode tc) {
388        if (tc == null) {
389            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
390        }
391        TypeCodeImpl tci;
392        if (tc instanceof TypeCodeImpl) {
393            tci = (TypeCodeImpl) tc;
394        } else {
395            tci = new TypeCodeImpl(orb, tc);
396        }
397        tci.write_value((org.omg.CORBA_2_3.portable.OutputStream) parent);
398    }
399
400    public final void write_any(Any any) {
401        if (any == null) {
402            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
403        }
404        write_TypeCode(any.type());
405        any.write_value(parent);
406    }
407
408    public final void write_Principal(Principal p) {
409        // We don't need an implementation for this method, since principal
410        // is absent in GIOP version 1.2 or above.
411        write_long(p.name().length);
412        write_octet_array(p.name(), 0, p.name().length);
413    }
414
415    public final void write_fixed(java.math.BigDecimal bigDecimal) {
416        // This string might contain sign and/or dot
417        this.write_fixed(bigDecimal.toString(), bigDecimal.signum());
418    }
419
420    // The string may contain a sign and dot
421    private void write_fixed(String string, int signum) {
422
423        int stringLength = string.length();
424
425        // Each octet contains (up to) two decimal digits.
426        byte doubleDigit = 0;
427        char ch;
428        byte digit;
429
430        // First calculate the string length without optional sign and dot.
431        int numDigits = 0;
432        for (int i=0; i<stringLength; i++) {
433            ch = string.charAt(i);
434            if (ch == '-' || ch == '+' || ch == '.')
435                continue;
436            numDigits++;
437        }
438
439        for (int i=0; i<stringLength; i++) {
440            ch = string.charAt(i);
441            if (ch == '-' || ch == '+' || ch == '.')
442                continue;
443            digit = (byte)Character.digit(ch, 10);
444            if (digit == -1) {
445                throw wrapper.badDigitInFixed(
446                                            CompletionStatus.COMPLETED_MAYBE);
447            }
448            // If the fixed type has an odd number of decimal digits, then the
449            // representation begins with the first (most significant) digit.
450            // Otherwise, this first half-octet is all zero, and the first
451            // digit is in the second half-octet.
452            if (numDigits % 2 == 0) {
453                doubleDigit |= digit;
454                this.write_octet(doubleDigit);
455                doubleDigit = 0;
456            } else {
457                doubleDigit |= (digit << 4);
458            }
459            numDigits--;
460        }
461
462        // The sign configuration in the last half-octet of the representation,
463        // is 0xD for negative numbers and 0xC for positive and zero values.
464        if (signum == -1) {
465            doubleDigit |= 0xd;
466        } else {
467            doubleDigit |= 0xc;
468        }
469        this.write_octet(doubleDigit);
470    }
471
472    public final org.omg.CORBA.ORB orb() {
473        return this.orb;
474    }
475
476    // org.omg.CORBA_2_3.portable.OutputStream
477
478    public final void write_value(java.io.Serializable value) {
479        write_value(value, (String) null);
480    }
481
482    public final void write_value(java.io.Serializable value,
483                                  java.lang.Class clz) {
484        write_value(value);
485    }
486
487    public final void write_value(java.io.Serializable value,
488                                  String repository_id) {
489        try {
490            os.writeObject(value);
491        } catch (Exception e) {
492            throw wrapper.javaSerializationException(e, "write_value");
493        }
494    }
495
496    public final void write_value(java.io.Serializable value,
497                             org.omg.CORBA.portable.BoxedValueHelper factory) {
498        this.write_value(value, (String) null);
499    }
500
501    public final void write_abstract_interface(java.lang.Object obj) {
502
503        boolean isCorbaObject = false; // Assume value type.
504        org.omg.CORBA.Object theCorbaObject = null;
505
506        // Is it a CORBA.Object?
507        if (obj != null && obj instanceof org.omg.CORBA.Object) {
508            theCorbaObject = (org.omg.CORBA.Object)obj;
509            isCorbaObject = true;
510        }
511
512        // Write the boolean flag.
513        this.write_boolean(isCorbaObject);
514
515        // Now write out the object.
516        if (isCorbaObject) {
517            write_Object(theCorbaObject);
518        } else {
519            try {
520                write_value((java.io.Serializable)obj);
521            } catch(ClassCastException cce) {
522                if (obj instanceof java.io.Serializable) {
523                    throw cce;
524                } else {
525                    ORBUtility.throwNotSerializableForCorba(
526                                                    obj.getClass().getName());
527                }
528            }
529        }
530    }
531
532    // com.sun.corba.se.os.encoding.MarshalOutputStream
533
534    public final void start_block() {
535        throw wrapper.giopVersionError();
536    }
537
538    public final void end_block() {
539        throw wrapper.giopVersionError();
540    }
541
542    public final void putEndian() {
543        throw wrapper.giopVersionError();
544    }
545
546    public void writeTo(java.io.OutputStream s) throws IOException {
547        try {
548            os.flush();
549            bos.writeTo(s);
550        } catch (Exception e) {
551            throw wrapper.javaSerializationException(e, "writeTo");
552        }
553    }
554
555    public final byte[] toByteArray() {
556        try {
557            os.flush();
558            return bos.toByteArray(); // new copy.
559        } catch (Exception e) {
560            throw wrapper.javaSerializationException(e, "toByteArray");
561        }
562    }
563
564    // org.omg.CORBA.DataOutputStream
565
566    public final void write_Abstract (java.lang.Object value) {
567        write_abstract_interface(value);
568    }
569
570    public final void write_Value(java.io.Serializable value) {
571        write_value(value);
572    }
573
574    public final void write_any_array(org.omg.CORBA.Any[] value,
575                                      int offset, int length) {
576        for(int i = 0; i < length; i++) {
577            write_any(value[offset + i]);
578        }
579    }
580
581    // org.omg.CORBA.portable.ValueBase
582
583    public final String[] _truncatable_ids() {
584        throw wrapper.giopVersionError();
585    }
586
587    // Other.
588
589    public final int getSize() {
590        try {
591            os.flush();
592            return bos.size();
593        } catch (Exception e) {
594            throw wrapper.javaSerializationException(e, "write_boolean");
595        }
596    }
597
598    public final int getIndex() {
599        return getSize();
600    }
601
602    protected int getRealIndex(int index) {
603        return getSize();
604    }
605
606    public final void setIndex(int value) {
607        throw wrapper.giopVersionError();
608    }
609
610    public final ByteBuffer getByteBuffer() {
611        throw wrapper.giopVersionError();
612    }
613
614    public final void setByteBuffer(ByteBuffer byteBuffer) {
615        throw wrapper.giopVersionError();
616    }
617
618    public final boolean isLittleEndian() {
619        // Java serialization uses network byte order, that is, big-endian.
620        return false;
621    }
622
623    public ByteBufferWithInfo getByteBufferWithInfo() {
624        try {
625            os.flush();
626        } catch (Exception e) {
627            throw wrapper.javaSerializationException(
628                                            e, "getByteBufferWithInfo");
629        }
630        ByteBuffer byteBuffer = ByteBuffer.wrap(bos.getByteArray());
631        byteBuffer.limit(bos.size());
632        return new ByteBufferWithInfo(this.orb, byteBuffer, bos.size());
633    }
634
635    public void setByteBufferWithInfo(ByteBufferWithInfo bbwi) {
636        throw wrapper.giopVersionError();
637    }
638
639    public final BufferManagerWrite getBufferManager() {
640        return bufferManager;
641    }
642
643    // This will stay a custom add-on until the java-rtf issue is resolved.
644    // Then it should be declared in org.omg.CORBA.portable.OutputStream.
645    //
646    // Pads the string representation of bigDecimal with zeros to fit the given
647    // digits and scale before it gets written to the stream.
648    public final void write_fixed(java.math.BigDecimal bigDecimal,
649                                  short digits, short scale) {
650        String string = bigDecimal.toString();
651        String integerPart;
652        String fractionPart;
653        StringBuffer stringBuffer;
654
655        // Get rid of the sign
656        if (string.charAt(0) == '-' || string.charAt(0) == '+') {
657            string = string.substring(1);
658        }
659
660        // Determine integer and fraction parts
661        int dotIndex = string.indexOf('.');
662        if (dotIndex == -1) {
663            integerPart = string;
664            fractionPart = null;
665        } else if (dotIndex == 0 ) {
666            integerPart = null;
667            fractionPart = string;
668        } else {
669            integerPart = string.substring(0, dotIndex);
670            fractionPart = string.substring(dotIndex + 1);
671        }
672
673        // Pad both parts with zeros as necessary
674        stringBuffer = new StringBuffer(digits);
675        if (fractionPart != null) {
676            stringBuffer.append(fractionPart);
677        }
678        while (stringBuffer.length() < scale) {
679            stringBuffer.append('0');
680        }
681        if (integerPart != null) {
682            stringBuffer.insert(0, integerPart);
683        }
684        while (stringBuffer.length() < digits) {
685            stringBuffer.insert(0, '0');
686        }
687
688        // This string contains no sign or dot
689        this.write_fixed(stringBuffer.toString(), bigDecimal.signum());
690    }
691
692    public final void writeOctetSequenceTo(
693            org.omg.CORBA.portable.OutputStream s) {
694        byte[] buf = this.toByteArray(); // new copy.
695        s.write_long(buf.length);
696        s.write_octet_array(buf, 0, buf.length);
697    }
698
699    public final GIOPVersion getGIOPVersion() {
700        return GIOPVersion.V1_2;
701    }
702
703    public final void writeIndirection(int tag, int posIndirectedTo) {
704        throw wrapper.giopVersionError();
705    }
706
707    void freeInternalCaches() {}
708
709    void printBuffer() {
710        byte[] buf = this.toByteArray();
711
712        System.out.println("+++++++ Output Buffer ++++++++");
713        System.out.println();
714        System.out.println("Current position: " + buf.length);
715        //System.out.println("Total length : " + buf.length);
716        System.out.println();
717
718        char[] charBuf = new char[16];
719
720        try {
721
722            for (int i = 0; i < buf.length; i += 16) {
723
724                int j = 0;
725
726                // For every 16 bytes, there is one line
727                // of output.  First, the hex output of
728                // the 16 bytes with each byte separated
729                // by a space.
730                while (j < 16 && j + i < buf.length) {
731                    int k = buf[i + j];
732                    if (k < 0)
733                        k = 256 + k;
734                    String hex = Integer.toHexString(k);
735                    if (hex.length() == 1)
736                        hex = "0" + hex;
737                    System.out.print(hex + " ");
738                    j++;
739                }
740
741                // Add any extra spaces to align the
742                // text column in case we didn't end
743                // at 16
744                while (j < 16) {
745                    System.out.print("   ");
746                    j++;
747                }
748
749                // Now output the ASCII equivalents.  Non-ASCII
750                // characters are shown as periods.
751                int x = 0;
752
753                while (x < 16 && x + i < buf.length) {
754                    if (ORBUtility.isPrintable((char)buf[i + x])) {
755                        charBuf[x] = (char) buf[i + x];
756                    } else {
757                        charBuf[x] = '.';
758                    }
759                    x++;
760                }
761                System.out.println(new String(charBuf, 0, x));
762            }
763        } catch (Throwable t) {
764            t.printStackTrace();
765        }
766        System.out.println("++++++++++++++++++++++++++++++");
767    }
768
769    public void alignOnBoundary(int octetBoundary) {
770        throw wrapper.giopVersionError();
771    }
772
773    // Needed by request and reply messages for GIOP versions >= 1.2 only.
774    public void setHeaderPadding(boolean headerPadding) {
775        // no-op. We don't care about body alignment while using
776        // Java serialization. What the GIOP spec states does not apply here.
777    }
778
779    // ValueOutputStream -----------------------------
780
781    public void start_value(String rep_id) {
782        throw wrapper.giopVersionError();
783    }
784
785    public void end_value() {
786        throw wrapper.giopVersionError();
787    }
788
789    // java.io.OutputStream
790
791    // Note: These methods are defined in the super class and accessible.
792
793    //public abstract void write(byte b[]) throws IOException;
794    //public abstract void write(byte b[], int off, int len)
795    //    throws IOException;
796    //public abstract void flush() throws IOException;
797    //public abstract void close() throws IOException;
798}
799