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