1 /* 2 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 /* 26 * Licensed Materials - Property of IBM 27 * RMI-IIOP v1.0 28 * Copyright IBM Corp. 1998 1999 All Rights Reserved 29 * 30 */ 31 32 package com.sun.corba.se.impl.orbutil; 33 34 import java.security.MessageDigest; 35 import java.security.NoSuchAlgorithmException; 36 import java.security.DigestOutputStream; 37 import java.security.AccessController; 38 import java.security.PrivilegedExceptionAction; 39 import java.security.PrivilegedActionException; 40 import java.security.PrivilegedAction; 41 42 import java.lang.reflect.Modifier; 43 import java.lang.reflect.Array; 44 import java.lang.reflect.Field; 45 import java.lang.reflect.Member; 46 import java.lang.reflect.Method; 47 import java.lang.reflect.Constructor; 48 import java.lang.reflect.Proxy; 49 import java.lang.reflect.InvocationTargetException; 50 51 import java.io.IOException; 52 import java.io.DataOutputStream; 53 import java.io.ByteArrayOutputStream; 54 import java.io.InvalidClassException; 55 import java.io.Serializable; 56 57 import java.util.Arrays; 58 import java.util.Comparator; 59 import java.util.Hashtable; 60 61 import org.omg.CORBA.ValueMember; 62 63 import com.sun.corba.se.impl.io.ValueUtility; 64 import com.sun.corba.se.impl.io.ObjectStreamClass; 65 66 /** 67 * This is duplicated here to preserve the JDK 1.3.1FCS behavior 68 * of calculating the OMG hash code incorrectly when serialPersistentFields 69 * is used, but some of the fields no longer exist in the class itself. 70 * 71 * We have to duplicate it since we aren't allowed to modify the 72 * com.sun.corba.se.impl.io version further, and can't make it 73 * public outside of its package for security reasons. 74 */ 75 /** 76 * A ObjectStreamClass_1_3_1 describes a class that can be serialized to a stream 77 * or a class that was serialized to a stream. It contains the name 78 * and the serialVersionUID of the class. 79 * <br> 80 * The ObjectStreamClass_1_3_1 for a specific class loaded in this Java VM can 81 * be found using the lookup method. 82 * 83 * @author Roger Riggs 84 * @since JDK1.1 85 */ 86 public class ObjectStreamClass_1_3_1 implements java.io.Serializable { 87 88 public static final long kDefaultUID = -1; 89 90 private static Object noArgsList[] = {}; 91 private static Class noTypesList[] = {}; 92 93 private static Hashtable translatedFields; 94 95 /** Find the descriptor for a class that can be serialized. Null 96 * is returned if the specified class does not implement 97 * java.io.Serializable or java.io.Externalizable. 98 */ 99 static final ObjectStreamClass_1_3_1 lookup(Class cl) 100 { 101 ObjectStreamClass_1_3_1 desc = lookupInternal(cl); 102 if (desc.isSerializable() || desc.isExternalizable()) 103 return desc; 104 return null; 105 } 106 107 /* 108 * Find the class descriptor for the specified class. 109 * Package access only so it can be called from ObjectIn/OutStream. 110 */ 111 static ObjectStreamClass_1_3_1 lookupInternal(Class cl) 112 { 113 /* Synchronize on the hashtable so no two threads will do 114 * this at the same time. 115 */ 116 ObjectStreamClass_1_3_1 desc = null; 117 synchronized (descriptorFor) { 118 /* Find the matching descriptor if it already known */ 119 desc = findDescriptorFor(cl); 120 if (desc != null) { 121 return desc; 122 } 123 124 /* Check if it's serializable */ 125 boolean serializable = classSerializable.isAssignableFrom(cl); 126 /* If the class is only Serializable, 127 * lookup the descriptor for the superclass. 128 */ 129 ObjectStreamClass_1_3_1 superdesc = null; 130 if (serializable) { 131 Class superclass = cl.getSuperclass(); 132 if (superclass != null) 133 superdesc = lookup(superclass); 134 } 135 136 /* Check if its' externalizable. 137 * If it's Externalizable, clear the serializable flag. 138 * Only one or the other may be set in the protocol. 139 */ 140 boolean externalizable = false; 141 if (serializable) { 142 externalizable = 143 ((superdesc != null) && superdesc.isExternalizable()) || 144 classExternalizable.isAssignableFrom(cl); 145 if (externalizable) { 146 serializable = false; 147 } 148 } 149 150 /* Create a new version descriptor, 151 * it put itself in the known table. 152 */ 153 desc = new ObjectStreamClass_1_3_1(cl, superdesc, 154 serializable, externalizable); 155 } 156 desc.init(); 157 return desc; 158 } 159 160 /** 161 * The name of the class described by this descriptor. 162 */ 163 public final String getName() { 164 return name; 165 } 166 167 /** 168 * Return the serialVersionUID for this class. 169 * The serialVersionUID defines a set of classes all with the same name 170 * that have evolved from a common root class and agree to be serialized 171 * and deserialized using a common format. 172 */ 173 public static final long getSerialVersionUID( java.lang.Class clazz) { 174 ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz ); 175 if( theosc != null ) 176 { 177 return theosc.getSerialVersionUID( ); 178 } 179 return 0; 180 } 181 182 /** 183 * Return the serialVersionUID for this class. 184 * The serialVersionUID defines a set of classes all with the same name 185 * that have evolved from a common root class and agree to be serialized 186 * and deserialized using a common format. 187 */ 188 public final long getSerialVersionUID() { 189 return suid; 190 } 191 192 /** 193 * Return the serialVersionUID string for this class. 194 * The serialVersionUID defines a set of classes all with the same name 195 * that have evolved from a common root class and agree to be serialized 196 * and deserialized using a common format. 197 */ 198 public final String getSerialVersionUIDStr() { 199 if (suidStr == null) 200 suidStr = Long.toHexString(suid).toUpperCase(); 201 return suidStr; 202 } 203 204 /** 205 * Return the actual (computed) serialVersionUID for this class. 206 */ 207 public static final long getActualSerialVersionUID( java.lang.Class clazz ) 208 { 209 ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz ); 210 if( theosc != null ) 211 { 212 return theosc.getActualSerialVersionUID( ); 213 } 214 return 0; 215 } 216 217 /** 218 * Return the actual (computed) serialVersionUID for this class. 219 */ 220 public final long getActualSerialVersionUID() { 221 return actualSuid; 222 } 223 224 /** 225 * Return the actual (computed) serialVersionUID for this class. 226 */ 227 public final String getActualSerialVersionUIDStr() { 228 if (actualSuidStr == null) 229 actualSuidStr = Long.toHexString(actualSuid).toUpperCase(); 230 return actualSuidStr; 231 } 232 233 /** 234 * Return the class in the local VM that this version is mapped to. 235 * Null is returned if there is no corresponding local class. 236 */ 237 public final Class forClass() { 238 return ofClass; 239 } 240 241 /** 242 * Return an array of the fields of this serializable class. 243 * @return an array containing an element for each persistent 244 * field of this class. Returns an array of length zero if 245 * there are no fields. 246 * @since JDK1.2 247 */ 248 public ObjectStreamField[] getFields() { 249 // Return a copy so the caller can't change the fields. 250 if (fields.length > 0) { 251 ObjectStreamField[] dup = new ObjectStreamField[fields.length]; 252 System.arraycopy(fields, 0, dup, 0, fields.length); 253 return dup; 254 } else { 255 return fields; 256 } 257 } 258 259 public boolean hasField(ValueMember field){ 260 261 for (int i = 0; i < fields.length; i++){ 262 try{ 263 if (fields[i].getName().equals(field.name)) { 264 265 if (fields[i].getSignature().equals(ValueUtility.getSignature(field))) 266 return true; 267 } 268 } 269 catch(Throwable t){} 270 } 271 return false; 272 } 273 274 /* Avoid unnecessary allocations. */ 275 final ObjectStreamField[] getFieldsNoCopy() { 276 return fields; 277 } 278 279 /** 280 * Get the field of this class by name. 281 * @return The ObjectStreamField object of the named field or null if there 282 * is no such named field. 283 */ 284 public final ObjectStreamField getField(String name) { 285 /* Binary search of fields by name. 286 */ 287 for (int i = fields.length-1; i >= 0; i--) { 288 if (name.equals(fields[i].getName())) { 289 return fields[i]; 290 } 291 } 292 return null; 293 } 294 295 public Serializable writeReplace(Serializable value) { 296 if (writeReplaceObjectMethod != null) { 297 try { 298 return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList); 299 } 300 catch(Throwable t) { 301 throw new RuntimeException(t.getMessage()); 302 } 303 } 304 else return value; 305 } 306 307 public Object readResolve(Object value) { 308 if (readResolveObjectMethod != null) { 309 try { 310 return readResolveObjectMethod.invoke(value,noArgsList); 311 } 312 catch(Throwable t) { 313 throw new RuntimeException(t.getMessage()); 314 } 315 } 316 else return value; 317 } 318 319 /** 320 * Return a string describing this ObjectStreamClass_1_3_1. 321 */ 322 public final String toString() { 323 StringBuffer sb = new StringBuffer(); 324 325 sb.append(name); 326 sb.append(": static final long serialVersionUID = "); 327 sb.append(Long.toString(suid)); 328 sb.append("L;"); 329 return sb.toString(); 330 } 331 332 /* 333 * Create a new ObjectStreamClass_1_3_1 from a loaded class. 334 * Don't call this directly, call lookup instead. 335 */ 336 private ObjectStreamClass_1_3_1(java.lang.Class cl, ObjectStreamClass_1_3_1 superdesc, 337 boolean serial, boolean extern) 338 { 339 ofClass = cl; /* created from this class */ 340 341 if (Proxy.isProxyClass(cl)) { 342 forProxyClass = true; 343 } 344 345 name = cl.getName(); 346 superclass = superdesc; 347 serializable = serial; 348 if (!forProxyClass) { 349 // proxy classes are never externalizable 350 externalizable = extern; 351 } 352 353 /* 354 * Enter this class in the table of known descriptors. 355 * Otherwise, when the fields are read it may recurse 356 * trying to find the descriptor for itself. 357 */ 358 insertDescriptorFor(this); 359 360 /* 361 * The remainder of initialization occurs in init(), which is called 362 * after the lock on the global class descriptor table has been 363 * released. 364 */ 365 } 366 367 /* 368 * Initialize class descriptor. This method is only invoked on class 369 * descriptors created via calls to lookupInternal(). This method is kept 370 * separate from the ObjectStreamClass_1_3_1 constructor so that lookupInternal 371 * does not have to hold onto a global class descriptor table lock while the 372 * class descriptor is being initialized (see bug 4165204). 373 */ 374 375 376 private void init() { 377 synchronized (lock) { 378 379 final Class cl = ofClass; 380 381 if (fields != null) // already initialized 382 return; 383 384 385 if (!serializable || 386 externalizable || 387 forProxyClass || 388 name.equals("java.lang.String")) { 389 fields = NO_FIELDS; 390 } else if (serializable) { 391 392 /* Ask for permission to override field access checks. 393 */ 394 AccessController.doPrivileged(new PrivilegedAction() { 395 public Object run() { 396 /* Fill in the list of persistent fields. 397 * If it is declared, use the declared serialPersistentFields. 398 * Otherwise, extract the fields from the class itself. 399 */ 400 try { 401 Field pf = cl.getDeclaredField("serialPersistentFields"); 402 // serial bug 7; the serialPersistentFields were not 403 // being read and stored as Accessible bit was not set 404 pf.setAccessible(true); 405 // serial bug 7; need to find if the field is of type 406 // java.io.ObjectStreamField 407 java.io.ObjectStreamField[] f = 408 (java.io.ObjectStreamField[])pf.get(cl); 409 int mods = pf.getModifiers(); 410 if ((Modifier.isPrivate(mods)) && 411 (Modifier.isStatic(mods)) && 412 (Modifier.isFinal(mods))) 413 { 414 fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl)); 415 } 416 } catch (NoSuchFieldException e) { 417 fields = null; 418 } catch (IllegalAccessException e) { 419 fields = null; 420 } catch (IllegalArgumentException e) { 421 fields = null; 422 } catch (ClassCastException e) { 423 /* Thrown if a field serialPersistentField exists 424 * but it is not of type ObjectStreamField. 425 */ 426 fields = null; 427 } 428 429 430 if (fields == null) { 431 /* Get all of the declared fields for this 432 * Class. setAccessible on all fields so they 433 * can be accessed later. Create a temporary 434 * ObjectStreamField array to hold each 435 * non-static, non-transient field. Then copy the 436 * temporary array into an array of the correct 437 * size once the number of fields is known. 438 */ 439 Field[] actualfields = cl.getDeclaredFields(); 440 441 int numFields = 0; 442 ObjectStreamField[] tempFields = 443 new ObjectStreamField[actualfields.length]; 444 for (int i = 0; i < actualfields.length; i++) { 445 int modifiers = actualfields[i].getModifiers(); 446 if (!Modifier.isStatic(modifiers) && 447 !Modifier.isTransient(modifiers)) { 448 tempFields[numFields++] = 449 new ObjectStreamField(actualfields[i]); 450 } 451 } 452 fields = new ObjectStreamField[numFields]; 453 System.arraycopy(tempFields, 0, fields, 0, numFields); 454 455 } else { 456 // For each declared persistent field, look for an actual 457 // reflected Field. If there is one, make sure it's the correct 458 // type and cache it in the ObjectStreamClass_1_3_1 for that field. 459 for (int j = fields.length-1; j >= 0; j--) { 460 try { 461 Field reflField = cl.getDeclaredField(fields[j].getName()); 462 if (fields[j].getType() == reflField.getType()) { 463 // reflField.setAccessible(true); 464 fields[j].setField(reflField); 465 } 466 } catch (NoSuchFieldException e) { 467 // Nothing to do 468 } 469 } 470 } 471 return null; 472 } 473 }); 474 475 if (fields.length > 1) 476 Arrays.sort(fields); 477 478 /* Set up field data for use while writing using the API api. */ 479 computeFieldInfo(); 480 } 481 482 /* Get the serialVersionUID from the class. 483 * It uses the access override mechanism so make sure 484 * the field objects is only used here. 485 * 486 * NonSerializable classes have a serialVerisonUID of 0L. 487 */ 488 if (isNonSerializable()) { 489 suid = 0L; 490 } else { 491 // Lookup special Serializable members using reflection. 492 AccessController.doPrivileged(new PrivilegedAction() { 493 public Object run() { 494 if (forProxyClass) { 495 // proxy classes always have serialVersionUID of 0L 496 suid = 0L; 497 } else { 498 try { 499 final Field f = cl.getDeclaredField("serialVersionUID"); 500 int mods = f.getModifiers(); 501 // SerialBug 5: static final SUID should be read 502 if (Modifier.isStatic(mods) && 503 Modifier.isFinal(mods) ) { 504 f.setAccessible(true); 505 suid = f.getLong(cl); 506 // get rid of native code 507 // suid = getSerialVersionUIDField(cl); 508 // SerialBug 2: should be computed after writeObject 509 // actualSuid = computeStructuralUID(cl); 510 } else { 511 suid = ObjectStreamClass.getSerialVersionUID(cl); 512 // SerialBug 2: should be computed after writeObject 513 // actualSuid = computeStructuralUID(cl); 514 } 515 } catch (NoSuchFieldException ex) { 516 suid = ObjectStreamClass.getSerialVersionUID(cl); 517 // SerialBug 2: should be computed after writeObject 518 // actualSuid = computeStructuralUID(cl); 519 } catch (IllegalAccessException ex) { 520 suid = ObjectStreamClass.getSerialVersionUID(cl); 521 } 522 } 523 524 525 try { 526 writeReplaceObjectMethod = cl.getDeclaredMethod("writeReplace", noTypesList); 527 if (Modifier.isStatic(writeReplaceObjectMethod.getModifiers())) { 528 writeReplaceObjectMethod = null; 529 } else { 530 writeReplaceObjectMethod.setAccessible(true); 531 } 532 533 } catch (NoSuchMethodException e2) { 534 535 } 536 537 try { 538 readResolveObjectMethod = cl.getDeclaredMethod("readResolve", noTypesList); 539 if (Modifier.isStatic(readResolveObjectMethod.getModifiers())) { 540 readResolveObjectMethod = null; 541 } else { 542 readResolveObjectMethod.setAccessible(true); 543 } 544 545 } catch (NoSuchMethodException e2) { 546 547 } 548 549 /* Cache lookup of writeObject and readObject for 550 * Serializable classes. (Do not lookup for 551 * Externalizable) 552 */ 553 554 if (serializable && !forProxyClass) { 555 556 /* Look for the writeObject method 557 * Set the accessible flag on it here. ObjectOutputStream 558 * will call it as necessary. 559 */ 560 try { 561 Class[] args = {java.io.ObjectOutputStream.class}; 562 writeObjectMethod = cl.getDeclaredMethod("writeObject", args); 563 hasWriteObjectMethod = true; 564 int mods = writeObjectMethod.getModifiers(); 565 566 // Method must be private and non-static 567 if (!Modifier.isPrivate(mods) || 568 Modifier.isStatic(mods)) { 569 writeObjectMethod = null; 570 hasWriteObjectMethod = false; 571 } 572 573 } catch (NoSuchMethodException e) { 574 } 575 576 /* Look for the readObject method 577 * set the access override and save the reference for 578 * ObjectInputStream so it can all the method directly. 579 */ 580 try { 581 Class[] args = {java.io.ObjectInputStream.class}; 582 readObjectMethod = cl.getDeclaredMethod("readObject", args); 583 int mods = readObjectMethod.getModifiers(); 584 585 // Method must be private and non-static 586 if (!Modifier.isPrivate(mods) || 587 Modifier.isStatic(mods)) { 588 readObjectMethod = null; 589 } 590 } catch (NoSuchMethodException e) { 591 } 592 // Compute the structural UID. This must be done after the 593 // calculation for writeObject. Fixed 4/20/2000, eea1 594 // SerialBug 2: to have correct value in RepId 595 } 596 return null; 597 } 598 }); 599 } 600 601 actualSuid = computeStructuralUID(this, cl); 602 } 603 604 } 605 606 /* 607 * Create an empty ObjectStreamClass_1_3_1 for a class about to be read. 608 * This is separate from read so ObjectInputStream can assign the 609 * wire handle early, before any nested ObjectStreamClass_1_3_1 might 610 * be read. 611 */ 612 ObjectStreamClass_1_3_1(String n, long s) { 613 name = n; 614 suid = s; 615 superclass = null; 616 } 617 618 private static Object[] translateFields(Object objs[]) 619 throws NoSuchFieldException { 620 try{ 621 java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs; 622 Object translation[] = null; 623 624 if (translatedFields == null) 625 translatedFields = new Hashtable(); 626 627 translation = (Object[])translatedFields.get(fields); 628 629 if (translation != null) 630 return translation; 631 else { 632 Class osfClass = com.sun.corba.se.impl.orbutil.ObjectStreamField.class; 633 634 translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length); 635 Object arg[] = new Object[2]; 636 Class types[] = {String.class, Class.class}; 637 Constructor constructor = osfClass.getDeclaredConstructor(types); 638 for (int i = fields.length -1; i >= 0; i--){ 639 arg[0] = fields[i].getName(); 640 arg[1] = fields[i].getType(); 641 642 translation[i] = constructor.newInstance(arg); 643 } 644 translatedFields.put(fields, translation); 645 646 } 647 648 return (Object[])translation; 649 } 650 catch(Throwable t){ 651 throw new NoSuchFieldException(); 652 } 653 } 654 655 /* Compare the base class names of streamName and localName. 656 * 657 * @return Return true iff the base class name compare. 658 * @parameter streamName Fully qualified class name. 659 * @parameter localName Fully qualified class name. 660 * @parameter pkgSeparator class names use either '.' or '/'. 661 * 662 * Only compare base class name to allow package renaming. 663 */ 664 static boolean compareClassNames(String streamName, 665 String localName, 666 char pkgSeparator) { 667 /* compare the class names, stripping off package names. */ 668 int streamNameIndex = streamName.lastIndexOf(pkgSeparator); 669 if (streamNameIndex < 0) 670 streamNameIndex = 0; 671 672 int localNameIndex = localName.lastIndexOf(pkgSeparator); 673 if (localNameIndex < 0) 674 localNameIndex = 0; 675 676 return streamName.regionMatches(false, streamNameIndex, 677 localName, localNameIndex, 678 streamName.length() - streamNameIndex); 679 } 680 681 /* 682 * Compare the types of two class descriptors. 683 * They match if they have the same class name and suid 684 */ 685 final boolean typeEquals(ObjectStreamClass_1_3_1 other) { 686 return (suid == other.suid) && 687 compareClassNames(name, other.name, '.'); 688 } 689 690 /* 691 * Return the superclass descriptor of this descriptor. 692 */ 693 final void setSuperclass(ObjectStreamClass_1_3_1 s) { 694 superclass = s; 695 } 696 697 /* 698 * Return the superclass descriptor of this descriptor. 699 */ 700 final ObjectStreamClass_1_3_1 getSuperclass() { 701 return superclass; 702 } 703 704 /* 705 * Return whether the class has a writeObject method 706 */ 707 final boolean hasWriteObject() { 708 return hasWriteObjectMethod; 709 } 710 711 final boolean isCustomMarshaled() { 712 return (hasWriteObject() || isExternalizable()); 713 } 714 715 /* 716 * Return true if all instances of 'this' Externalizable class 717 * are written in block-data mode from the stream that 'this' was read 718 * from. <p> 719 * 720 * In JDK 1.1, all Externalizable instances are not written 721 * in block-data mode. 722 * In JDK 1.2, all Externalizable instances, by default, are written 723 * in block-data mode and the Externalizable instance is terminated with 724 * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable 725 * instances. 726 * 727 * IMPLEMENTATION NOTE: 728 * This should have been a mode maintained per stream; however, 729 * for compatibility reasons, it was only possible to record 730 * this change per class. All Externalizable classes within 731 * a given stream should either have this mode enabled or 732 * disabled. This is enforced by not allowing the PROTOCOL_VERSION 733 * of a stream to he changed after any objects have been written. 734 * 735 * @see ObjectOutputStream#useProtocolVersion 736 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 737 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 738 * 739 * @since JDK 1.2 740 */ 741 boolean hasExternalizableBlockDataMode() { 742 return hasExternalizableBlockData; 743 } 744 745 /* 746 * Return the ObjectStreamClass_1_3_1 of the local class this one is based on. 747 */ 748 final ObjectStreamClass_1_3_1 localClassDescriptor() { 749 return localClassDesc; 750 } 751 752 /* 753 * Get the Serializability of the class. 754 */ 755 boolean isSerializable() { 756 return serializable; 757 } 758 759 /* 760 * Get the externalizability of the class. 761 */ 762 boolean isExternalizable() { 763 return externalizable; 764 } 765 766 boolean isNonSerializable() { 767 return ! (externalizable || serializable); 768 } 769 770 /* 771 * Calculate the size of the array needed to store primitive data and the 772 * number of object references to read when reading from the input 773 * stream. 774 */ 775 private void computeFieldInfo() { 776 primBytes = 0; 777 objFields = 0; 778 779 for (int i = 0; i < fields.length; i++ ) { 780 switch (fields[i].getTypeCode()) { 781 case 'B': 782 case 'Z': 783 primBytes += 1; 784 break; 785 case 'C': 786 case 'S': 787 primBytes += 2; 788 break; 789 790 case 'I': 791 case 'F': 792 primBytes += 4; 793 break; 794 case 'J': 795 case 'D' : 796 primBytes += 8; 797 break; 798 799 case 'L': 800 case '[': 801 objFields += 1; 802 break; 803 } 804 } 805 } 806 807 private static long computeStructuralUID(ObjectStreamClass_1_3_1 osc, Class cl) { 808 ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); 809 810 long h = 0; 811 try { 812 813 if ((!java.io.Serializable.class.isAssignableFrom(cl)) || 814 (cl.isInterface())){ 815 return 0; 816 } 817 818 if (java.io.Externalizable.class.isAssignableFrom(cl)) { 819 return 1; 820 } 821 822 MessageDigest md = MessageDigest.getInstance("SHA"); 823 DigestOutputStream mdo = new DigestOutputStream(devnull, md); 824 DataOutputStream data = new DataOutputStream(mdo); 825 826 // Get SUID of parent 827 Class parent = cl.getSuperclass(); 828 if ((parent != null)) 829 // SerialBug 1; acc. to spec the one for 830 // java.lang.object 831 // should be computed and put 832 // && (parent != java.lang.Object.class)) 833 { 834 //data.writeLong(computeSerialVersionUID(null,parent)); 835 data.writeLong(computeStructuralUID(lookup(parent), parent)); 836 } 837 838 if (osc.hasWriteObject()) 839 data.writeInt(2); 840 else 841 data.writeInt(1); 842 843 /* Sort the field names to get a deterministic order */ 844 // Field[] field = ObjectStreamClass_1_3_1.getDeclaredFields(cl); 845 846 ObjectStreamField[] fields = osc.getFields(); 847 848 // Must make sure that the Field array we allocate 849 // below is exactly the right size. Bug fix for 850 // 4397133. 851 int numNonNullFields = 0; 852 for (int i = 0; i < fields.length; i++) 853 if (fields[i].getField() != null) 854 numNonNullFields++; 855 856 Field [] field = new java.lang.reflect.Field[numNonNullFields]; 857 for (int i = 0, fieldNum = 0; i < fields.length; i++) { 858 if (fields[i].getField() != null) { 859 field[fieldNum++] = fields[i].getField(); 860 } 861 } 862 863 if (field.length > 1) 864 Arrays.sort(field, compareMemberByName); 865 866 for (int i = 0; i < field.length; i++) { 867 Field f = field[i]; 868 869 /* Include in the hash all fields except those that are 870 * transient 871 */ 872 int m = f.getModifiers(); 873 //Serial 6 874 //if (Modifier.isTransient(m) || Modifier.isStatic(m)) 875 // spec reference 00-01-06.pdf, 1.3.5.6, states non-static 876 // non-transient, public fields are mapped to Java IDL. 877 // 878 // Here's the quote from the first paragraph: 879 // Java non-static non-transient public fields are mapped to 880 // OMG IDL public data members, and other Java fields are 881 // not mapped. 882 883 // if (Modifier.isTransient(m) || Modifier.isStatic(m)) 884 // continue; 885 886 data.writeUTF(f.getName()); 887 data.writeUTF(getSignature(f.getType())); 888 } 889 890 /* Compute the hash value for this class. 891 * Use only the first 64 bits of the hash. 892 */ 893 data.flush(); 894 byte hasharray[] = md.digest(); 895 // int minimum = Math.min(8, hasharray.length); 896 // SerialBug 3: SHA computation is wrong; for loop reversed 897 //for (int i = minimum; i > 0; i--) 898 for (int i = 0; i < Math.min(8, hasharray.length); i++) { 899 h += (long)(hasharray[i] & 255) << (i * 8); 900 } 901 } catch (IOException ignore) { 902 /* can't happen, but be deterministic anyway. */ 903 h = -1; 904 } catch (NoSuchAlgorithmException complain) { 905 throw new SecurityException(complain.getMessage()); 906 } 907 return h; 908 } 909 910 /** 911 * Compute the JVM signature for the class. 912 */ 913 static String getSignature(Class clazz) { 914 String type = null; 915 if (clazz.isArray()) { 916 Class cl = clazz; 917 int dimensions = 0; 918 while (cl.isArray()) { 919 dimensions++; 920 cl = cl.getComponentType(); 921 } 922 StringBuffer sb = new StringBuffer(); 923 for (int i = 0; i < dimensions; i++) { 924 sb.append("["); 925 } 926 sb.append(getSignature(cl)); 927 type = sb.toString(); 928 } else if (clazz.isPrimitive()) { 929 if (clazz == Integer.TYPE) { 930 type = "I"; 931 } else if (clazz == Byte.TYPE) { 932 type = "B"; 933 } else if (clazz == Long.TYPE) { 934 type = "J"; 935 } else if (clazz == Float.TYPE) { 936 type = "F"; 937 } else if (clazz == Double.TYPE) { 938 type = "D"; 939 } else if (clazz == Short.TYPE) { 940 type = "S"; 941 } else if (clazz == Character.TYPE) { 942 type = "C"; 943 } else if (clazz == Boolean.TYPE) { 944 type = "Z"; 945 } else if (clazz == Void.TYPE) { 946 type = "V"; 947 } 948 } else { 949 type = "L" + clazz.getName().replace('.', '/') + ";"; 950 } 951 return type; 952 } 953 954 /* 955 * Compute the JVM method descriptor for the method. 956 */ 957 static String getSignature(Method meth) { 958 StringBuffer sb = new StringBuffer(); 959 960 sb.append("("); 961 962 Class[] params = meth.getParameterTypes(); // avoid clone 963 for (int j = 0; j < params.length; j++) { 964 sb.append(getSignature(params[j])); 965 } 966 sb.append(")"); 967 sb.append(getSignature(meth.getReturnType())); 968 return sb.toString(); 969 } 970 971 /* 972 * Compute the JVM constructor descriptor for the constructor. 973 */ 974 static String getSignature(Constructor cons) { 975 StringBuffer sb = new StringBuffer(); 976 977 sb.append("("); 978 979 Class[] params = cons.getParameterTypes(); // avoid clone 980 for (int j = 0; j < params.length; j++) { 981 sb.append(getSignature(params[j])); 982 } 983 sb.append(")V"); 984 return sb.toString(); 985 } 986 987 /* 988 * Cache of Class -> ClassDescriptor Mappings. 989 */ 990 static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61]; 991 992 /* 993 * findDescriptorFor a Class. This looks in the cache for a 994 * mapping from Class -> ObjectStreamClass mappings. The hashCode 995 * of the Class is used for the lookup since the Class is the key. 996 * The entries are extended from java.lang.ref.SoftReference so the 997 * gc will be able to free them if needed. 998 */ 999 private static ObjectStreamClass_1_3_1 findDescriptorFor(Class cl) { 1000 1001 int hash = cl.hashCode(); 1002 int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1003 ObjectStreamClassEntry e; 1004 ObjectStreamClassEntry prev; 1005 1006 /* Free any initial entries whose refs have been cleared */ 1007 while ((e = descriptorFor[index]) != null && e.get() == null) { 1008 descriptorFor[index] = e.next; 1009 } 1010 1011 /* Traverse the chain looking for a descriptor with ofClass == cl. 1012 * unlink entries that are unresolved. 1013 */ 1014 prev = e; 1015 while (e != null ) { 1016 ObjectStreamClass_1_3_1 desc = (ObjectStreamClass_1_3_1)(e.get()); 1017 if (desc == null) { 1018 // This entry has been cleared, unlink it 1019 prev.next = e.next; 1020 } else { 1021 if (desc.ofClass == cl) 1022 return desc; 1023 prev = e; 1024 } 1025 e = e.next; 1026 } 1027 return null; 1028 } 1029 1030 /* 1031 * insertDescriptorFor a Class -> ObjectStreamClass_1_3_1 mapping. 1032 */ 1033 private static void insertDescriptorFor(ObjectStreamClass_1_3_1 desc) { 1034 // Make sure not already present 1035 if (findDescriptorFor(desc.ofClass) != null) { 1036 return; 1037 } 1038 1039 int hash = desc.ofClass.hashCode(); 1040 int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1041 ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc); 1042 e.next = descriptorFor[index]; 1043 descriptorFor[index] = e; 1044 } 1045 1046 private static Field[] getDeclaredFields(final Class clz) { 1047 return (Field[]) AccessController.doPrivileged(new PrivilegedAction() { 1048 public Object run() { 1049 return clz.getDeclaredFields(); 1050 } 1051 }); 1052 } 1053 1054 1055 /* 1056 * The name of this descriptor 1057 */ 1058 private String name; 1059 1060 /* 1061 * The descriptor of the supertype. 1062 */ 1063 private ObjectStreamClass_1_3_1 superclass; 1064 1065 /* 1066 * Flags for Serializable and Externalizable. 1067 */ 1068 private boolean serializable; 1069 private boolean externalizable; 1070 1071 /* 1072 * Array of persistent fields of this class, sorted by 1073 * type and name. 1074 */ 1075 private ObjectStreamField[] fields; 1076 1077 /* 1078 * Class that is a descriptor for in this virtual machine. 1079 */ 1080 private Class ofClass; 1081 1082 /* 1083 * True if descriptor for a proxy class. 1084 */ 1085 boolean forProxyClass; 1086 1087 1088 /* 1089 * SerialVersionUID for this class. 1090 */ 1091 private long suid = kDefaultUID; 1092 private String suidStr = null; 1093 1094 /* 1095 * Actual (computed) SerialVersionUID for this class. 1096 */ 1097 private long actualSuid = kDefaultUID; 1098 private String actualSuidStr = null; 1099 1100 /* 1101 * The total number of bytes of primitive fields. 1102 * The total number of object fields. 1103 */ 1104 int primBytes; 1105 int objFields; 1106 1107 /* Internal lock object. */ 1108 private Object lock = new Object(); 1109 1110 /* True if this class has/had a writeObject method */ 1111 private boolean hasWriteObjectMethod; 1112 1113 /* In JDK 1.1, external data was not written in block mode. 1114 * As of JDK 1.2, external data is written in block data mode. This 1115 * flag enables JDK 1.2 to be able to read JDK 1.1 written external data. 1116 * 1117 * @since JDK 1.2 1118 */ 1119 private boolean hasExternalizableBlockData; 1120 Method writeObjectMethod; 1121 Method readObjectMethod; 1122 private transient Method writeReplaceObjectMethod; 1123 private transient Method readResolveObjectMethod; 1124 1125 /* 1126 * ObjectStreamClass_1_3_1 that this one was built from. 1127 */ 1128 private ObjectStreamClass_1_3_1 localClassDesc; 1129 1130 /* Get the private static final field for serial version UID */ 1131 // private static native long getSerialVersionUIDField(Class cl); 1132 1133 /* The Class Object for java.io.Serializable */ 1134 private static Class classSerializable = null; 1135 private static Class classExternalizable = null; 1136 1137 /* 1138 * Resolve java.io.Serializable at load time. 1139 */ 1140 static { 1141 try { 1142 classSerializable = Class.forName("java.io.Serializable"); 1143 classExternalizable = Class.forName("java.io.Externalizable"); 1144 } catch (Throwable e) { 1145 System.err.println("Could not load java.io.Serializable or java.io.Externalizable."); 1146 } 1147 } 1148 1149 /** use serialVersionUID from JDK 1.1. for interoperability */ 1150 private static final long serialVersionUID = -6120832682080437368L; 1151 1152 /** 1153 * Set serialPersistentFields of a Serializable class to this value to 1154 * denote that the class has no Serializable fields. 1155 */ 1156 public static final ObjectStreamField[] NO_FIELDS = 1157 new ObjectStreamField[0]; 1158 1159 /* 1160 * Entries held in the Cache of known ObjectStreamClass_1_3_1 objects. 1161 * Entries are chained together with the same hash value (modulo array size). 1162 */ 1163 private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference 1164 { 1165 ObjectStreamClassEntry(ObjectStreamClass_1_3_1 c) { 1166 //super(c); 1167 this.c = c; 1168 } 1169 ObjectStreamClassEntry next; 1170 1171 public Object get() 1172 { 1173 return c; 1174 } 1175 private ObjectStreamClass_1_3_1 c; 1176 } 1177 1178 /* 1179 * Comparator object for Classes and Interfaces 1180 */ 1181 private static Comparator compareClassByName = 1182 new CompareClassByName(); 1183 1184 private static class CompareClassByName implements Comparator { 1185 public int compare(Object o1, Object o2) { 1186 Class c1 = (Class)o1; 1187 Class c2 = (Class)o2; 1188 return (c1.getName()).compareTo(c2.getName()); 1189 } 1190 } 1191 1192 /* 1193 * Comparator object for Members, Fields, and Methods 1194 */ 1195 private static Comparator compareMemberByName = 1196 new CompareMemberByName(); 1197 1198 private static class CompareMemberByName implements Comparator { 1199 public int compare(Object o1, Object o2) { 1200 String s1 = ((Member)o1).getName(); 1201 String s2 = ((Member)o2).getName(); 1202 1203 if (o1 instanceof Method) { 1204 s1 += getSignature((Method)o1); 1205 s2 += getSignature((Method)o2); 1206 } else if (o1 instanceof Constructor) { 1207 s1 += getSignature((Constructor)o1); 1208 s2 += getSignature((Constructor)o2); 1209 } 1210 return s1.compareTo(s2); 1211 } 1212 } 1213 1214 /* It is expensive to recompute a method or constructor signature 1215 many times, so compute it only once using this data structure. */ 1216 private static class MethodSignature implements Comparator { 1217 Member member; 1218 String signature; // cached parameter signature 1219 1220 /* Given an array of Method or Constructor members, 1221 return a sorted array of the non-private members.*/ 1222 /* A better implementation would be to implement the returned data 1223 structure as an insertion sorted link list.*/ 1224 static MethodSignature[] removePrivateAndSort(Member[] m) { 1225 int numNonPrivate = 0; 1226 for (int i = 0; i < m.length; i++) { 1227 if (! Modifier.isPrivate(m[i].getModifiers())) { 1228 numNonPrivate++; 1229 } 1230 } 1231 MethodSignature[] cm = new MethodSignature[numNonPrivate]; 1232 int cmi = 0; 1233 for (int i = 0; i < m.length; i++) { 1234 if (! Modifier.isPrivate(m[i].getModifiers())) { 1235 cm[cmi] = new MethodSignature(m[i]); 1236 cmi++; 1237 } 1238 } 1239 if (cmi > 0) 1240 Arrays.sort(cm, cm[0]); 1241 return cm; 1242 } 1243 1244 /* Assumes that o1 and o2 are either both methods 1245 or both constructors.*/ 1246 public int compare(Object o1, Object o2) { 1247 /* Arrays.sort calls compare when o1 and o2 are equal.*/ 1248 if (o1 == o2) 1249 return 0; 1250 1251 MethodSignature c1 = (MethodSignature)o1; 1252 MethodSignature c2 = (MethodSignature)o2; 1253 1254 int result; 1255 if (isConstructor()) { 1256 result = c1.signature.compareTo(c2.signature); 1257 } else { // is a Method. 1258 result = c1.member.getName().compareTo(c2.member.getName()); 1259 if (result == 0) 1260 result = c1.signature.compareTo(c2.signature); 1261 } 1262 return result; 1263 } 1264 1265 final private boolean isConstructor() { 1266 return member instanceof Constructor; 1267 } 1268 private MethodSignature(Member m) { 1269 member = m; 1270 if (isConstructor()) { 1271 signature = ObjectStreamClass_1_3_1.getSignature((Constructor)m); 1272 } else { 1273 signature = ObjectStreamClass_1_3_1.getSignature((Method)m); 1274 } 1275 } 1276 } 1277 }