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.NotActiveException;
  36 import java.io.OutputStream;
  37 import java.io.ObjectOutputStream;
  38 import java.io.ObjectOutput;
  39 import java.util.Map;
  40 import java.util.HashMap;
  41 
  42 import org.omg.CORBA.INTERNAL;
  43 
  44 public 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 }