Source for java.io.ObjectStreamClass

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