1 /*
   2  * Copyright (c) 1999, 2014, 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 ObjectOutputStream.PutField putFields()
 156         throws IOException {
 157         putFields = new HookPutFields();
 158         return putFields;
 159     }
 160 
 161     // Stream format version, saved/restored during recursive calls
 162     protected byte streamFormatVersion = 1;
 163 
 164     // Return the stream format version currently being used
 165     // to serialize an object
 166     public byte getStreamFormatVersion() {
 167         return streamFormatVersion;
 168     }
 169 
 170     abstract ObjectStreamField[] getFieldsNoCopy();
 171 
 172     // User uses PutFields to simulate default data.
 173     // See java.io.ObjectOutputStream.PutFields
 174     public void writeFields()
 175         throws IOException {
 176 
 177         writeObjectState.defaultWriteObject(this);
 178 
 179         putFields.write(this);
 180     }
 181 
 182     abstract org.omg.CORBA_2_3.portable.OutputStream getOrbStream();
 183 
 184     protected abstract void beginOptionalCustomData();
 185 
 186 
 187     // The following is a State pattern implementation of what
 188     // should be done when a Serializable has a
 189     // writeObject method.  This was especially necessary for
 190     // RMI-IIOP stream format version 2.  Please see the
 191     // state diagrams in the docs directory of the workspace.
 192 
 193     protected WriteObjectState writeObjectState = NOT_IN_WRITE_OBJECT;
 194 
 195     protected void setState(WriteObjectState newState) {
 196         writeObjectState = newState;
 197     }
 198 
 199     // Description of possible actions
 200     protected static class WriteObjectState {
 201         public void enterWriteObject(OutputStreamHook stream) throws IOException {}
 202         public void exitWriteObject(OutputStreamHook stream) throws IOException {}
 203         public void defaultWriteObject(OutputStreamHook stream) throws IOException {}
 204         public void writeData(OutputStreamHook stream) throws IOException {}
 205     }
 206 
 207     protected static class DefaultState extends WriteObjectState {
 208         public void enterWriteObject(OutputStreamHook stream) throws IOException {
 209             stream.setState(IN_WRITE_OBJECT);
 210         }
 211     }
 212 
 213     protected static final WriteObjectState NOT_IN_WRITE_OBJECT = new DefaultState();
 214     protected static final WriteObjectState IN_WRITE_OBJECT = new InWriteObjectState();
 215     protected static final WriteObjectState WROTE_DEFAULT_DATA = new WroteDefaultDataState();
 216     protected static final WriteObjectState WROTE_CUSTOM_DATA = new WroteCustomDataState();
 217 
 218     protected static class InWriteObjectState extends WriteObjectState {
 219 
 220         public void enterWriteObject(OutputStreamHook stream) throws IOException {
 221             // XXX I18N, logging needed.
 222             throw new IOException("Internal state failure: Entered writeObject twice");
 223         }
 224 
 225         public void exitWriteObject(OutputStreamHook stream) throws IOException {
 226 
 227             // We didn't write any data, so write the
 228             // called defaultWriteObject indicator as false
 229             stream.getOrbStream().write_boolean(false);
 230 
 231             // If we're in stream format verison 2, we must
 232             // put the "null" marker to say that there isn't
 233             // any optional data
 234             if (stream.getStreamFormatVersion() == 2)
 235                 stream.getOrbStream().write_long(0);
 236 
 237             stream.setState(NOT_IN_WRITE_OBJECT);
 238         }
 239 
 240         public void defaultWriteObject(OutputStreamHook stream) throws IOException {
 241 
 242             // The writeObject method called defaultWriteObject
 243             // or writeFields, so put the called defaultWriteObject
 244             // indicator as true
 245             stream.getOrbStream().write_boolean(true);
 246 
 247             stream.setState(WROTE_DEFAULT_DATA);
 248         }
 249 
 250         public void writeData(OutputStreamHook stream) throws IOException {
 251 
 252             // The writeObject method first called a direct
 253             // write operation.  Write the called defaultWriteObject
 254             // indicator as false, put the special stream format
 255             // version 2 header (if stream format version 2, of course),
 256             // and write the data
 257             stream.getOrbStream().write_boolean(false);
 258             stream.beginOptionalCustomData();
 259             stream.setState(WROTE_CUSTOM_DATA);
 260         }
 261     }
 262 
 263     protected static class WroteDefaultDataState extends InWriteObjectState {
 264 
 265         public void exitWriteObject(OutputStreamHook stream) throws IOException {
 266 
 267             // We only wrote default data, so if in stream format
 268             // version 2, put the null indicator to say that there
 269             // is no optional data
 270             if (stream.getStreamFormatVersion() == 2)
 271                 stream.getOrbStream().write_long(0);
 272 
 273             stream.setState(NOT_IN_WRITE_OBJECT);
 274         }
 275 
 276         public void defaultWriteObject(OutputStreamHook stream) throws IOException {
 277             // XXX I18N, logging needed.
 278             throw new IOException("Called defaultWriteObject/writeFields twice");
 279         }
 280 
 281         public void writeData(OutputStreamHook stream) throws IOException {
 282 
 283             // The writeObject method called a direct write operation.
 284             // If in stream format version 2, put the fake valuetype
 285             // header.
 286             stream.beginOptionalCustomData();
 287 
 288             stream.setState(WROTE_CUSTOM_DATA);
 289         }
 290     }
 291 
 292     protected static class WroteCustomDataState extends InWriteObjectState {
 293 
 294         public void exitWriteObject(OutputStreamHook stream) throws IOException {
 295             // In stream format version 2, we must tell the ORB
 296             // stream to close the fake custom valuetype.
 297             if (stream.getStreamFormatVersion() == 2)
 298                 ((org.omg.CORBA.portable.ValueOutputStream)stream.getOrbStream()).end_value();
 299 
 300             stream.setState(NOT_IN_WRITE_OBJECT);
 301         }
 302 
 303         public void defaultWriteObject(OutputStreamHook stream) throws IOException {
 304             // XXX I18N, logging needed.
 305             throw new IOException("Cannot call defaultWriteObject/writeFields after writing custom data in RMI-IIOP");
 306         }
 307 
 308         // We don't have to do anything special here, just let
 309         // the stream write the data.
 310         public void writeData(OutputStreamHook stream) throws IOException {}
 311     }
 312 }