001/* ObjectInputStream.java -- Class used to read serialized objects 002 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008 003 Free Software Foundation, Inc. 004 005This file is part of GNU Classpath. 006 007GNU Classpath is free software; you can redistribute it and/or modify 008it under the terms of the GNU General Public License as published by 009the Free Software Foundation; either version 2, or (at your option) 010any later version. 011 012GNU Classpath is distributed in the hope that it will be useful, but 013WITHOUT ANY WARRANTY; without even the implied warranty of 014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015General Public License for more details. 016 017You should have received a copy of the GNU General Public License 018along with GNU Classpath; see the file COPYING. If not, write to the 019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02002110-1301 USA. 021 022Linking this library statically or dynamically with other modules is 023making a combined work based on this library. Thus, the terms and 024conditions of the GNU General Public License cover the whole 025combination. 026 027As a special exception, the copyright holders of this library give you 028permission to link this library with independent modules to produce an 029executable, regardless of the license terms of these independent 030modules, and to copy and distribute the resulting executable under 031terms of your choice, provided that you also meet, for each linked 032independent module, the terms and conditions of the license of that 033module. An independent module is a module which is not derived from 034or based on this library. If you modify this library, you may extend 035this exception to your version of the library, but you are not 036obligated to do so. If you do not wish to do so, delete this 037exception statement from your version. */ 038 039 040package java.io; 041 042import gnu.classpath.Pair; 043import gnu.classpath.VMStackWalker; 044 045import java.lang.reflect.Array; 046import java.lang.reflect.Constructor; 047import java.lang.reflect.Field; 048import java.lang.reflect.InvocationTargetException; 049import java.lang.reflect.Method; 050import java.lang.reflect.Modifier; 051import java.lang.reflect.Proxy; 052import java.security.AccessController; 053import java.security.PrivilegedAction; 054import java.util.HashMap; 055import java.util.Hashtable; 056import java.util.Iterator; 057import java.util.Map; 058import java.util.TreeSet; 059 060/** 061 * @author Tom Tromey (tromey@redhat.com) 062 * @author Jeroen Frijters (jeroen@frijters.net) 063 * @author Guilhem Lavaux (guilhem@kaffe.org) 064 * @author Michael Koch (konqueror@gmx.de) 065 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 066 */ 067public class ObjectInputStream extends InputStream 068 implements ObjectInput, ObjectStreamConstants 069{ 070 /** 071 * Creates a new <code>ObjectInputStream</code> that will do all of 072 * its reading from <code>in</code>. This method also checks 073 * the stream by reading the header information (stream magic number 074 * and stream version). 075 * 076 * @exception IOException Reading stream header from underlying 077 * stream cannot be completed. 078 * 079 * @exception StreamCorruptedException An invalid stream magic 080 * number or stream version was read from the stream. 081 * 082 * @see #readStreamHeader() 083 */ 084 public ObjectInputStream(InputStream in) 085 throws IOException, StreamCorruptedException 086 { 087 if (DEBUG) 088 { 089 String val = System.getProperty("gcj.dumpobjects"); 090 if (dump == false && val != null && !val.equals("")) 091 { 092 dump = true; 093 System.out.println ("Serialization debugging enabled"); 094 } 095 else if (dump == true && (val == null || val.equals(""))) 096 { 097 dump = false; 098 System.out.println ("Serialization debugging disabled"); 099 } 100 } 101 102 this.resolveEnabled = false; 103 this.blockDataPosition = 0; 104 this.blockDataBytes = 0; 105 this.blockData = new byte[BUFFER_SIZE]; 106 this.blockDataInput = new DataInputStream(this); 107 this.realInputStream = new DataInputStream(in); 108 this.nextOID = baseWireHandle; 109 handles = new HashMap<Integer,Pair<Boolean,Object>>(); 110 this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); 111 setBlockDataMode(true); 112 readStreamHeader(); 113 } 114 115 116 /** 117 * Returns the next deserialized object read from the underlying stream. 118 * 119 * This method can be overriden by a class by implementing 120 * <code>private void readObject (ObjectInputStream)</code>. 121 * 122 * If an exception is thrown from this method, the stream is left in 123 * an undefined state. This method can also throw Errors and 124 * RuntimeExceptions if caused by existing readResolve() user code. 125 * 126 * @return The object read from the underlying stream. 127 * 128 * @exception ClassNotFoundException The class that an object being 129 * read in belongs to cannot be found. 130 * 131 * @exception IOException Exception from underlying 132 * <code>InputStream</code>. 133 */ 134 public final Object readObject() 135 throws ClassNotFoundException, IOException 136 { 137 return readObject(true); 138 } 139 140 /** 141 * <p> 142 * Returns the next deserialized object read from the 143 * underlying stream in an unshared manner. Any object 144 * returned by this method will not be returned by 145 * subsequent calls to either this method or {@link #readObject()}. 146 * </p> 147 * <p> 148 * This behaviour is achieved by: 149 * </p> 150 * <ul> 151 * <li>Marking the handles created by successful calls to this 152 * method, so that future calls to {@link #readObject()} or 153 * {@link #readUnshared()} will throw an {@link ObjectStreamException} 154 * rather than returning the same object reference.</li> 155 * <li>Throwing an {@link ObjectStreamException} if the next 156 * element in the stream is a reference to an earlier object.</li> 157 * </ul> 158 * 159 * @return a reference to the deserialized object. 160 * @throws ClassNotFoundException if the class of the object being 161 * deserialized can not be found. 162 * @throws StreamCorruptedException if information in the stream 163 * is inconsistent. 164 * @throws ObjectStreamException if the next object has already been 165 * returned by an earlier call to this 166 * method or {@link #readObject()}. 167 * @throws OptionalDataException if primitive data occurs next in the stream. 168 * @throws IOException if an I/O error occurs from the stream. 169 * @since 1.4 170 * @see #readObject() 171 */ 172 public Object readUnshared() 173 throws IOException, ClassNotFoundException 174 { 175 return readObject(false); 176 } 177 178 /** 179 * Returns the next deserialized object read from the underlying stream. 180 * 181 * This method can be overriden by a class by implementing 182 * <code>private void readObject (ObjectInputStream)</code>. 183 * 184 * If an exception is thrown from this method, the stream is left in 185 * an undefined state. This method can also throw Errors and 186 * RuntimeExceptions if caused by existing readResolve() user code. 187 * 188 * @param shared true if handles created by this call should be shared 189 * with later calls. 190 * @return The object read from the underlying stream. 191 * 192 * @exception ClassNotFoundException The class that an object being 193 * read in belongs to cannot be found. 194 * 195 * @exception IOException Exception from underlying 196 * <code>InputStream</code>. 197 */ 198 private final Object readObject(boolean shared) 199 throws ClassNotFoundException, IOException 200 { 201 if (this.useSubclassMethod) 202 return readObjectOverride(); 203 204 Object ret_val; 205 boolean old_mode = setBlockDataMode(false); 206 byte marker = this.realInputStream.readByte(); 207 208 if (DEBUG) 209 depth += 2; 210 211 if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); 212 213 try 214 { 215 ret_val = parseContent(marker, shared); 216 } 217 finally 218 { 219 setBlockDataMode(old_mode); 220 if (DEBUG) 221 depth -= 2; 222 } 223 224 return ret_val; 225 } 226 227 /** 228 * Handles a content block within the stream, which begins with a marker 229 * byte indicating its type. 230 * 231 * @param marker the byte marker. 232 * @param shared true if handles created by this call should be shared 233 * with later calls. 234 * @return an object which represents the parsed content. 235 * @throws ClassNotFoundException if the class of an object being 236 * read in cannot be found. 237 * @throws IOException if invalid data occurs or one is thrown by the 238 * underlying <code>InputStream</code>. 239 */ 240 private Object parseContent(byte marker, boolean shared) 241 throws ClassNotFoundException, IOException 242 { 243 Object ret_val; 244 boolean is_consumed = false; 245 246 switch (marker) 247 { 248 case TC_ENDBLOCKDATA: 249 { 250 ret_val = null; 251 is_consumed = true; 252 break; 253 } 254 255 case TC_BLOCKDATA: 256 case TC_BLOCKDATALONG: 257 { 258 if (marker == TC_BLOCKDATALONG) 259 { if(dump) dumpElementln("BLOCKDATALONG"); } 260 else 261 { if(dump) dumpElementln("BLOCKDATA"); } 262 readNextBlock(marker); 263 } 264 265 case TC_NULL: 266 { 267 if(dump) dumpElementln("NULL"); 268 ret_val = null; 269 break; 270 } 271 272 case TC_REFERENCE: 273 { 274 if(dump) dumpElement("REFERENCE "); 275 int oid = realInputStream.readInt(); 276 if(dump) dumpElementln(Integer.toHexString(oid)); 277 ret_val = lookupHandle(oid); 278 if (!shared) 279 throw new 280 InvalidObjectException("References can not be read unshared."); 281 break; 282 } 283 284 case TC_CLASS: 285 { 286 if(dump) dumpElementln("CLASS"); 287 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 288 Class clazz = osc.forClass(); 289 assignNewHandle(clazz,shared); 290 ret_val = clazz; 291 break; 292 } 293 294 case TC_PROXYCLASSDESC: 295 { 296 if(dump) dumpElementln("PROXYCLASS"); 297 298/* GCJ LOCAL */ 299 // The grammar at this point is 300 // TC_PROXYCLASSDESC newHandle proxyClassDescInfo 301 // i.e. we have to assign the handle immediately after 302 // reading the marker. 303 int handle = assignNewHandle("Dummy proxy",shared); 304/* END GCJ LOCAL */ 305 306 int n_intf = this.realInputStream.readInt(); 307 String[] intfs = new String[n_intf]; 308 for (int i = 0; i < n_intf; i++) 309 { 310 intfs[i] = this.realInputStream.readUTF(); 311 } 312 313 boolean oldmode = setBlockDataMode(true); 314 Class cl = resolveProxyClass(intfs); 315 setBlockDataMode(oldmode); 316 317 ObjectStreamClass osc = lookupClass(cl); 318 if (osc.firstNonSerializableParentConstructor == null) 319 { 320 osc.realClassIsSerializable = true; 321 osc.fields = osc.fieldMapping = new ObjectStreamField[0]; 322 try 323 { 324 osc.firstNonSerializableParentConstructor = 325 Object.class.getConstructor(new Class[0]); 326 } 327 catch (NoSuchMethodException x) 328 { 329 throw (InternalError) 330 new InternalError("Object ctor missing").initCause(x); 331 } 332 } 333/* GCJ LOCAL */ 334 rememberHandle(osc,shared,handle); 335/* END GCJ LOCAL */ 336 337 if (!is_consumed) 338 { 339 byte b = this.realInputStream.readByte(); 340 if (b != TC_ENDBLOCKDATA) 341 throw new IOException("Data annotated to class was not consumed." + b); 342 } 343 else 344 is_consumed = false; 345 ObjectStreamClass superosc = (ObjectStreamClass)readObject(); 346 osc.setSuperclass(superosc); 347 ret_val = osc; 348 break; 349 } 350 351 case TC_CLASSDESC: 352 { 353 ObjectStreamClass osc = readClassDescriptor(); 354 355 if (!is_consumed) 356 { 357 byte b = this.realInputStream.readByte(); 358 if (b != TC_ENDBLOCKDATA) 359 throw new IOException("Data annotated to class was not consumed." + b); 360 } 361 else 362 is_consumed = false; 363 364 osc.setSuperclass ((ObjectStreamClass)readObject()); 365 ret_val = osc; 366 break; 367 } 368 369 case TC_STRING: 370 { 371 if(dump) dumpElement("STRING="); 372 String s = this.realInputStream.readUTF(); 373 if(dump) dumpElementln(s); 374 ret_val = processResolution(null, s, assignNewHandle(s,shared), 375 shared); 376 break; 377 } 378 379 case TC_LONGSTRING: 380 { 381 if(dump) dumpElement("STRING="); 382 String s = this.realInputStream.readUTFLong(); 383 if(dump) dumpElementln(s); 384 ret_val = processResolution(null, s, assignNewHandle(s,shared), 385 shared); 386 break; 387 } 388 389 case TC_ARRAY: 390 { 391 if(dump) dumpElementln("ARRAY"); 392 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 393 Class componentType = osc.forClass().getComponentType(); 394 if(dump) dumpElement("ARRAY LENGTH="); 395 int length = this.realInputStream.readInt(); 396 if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); 397 Object array = Array.newInstance(componentType, length); 398 int handle = assignNewHandle(array,shared); 399 readArrayElements(array, componentType); 400 if(dump) 401 for (int i = 0, len = Array.getLength(array); i < len; i++) 402 dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i)); 403 ret_val = processResolution(null, array, handle, shared); 404 break; 405 } 406 407 case TC_OBJECT: 408 { 409 if(dump) dumpElementln("OBJECT"); 410 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 411 Class clazz = osc.forClass(); 412 413 if (!osc.realClassIsSerializable) 414 throw new NotSerializableException 415 (clazz + " is not Serializable, and thus cannot be deserialized."); 416 417 if (osc.realClassIsExternalizable) 418 { 419 Externalizable obj = osc.newInstance(); 420 421 int handle = assignNewHandle(obj,shared); 422 423 boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); 424 425 boolean oldmode = this.readDataFromBlock; 426 if (read_from_blocks) 427 setBlockDataMode(true); 428 429 obj.readExternal(this); 430 431 if (read_from_blocks) 432 { 433 setBlockDataMode(oldmode); 434 if (!oldmode) 435 if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) 436 throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); 437 } 438 439 ret_val = processResolution(osc, obj, handle,shared); 440 break; 441 442 } // end if (osc.realClassIsExternalizable) 443 444 Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); 445 446 int handle = assignNewHandle(obj,shared); 447 Object prevObject = this.currentObject; 448 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; 449 TreeSet<ValidatorAndPriority> prevObjectValidators = 450 this.currentObjectValidators; 451 452 this.currentObject = obj; 453 this.currentObjectValidators = null; 454 ObjectStreamClass[] hierarchy = hierarchy(clazz); 455 456 for (int i = 0; i < hierarchy.length; i++) 457 { 458 this.currentObjectStreamClass = hierarchy[i]; 459 if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); 460 461 // XXX: should initialize fields in classes in the hierarchy 462 // that aren't in the stream 463 // should skip over classes in the stream that aren't in the 464 // real classes hierarchy 465 466 Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; 467 if (readObjectMethod != null) 468 { 469 fieldsAlreadyRead = false; 470 boolean oldmode = setBlockDataMode(true); 471 callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); 472 setBlockDataMode(oldmode); 473 } 474 else 475 { 476 readFields(obj, currentObjectStreamClass); 477 } 478 479 if (this.currentObjectStreamClass.hasWriteMethod()) 480 { 481 if(dump) dumpElement("ENDBLOCKDATA? "); 482 try 483 { 484 /* Read blocks until an end marker */ 485 byte writeMarker = this.realInputStream.readByte(); 486 while (writeMarker != TC_ENDBLOCKDATA) 487 { 488 parseContent(writeMarker, shared); 489 writeMarker = this.realInputStream.readByte(); 490 } 491 if(dump) dumpElementln("yes"); 492 } 493 catch (EOFException e) 494 { 495 throw (IOException) new IOException 496 ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e); 497 } 498 } 499 } 500 501 this.currentObject = prevObject; 502 this.currentObjectStreamClass = prevObjectStreamClass; 503 ret_val = processResolution(osc, obj, handle, shared); 504 if (currentObjectValidators != null) 505 invokeValidators(); 506 this.currentObjectValidators = prevObjectValidators; 507 508 break; 509 } 510 511 case TC_RESET: 512 if(dump) dumpElementln("RESET"); 513 clearHandles(); 514 ret_val = readObject(); 515 break; 516 517 case TC_EXCEPTION: 518 { 519 if(dump) dumpElement("EXCEPTION="); 520 Exception e = (Exception)readObject(); 521 if(dump) dumpElementln(e.toString()); 522 clearHandles(); 523 throw new WriteAbortedException("Exception thrown during writing of stream", e); 524 } 525 526 case TC_ENUM: 527 { 528 /* TC_ENUM classDesc newHandle enumConstantName */ 529 if (dump) 530 dumpElementln("ENUM="); 531 ObjectStreamClass osc = (ObjectStreamClass) readObject(); 532 String constantName = (String) readObject(); 533 if (dump) 534 dumpElementln("CONSTANT NAME = " + constantName); 535 Class clazz = osc.forClass(); 536 Enum instance = Enum.valueOf(clazz, constantName); 537 assignNewHandle(instance,shared); 538 ret_val = instance; 539 break; 540 } 541 542 default: 543 throw new IOException("Unknown marker on stream: " + marker); 544 } 545 return ret_val; 546 } 547 548 /** 549 * This method makes a partial check of types for the fields 550 * contained given in arguments. It checks primitive types of 551 * fields1 against non primitive types of fields2. This method 552 * assumes the two lists has already been sorted according to 553 * the Java specification. 554 * 555 * @param name Name of the class owning the given fields. 556 * @param fields1 First list to check. 557 * @param fields2 Second list to check. 558 * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present 559 * in the non primitive part in fields2. 560 */ 561 private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) 562 throws InvalidClassException 563 { 564 int nonPrimitive = 0; 565 566 for (nonPrimitive = 0; 567 nonPrimitive < fields1.length 568 && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) 569 { 570 } 571 572 if (nonPrimitive == fields1.length) 573 return; 574 575 int i = 0; 576 ObjectStreamField f1; 577 ObjectStreamField f2; 578 579 while (i < fields2.length 580 && nonPrimitive < fields1.length) 581 { 582 f1 = fields1[nonPrimitive]; 583 f2 = fields2[i]; 584 585 if (!f2.isPrimitive()) 586 break; 587 588 int compVal = f1.getName().compareTo (f2.getName()); 589 590 if (compVal < 0) 591 { 592 nonPrimitive++; 593 } 594 else if (compVal > 0) 595 { 596 i++; 597 } 598 else 599 { 600 throw new InvalidClassException 601 ("invalid field type for " + f2.getName() + 602 " in class " + name); 603 } 604 } 605 } 606 607 /** 608 * This method reads a class descriptor from the real input stream 609 * and use these data to create a new instance of ObjectStreamClass. 610 * Fields are sorted and ordered for the real read which occurs for 611 * each instance of the described class. Be aware that if you call that 612 * method you must ensure that the stream is synchronized, in the other 613 * case it may be completely desynchronized. 614 * 615 * @return A new instance of ObjectStreamClass containing the freshly 616 * created descriptor. 617 * @throws ClassNotFoundException if the required class to build the 618 * descriptor has not been found in the system. 619 * @throws IOException An input/output error occured. 620 * @throws InvalidClassException If there was a compatibility problem 621 * between the class present in the system and the serialized class. 622 */ 623 protected ObjectStreamClass readClassDescriptor() 624 throws ClassNotFoundException, IOException 625 { 626 if(dump) dumpElement("CLASSDESC NAME="); 627 String name = this.realInputStream.readUTF(); 628 if(dump) dumpElement(name + "; UID="); 629 long uid = this.realInputStream.readLong (); 630 if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); 631 byte flags = this.realInputStream.readByte (); 632 if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); 633 short field_count = this.realInputStream.readShort(); 634 if(dump) dumpElementln(Short.toString(field_count)); 635 ObjectStreamField[] fields = new ObjectStreamField[field_count]; 636 ObjectStreamClass osc = new ObjectStreamClass(name, uid, 637 flags, fields); 638 assignNewHandle(osc,true); 639 640 for (int i = 0; i < field_count; i++) 641 { 642 if(dump) dumpElement(" TYPE CODE="); 643 char type_code = (char)this.realInputStream.readByte(); 644 if(dump) dumpElement(type_code + "; FIELD NAME="); 645 String field_name = this.realInputStream.readUTF(); 646 if(dump) dumpElementln(field_name); 647 String class_name; 648 649 // If the type code is an array or an object we must 650 // decode a String here. In the other case we convert 651 // the type code and pass it to ObjectStreamField. 652 // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. 653 if (type_code == 'L' || type_code == '[') 654 class_name = (String)readObject(); 655 else 656 class_name = String.valueOf(type_code); 657 658 fields[i] = 659 new ObjectStreamField(field_name, class_name); 660 } 661 662 /* Now that fields have been read we may resolve the class 663 * (and read annotation if needed). */ 664 Class clazz = resolveClass(osc); 665 ClassLoader loader = clazz.getClassLoader(); 666 for (int i = 0; i < field_count; i++) 667 { 668 fields[i].resolveType(loader); 669 } 670 boolean oldmode = setBlockDataMode(true); 671 osc.setClass(clazz, lookupClass(clazz.getSuperclass())); 672 classLookupTable.put(clazz, osc); 673 setBlockDataMode(oldmode); 674 675 // find the first non-serializable class in clazz's inheritance hierarchy 676 Class first_nonserial = clazz.getSuperclass(); 677 // Maybe it is a primitive class, those don't have a super class, 678 // or Object itself. Otherwise we can keep getting the superclass 679 // till we hit the Object class, or some other non-serializable class. 680 681 if (first_nonserial == null) 682 first_nonserial = clazz; 683 else 684 while (Serializable.class.isAssignableFrom(first_nonserial)) 685 first_nonserial = first_nonserial.getSuperclass(); 686 687 final Class local_constructor_class = first_nonserial; 688 689 osc.firstNonSerializableParentConstructor = 690 (Constructor)AccessController.doPrivileged(new PrivilegedAction() 691 { 692 public Object run() 693 { 694 try 695 { 696 Constructor c = local_constructor_class. 697 getDeclaredConstructor(new Class[0]); 698 if (Modifier.isPrivate(c.getModifiers())) 699 return null; 700 return c; 701 } 702 catch (NoSuchMethodException e) 703 { 704 // error will be reported later, in newObject() 705 return null; 706 } 707 } 708 }); 709 710 osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); 711 osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); 712 713 ObjectStreamField[] stream_fields = osc.fields; 714 ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; 715 ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; 716 717 int stream_idx = 0; 718 int real_idx = 0; 719 int map_idx = 0; 720 721 /* 722 * Check that there is no type inconsistencies between the lists. 723 * A special checking must be done for the two groups: primitive types and 724 * not primitive types. 725 */ 726 checkTypeConsistency(name, real_fields, stream_fields); 727 checkTypeConsistency(name, stream_fields, real_fields); 728 729 730 while (stream_idx < stream_fields.length 731 || real_idx < real_fields.length) 732 { 733 ObjectStreamField stream_field = null; 734 ObjectStreamField real_field = null; 735 736 if (stream_idx == stream_fields.length) 737 { 738 real_field = real_fields[real_idx++]; 739 } 740 else if (real_idx == real_fields.length) 741 { 742 stream_field = stream_fields[stream_idx++]; 743 } 744 else 745 { 746 int comp_val = 747 real_fields[real_idx].compareTo (stream_fields[stream_idx]); 748 749 if (comp_val < 0) 750 { 751 real_field = real_fields[real_idx++]; 752 } 753 else if (comp_val > 0) 754 { 755 stream_field = stream_fields[stream_idx++]; 756 } 757 else 758 { 759 stream_field = stream_fields[stream_idx++]; 760 real_field = real_fields[real_idx++]; 761 if (stream_field.getType() != real_field.getType()) 762 throw new InvalidClassException 763 ("invalid field type for " + real_field.getName() + 764 " in class " + name); 765 } 766 } 767 768 /* If some of stream_fields does not correspond to any of real_fields, 769 * or the opposite, then fieldmapping will go short. 770 */ 771 if (map_idx == fieldmapping.length) 772 { 773 ObjectStreamField[] newfieldmapping = 774 new ObjectStreamField[fieldmapping.length + 2]; 775 System.arraycopy(fieldmapping, 0, 776 newfieldmapping, 0, fieldmapping.length); 777 fieldmapping = newfieldmapping; 778 } 779 fieldmapping[map_idx++] = stream_field; 780 fieldmapping[map_idx++] = real_field; 781 } 782 osc.fieldMapping = fieldmapping; 783 784 return osc; 785 } 786 787 /** 788 * Reads the current objects non-transient, non-static fields from 789 * the current class from the underlying output stream. 790 * 791 * This method is intended to be called from within a object's 792 * <code>private void readObject (ObjectInputStream)</code> 793 * method. 794 * 795 * @exception ClassNotFoundException The class that an object being 796 * read in belongs to cannot be found. 797 * 798 * @exception NotActiveException This method was called from a 799 * context other than from the current object's and current class's 800 * <code>private void readObject (ObjectInputStream)</code> 801 * method. 802 * 803 * @exception IOException Exception from underlying 804 * <code>OutputStream</code>. 805 */ 806 public void defaultReadObject() 807 throws ClassNotFoundException, IOException, NotActiveException 808 { 809 if (this.currentObject == null || this.currentObjectStreamClass == null) 810 throw new NotActiveException("defaultReadObject called by non-active" 811 + " class and/or object"); 812 813 if (fieldsAlreadyRead) 814 throw new NotActiveException("defaultReadObject called but fields " 815 + "already read from stream (by " 816 + "defaultReadObject or readFields)"); 817 818 boolean oldmode = setBlockDataMode(false); 819 readFields(this.currentObject, this.currentObjectStreamClass); 820 setBlockDataMode(oldmode); 821 822 fieldsAlreadyRead = true; 823 } 824 825 826 /** 827 * Registers a <code>ObjectInputValidation</code> to be carried out 828 * on the object graph currently being deserialized before it is 829 * returned to the original caller of <code>readObject ()</code>. 830 * The order of validation for multiple 831 * <code>ObjectInputValidation</code>s can be controled using 832 * <code>priority</code>. Validators with higher priorities are 833 * called first. 834 * 835 * @see java.io.ObjectInputValidation 836 * 837 * @exception InvalidObjectException <code>validator</code> is 838 * <code>null</code> 839 * 840 * @exception NotActiveException an attempt was made to add a 841 * validator outside of the <code>readObject</code> method of the 842 * object currently being deserialized 843 */ 844 public void registerValidation(ObjectInputValidation validator, 845 int priority) 846 throws InvalidObjectException, NotActiveException 847 { 848 if (this.currentObject == null || this.currentObjectStreamClass == null) 849 throw new NotActiveException("registerValidation called by non-active " 850 + "class and/or object"); 851 852 if (validator == null) 853 throw new InvalidObjectException("attempt to add a null " 854 + "ObjectInputValidation object"); 855 856 if (currentObjectValidators == null) 857 currentObjectValidators = new TreeSet<ValidatorAndPriority>(); 858 859 currentObjectValidators.add(new ValidatorAndPriority(validator, priority)); 860 } 861 862 863 /** 864 * Called when a class is being deserialized. This is a hook to 865 * allow subclasses to read in information written by the 866 * <code>annotateClass (Class)</code> method of an 867 * <code>ObjectOutputStream</code>. 868 * 869 * This implementation looks up the active call stack for a 870 * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, 871 * it is used to load the class associated with <code>osc</code>, 872 * otherwise, the default system <code>ClassLoader</code> is used. 873 * 874 * @exception IOException Exception from underlying 875 * <code>OutputStream</code>. 876 * 877 * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) 878 */ 879 protected Class<?> resolveClass(ObjectStreamClass osc) 880 throws ClassNotFoundException, IOException 881 { 882 String name = osc.getName(); 883 try 884 { 885 return Class.forName(name, true, currentLoader()); 886 } 887 catch(ClassNotFoundException x) 888 { 889 if (name.equals("void")) 890 return Void.TYPE; 891 else if (name.equals("boolean")) 892 return Boolean.TYPE; 893 else if (name.equals("byte")) 894 return Byte.TYPE; 895 else if (name.equals("char")) 896 return Character.TYPE; 897 else if (name.equals("short")) 898 return Short.TYPE; 899 else if (name.equals("int")) 900 return Integer.TYPE; 901 else if (name.equals("long")) 902 return Long.TYPE; 903 else if (name.equals("float")) 904 return Float.TYPE; 905 else if (name.equals("double")) 906 return Double.TYPE; 907 else 908 throw x; 909 } 910 } 911 912 /** 913 * Returns the most recent user defined ClassLoader on the execution stack 914 * or null if none is found. 915 */ 916 private ClassLoader currentLoader() 917 { 918 return VMStackWalker.firstNonNullClassLoader(); 919 } 920 921 /** 922 * Lookup a class stored in the local hashtable. If it is not 923 * use the global lookup function in ObjectStreamClass to build 924 * the ObjectStreamClass. This method is requested according to 925 * the behaviour detected in the JDK by Kaffe's team. 926 * 927 * @param clazz Class to lookup in the hash table or for which 928 * we must build a descriptor. 929 * @return A valid instance of ObjectStreamClass corresponding 930 * to the specified class. 931 */ 932 private ObjectStreamClass lookupClass(Class clazz) 933 { 934 if (clazz == null) 935 return null; 936 937 ObjectStreamClass oclazz; 938 oclazz = classLookupTable.get(clazz); 939 if (oclazz == null) 940 return ObjectStreamClass.lookup(clazz); 941 else 942 return oclazz; 943 } 944 945 /** 946 * Reconstruct class hierarchy the same way {@link 947 * java.io.ObjectStreamClass#hierarchy} does but using lookupClass 948 * instead of ObjectStreamClass.lookup. 949 * 950 * @param clazz This is the class for which we want the hierarchy. 951 * 952 * @return An array of valid {@link java.io.ObjectStreamClass} instances which 953 * represent the class hierarchy for clazz. 954 */ 955 private ObjectStreamClass[] hierarchy(Class clazz) 956 { 957 ObjectStreamClass osc = lookupClass(clazz); 958 959 return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); 960 } 961 962 /** 963 * Allows subclasses to resolve objects that are read from the 964 * stream with other objects to be returned in their place. This 965 * method is called the first time each object is encountered. 966 * 967 * This method must be enabled before it will be called in the 968 * serialization process. 969 * 970 * @exception IOException Exception from underlying 971 * <code>OutputStream</code>. 972 * 973 * @see #enableResolveObject(boolean) 974 */ 975 protected Object resolveObject(Object obj) throws IOException 976 { 977 return obj; 978 } 979 980 981 protected Class<?> resolveProxyClass(String[] intfs) 982 throws IOException, ClassNotFoundException 983 { 984 ClassLoader cl = currentLoader(); 985 986 Class<?>[] clss = new Class<?>[intfs.length]; 987 if(cl == null) 988 { 989 for (int i = 0; i < intfs.length; i++) 990 clss[i] = Class.forName(intfs[i]); 991 cl = ClassLoader.getSystemClassLoader(); 992 } 993 else 994 for (int i = 0; i < intfs.length; i++) 995 clss[i] = Class.forName(intfs[i], false, cl); 996 try 997 { 998 return Proxy.getProxyClass(cl, clss); 999 } 1000 catch (IllegalArgumentException e) 1001 { 1002 throw new ClassNotFoundException(null, e); 1003 } 1004 } 1005 1006 /** 1007 * If <code>enable</code> is <code>true</code> and this object is 1008 * trusted, then <code>resolveObject (Object)</code> will be called 1009 * in subsequent calls to <code>readObject (Object)</code>. 1010 * Otherwise, <code>resolveObject (Object)</code> will not be called. 1011 * 1012 * @exception SecurityException This class is not trusted. 1013 */ 1014 protected boolean enableResolveObject (boolean enable) 1015 throws SecurityException 1016 { 1017 if (enable) 1018 { 1019 SecurityManager sm = System.getSecurityManager(); 1020 if (sm != null) 1021 sm.checkPermission(new SerializablePermission("enableSubstitution")); 1022 } 1023 1024 boolean old_val = this.resolveEnabled; 1025 this.resolveEnabled = enable; 1026 return old_val; 1027 } 1028 1029 /** 1030 * Reads stream magic and stream version information from the 1031 * underlying stream. 1032 * 1033 * @exception IOException Exception from underlying stream. 1034 * 1035 * @exception StreamCorruptedException An invalid stream magic 1036 * number or stream version was read from the stream. 1037 */ 1038 protected void readStreamHeader() 1039 throws IOException, StreamCorruptedException 1040 { 1041 if(dump) dumpElement("STREAM MAGIC "); 1042 if (this.realInputStream.readShort() != STREAM_MAGIC) 1043 throw new StreamCorruptedException("Invalid stream magic number"); 1044 1045 if(dump) dumpElementln("STREAM VERSION "); 1046 if (this.realInputStream.readShort() != STREAM_VERSION) 1047 throw new StreamCorruptedException("Invalid stream version number"); 1048 } 1049 1050 public int read() throws IOException 1051 { 1052 if (this.readDataFromBlock) 1053 { 1054 if (this.blockDataPosition >= this.blockDataBytes) 1055 readNextBlock(); 1056 return (this.blockData[this.blockDataPosition++] & 0xff); 1057 } 1058 else 1059 return this.realInputStream.read(); 1060 } 1061 1062 public int read(byte[] data, int offset, int length) throws IOException 1063 { 1064 if (this.readDataFromBlock) 1065 { 1066 int remain = this.blockDataBytes - this.blockDataPosition; 1067 if (remain == 0) 1068 { 1069 readNextBlock(); 1070 remain = this.blockDataBytes - this.blockDataPosition; 1071 } 1072 length = Math.min(length, remain); 1073 System.arraycopy(this.blockData, this.blockDataPosition, 1074 data, offset, length); 1075 this.blockDataPosition += length; 1076 1077 return length; 1078 } 1079 else 1080 return this.realInputStream.read(data, offset, length); 1081 } 1082 1083 public int available() throws IOException 1084 { 1085 if (this.readDataFromBlock) 1086 { 1087 if (this.blockDataPosition >= this.blockDataBytes) 1088 readNextBlock (); 1089 1090 return this.blockDataBytes - this.blockDataPosition; 1091 } 1092 else 1093 return this.realInputStream.available(); 1094 } 1095 1096 public void close() throws IOException 1097 { 1098 this.realInputStream.close(); 1099 } 1100 1101 public boolean readBoolean() throws IOException 1102 { 1103 boolean switchmode = true; 1104 boolean oldmode = this.readDataFromBlock; 1105 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1106 switchmode = false; 1107 if (switchmode) 1108 oldmode = setBlockDataMode (true); 1109 boolean value = this.dataInputStream.readBoolean (); 1110 if (switchmode) 1111 setBlockDataMode (oldmode); 1112 return value; 1113 } 1114 1115 public byte readByte() throws IOException 1116 { 1117 boolean switchmode = true; 1118 boolean oldmode = this.readDataFromBlock; 1119 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1120 switchmode = false; 1121 if (switchmode) 1122 oldmode = setBlockDataMode(true); 1123 byte value = this.dataInputStream.readByte(); 1124 if (switchmode) 1125 setBlockDataMode(oldmode); 1126 return value; 1127 } 1128 1129 public int readUnsignedByte() throws IOException 1130 { 1131 boolean switchmode = true; 1132 boolean oldmode = this.readDataFromBlock; 1133 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1134 switchmode = false; 1135 if (switchmode) 1136 oldmode = setBlockDataMode(true); 1137 int value = this.dataInputStream.readUnsignedByte(); 1138 if (switchmode) 1139 setBlockDataMode(oldmode); 1140 return value; 1141 } 1142 1143 public short readShort() throws IOException 1144 { 1145 boolean switchmode = true; 1146 boolean oldmode = this.readDataFromBlock; 1147 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1148 switchmode = false; 1149 if (switchmode) 1150 oldmode = setBlockDataMode(true); 1151 short value = this.dataInputStream.readShort(); 1152 if (switchmode) 1153 setBlockDataMode(oldmode); 1154 return value; 1155 } 1156 1157 public int readUnsignedShort() throws IOException 1158 { 1159 boolean switchmode = true; 1160 boolean oldmode = this.readDataFromBlock; 1161 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1162 switchmode = false; 1163 if (switchmode) 1164 oldmode = setBlockDataMode(true); 1165 int value = this.dataInputStream.readUnsignedShort(); 1166 if (switchmode) 1167 setBlockDataMode(oldmode); 1168 return value; 1169 } 1170 1171 public char readChar() throws IOException 1172 { 1173 boolean switchmode = true; 1174 boolean oldmode = this.readDataFromBlock; 1175 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1176 switchmode = false; 1177 if (switchmode) 1178 oldmode = setBlockDataMode(true); 1179 char value = this.dataInputStream.readChar(); 1180 if (switchmode) 1181 setBlockDataMode(oldmode); 1182 return value; 1183 } 1184 1185 public int readInt() throws IOException 1186 { 1187 boolean switchmode = true; 1188 boolean oldmode = this.readDataFromBlock; 1189 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) 1190 switchmode = false; 1191 if (switchmode) 1192 oldmode = setBlockDataMode(true); 1193 int value = this.dataInputStream.readInt(); 1194 if (switchmode) 1195 setBlockDataMode(oldmode); 1196 return value; 1197 } 1198 1199 public long readLong() throws IOException 1200 { 1201 boolean switchmode = true; 1202 boolean oldmode = this.readDataFromBlock; 1203 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) 1204 switchmode = false; 1205 if (switchmode) 1206 oldmode = setBlockDataMode(true); 1207 long value = this.dataInputStream.readLong(); 1208 if (switchmode) 1209 setBlockDataMode(oldmode); 1210 return value; 1211 } 1212 1213 public float readFloat() throws IOException 1214 { 1215 boolean switchmode = true; 1216 boolean oldmode = this.readDataFromBlock; 1217 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) 1218 switchmode = false; 1219 if (switchmode) 1220 oldmode = setBlockDataMode(true); 1221 float value = this.dataInputStream.readFloat(); 1222 if (switchmode) 1223 setBlockDataMode(oldmode); 1224 return value; 1225 } 1226 1227 public double readDouble() throws IOException 1228 { 1229 boolean switchmode = true; 1230 boolean oldmode = this.readDataFromBlock; 1231 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) 1232 switchmode = false; 1233 if (switchmode) 1234 oldmode = setBlockDataMode(true); 1235 double value = this.dataInputStream.readDouble(); 1236 if (switchmode) 1237 setBlockDataMode(oldmode); 1238 return value; 1239 } 1240 1241 public void readFully(byte data[]) throws IOException 1242 { 1243 this.dataInputStream.readFully(data); 1244 } 1245 1246 public void readFully(byte data[], int offset, int size) 1247 throws IOException 1248 { 1249 this.dataInputStream.readFully(data, offset, size); 1250 } 1251 1252 public int skipBytes(int len) throws IOException 1253 { 1254 return this.dataInputStream.skipBytes(len); 1255 } 1256 1257 /** 1258 * @deprecated 1259 * @see java.io.DataInputStream#readLine () 1260 */ 1261 public String readLine() throws IOException 1262 { 1263 return this.dataInputStream.readLine(); 1264 } 1265 1266 public String readUTF() throws IOException 1267 { 1268 return this.dataInputStream.readUTF(); 1269 } 1270 1271 /** 1272 * This class allows a class to specify exactly which fields should 1273 * be read, and what values should be read for these fields. 1274 * 1275 * XXX: finish up comments 1276 */ 1277 public abstract static class GetField 1278 { 1279 public abstract ObjectStreamClass getObjectStreamClass(); 1280 1281 public abstract boolean defaulted(String name) 1282 throws IOException, IllegalArgumentException; 1283 1284 public abstract boolean get(String name, boolean defvalue) 1285 throws IOException, IllegalArgumentException; 1286 1287 public abstract char get(String name, char defvalue) 1288 throws IOException, IllegalArgumentException; 1289 1290 public abstract byte get(String name, byte defvalue) 1291 throws IOException, IllegalArgumentException; 1292 1293 public abstract short get(String name, short defvalue) 1294 throws IOException, IllegalArgumentException; 1295 1296 public abstract int get(String name, int defvalue) 1297 throws IOException, IllegalArgumentException; 1298 1299 public abstract long get(String name, long defvalue) 1300 throws IOException, IllegalArgumentException; 1301 1302 public abstract float get(String name, float defvalue) 1303 throws IOException, IllegalArgumentException; 1304 1305 public abstract double get(String name, double defvalue) 1306 throws IOException, IllegalArgumentException; 1307 1308 public abstract Object get(String name, Object defvalue) 1309 throws IOException, IllegalArgumentException; 1310 } 1311 1312 /** 1313 * This method should be called by a method called 'readObject' in the 1314 * deserializing class (if present). It cannot (and should not)be called 1315 * outside of it. Its goal is to read all fields in the real input stream 1316 * and keep them accessible through the {@link GetField} class. Calling 1317 * this method will not alter the deserializing object. 1318 * 1319 * @return A valid freshly created 'GetField' instance to get access to 1320 * the deserialized stream. 1321 * @throws IOException An input/output exception occured. 1322 * @throws ClassNotFoundException 1323 * @throws NotActiveException 1324 */ 1325 public GetField readFields() 1326 throws IOException, ClassNotFoundException, NotActiveException 1327 { 1328 if (this.currentObject == null || this.currentObjectStreamClass == null) 1329 throw new NotActiveException("readFields called by non-active class and/or object"); 1330 1331 if (prereadFields != null) 1332 return prereadFields; 1333 1334 if (fieldsAlreadyRead) 1335 throw new NotActiveException("readFields called but fields already read from" 1336 + " stream (by defaultReadObject or readFields)"); 1337 1338 final ObjectStreamClass clazz = this.currentObjectStreamClass; 1339 final byte[] prim_field_data = new byte[clazz.primFieldSize]; 1340 final Object[] objs = new Object[clazz.objectFieldCount]; 1341 1342 // Apparently Block data is not used with GetField as per 1343 // empirical evidence against JDK 1.2. Also see Mauve test 1344 // java.io.ObjectInputOutput.Test.GetPutField. 1345 boolean oldmode = setBlockDataMode(false); 1346 readFully(prim_field_data); 1347 for (int i = 0; i < objs.length; ++ i) 1348 objs[i] = readObject(); 1349 setBlockDataMode(oldmode); 1350 1351 prereadFields = new GetField() 1352 { 1353 public ObjectStreamClass getObjectStreamClass() 1354 { 1355 return clazz; 1356 } 1357 1358 public boolean defaulted(String name) 1359 throws IOException, IllegalArgumentException 1360 { 1361 ObjectStreamField f = clazz.getField(name); 1362 1363 /* First if we have a serialized field use the descriptor */ 1364 if (f != null) 1365 { 1366 /* It is in serialPersistentFields but setClass tells us 1367 * it should not be set. This value is defaulted. 1368 */ 1369 if (f.isPersistent() && !f.isToSet()) 1370 return true; 1371 1372 return false; 1373 } 1374 1375 /* This is not a serialized field. There should be 1376 * a default value only if the field really exists. 1377 */ 1378 try 1379 { 1380 return (clazz.forClass().getDeclaredField (name) != null); 1381 } 1382 catch (NoSuchFieldException e) 1383 { 1384 throw new IllegalArgumentException(e); 1385 } 1386 } 1387 1388 public boolean get(String name, boolean defvalue) 1389 throws IOException, IllegalArgumentException 1390 { 1391 ObjectStreamField field = getField(name, Boolean.TYPE); 1392 1393 if (field == null) 1394 return defvalue; 1395 1396 return prim_field_data[field.getOffset()] == 0 ? false : true; 1397 } 1398 1399 public char get(String name, char defvalue) 1400 throws IOException, IllegalArgumentException 1401 { 1402 ObjectStreamField field = getField(name, Character.TYPE); 1403 1404 if (field == null) 1405 return defvalue; 1406 1407 int off = field.getOffset(); 1408 1409 return (char)(((prim_field_data[off++] & 0xFF) << 8) 1410 | (prim_field_data[off] & 0xFF)); 1411 } 1412 1413 public byte get(String name, byte defvalue) 1414 throws IOException, IllegalArgumentException 1415 { 1416 ObjectStreamField field = getField(name, Byte.TYPE); 1417 1418 if (field == null) 1419 return defvalue; 1420 1421 return prim_field_data[field.getOffset()]; 1422 } 1423 1424 public short get(String name, short defvalue) 1425 throws IOException, IllegalArgumentException 1426 { 1427 ObjectStreamField field = getField(name, Short.TYPE); 1428 1429 if (field == null) 1430 return defvalue; 1431 1432 int off = field.getOffset(); 1433 1434 return (short)(((prim_field_data[off++] & 0xFF) << 8) 1435 | (prim_field_data[off] & 0xFF)); 1436 } 1437 1438 public int get(String name, int defvalue) 1439 throws IOException, IllegalArgumentException 1440 { 1441 ObjectStreamField field = getField(name, Integer.TYPE); 1442 1443 if (field == null) 1444 return defvalue; 1445 1446 int off = field.getOffset(); 1447 1448 return ((prim_field_data[off++] & 0xFF) << 24) 1449 | ((prim_field_data[off++] & 0xFF) << 16) 1450 | ((prim_field_data[off++] & 0xFF) << 8) 1451 | (prim_field_data[off] & 0xFF); 1452 } 1453 1454 public long get(String name, long defvalue) 1455 throws IOException, IllegalArgumentException 1456 { 1457 ObjectStreamField field = getField(name, Long.TYPE); 1458 1459 if (field == null) 1460 return defvalue; 1461 1462 int off = field.getOffset(); 1463 1464 return (long)(((prim_field_data[off++] & 0xFFL) << 56) 1465 | ((prim_field_data[off++] & 0xFFL) << 48) 1466 | ((prim_field_data[off++] & 0xFFL) << 40) 1467 | ((prim_field_data[off++] & 0xFFL) << 32) 1468 | ((prim_field_data[off++] & 0xFF) << 24) 1469 | ((prim_field_data[off++] & 0xFF) << 16) 1470 | ((prim_field_data[off++] & 0xFF) << 8) 1471 | (prim_field_data[off] & 0xFF)); 1472 } 1473 1474 public float get(String name, float defvalue) 1475 throws IOException, IllegalArgumentException 1476 { 1477 ObjectStreamField field = getField(name, Float.TYPE); 1478 1479 if (field == null) 1480 return defvalue; 1481 1482 int off = field.getOffset(); 1483 1484 return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) 1485 | ((prim_field_data[off++] & 0xFF) << 16) 1486 | ((prim_field_data[off++] & 0xFF) << 8) 1487 | (prim_field_data[off] & 0xFF)); 1488 } 1489 1490 public double get(String name, double defvalue) 1491 throws IOException, IllegalArgumentException 1492 { 1493 ObjectStreamField field = getField(name, Double.TYPE); 1494 1495 if (field == null) 1496 return defvalue; 1497 1498 int off = field.getOffset(); 1499 1500 return Double.longBitsToDouble 1501 ( (long) (((prim_field_data[off++] & 0xFFL) << 56) 1502 | ((prim_field_data[off++] & 0xFFL) << 48) 1503 | ((prim_field_data[off++] & 0xFFL) << 40) 1504 | ((prim_field_data[off++] & 0xFFL) << 32) 1505 | ((prim_field_data[off++] & 0xFF) << 24) 1506 | ((prim_field_data[off++] & 0xFF) << 16) 1507 | ((prim_field_data[off++] & 0xFF) << 8) 1508 | (prim_field_data[off] & 0xFF))); 1509 } 1510 1511 public Object get(String name, Object defvalue) 1512 throws IOException, IllegalArgumentException 1513 { 1514 ObjectStreamField field = 1515 getField(name, defvalue == null ? null : defvalue.getClass ()); 1516 1517 if (field == null) 1518 return defvalue; 1519 1520 return objs[field.getOffset()]; 1521 } 1522 1523 private ObjectStreamField getField(String name, Class type) 1524 throws IllegalArgumentException 1525 { 1526 ObjectStreamField field = clazz.getField(name); 1527 boolean illegal = false; 1528 1529 // XXX This code is horrible and needs to be rewritten! 1530 try 1531 { 1532 try 1533 { 1534 Class field_type = field.getType(); 1535 1536 if (type == field_type || 1537 (type == null && !field_type.isPrimitive())) 1538 { 1539 /* See defaulted */ 1540 return field; 1541 } 1542 1543 illegal = true; 1544 throw new IllegalArgumentException 1545 ("Field requested is of type " 1546 + field_type.getName() 1547 + ", but requested type was " 1548 + (type == null ? "Object" : type.getName())); 1549 } 1550 catch (NullPointerException _) 1551 { 1552 /* Here we catch NullPointerException, because it may 1553 only come from the call 'field.getType()'. If field 1554 is null, we have to return null and classpath ethic 1555 say we must try to avoid 'if (xxx == null)'. 1556 */ 1557 } 1558 catch (IllegalArgumentException e) 1559 { 1560 throw e; 1561 } 1562 1563 return null; 1564 } 1565 finally 1566 { 1567 /* If this is an unassigned field we should return 1568 * the default value. 1569 */ 1570 if (!illegal && field != null && !field.isToSet() && field.isPersistent()) 1571 return null; 1572 1573 /* We do not want to modify transient fields. They should 1574 * be left to 0. 1575 */ 1576 try 1577 { 1578 Field f = clazz.forClass().getDeclaredField(name); 1579 if (Modifier.isTransient(f.getModifiers())) 1580 throw new IllegalArgumentException 1581 ("no such field (non transient) " + name); 1582 if (field == null && f.getType() != type) 1583 throw new IllegalArgumentException 1584 ("Invalid requested type for field " + name); 1585 } 1586 catch (NoSuchFieldException e) 1587 { 1588 if (field == null) 1589 throw new IllegalArgumentException(e); 1590 } 1591 1592 } 1593 } 1594 }; 1595 1596 fieldsAlreadyRead = true; 1597 return prereadFields; 1598 } 1599 1600 /** 1601 * Protected constructor that allows subclasses to override 1602 * deserialization. This constructor should be called by subclasses 1603 * that wish to override <code>readObject (Object)</code>. This 1604 * method does a security check <i>NOTE: currently not 1605 * implemented</i>, then sets a flag that informs 1606 * <code>readObject (Object)</code> to call the subclasses 1607 * <code>readObjectOverride (Object)</code> method. 1608 * 1609 * @see #readObjectOverride() 1610 */ 1611 protected ObjectInputStream() 1612 throws IOException, SecurityException 1613 { 1614 SecurityManager sec_man = System.getSecurityManager(); 1615 if (sec_man != null) 1616 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 1617 this.useSubclassMethod = true; 1618 } 1619 1620 /** 1621 * This method allows subclasses to override the default 1622 * de serialization mechanism provided by 1623 * <code>ObjectInputStream</code>. To make this method be used for 1624 * writing objects, subclasses must invoke the 0-argument 1625 * constructor on this class from their constructor. 1626 * 1627 * @see #ObjectInputStream() 1628 */ 1629 protected Object readObjectOverride() 1630 throws ClassNotFoundException, IOException, OptionalDataException 1631 { 1632 throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); 1633 } 1634 1635 /** 1636 * Assigns the next available handle to <code>obj</code>. 1637 * 1638 * @param obj The object for which we want a new handle. 1639 * @param shared True if the handle should be shared 1640 * with later calls. 1641 * @return A valid handle for the specified object. 1642 */ 1643 private int assignNewHandle(Object obj, boolean shared) 1644 { 1645 int handle = this.nextOID; 1646 this.nextOID = handle + 1; 1647 rememberHandle(obj,shared,handle); 1648 return handle; 1649 } 1650 1651 /** 1652 * Remember the object associated with the given handle. 1653 * 1654 * @param obj an object 1655 * @param shared true if the reference should be shared 1656 * with later calls. 1657 * @param handle a handle, must be >= baseWireHandle 1658 * 1659 * @see #lookupHandle 1660 */ 1661 private void rememberHandle(Object obj, boolean shared, 1662 int handle) 1663 { 1664 handles.put(handle, new Pair<Boolean,Object>(shared, obj)); 1665 } 1666 1667 /** 1668 * Look up the object associated with a given handle. 1669 * 1670 * @param handle a handle, must be >= baseWireHandle 1671 * @return the object remembered for handle or null if none. 1672 * @throws StreamCorruptedException if the handle is invalid. 1673 * @throws InvalidObjectException if the reference is not shared. 1674 * @see #rememberHandle 1675 */ 1676 private Object lookupHandle(int handle) 1677 throws ObjectStreamException 1678 { 1679 Pair<Boolean,Object> result = handles.get(handle); 1680 if (result == null) 1681 throw new StreamCorruptedException("The handle, " + 1682 Integer.toHexString(handle) + 1683 ", is invalid."); 1684 if (!result.getLeft()) 1685 throw new InvalidObjectException("The handle, " + 1686 Integer.toHexString(handle) + 1687 ", is not shared."); 1688 return result.getRight(); 1689 } 1690 1691 private Object processResolution(ObjectStreamClass osc, Object obj, int handle, 1692 boolean shared) 1693 throws IOException 1694 { 1695 if (osc != null && obj instanceof Serializable) 1696 { 1697 try 1698 { 1699 Method m = osc.readResolveMethod; 1700 if(m != null) 1701 { 1702 obj = m.invoke(obj, new Object[] {}); 1703 } 1704 } 1705 catch (IllegalAccessException ignore) 1706 { 1707 } 1708 catch (InvocationTargetException exception) 1709 { 1710 Throwable cause = exception.getCause(); 1711 if (cause instanceof ObjectStreamException) 1712 throw (ObjectStreamException) cause; 1713 else if (cause instanceof RuntimeException) 1714 throw (RuntimeException) cause; 1715 else if (cause instanceof Error) 1716 throw (Error) cause; 1717 } 1718 } 1719 1720 if (this.resolveEnabled) 1721 obj = resolveObject(obj); 1722 1723 rememberHandle(obj, shared, handle); 1724 if (!shared) 1725 { 1726 if (obj instanceof byte[]) 1727 return ((byte[]) obj).clone(); 1728 if (obj instanceof short[]) 1729 return ((short[]) obj).clone(); 1730 if (obj instanceof int[]) 1731 return ((int[]) obj).clone(); 1732 if (obj instanceof long[]) 1733 return ((long[]) obj).clone(); 1734 if (obj instanceof char[]) 1735 return ((char[]) obj).clone(); 1736 if (obj instanceof boolean[]) 1737 return ((boolean[]) obj).clone(); 1738 if (obj instanceof float[]) 1739 return ((float[]) obj).clone(); 1740 if (obj instanceof double[]) 1741 return ((double[]) obj).clone(); 1742 if (obj instanceof Object[]) 1743 return ((Object[]) obj).clone(); 1744 } 1745 return obj; 1746 } 1747 1748 private void clearHandles() 1749 { 1750 handles.clear(); 1751 this.nextOID = baseWireHandle; 1752 } 1753 1754 private void readNextBlock() throws IOException 1755 { 1756 byte marker = this.realInputStream.readByte(); 1757 while (marker == TC_RESET) 1758 { 1759 if(dump) dumpElementln("RESET"); 1760 clearHandles(); 1761 marker = this.realInputStream.readByte(); 1762 } 1763 readNextBlock(marker); 1764 } 1765 1766 private void readNextBlock(byte marker) throws IOException 1767 { 1768 if (marker == TC_BLOCKDATA) 1769 { 1770 if(dump) dumpElement("BLOCK DATA SIZE="); 1771 this.blockDataBytes = this.realInputStream.readUnsignedByte(); 1772 if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); 1773 } 1774 else if (marker == TC_BLOCKDATALONG) 1775 { 1776 if(dump) dumpElement("BLOCK DATA LONG SIZE="); 1777 this.blockDataBytes = this.realInputStream.readInt(); 1778 if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); 1779 } 1780 else 1781 { 1782 throw new EOFException("Attempt to read primitive data, but no data block is active."); 1783 } 1784 1785 if (this.blockData.length < this.blockDataBytes) 1786 this.blockData = new byte[this.blockDataBytes]; 1787 1788 this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); 1789 this.blockDataPosition = 0; 1790 } 1791 1792 private void readArrayElements (Object array, Class clazz) 1793 throws ClassNotFoundException, IOException 1794 { 1795 if (clazz.isPrimitive()) 1796 { 1797 if (clazz == Boolean.TYPE) 1798 { 1799 boolean[] cast_array = (boolean[])array; 1800 for (int i=0; i < cast_array.length; i++) 1801 cast_array[i] = this.realInputStream.readBoolean(); 1802 return; 1803 } 1804 if (clazz == Byte.TYPE) 1805 { 1806 byte[] cast_array = (byte[])array; 1807 for (int i=0; i < cast_array.length; i++) 1808 cast_array[i] = this.realInputStream.readByte(); 1809 return; 1810 } 1811 if (clazz == Character.TYPE) 1812 { 1813 char[] cast_array = (char[])array; 1814 for (int i=0; i < cast_array.length; i++) 1815 cast_array[i] = this.realInputStream.readChar(); 1816 return; 1817 } 1818 if (clazz == Double.TYPE) 1819 { 1820 double[] cast_array = (double[])array; 1821 for (int i=0; i < cast_array.length; i++) 1822 cast_array[i] = this.realInputStream.readDouble(); 1823 return; 1824 } 1825 if (clazz == Float.TYPE) 1826 { 1827 float[] cast_array = (float[])array; 1828 for (int i=0; i < cast_array.length; i++) 1829 cast_array[i] = this.realInputStream.readFloat(); 1830 return; 1831 } 1832 if (clazz == Integer.TYPE) 1833 { 1834 int[] cast_array = (int[])array; 1835 for (int i=0; i < cast_array.length; i++) 1836 cast_array[i] = this.realInputStream.readInt(); 1837 return; 1838 } 1839 if (clazz == Long.TYPE) 1840 { 1841 long[] cast_array = (long[])array; 1842 for (int i=0; i < cast_array.length; i++) 1843 cast_array[i] = this.realInputStream.readLong(); 1844 return; 1845 } 1846 if (clazz == Short.TYPE) 1847 { 1848 short[] cast_array = (short[])array; 1849 for (int i=0; i < cast_array.length; i++) 1850 cast_array[i] = this.realInputStream.readShort(); 1851 return; 1852 } 1853 } 1854 else 1855 { 1856 Object[] cast_array = (Object[])array; 1857 for (int i=0; i < cast_array.length; i++) 1858 cast_array[i] = readObject(); 1859 } 1860 } 1861 1862 private void readFields (Object obj, ObjectStreamClass stream_osc) 1863 throws ClassNotFoundException, IOException 1864 { 1865 ObjectStreamField[] fields = stream_osc.fieldMapping; 1866 1867 for (int i = 0; i < fields.length; i += 2) 1868 { 1869 ObjectStreamField stream_field = fields[i]; 1870 ObjectStreamField real_field = fields[i + 1]; 1871 boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); 1872 boolean set_value = (real_field != null && real_field.isToSet()); 1873 String field_name; 1874 char type; 1875 1876 if (stream_field != null) 1877 { 1878 field_name = stream_field.getName(); 1879 type = stream_field.getTypeCode(); 1880 } 1881 else 1882 { 1883 field_name = real_field.getName(); 1884 type = real_field.getTypeCode(); 1885 } 1886 1887 switch(type) 1888 { 1889 case 'Z': 1890 { 1891 boolean value = 1892 read_value ? this.realInputStream.readBoolean() : false; 1893 if (dump && read_value && set_value) 1894 dumpElementln(" " + field_name + ": " + value); 1895 if (set_value) 1896 real_field.setBooleanField(obj, value); 1897 break; 1898 } 1899 case 'B': 1900 { 1901 byte value = 1902 read_value ? this.realInputStream.readByte() : 0; 1903 if (dump && read_value && set_value) 1904 dumpElementln(" " + field_name + ": " + value); 1905 if (set_value) 1906 real_field.setByteField(obj, value); 1907 break; 1908 } 1909 case 'C': 1910 { 1911 char value = 1912 read_value ? this.realInputStream.readChar(): 0; 1913 if (dump && read_value && set_value) 1914 dumpElementln(" " + field_name + ": " + value); 1915 if (set_value) 1916 real_field.setCharField(obj, value); 1917 break; 1918 } 1919 case 'D': 1920 { 1921 double value = 1922 read_value ? this.realInputStream.readDouble() : 0; 1923 if (dump && read_value && set_value) 1924 dumpElementln(" " + field_name + ": " + value); 1925 if (set_value) 1926 real_field.setDoubleField(obj, value); 1927 break; 1928 } 1929 case 'F': 1930 { 1931 float value = 1932 read_value ? this.realInputStream.readFloat() : 0; 1933 if (dump && read_value && set_value) 1934 dumpElementln(" " + field_name + ": " + value); 1935 if (set_value) 1936 real_field.setFloatField(obj, value); 1937 break; 1938 } 1939 case 'I': 1940 { 1941 int value = 1942 read_value ? this.realInputStream.readInt() : 0; 1943 if (dump && read_value && set_value) 1944 dumpElementln(" " + field_name + ": " + value); 1945 if (set_value) 1946 real_field.setIntField(obj, value); 1947 break; 1948 } 1949 case 'J': 1950 { 1951 long value = 1952 read_value ? this.realInputStream.readLong() : 0; 1953 if (dump && read_value && set_value) 1954 dumpElementln(" " + field_name + ": " + value); 1955 if (set_value) 1956 real_field.setLongField(obj, value); 1957 break; 1958 } 1959 case 'S': 1960 { 1961 short value = 1962 read_value ? this.realInputStream.readShort() : 0; 1963 if (dump && read_value && set_value) 1964 dumpElementln(" " + field_name + ": " + value); 1965 if (set_value) 1966 real_field.setShortField(obj, value); 1967 break; 1968 } 1969 case 'L': 1970 case '[': 1971 { 1972 Object value = 1973 read_value ? readObject() : null; 1974 if (set_value) 1975 real_field.setObjectField(obj, value); 1976 break; 1977 } 1978 default: 1979 throw new InternalError("Invalid type code: " + type); 1980 } 1981 } 1982 } 1983 1984 // Toggles writing primitive data to block-data buffer. 1985 private boolean setBlockDataMode (boolean on) 1986 { 1987 boolean oldmode = this.readDataFromBlock; 1988 this.readDataFromBlock = on; 1989 1990 if (on) 1991 this.dataInputStream = this.blockDataInput; 1992 else 1993 this.dataInputStream = this.realInputStream; 1994 return oldmode; 1995 } 1996 1997 // returns a new instance of REAL_CLASS that has been constructed 1998 // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) 1999 private Object newObject (Class real_class, Constructor constructor) 2000 throws ClassNotFoundException, IOException 2001 { 2002 if (constructor == null) 2003 throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); 2004 try 2005 { 2006 return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); 2007 } 2008 catch (InstantiationException e) 2009 { 2010 throw (ClassNotFoundException) new ClassNotFoundException 2011 ("Instance of " + real_class + " could not be created").initCause(e); 2012 } 2013 } 2014 2015 // runs all registered ObjectInputValidations in prioritized order 2016 // on OBJ 2017 private void invokeValidators() throws InvalidObjectException 2018 { 2019 try 2020 { 2021 Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator(); 2022 while(it.hasNext()) 2023 { 2024 ValidatorAndPriority vap = it.next(); 2025 ObjectInputValidation validator = vap.validator; 2026 validator.validateObject(); 2027 } 2028 } 2029 finally 2030 { 2031 currentObjectValidators = null; 2032 } 2033 } 2034 2035 private void callReadMethod (Method readObject, Class klass, Object obj) 2036 throws ClassNotFoundException, IOException 2037 { 2038 try 2039 { 2040 readObject.invoke(obj, new Object[] { this }); 2041 } 2042 catch (InvocationTargetException x) 2043 { 2044 /* Rethrow if possible. */ 2045 Throwable exception = x.getTargetException(); 2046 if (exception instanceof RuntimeException) 2047 throw (RuntimeException) exception; 2048 if (exception instanceof IOException) 2049 throw (IOException) exception; 2050 if (exception instanceof ClassNotFoundException) 2051 throw (ClassNotFoundException) exception; 2052 2053 throw (IOException) new IOException( 2054 "Exception thrown from readObject() on " + klass).initCause(x); 2055 } 2056 catch (Exception x) 2057 { 2058 throw (IOException) new IOException( 2059 "Failure invoking readObject() on " + klass).initCause(x); 2060 } 2061 2062 // Invalidate fields which has been read through readFields. 2063 prereadFields = null; 2064 } 2065 2066 private static final int BUFFER_SIZE = 1024; 2067 2068 private DataInputStream realInputStream; 2069 private DataInputStream dataInputStream; 2070 private DataInputStream blockDataInput; 2071 private int blockDataPosition; 2072 private int blockDataBytes; 2073 private byte[] blockData; 2074 private boolean useSubclassMethod; 2075 private int nextOID; 2076 private boolean resolveEnabled; 2077 private Map<Integer,Pair<Boolean,Object>> handles; 2078 private Object currentObject; 2079 private ObjectStreamClass currentObjectStreamClass; 2080 private TreeSet<ValidatorAndPriority> currentObjectValidators; 2081 private boolean readDataFromBlock; 2082 private boolean fieldsAlreadyRead; 2083 private Hashtable<Class,ObjectStreamClass> classLookupTable; 2084 private GetField prereadFields; 2085 2086 private static boolean dump; 2087 2088 // The nesting depth for debugging output 2089 private int depth = 0; 2090 2091 private static final boolean DEBUG = false; 2092 2093 private void dumpElement (String msg) 2094 { 2095 System.out.print(msg); 2096 } 2097 2098 private void dumpElementln (String msg) 2099 { 2100 System.out.println(msg); 2101 for (int i = 0; i < depth; i++) 2102 System.out.print (" "); 2103 System.out.print (Thread.currentThread() + ": "); 2104 } 2105 2106 private void dumpElementln (String msg, Object obj) 2107 { 2108 try 2109 { 2110 System.out.print(msg); 2111 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) 2112 System.out.println(obj.getClass()); 2113 else 2114 System.out.println(obj); 2115 } 2116 catch (Exception _) 2117 { 2118 } 2119 for (int i = 0; i < depth; i++) 2120 System.out.print (" "); 2121 System.out.print (Thread.currentThread() + ": "); 2122 } 2123 2124 // used to keep a prioritized list of object validators 2125 private static final class ValidatorAndPriority implements Comparable 2126 { 2127 int priority; 2128 ObjectInputValidation validator; 2129 2130 ValidatorAndPriority (ObjectInputValidation validator, int priority) 2131 { 2132 this.priority = priority; 2133 this.validator = validator; 2134 } 2135 2136 public int compareTo (Object o) 2137 { 2138 ValidatorAndPriority vap = (ValidatorAndPriority)o; 2139 return this.priority - vap.priority; 2140 } 2141 } 2142}