1: /* ObjectStreamClass.java -- Class used to write class information 2: about serialized objects. 3: Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. 4: 5: This file is part of GNU Classpath. 6: 7: GNU Classpath is free software; you can redistribute it and/or modify 8: it under the terms of the GNU General Public License as published by 9: the Free Software Foundation; either version 2, or (at your option) 10: any later version. 11: 12: GNU Classpath is distributed in the hope that it will be useful, but 13: WITHOUT ANY WARRANTY; without even the implied warranty of 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15: General Public License for more details. 16: 17: You should have received a copy of the GNU General Public License 18: along with GNU Classpath; see the file COPYING. If not, write to the 19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20: 02110-1301 USA. 21: 22: Linking this library statically or dynamically with other modules is 23: making a combined work based on this library. Thus, the terms and 24: conditions of the GNU General Public License cover the whole 25: combination. 26: 27: As a special exception, the copyright holders of this library give you 28: permission to link this library with independent modules to produce an 29: executable, regardless of the license terms of these independent 30: modules, and to copy and distribute the resulting executable under 31: terms of your choice, provided that you also meet, for each linked 32: independent module, the terms and conditions of the license of that 33: module. An independent module is a module which is not derived from 34: or based on this library. If you modify this library, you may extend 35: this exception to your version of the library, but you are not 36: obligated to do so. If you do not wish to do so, delete this 37: exception statement from your version. */ 38: 39: 40: package java.io; 41: 42: import gnu.java.io.NullOutputStream; 43: import gnu.java.lang.reflect.TypeSignature; 44: import gnu.java.security.action.SetAccessibleAction; 45: import gnu.java.security.provider.Gnu; 46: 47: import java.lang.reflect.Constructor; 48: import java.lang.reflect.Field; 49: import java.lang.reflect.Member; 50: import java.lang.reflect.Method; 51: import java.lang.reflect.Modifier; 52: import java.lang.reflect.Proxy; 53: import java.security.AccessController; 54: import java.security.DigestOutputStream; 55: import java.security.MessageDigest; 56: import java.security.NoSuchAlgorithmException; 57: import java.security.PrivilegedAction; 58: import java.security.Security; 59: import java.util.Arrays; 60: import java.util.Comparator; 61: import java.util.Hashtable; 62: 63: /** 64: * @author Tom Tromey (tromey@redhat.com) 65: * @author Jeroen Frijters (jeroen@frijters.net) 66: * @author Guilhem Lavaux (guilhem@kaffe.org) 67: * @author Michael Koch (konqueror@gmx.de) 68: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 69: */ 70: public class ObjectStreamClass implements Serializable 71: { 72: static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0]; 73: 74: /** 75: * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. 76: * If <code>cl</code> is null, or is not <code>Serializable</code>, 77: * null is returned. <code>ObjectStreamClass</code>'s are memorized; 78: * later calls to this method with the same class will return the 79: * same <code>ObjectStreamClass</code> object and no recalculation 80: * will be done. 81: * 82: * Warning: If this class contains an invalid serialPersistentField arrays 83: * lookup will not throw anything. However {@link #getFields()} will return 84: * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an 85: * {@link java.io.InvalidClassException}. 86: * 87: * @see java.io.Serializable 88: */ 89: public static ObjectStreamClass lookup(Class<?> cl) 90: { 91: if (cl == null) 92: return null; 93: if (! (Serializable.class).isAssignableFrom(cl)) 94: return null; 95: 96: return lookupForClassObject(cl); 97: } 98: 99: /** 100: * This lookup for internal use by ObjectOutputStream. Suppose 101: * we have a java.lang.Class object C for class A, though A is not 102: * serializable, but it's okay to serialize C. 103: */ 104: static ObjectStreamClass lookupForClassObject(Class cl) 105: { 106: if (cl == null) 107: return null; 108: 109: ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl); 110: 111: if (osc != null) 112: return osc; 113: else 114: { 115: osc = new ObjectStreamClass(cl); 116: classLookupTable.put(cl, osc); 117: return osc; 118: } 119: } 120: 121: /** 122: * Returns the name of the class that this 123: * <code>ObjectStreamClass</code> represents. 124: * 125: * @return the name of the class. 126: */ 127: public String getName() 128: { 129: return name; 130: } 131: 132: /** 133: * Returns the class that this <code>ObjectStreamClass</code> 134: * represents. Null could be returned if this 135: * <code>ObjectStreamClass</code> was read from an 136: * <code>ObjectInputStream</code> and the class it represents cannot 137: * be found or loaded. 138: * 139: * @see java.io.ObjectInputStream 140: */ 141: public Class<?> forClass() 142: { 143: return clazz; 144: } 145: 146: /** 147: * Returns the serial version stream-unique identifier for the class 148: * represented by this <code>ObjectStreamClass</code>. This SUID is 149: * either defined by the class as <code>static final long 150: * serialVersionUID</code> or is calculated as specified in 151: * Javasoft's "Object Serialization Specification" XXX: add reference 152: * 153: * @return the serial version UID. 154: */ 155: public long getSerialVersionUID() 156: { 157: return uid; 158: } 159: 160: /** 161: * Returns the serializable (non-static and non-transient) Fields 162: * of the class represented by this ObjectStreamClass. The Fields 163: * are sorted by name. 164: * If fields were obtained using serialPersistentFields and this array 165: * is faulty then the returned array of this method will be empty. 166: * 167: * @return the fields. 168: */ 169: public ObjectStreamField[] getFields() 170: { 171: ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; 172: System.arraycopy(fields, 0, copy, 0, fields.length); 173: return copy; 174: } 175: 176: // XXX doc 177: // Can't do binary search since fields is sorted by name and 178: // primitiveness. 179: public ObjectStreamField getField (String name) 180: { 181: for (int i = 0; i < fields.length; i++) 182: if (fields[i].getName().equals(name)) 183: return fields[i]; 184: return null; 185: } 186: 187: /** 188: * Returns a textual representation of this 189: * <code>ObjectStreamClass</code> object including the name of the 190: * class it represents as well as that class's serial version 191: * stream-unique identifier. 192: * 193: * @see #getSerialVersionUID() 194: * @see #getName() 195: */ 196: public String toString() 197: { 198: return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; 199: } 200: 201: // Returns true iff the class that this ObjectStreamClass represents 202: // has the following method: 203: // 204: // private void writeObject (ObjectOutputStream) 205: // 206: // This method is used by the class to override default 207: // serialization behavior. 208: boolean hasWriteMethod() 209: { 210: return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; 211: } 212: 213: // Returns true iff the class that this ObjectStreamClass represents 214: // implements Serializable but does *not* implement Externalizable. 215: boolean isSerializable() 216: { 217: return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; 218: } 219: 220: 221: // Returns true iff the class that this ObjectStreamClass represents 222: // implements Externalizable. 223: boolean isExternalizable() 224: { 225: return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; 226: } 227: 228: // Returns true iff the class that this ObjectStreamClass represents 229: // implements Externalizable. 230: boolean isEnum() 231: { 232: return (flags & ObjectStreamConstants.SC_ENUM) != 0; 233: } 234: 235: // Returns the <code>ObjectStreamClass</code> that represents the 236: // class that is the superclass of the class this 237: // <code>ObjectStreamClass</code> represents. If the superclass is 238: // not Serializable, null is returned. 239: ObjectStreamClass getSuper() 240: { 241: return superClass; 242: } 243: 244: /** 245: * returns an array of ObjectStreamClasses that represent the super 246: * classes of the class represented by this and the class 247: * represented by this itself in order from most super to this. 248: * ObjectStreamClass[0] is the highest superclass of this that is 249: * serializable. 250: * 251: * The result of consecutive calls this hierarchy() will be the same 252: * array instance. 253: * 254: * @return an array of ObjectStreamClass representing the 255: * super-class hierarchy of serializable classes. 256: */ 257: ObjectStreamClass[] hierarchy() 258: { 259: ObjectStreamClass[] result = hierarchy; 260: if (result == null) 261: { 262: int d = 0; 263: 264: for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) 265: d++; 266: 267: result = new ObjectStreamClass[d]; 268: 269: for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) 270: { 271: result[--d] = osc; 272: } 273: 274: hierarchy = result; 275: } 276: return result; 277: } 278: 279: /** 280: * Cache for hierarchy() result. 281: */ 282: private ObjectStreamClass[] hierarchy = null; 283: 284: // Returns an integer that consists of bit-flags that indicate 285: // properties of the class represented by this ObjectStreamClass. 286: // The bit-flags that could be present are those defined in 287: // ObjectStreamConstants that begin with `SC_' 288: int getFlags() 289: { 290: return flags; 291: } 292: 293: 294: ObjectStreamClass(String name, long uid, byte flags, 295: ObjectStreamField[] fields) 296: { 297: this.name = name; 298: this.uid = uid; 299: this.flags = flags; 300: this.fields = fields; 301: } 302: 303: /** 304: * This method builds the internal description corresponding to a Java Class. 305: * As the constructor only assign a name to the current ObjectStreamClass instance, 306: * that method sets the serial UID, chose the fields which will be serialized, 307: * and compute the position of the fields in the serialized stream. 308: * 309: * @param cl The Java class which is used as a reference for building the descriptor. 310: * @param superClass The descriptor of the super class for this class descriptor. 311: * @throws InvalidClassException if an incompatibility between computed UID and 312: * already set UID is found. 313: */ 314: void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException 315: {hierarchy = null; 316: this.clazz = cl; 317: 318: cacheMethods(); 319: 320: long class_uid = getClassUID(cl); 321: if (uid == 0) 322: uid = class_uid; 323: else 324: { 325: // Check that the actual UID of the resolved class matches the UID from 326: // the stream. Mismatches for array classes are ignored. 327: if (!cl.isArray() && uid != class_uid) 328: { 329: String msg = cl + 330: ": Local class not compatible: stream serialVersionUID=" 331: + uid + ", local serialVersionUID=" + class_uid; 332: throw new InvalidClassException (msg); 333: } 334: } 335: 336: isProxyClass = clazz != null && Proxy.isProxyClass(clazz); 337: this.superClass = superClass; 338: calculateOffsets(); 339: 340: try 341: { 342: ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); 343: 344: if (exportedFields == null) 345: return; 346: 347: ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; 348: int i, j, k; 349: 350: /* We now check the import fields against the exported fields. 351: * There should not be contradiction (e.g. int x and String x) 352: * but extra virtual fields can be added to the class. 353: */ 354: 355: Arrays.sort(exportedFields); 356: 357: i = 0; j = 0; k = 0; 358: while (i < fields.length && j < exportedFields.length) 359: { 360: int comp = fields[i].compareTo(exportedFields[j]); 361: 362: if (comp < 0) 363: { 364: newFieldList[k] = fields[i]; 365: fields[i].setPersistent(false); 366: fields[i].setToSet(false); 367: i++; 368: } 369: else if (comp > 0) 370: { 371: /* field not found in imported fields. We add it 372: * in the list of supported fields. 373: */ 374: newFieldList[k] = exportedFields[j]; 375: newFieldList[k].setPersistent(true); 376: newFieldList[k].setToSet(false); 377: try 378: { 379: newFieldList[k].lookupField(clazz); 380: newFieldList[k].checkFieldType(); 381: } 382: catch (NoSuchFieldException _) 383: { 384: } 385: j++; 386: } 387: else 388: { 389: try 390: { 391: exportedFields[j].lookupField(clazz); 392: exportedFields[j].checkFieldType(); 393: } 394: catch (NoSuchFieldException _) 395: { 396: } 397: 398: if (!fields[i].getType().equals(exportedFields[j].getType())) 399: throw new InvalidClassException 400: ("serialPersistentFields must be compatible with" + 401: " imported fields (about " + fields[i].getName() + ")"); 402: newFieldList[k] = fields[i]; 403: fields[i].setPersistent(true); 404: i++; 405: j++; 406: } 407: k++; 408: } 409: 410: if (i < fields.length) 411: for (;i<fields.length;i++,k++) 412: { 413: fields[i].setPersistent(false); 414: fields[i].setToSet(false); 415: newFieldList[k] = fields[i]; 416: } 417: else 418: if (j < exportedFields.length) 419: for (;j<exportedFields.length;j++,k++) 420: { 421: exportedFields[j].setPersistent(true); 422: exportedFields[j].setToSet(false); 423: newFieldList[k] = exportedFields[j]; 424: } 425: 426: fields = new ObjectStreamField[k]; 427: System.arraycopy(newFieldList, 0, fields, 0, k); 428: } 429: catch (NoSuchFieldException ignore) 430: { 431: return; 432: } 433: catch (IllegalAccessException ignore) 434: { 435: return; 436: } 437: } 438: 439: void setSuperclass (ObjectStreamClass osc) 440: { 441: superClass = osc; 442: hierarchy = null; 443: } 444: 445: void calculateOffsets() 446: { 447: int i; 448: ObjectStreamField field; 449: primFieldSize = 0; 450: int fcount = fields.length; 451: for (i = 0; i < fcount; ++ i) 452: { 453: field = fields[i]; 454: 455: if (! field.isPrimitive()) 456: break; 457: 458: field.setOffset(primFieldSize); 459: switch (field.getTypeCode()) 460: { 461: case 'B': 462: case 'Z': 463: ++ primFieldSize; 464: break; 465: case 'C': 466: case 'S': 467: primFieldSize += 2; 468: break; 469: case 'I': 470: case 'F': 471: primFieldSize += 4; 472: break; 473: case 'D': 474: case 'J': 475: primFieldSize += 8; 476: break; 477: } 478: } 479: 480: for (objectFieldCount = 0; i < fcount; ++ i) 481: fields[i].setOffset(objectFieldCount++); 482: } 483: 484: private Method findMethod(Method[] methods, String name, Class[] params, 485: Class returnType, boolean mustBePrivate) 486: { 487: outer: 488: for (int i = 0; i < methods.length; i++) 489: { 490: final Method m = methods[i]; 491: int mods = m.getModifiers(); 492: if (Modifier.isStatic(mods) 493: || (mustBePrivate && !Modifier.isPrivate(mods))) 494: { 495: continue; 496: } 497: 498: if (m.getName().equals(name) 499: && m.getReturnType() == returnType) 500: { 501: Class[] mp = m.getParameterTypes(); 502: if (mp.length == params.length) 503: { 504: for (int j = 0; j < mp.length; j++) 505: { 506: if (mp[j] != params[j]) 507: { 508: continue outer; 509: } 510: } 511: AccessController.doPrivileged(new SetAccessibleAction(m)); 512: return m; 513: } 514: } 515: } 516: return null; 517: } 518: 519: private static boolean inSamePackage(Class c1, Class c2) 520: { 521: String name1 = c1.getName(); 522: String name2 = c2.getName(); 523: 524: int id1 = name1.lastIndexOf('.'); 525: int id2 = name2.lastIndexOf('.'); 526: 527: // Handle the default package 528: if (id1 == -1 || id2 == -1) 529: return id1 == id2; 530: 531: String package1 = name1.substring(0, id1); 532: String package2 = name2.substring(0, id2); 533: 534: return package1.equals(package2); 535: } 536: 537: final static Class[] noArgs = new Class[0]; 538: 539: private static Method findAccessibleMethod(String name, Class from) 540: { 541: for (Class c = from; c != null; c = c.getSuperclass()) 542: { 543: try 544: { 545: Method res = c.getDeclaredMethod(name, noArgs); 546: int mods = res.getModifiers(); 547: 548: if (c == from 549: || Modifier.isProtected(mods) 550: || Modifier.isPublic(mods) 551: || (! Modifier.isPrivate(mods) && inSamePackage(c, from))) 552: { 553: AccessController.doPrivileged(new SetAccessibleAction(res)); 554: return res; 555: } 556: } 557: catch (NoSuchMethodException e) 558: { 559: } 560: } 561: 562: return null; 563: } 564: 565: /** 566: * Helper routine to check if a class was loaded by boot or 567: * application class loader. Classes for which this is not the case 568: * should not be cached since caching prevent class file garbage 569: * collection. 570: * 571: * @param cl a class 572: * 573: * @return true if cl was loaded by boot or application class loader, 574: * false if cl was loaded by a user class loader. 575: */ 576: private static boolean loadedByBootOrApplicationClassLoader(Class cl) 577: { 578: ClassLoader l = cl.getClassLoader(); 579: return 580: ( l == null /* boot loader */ ) 581: || (l == ClassLoader.getSystemClassLoader() /* application loader */); 582: } 583: 584: static Hashtable methodCache = new Hashtable(); 585: 586: static final Class[] readObjectSignature = { ObjectInputStream.class }; 587: static final Class[] writeObjectSignature = { ObjectOutputStream.class }; 588: 589: private void cacheMethods() 590: { 591: Class cl = forClass(); 592: Method[] cached = (Method[]) methodCache.get(cl); 593: if (cached == null) 594: { 595: cached = new Method[4]; 596: Method[] methods = cl.getDeclaredMethods(); 597: 598: cached[0] = findMethod(methods, "readObject", 599: readObjectSignature, 600: Void.TYPE, true); 601: cached[1] = findMethod(methods, "writeObject", 602: writeObjectSignature, 603: Void.TYPE, true); 604: 605: // readResolve and writeReplace can be in parent classes, as long as they 606: // are accessible from this class. 607: cached[2] = findAccessibleMethod("readResolve", cl); 608: cached[3] = findAccessibleMethod("writeReplace", cl); 609: 610: /* put in cache if classes not loaded by user class loader. 611: * For a user class loader, the cache may otherwise grow 612: * without limit. 613: */ 614: if (loadedByBootOrApplicationClassLoader(cl)) 615: methodCache.put(cl,cached); 616: } 617: readObjectMethod = cached[0]; 618: writeObjectMethod = cached[1]; 619: readResolveMethod = cached[2]; 620: writeReplaceMethod = cached[3]; 621: } 622: 623: private ObjectStreamClass(Class cl) 624: { 625: uid = 0; 626: flags = 0; 627: isProxyClass = Proxy.isProxyClass(cl); 628: 629: clazz = cl; 630: cacheMethods(); 631: name = cl.getName(); 632: setFlags(cl); 633: setFields(cl); 634: // to those class nonserializable, its uid field is 0 635: if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) 636: uid = getClassUID(cl); 637: superClass = lookup(cl.getSuperclass()); 638: } 639: 640: 641: // Sets bits in flags according to features of CL. 642: private void setFlags(Class cl) 643: { 644: if ((java.io.Externalizable.class).isAssignableFrom(cl)) 645: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; 646: else if ((java.io.Serializable.class).isAssignableFrom(cl)) 647: // only set this bit if CL is NOT Externalizable 648: flags |= ObjectStreamConstants.SC_SERIALIZABLE; 649: 650: if (writeObjectMethod != null) 651: flags |= ObjectStreamConstants.SC_WRITE_METHOD; 652: 653: if (cl.isEnum() || cl == Enum.class) 654: flags |= ObjectStreamConstants.SC_ENUM; 655: } 656: 657: 658: // Sets fields to be a sorted array of the serializable fields of 659: // clazz. 660: private void setFields(Class cl) 661: { 662: SetAccessibleAction setAccessible = new SetAccessibleAction(); 663: 664: if (!isSerializable() || isExternalizable() || isEnum()) 665: { 666: fields = NO_FIELDS; 667: return; 668: } 669: 670: try 671: { 672: final Field f = 673: cl.getDeclaredField("serialPersistentFields"); 674: setAccessible.setMember(f); 675: AccessController.doPrivileged(setAccessible); 676: int modifiers = f.getModifiers(); 677: 678: if (Modifier.isStatic(modifiers) 679: && Modifier.isFinal(modifiers) 680: && Modifier.isPrivate(modifiers)) 681: { 682: fields = getSerialPersistentFields(cl); 683: if (fields != null) 684: { 685: ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length]; 686: System.arraycopy(fields, 0, fieldsName, 0, fields.length); 687: 688: Arrays.sort (fieldsName, new Comparator() { 689: public int compare(Object o1, Object o2) 690: { 691: ObjectStreamField f1 = (ObjectStreamField)o1; 692: ObjectStreamField f2 = (ObjectStreamField)o2; 693: 694: return f1.getName().compareTo(f2.getName()); 695: } 696: }); 697: 698: for (int i=1; i < fields.length; i++) 699: { 700: if (fieldsName[i-1].getName().equals(fieldsName[i].getName())) 701: { 702: fields = INVALID_FIELDS; 703: return; 704: } 705: } 706: 707: Arrays.sort (fields); 708: // Retrieve field reference. 709: for (int i=0; i < fields.length; i++) 710: { 711: try 712: { 713: fields[i].lookupField(cl); 714: } 715: catch (NoSuchFieldException _) 716: { 717: fields[i].setToSet(false); 718: } 719: } 720: 721: calculateOffsets(); 722: return; 723: } 724: } 725: } 726: catch (NoSuchFieldException ignore) 727: { 728: } 729: catch (IllegalAccessException ignore) 730: { 731: } 732: 733: int num_good_fields = 0; 734: Field[] all_fields = cl.getDeclaredFields(); 735: 736: int modifiers; 737: // set non-serializable fields to null in all_fields 738: for (int i = 0; i < all_fields.length; i++) 739: { 740: modifiers = all_fields[i].getModifiers(); 741: if (Modifier.isTransient(modifiers) 742: || Modifier.isStatic(modifiers)) 743: all_fields[i] = null; 744: else 745: num_good_fields++; 746: } 747: 748: // make a copy of serializable (non-null) fields 749: fields = new ObjectStreamField[ num_good_fields ]; 750: for (int from = 0, to = 0; from < all_fields.length; from++) 751: if (all_fields[from] != null) 752: { 753: final Field f = all_fields[from]; 754: setAccessible.setMember(f); 755: AccessController.doPrivileged(setAccessible); 756: fields[to] = new ObjectStreamField(all_fields[from]); 757: to++; 758: } 759: 760: Arrays.sort(fields); 761: // Make sure we don't have any duplicate field names 762: // (Sun JDK 1.4.1. throws an Internal Error as well) 763: for (int i = 1; i < fields.length; i++) 764: { 765: if(fields[i - 1].getName().equals(fields[i].getName())) 766: throw new InternalError("Duplicate field " + 767: fields[i].getName() + " in class " + cl.getName()); 768: } 769: calculateOffsets(); 770: } 771: 772: static Hashtable uidCache = new Hashtable(); 773: 774: // Returns the serial version UID defined by class, or if that 775: // isn't present, calculates value of serial version UID. 776: private long getClassUID(Class cl) 777: { 778: long result = 0; 779: Long cache = (Long) uidCache.get(cl); 780: if (cache != null) 781: result = cache.longValue(); 782: else 783: { 784: try 785: { 786: result = getClassUIDFromField(cl); 787: } 788: catch (NoSuchFieldException ignore) 789: { 790: try 791: { 792: result = calculateClassUID(cl); 793: } 794: catch (NoSuchAlgorithmException e) 795: { 796: throw new RuntimeException 797: ("The SHA algorithm was not found to use in computing the Serial Version UID for class " 798: + cl.getName(), e); 799: } 800: catch (IOException ioe) 801: { 802: throw new RuntimeException(ioe); 803: } 804: } 805: 806: if (loadedByBootOrApplicationClassLoader(cl)) 807: uidCache.put(cl,new Long(result)); 808: } 809: return result; 810: } 811: 812: /** 813: * Search for a serialVersionUID field in the given class and read 814: * its value. 815: * 816: * @return the contents of the serialVersionUID field 817: * 818: * @throws NoSuchFieldException if such a field does not exist or is 819: * not static, not final, not of type Long or not accessible. 820: */ 821: long getClassUIDFromField(Class cl) 822: throws NoSuchFieldException 823: { 824: long result; 825: 826: try 827: { 828: // Use getDeclaredField rather than getField, since serialVersionUID 829: // may not be public AND we only want the serialVersionUID of this 830: // class, not a superclass or interface. 831: final Field suid = cl.getDeclaredField("serialVersionUID"); 832: SetAccessibleAction setAccessible = new SetAccessibleAction(suid); 833: AccessController.doPrivileged(setAccessible); 834: int modifiers = suid.getModifiers(); 835: 836: if (Modifier.isStatic(modifiers) 837: && Modifier.isFinal(modifiers) 838: && suid.getType() == Long.TYPE) 839: result = suid.getLong(null); 840: else 841: throw new NoSuchFieldException(); 842: } 843: catch (IllegalAccessException ignore) 844: { 845: throw new NoSuchFieldException(); 846: } 847: 848: return result; 849: } 850: 851: /** 852: * Calculate class serial version UID for a class that does not 853: * define serialVersionUID: 854: * 855: * @param cl a class 856: * 857: * @return the calculated serial varsion UID. 858: * 859: * @throws NoSuchAlgorithmException if SHA algorithm not found 860: * 861: * @throws IOException if writing to the DigestOutputStream causes 862: * an IOException. 863: */ 864: long calculateClassUID(Class cl) 865: throws NoSuchAlgorithmException, IOException 866: { 867: long result; 868: MessageDigest md; 869: try 870: { 871: md = MessageDigest.getInstance("SHA"); 872: } 873: catch (NoSuchAlgorithmException e) 874: { 875: // If a provider already provides SHA, use it; otherwise, use this. 876: Gnu gnuProvider = new Gnu(); 877: Security.addProvider(gnuProvider); 878: md = MessageDigest.getInstance("SHA"); 879: } 880: 881: DigestOutputStream digest_out = 882: new DigestOutputStream(nullOutputStream, md); 883: DataOutputStream data_out = new DataOutputStream(digest_out); 884: 885: data_out.writeUTF(cl.getName()); 886: 887: int modifiers = cl.getModifiers(); 888: // just look at interesting bits 889: modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL 890: | Modifier.INTERFACE | Modifier.PUBLIC); 891: data_out.writeInt(modifiers); 892: 893: // Pretend that an array has no interfaces, because when array 894: // serialization was defined (JDK 1.1), arrays didn't have it. 895: if (! cl.isArray()) 896: { 897: Class[] interfaces = cl.getInterfaces(); 898: Arrays.sort(interfaces, interfaceComparator); 899: for (int i = 0; i < interfaces.length; i++) 900: data_out.writeUTF(interfaces[i].getName()); 901: } 902: 903: Field field; 904: Field[] fields = cl.getDeclaredFields(); 905: Arrays.sort(fields, memberComparator); 906: for (int i = 0; i < fields.length; i++) 907: { 908: field = fields[i]; 909: modifiers = field.getModifiers(); 910: if (Modifier.isPrivate(modifiers) 911: && (Modifier.isStatic(modifiers) 912: || Modifier.isTransient(modifiers))) 913: continue; 914: 915: data_out.writeUTF(field.getName()); 916: data_out.writeInt(modifiers); 917: data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); 918: } 919: 920: // write class initializer method if present 921: if (VMObjectStreamClass.hasClassInitializer(cl)) 922: { 923: data_out.writeUTF("<clinit>"); 924: data_out.writeInt(Modifier.STATIC); 925: data_out.writeUTF("()V"); 926: } 927: 928: Constructor constructor; 929: Constructor[] constructors = cl.getDeclaredConstructors(); 930: Arrays.sort (constructors, memberComparator); 931: for (int i = 0; i < constructors.length; i++) 932: { 933: constructor = constructors[i]; 934: modifiers = constructor.getModifiers(); 935: if (Modifier.isPrivate(modifiers)) 936: continue; 937: 938: data_out.writeUTF("<init>"); 939: data_out.writeInt(modifiers); 940: 941: // the replacement of '/' with '.' was needed to make computed 942: // SUID's agree with those computed by JDK 943: data_out.writeUTF 944: (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); 945: } 946: 947: Method method; 948: Method[] methods = cl.getDeclaredMethods(); 949: Arrays.sort(methods, memberComparator); 950: for (int i = 0; i < methods.length; i++) 951: { 952: method = methods[i]; 953: modifiers = method.getModifiers(); 954: if (Modifier.isPrivate(modifiers)) 955: continue; 956: 957: data_out.writeUTF(method.getName()); 958: data_out.writeInt(modifiers); 959: 960: // the replacement of '/' with '.' was needed to make computed 961: // SUID's agree with those computed by JDK 962: data_out.writeUTF 963: (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); 964: } 965: 966: data_out.close(); 967: byte[] sha = md.digest(); 968: result = 0; 969: int len = sha.length < 8 ? sha.length : 8; 970: for (int i = 0; i < len; i++) 971: result += (long) (sha[i] & 0xFF) << (8 * i); 972: 973: return result; 974: } 975: 976: /** 977: * Returns the value of CLAZZ's private static final field named 978: * `serialPersistentFields'. It performs some sanity checks before 979: * returning the real array. Besides, the returned array is a clean 980: * copy of the original. So it can be modified. 981: * 982: * @param clazz Class to retrieve 'serialPersistentFields' from. 983: * @return The content of 'serialPersistentFields'. 984: */ 985: private ObjectStreamField[] getSerialPersistentFields(Class clazz) 986: throws NoSuchFieldException, IllegalAccessException 987: { 988: ObjectStreamField[] fieldsArray = null; 989: ObjectStreamField[] o; 990: 991: // Use getDeclaredField rather than getField for the same reason 992: // as above in getDefinedSUID. 993: Field f = clazz.getDeclaredField("serialPersistentFields"); 994: f.setAccessible(true); 995: 996: int modifiers = f.getModifiers(); 997: if (!(Modifier.isStatic(modifiers) && 998: Modifier.isFinal(modifiers) && 999: Modifier.isPrivate(modifiers))) 1000: return null; 1001: 1002: o = (ObjectStreamField[]) f.get(null); 1003: 1004: if (o == null) 1005: return null; 1006: 1007: fieldsArray = new ObjectStreamField[ o.length ]; 1008: System.arraycopy(o, 0, fieldsArray, 0, o.length); 1009: 1010: return fieldsArray; 1011: } 1012: 1013: /** 1014: * Returns a new instance of the Class this ObjectStreamClass corresponds 1015: * to. 1016: * Note that this should only be used for Externalizable classes. 1017: * 1018: * @return A new instance. 1019: */ 1020: Externalizable newInstance() throws InvalidClassException 1021: { 1022: synchronized(this) 1023: { 1024: if (constructor == null) 1025: { 1026: try 1027: { 1028: final Constructor c = clazz.getConstructor(new Class[0]); 1029: 1030: AccessController.doPrivileged(new PrivilegedAction() 1031: { 1032: public Object run() 1033: { 1034: c.setAccessible(true); 1035: return null; 1036: } 1037: }); 1038: 1039: constructor = c; 1040: } 1041: catch(NoSuchMethodException x) 1042: { 1043: throw new InvalidClassException(clazz.getName(), 1044: "No public zero-argument constructor"); 1045: } 1046: } 1047: } 1048: 1049: try 1050: { 1051: return (Externalizable)constructor.newInstance(null); 1052: } 1053: catch(Exception x) 1054: { 1055: throw (InvalidClassException) 1056: new InvalidClassException(clazz.getName(), 1057: "Unable to instantiate").initCause(x); 1058: } 1059: } 1060: 1061: public static final ObjectStreamField[] NO_FIELDS = {}; 1062: 1063: private static Hashtable<Class,ObjectStreamClass> classLookupTable 1064: = new Hashtable<Class,ObjectStreamClass>(); 1065: private static final NullOutputStream nullOutputStream = new NullOutputStream(); 1066: private static final Comparator interfaceComparator = new InterfaceComparator(); 1067: private static final Comparator memberComparator = new MemberComparator(); 1068: private static final 1069: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; 1070: 1071: private ObjectStreamClass superClass; 1072: private Class<?> clazz; 1073: private String name; 1074: private long uid; 1075: private byte flags; 1076: 1077: // this field is package protected so that ObjectInputStream and 1078: // ObjectOutputStream can access it directly 1079: ObjectStreamField[] fields; 1080: 1081: // these are accessed by ObjectIn/OutputStream 1082: int primFieldSize = -1; // -1 if not yet calculated 1083: int objectFieldCount; 1084: 1085: Method readObjectMethod; 1086: Method readResolveMethod; 1087: Method writeReplaceMethod; 1088: Method writeObjectMethod; 1089: boolean realClassIsSerializable; 1090: boolean realClassIsExternalizable; 1091: ObjectStreamField[] fieldMapping; 1092: Constructor firstNonSerializableParentConstructor; 1093: private Constructor constructor; // default constructor for Externalizable 1094: 1095: boolean isProxyClass = false; 1096: 1097: // This is probably not necessary because this class is special cased already 1098: // but it will avoid showing up as a discrepancy when comparing SUIDs. 1099: private static final long serialVersionUID = -6120832682080437368L; 1100: 1101: 1102: // interfaces are compared only by name 1103: private static final class InterfaceComparator implements Comparator 1104: { 1105: public int compare(Object o1, Object o2) 1106: { 1107: return ((Class) o1).getName().compareTo(((Class) o2).getName()); 1108: } 1109: } 1110: 1111: 1112: // Members (Methods and Constructors) are compared first by name, 1113: // conflicts are resolved by comparing type signatures 1114: private static final class MemberComparator implements Comparator 1115: { 1116: public int compare(Object o1, Object o2) 1117: { 1118: Member m1 = (Member) o1; 1119: Member m2 = (Member) o2; 1120: 1121: int comp = m1.getName().compareTo(m2.getName()); 1122: 1123: if (comp == 0) 1124: return TypeSignature.getEncodingOfMember(m1). 1125: compareTo(TypeSignature.getEncodingOfMember(m2)); 1126: else 1127: return comp; 1128: } 1129: } 1130: }