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, 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: }