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