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