InputStreamHook.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1999, 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 java.io.IOException;
35import java.io.StreamCorruptedException;
36import java.io.NotActiveException;
37import java.io.InputStream;
38import java.io.ObjectInputStream;
39import java.util.*;
40import java.lang.reflect.Array;
41import java.lang.reflect.Constructor;
42
43import org.omg.CORBA.portable.ValueInputStream;
44
45import com.sun.corba.se.spi.orb.ORB;
46import com.sun.corba.se.spi.orb.ORBVersion;
47import com.sun.corba.se.spi.orb.ORBVersionFactory;
48import com.sun.corba.se.spi.logging.CORBALogDomains;
49import com.sun.corba.se.impl.logging.UtilSystemException;
50import com.sun.corba.se.impl.logging.OMGSystemException;
51
52public abstract class InputStreamHook extends ObjectInputStream
53{
54    // These should be visible in all the nested classes
55    static final OMGSystemException omgWrapper =
56        OMGSystemException.get( CORBALogDomains.RPC_ENCODING ) ;
57
58    static final UtilSystemException utilWrapper =
59        UtilSystemException.get( CORBALogDomains.RPC_ENCODING ) ;
60
61    private class HookGetFields extends ObjectInputStream.GetField {
62        private Map fields = null;
63
64        HookGetFields(Map fields){
65            this.fields = fields;
66        }
67
68        /**
69         * Get the ObjectStreamClass that describes the fields in the stream.
70         *
71         * REVISIT!  This doesn't work since we have our own ObjectStreamClass.
72         */
73        public java.io.ObjectStreamClass getObjectStreamClass() {
74            return null;
75        }
76
77        /**
78         * Return true if the named field is defaulted and has no value
79         * in this stream.
80         */
81        public boolean defaulted(String name)
82            throws IOException, IllegalArgumentException  {
83            return (!fields.containsKey(name));
84        }
85
86        /**
87         * Get the value of the named boolean field from the persistent field.
88         */
89        public boolean get(String name, boolean defvalue)
90            throws IOException, IllegalArgumentException {
91            if (defaulted(name))
92                return defvalue;
93            else return ((Boolean)fields.get(name)).booleanValue();
94        }
95
96        /**
97         * Get the value of the named char field from the persistent fields.
98         */
99        public char get(String name, char defvalue)
100            throws IOException, IllegalArgumentException {
101            if (defaulted(name))
102                return defvalue;
103            else return ((Character)fields.get(name)).charValue();
104
105        }
106
107        /**
108         * Get the value of the named byte field from the persistent fields.
109         */
110        public byte get(String name, byte defvalue)
111            throws IOException, IllegalArgumentException {
112            if (defaulted(name))
113                return defvalue;
114            else return ((Byte)fields.get(name)).byteValue();
115
116        }
117
118        /**
119         * Get the value of the named short field from the persistent fields.
120         */
121        public short get(String name, short defvalue)
122            throws IOException, IllegalArgumentException {
123            if (defaulted(name))
124                return defvalue;
125            else return ((Short)fields.get(name)).shortValue();
126
127        }
128
129        /**
130         * Get the value of the named int field from the persistent fields.
131         */
132        public int get(String name, int defvalue)
133            throws IOException, IllegalArgumentException {
134            if (defaulted(name))
135                return defvalue;
136            else return ((Integer)fields.get(name)).intValue();
137
138        }
139
140        /**
141         * Get the value of the named long field from the persistent fields.
142         */
143        public long get(String name, long defvalue)
144            throws IOException, IllegalArgumentException {
145            if (defaulted(name))
146                return defvalue;
147            else return ((Long)fields.get(name)).longValue();
148
149        }
150
151        /**
152         * Get the value of the named float field from the persistent fields.
153         */
154        public float get(String name, float defvalue)
155            throws IOException, IllegalArgumentException {
156            if (defaulted(name))
157                return defvalue;
158            else return ((Float)fields.get(name)).floatValue();
159
160        }
161
162        /**
163         * Get the value of the named double field from the persistent field.
164         */
165        public double get(String name, double defvalue)
166            throws IOException, IllegalArgumentException  {
167            if (defaulted(name))
168                return defvalue;
169            else return ((Double)fields.get(name)).doubleValue();
170
171        }
172
173        /**
174         * Get the value of the named Object field from the persistent field.
175         */
176        public Object get(String name, Object defvalue)
177            throws IOException, IllegalArgumentException {
178            if (defaulted(name))
179                return defvalue;
180            else return fields.get(name);
181
182        }
183
184        public String toString(){
185            return fields.toString();
186        }
187    }
188
189    public InputStreamHook()
190        throws IOException {
191        super();
192    }
193
194    public void defaultReadObject()
195        throws IOException, ClassNotFoundException, NotActiveException
196    {
197        readObjectState.beginDefaultReadObject(this);
198
199        defaultReadObjectDelegate();
200
201        readObjectState.endDefaultReadObject(this);
202    }
203
204    abstract void defaultReadObjectDelegate();
205
206    abstract void readFields(java.util.Map fieldToValueMap)
207        throws java.io.InvalidClassException, java.io.StreamCorruptedException,
208               ClassNotFoundException, java.io.IOException;
209
210
211    // See java.io.ObjectInputStream.GetField
212    // Remember that this is equivalent to defaultReadObject
213    // in RMI-IIOP
214    public ObjectInputStream.GetField readFields()
215        throws IOException, ClassNotFoundException, NotActiveException {
216
217        HashMap fieldValueMap = new HashMap();
218
219        // We were treating readFields same as defaultReadObject. It is
220        // incorrect if the state is readOptionalData. If this line
221        // is uncommented, it will throw a stream corrupted exception.
222        // _REVISIT_: The ideal fix would be to add a new state. In
223        // writeObject user may do one of the following
224        // 1. Call defaultWriteObject()
225        // 2. Put out optional fields
226        // 3. Call writeFields
227        // We have the state defined for (1) and (2) but not for (3), so
228        // we should ideally introduce a new state for 3 and have the
229        // beginDefaultReadObject do nothing.
230        //readObjectState.beginDefaultReadObject(this);
231
232        readFields(fieldValueMap);
233
234        readObjectState.endDefaultReadObject(this);
235
236        return new HookGetFields(fieldValueMap);
237    }
238
239    // The following is a State pattern implementation of what
240    // should be done when the sender's Serializable has a
241    // writeObject method.  This was especially necessary for
242    // RMI-IIOP stream format version 2.  Please see the
243    // state diagrams in the docs directory of the workspace.
244    //
245    // On the reader's side, the main factors are whether or not
246    // we have a readObject method and whether or not the
247    // sender wrote default data
248
249    protected void setState(ReadObjectState newState) {
250        readObjectState = newState;
251    }
252
253    protected abstract byte getStreamFormatVersion();
254    abstract org.omg.CORBA_2_3.portable.InputStream getOrbStream();
255
256    // Description of possible actions
257    protected static class ReadObjectState {
258        public void beginUnmarshalCustomValue(InputStreamHook stream,
259                                              boolean calledDefaultWriteObject,
260                                              boolean hasReadObject) throws IOException {}
261
262        public void endUnmarshalCustomValue(InputStreamHook stream) throws IOException {}
263        public void beginDefaultReadObject(InputStreamHook stream) throws IOException {}
264        public void endDefaultReadObject(InputStreamHook stream) throws IOException {}
265        public void readData(InputStreamHook stream) throws IOException {}
266    }
267
268    protected ReadObjectState readObjectState = DEFAULT_STATE;
269
270    protected static final ReadObjectState DEFAULT_STATE = new DefaultState();
271    protected static final ReadObjectState IN_READ_OBJECT_OPT_DATA
272        = new InReadObjectOptionalDataState();
273    protected static final ReadObjectState IN_READ_OBJECT_NO_MORE_OPT_DATA
274        = new InReadObjectNoMoreOptionalDataState();
275    protected static final ReadObjectState IN_READ_OBJECT_DEFAULTS_SENT
276        = new InReadObjectDefaultsSentState();
277    protected static final ReadObjectState NO_READ_OBJECT_DEFAULTS_SENT
278        = new NoReadObjectDefaultsSentState();
279
280    protected static final ReadObjectState IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED
281        = new InReadObjectRemoteDidNotUseWriteObjectState();
282    protected static final ReadObjectState IN_READ_OBJECT_PAST_DEFAULTS_REMOTE_NOT_CUSTOM
283        = new InReadObjectPastDefaultsRemoteDidNotUseWOState();
284
285    protected static class DefaultState extends ReadObjectState {
286
287        public void beginUnmarshalCustomValue(InputStreamHook stream,
288                                              boolean calledDefaultWriteObject,
289                                              boolean hasReadObject)
290            throws IOException {
291
292            if (hasReadObject) {
293                if (calledDefaultWriteObject)
294                    stream.setState(IN_READ_OBJECT_DEFAULTS_SENT);
295                else {
296                    try {
297                        if (stream.getStreamFormatVersion() == 2)
298                            ((ValueInputStream)stream.getOrbStream()).start_value();
299                    } catch( Exception e ) {
300                        // This will happen for Big Integer which uses
301                        // writeFields in it's writeObject. We should be past
302                        // start_value by now.
303                        // NOTE: If we don't log any exception here we should
304                        // be fine. If there is an error, it will be caught
305                        // while reading the optional data.
306
307                    }
308                    stream.setState(IN_READ_OBJECT_OPT_DATA);
309                }
310            } else {
311                if (calledDefaultWriteObject)
312                    stream.setState(NO_READ_OBJECT_DEFAULTS_SENT);
313                else
314                    // XXX I18N and logging needed.
315                    throw new StreamCorruptedException("No default data sent");
316            }
317        }
318    }
319
320    // REVISIT.  If a readObject exits here without reading
321    // default data, we won't skip it.  This could be done automatically
322    // as in line 1492 in IIOPInputStream.
323    protected static class InReadObjectRemoteDidNotUseWriteObjectState extends ReadObjectState {
324
325        public void beginUnmarshalCustomValue(InputStreamHook stream,
326                                              boolean calledDefaultWriteObject,
327                                              boolean hasReadObject)
328        {
329            throw utilWrapper.badBeginUnmarshalCustomValue() ;
330        }
331
332        public void endDefaultReadObject(InputStreamHook stream) {
333            stream.setState(IN_READ_OBJECT_PAST_DEFAULTS_REMOTE_NOT_CUSTOM);
334        }
335
336        public void readData(InputStreamHook stream) {
337            stream.throwOptionalDataIncompatibleException();
338        }
339    }
340
341    protected static class InReadObjectPastDefaultsRemoteDidNotUseWOState extends ReadObjectState {
342
343        public void beginUnmarshalCustomValue(InputStreamHook stream,
344                                              boolean calledDefaultWriteObject,
345                                              boolean hasReadObject)
346        {
347            throw utilWrapper.badBeginUnmarshalCustomValue() ;
348        }
349
350        public void beginDefaultReadObject(InputStreamHook stream) throws IOException
351        {
352            // XXX I18N and logging needed.
353            throw new StreamCorruptedException("Default data already read");
354        }
355
356
357        public void readData(InputStreamHook stream) {
358            stream.throwOptionalDataIncompatibleException();
359        }
360    }
361
362    protected void throwOptionalDataIncompatibleException()
363    {
364        throw omgWrapper.rmiiiopOptionalDataIncompatible2() ;
365    }
366
367
368    protected static class InReadObjectDefaultsSentState extends ReadObjectState {
369
370        public void beginUnmarshalCustomValue(InputStreamHook stream,
371                                              boolean calledDefaultWriteObject,
372                                              boolean hasReadObject) {
373            // This should never happen.
374            throw utilWrapper.badBeginUnmarshalCustomValue() ;
375        }
376
377        public void endUnmarshalCustomValue(InputStreamHook stream) {
378
379            // In stream format version 2, we can skip over
380            // the optional data this way.  In stream format version 1,
381            // we will probably wind up with an error if we're
382            // unmarshaling a superclass.
383            if (stream.getStreamFormatVersion() == 2) {
384                ((ValueInputStream)stream.getOrbStream()).start_value();
385                ((ValueInputStream)stream.getOrbStream()).end_value();
386            }
387
388            stream.setState(DEFAULT_STATE);
389        }
390
391        public void endDefaultReadObject(InputStreamHook stream) throws IOException {
392
393            // Read the fake valuetype header in stream format version 2
394            if (stream.getStreamFormatVersion() == 2)
395                ((ValueInputStream)stream.getOrbStream()).start_value();
396
397            stream.setState(IN_READ_OBJECT_OPT_DATA);
398        }
399
400        public void readData(InputStreamHook stream) throws IOException {
401            org.omg.CORBA.ORB orb = stream.getOrbStream().orb();
402            if ((orb == null) ||
403                    !(orb instanceof com.sun.corba.se.spi.orb.ORB)) {
404                throw new StreamCorruptedException(
405                                     "Default data must be read first");
406            }
407            ORBVersion clientOrbVersion =
408                ((com.sun.corba.se.spi.orb.ORB)orb).getORBVersion();
409
410            // Fix Date interop bug. For older versions of the ORB don't do
411            // anything for readData(). Before this used to throw
412            // StreamCorruptedException for older versions of the ORB where
413            // calledDefaultWriteObject always returns true.
414            if ((ORBVersionFactory.getPEORB().compareTo(clientOrbVersion) <= 0) ||
415                    (clientOrbVersion.equals(ORBVersionFactory.getFOREIGN()))) {
416                // XXX I18N and logging needed.
417                throw new StreamCorruptedException("Default data must be read first");
418            }
419        }
420    }
421
422    protected static class InReadObjectOptionalDataState extends ReadObjectState {
423
424        public void beginUnmarshalCustomValue(InputStreamHook stream,
425                                              boolean calledDefaultWriteObject,
426                                              boolean hasReadObject)
427        {
428            // This should never happen.
429            throw utilWrapper.badBeginUnmarshalCustomValue() ;
430        }
431
432        public void endUnmarshalCustomValue(InputStreamHook stream) throws IOException
433        {
434            if (stream.getStreamFormatVersion() == 2) {
435                ((ValueInputStream)stream.getOrbStream()).end_value();
436            }
437            stream.setState(DEFAULT_STATE);
438        }
439
440        public void beginDefaultReadObject(InputStreamHook stream) throws IOException
441        {
442            // XXX I18N and logging needed.
443            throw new StreamCorruptedException("Default data not sent or already read/passed");
444        }
445
446
447    }
448
449    protected static class InReadObjectNoMoreOptionalDataState
450        extends InReadObjectOptionalDataState {
451
452        public void readData(InputStreamHook stream) throws IOException {
453            stream.throwOptionalDataIncompatibleException();
454        }
455    }
456
457    protected static class NoReadObjectDefaultsSentState extends ReadObjectState {
458        public void endUnmarshalCustomValue(InputStreamHook stream) throws IOException {
459            // Code should read default fields before calling this
460
461            if (stream.getStreamFormatVersion() == 2) {
462                ((ValueInputStream)stream.getOrbStream()).start_value();
463                ((ValueInputStream)stream.getOrbStream()).end_value();
464            }
465
466            stream.setState(DEFAULT_STATE);
467        }
468    }
469}
470