OutputStreamHook.java revision 666:366c1c9fb6bb
1/*
2 * Copyright (c) 1999, 2015, 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 java.io.IOException;
35import java.io.NotActiveException;
36import java.io.OutputStream;
37import java.io.ObjectOutputStream;
38import java.io.ObjectOutput;
39import java.util.Map;
40import java.util.HashMap;
41
42import org.omg.CORBA.INTERNAL;
43
44public abstract class OutputStreamHook extends ObjectOutputStream
45{
46    private HookPutFields putFields = null;
47
48    /**
49     * Since ObjectOutputStream.PutField methods specify no exceptions,
50     * we are not checking for null parameters on put methods.
51     */
52    private class HookPutFields extends ObjectOutputStream.PutField
53    {
54        private Map<String,Object> fields = new HashMap<>();
55
56        /**
57         * Put the value of the named boolean field into the persistent field.
58         */
59        public void put(String name, boolean value){
60            fields.put(name, new Boolean(value));
61        }
62
63        /**
64         * Put the value of the named char field into the persistent fields.
65         */
66        public void put(String name, char value){
67            fields.put(name, new Character(value));
68        }
69
70        /**
71         * Put the value of the named byte field into the persistent fields.
72         */
73        public void put(String name, byte value){
74            fields.put(name, new Byte(value));
75        }
76
77        /**
78         * Put the value of the named short field into the persistent fields.
79         */
80        public void put(String name, short value){
81            fields.put(name, new Short(value));
82        }
83
84        /**
85         * Put the value of the named int field into the persistent fields.
86         */
87        public void put(String name, int value){
88            fields.put(name, new Integer(value));
89        }
90
91        /**
92         * Put the value of the named long field into the persistent fields.
93         */
94        public void put(String name, long value){
95            fields.put(name, new Long(value));
96        }
97
98        /**
99         * Put the value of the named float field into the persistent fields.
100         *
101         */
102        public void put(String name, float value){
103            fields.put(name, new Float(value));
104        }
105
106        /**
107         * Put the value of the named double field into the persistent field.
108         */
109        public void put(String name, double value){
110            fields.put(name, new Double(value));
111        }
112
113        /**
114         * Put the value of the named Object field into the persistent field.
115         */
116        public void put(String name, Object value){
117            fields.put(name, value);
118        }
119
120        /**
121         * Write the data and fields to the specified ObjectOutput stream.
122         */
123        public void write(ObjectOutput out) throws IOException {
124            OutputStreamHook hook = (OutputStreamHook)out;
125
126            ObjectStreamField[] osfields = hook.getFieldsNoCopy();
127
128            // Write the fields to the stream in the order
129            // provided by the ObjectStreamClass.  (They should
130            // be sorted appropriately already.)
131            for (int i = 0; i < osfields.length; i++) {
132
133                Object value = fields.get(osfields[i].getName());
134
135                hook.writeField(osfields[i], value);
136            }
137        }
138    }
139
140    abstract void writeField(ObjectStreamField field, Object value) throws IOException;
141
142    public OutputStreamHook()
143        throws java.io.IOException {
144        super();
145    }
146
147    public void defaultWriteObject() throws IOException {
148
149        writeObjectState.defaultWriteObject(this);
150
151        defaultWriteObjectDelegate();
152    }
153
154    public abstract void defaultWriteObjectDelegate();
155
156    public ObjectOutputStream.PutField putFields()
157        throws IOException {
158        if (putFields == null) {
159            putFields = new HookPutFields();
160        }
161        return putFields;
162    }
163
164    // Stream format version, saved/restored during recursive calls
165    protected byte streamFormatVersion = 1;
166
167    // Return the stream format version currently being used
168    // to serialize an object
169    public byte getStreamFormatVersion() {
170        return streamFormatVersion;
171    }
172
173    abstract ObjectStreamField[] getFieldsNoCopy();
174
175    // User uses PutFields to simulate default data.
176    // See java.io.ObjectOutputStream.PutFields
177    public void writeFields()
178        throws IOException {
179
180        writeObjectState.defaultWriteObject(this);
181        if (putFields != null) {
182            putFields.write(this);
183        } else {
184            throw new NotActiveException("no current PutField object");
185        }
186    }
187
188    abstract org.omg.CORBA_2_3.portable.OutputStream getOrbStream();
189
190    protected abstract void beginOptionalCustomData();
191
192
193    // The following is a State pattern implementation of what
194    // should be done when a Serializable has a
195    // writeObject method.  This was especially necessary for
196    // RMI-IIOP stream format version 2.  Please see the
197    // state diagrams in the docs directory of the workspace.
198
199    protected WriteObjectState writeObjectState = NOT_IN_WRITE_OBJECT;
200
201    protected void setState(WriteObjectState newState) {
202        writeObjectState = newState;
203    }
204
205    // Description of possible actions
206    protected static class WriteObjectState {
207        public void enterWriteObject(OutputStreamHook stream) throws IOException {}
208        public void exitWriteObject(OutputStreamHook stream) throws IOException {}
209        public void defaultWriteObject(OutputStreamHook stream) throws IOException {}
210        public void writeData(OutputStreamHook stream) throws IOException {}
211    }
212
213    protected static class DefaultState extends WriteObjectState {
214        public void enterWriteObject(OutputStreamHook stream) throws IOException {
215            stream.setState(IN_WRITE_OBJECT);
216        }
217    }
218
219    protected static final WriteObjectState NOT_IN_WRITE_OBJECT = new DefaultState();
220    protected static final WriteObjectState IN_WRITE_OBJECT = new InWriteObjectState();
221    protected static final WriteObjectState WROTE_DEFAULT_DATA = new WroteDefaultDataState();
222    protected static final WriteObjectState WROTE_CUSTOM_DATA = new WroteCustomDataState();
223
224    protected static class InWriteObjectState extends WriteObjectState {
225
226        public void enterWriteObject(OutputStreamHook stream) throws IOException {
227            // XXX I18N, logging needed.
228            throw new IOException("Internal state failure: Entered writeObject twice");
229        }
230
231        public void exitWriteObject(OutputStreamHook stream) throws IOException {
232
233            // We didn't write any data, so write the
234            // called defaultWriteObject indicator as false
235            stream.getOrbStream().write_boolean(false);
236
237            // If we're in stream format verison 2, we must
238            // put the "null" marker to say that there isn't
239            // any optional data
240            if (stream.getStreamFormatVersion() == 2)
241                stream.getOrbStream().write_long(0);
242
243            stream.setState(NOT_IN_WRITE_OBJECT);
244        }
245
246        public void defaultWriteObject(OutputStreamHook stream) throws IOException {
247
248            // The writeObject method called defaultWriteObject
249            // or writeFields, so put the called defaultWriteObject
250            // indicator as true
251            stream.getOrbStream().write_boolean(true);
252
253            stream.setState(WROTE_DEFAULT_DATA);
254        }
255
256        public void writeData(OutputStreamHook stream) throws IOException {
257
258            // The writeObject method first called a direct
259            // write operation.  Write the called defaultWriteObject
260            // indicator as false, put the special stream format
261            // version 2 header (if stream format version 2, of course),
262            // and write the data
263            stream.getOrbStream().write_boolean(false);
264            stream.beginOptionalCustomData();
265            stream.setState(WROTE_CUSTOM_DATA);
266        }
267    }
268
269    protected static class WroteDefaultDataState extends InWriteObjectState {
270
271        public void exitWriteObject(OutputStreamHook stream) throws IOException {
272
273            // We only wrote default data, so if in stream format
274            // version 2, put the null indicator to say that there
275            // is no optional data
276            if (stream.getStreamFormatVersion() == 2)
277                stream.getOrbStream().write_long(0);
278
279            stream.setState(NOT_IN_WRITE_OBJECT);
280        }
281
282        public void defaultWriteObject(OutputStreamHook stream) throws IOException {
283            // XXX I18N, logging needed.
284            throw new IOException("Called defaultWriteObject/writeFields twice");
285        }
286
287        public void writeData(OutputStreamHook stream) throws IOException {
288
289            // The writeObject method called a direct write operation.
290            // If in stream format version 2, put the fake valuetype
291            // header.
292            stream.beginOptionalCustomData();
293
294            stream.setState(WROTE_CUSTOM_DATA);
295        }
296    }
297
298    protected static class WroteCustomDataState extends InWriteObjectState {
299
300        public void exitWriteObject(OutputStreamHook stream) throws IOException {
301            // In stream format version 2, we must tell the ORB
302            // stream to close the fake custom valuetype.
303            if (stream.getStreamFormatVersion() == 2)
304                ((org.omg.CORBA.portable.ValueOutputStream)stream.getOrbStream()).end_value();
305
306            stream.setState(NOT_IN_WRITE_OBJECT);
307        }
308
309        public void defaultWriteObject(OutputStreamHook stream) throws IOException {
310            // XXX I18N, logging needed.
311            throw new IOException("Cannot call defaultWriteObject/writeFields after writing custom data in RMI-IIOP");
312        }
313
314        // We don't have to do anything special here, just let
315        // the stream write the data.
316        public void writeData(OutputStreamHook stream) throws IOException {}
317    }
318}
319