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