Source for java.io.ObjectOutputStream

   1: /* ObjectOutputStream.java -- Class used to write serialized objects
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
   3:    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.ObjectIdentityMap2Int;
  43: import gnu.java.lang.reflect.TypeSignature;
  44: import gnu.java.security.action.SetAccessibleAction;
  45: 
  46: import java.lang.reflect.Array;
  47: import java.lang.reflect.Field;
  48: import java.lang.reflect.InvocationTargetException;
  49: import java.lang.reflect.Method;
  50: 
  51: import java.security.AccessController;
  52: import java.security.PrivilegedAction;
  53: 
  54: /**
  55:  * An <code>ObjectOutputStream</code> can be used to write objects
  56:  * as well as primitive data in a platform-independent manner to an
  57:  * <code>OutputStream</code>.
  58:  *
  59:  * The data produced by an <code>ObjectOutputStream</code> can be read
  60:  * and reconstituted by an <code>ObjectInputStream</code>.
  61:  *
  62:  * <code>writeObject (Object)</code> is used to write Objects, the
  63:  * <code>write&lt;type&gt;</code> methods are used to write primitive
  64:  * data (as in <code>DataOutputStream</code>). Strings can be written
  65:  * as objects or as primitive data.
  66:  *
  67:  * Not all objects can be written out using an
  68:  * <code>ObjectOutputStream</code>.  Only those objects that are an
  69:  * instance of <code>java.io.Serializable</code> can be written.
  70:  *
  71:  * Using default serialization, information about the class of an
  72:  * object is written, all of the non-transient, non-static fields of
  73:  * the object are written, if any of these fields are objects, they are
  74:  * written out in the same manner.
  75:  *
  76:  * An object is only written out the first time it is encountered.  If
  77:  * the object is encountered later, a reference to it is written to
  78:  * the underlying stream.  Thus writing circular object graphs
  79:  * does not present a problem, nor are relationships between objects
  80:  * in a graph lost.
  81:  *
  82:  * Example usage:
  83:  * <pre>
  84:  * Hashtable map = new Hashtable ();
  85:  * map.put ("one", new Integer (1));
  86:  * map.put ("two", new Integer (2));
  87:  *
  88:  * ObjectOutputStream oos =
  89:  * new ObjectOutputStream (new FileOutputStream ("numbers"));
  90:  * oos.writeObject (map);
  91:  * oos.close ();
  92:  *
  93:  * ObjectInputStream ois =
  94:  * new ObjectInputStream (new FileInputStream ("numbers"));
  95:  * Hashtable newmap = (Hashtable)ois.readObject ();
  96:  *
  97:  * System.out.println (newmap);
  98:  * </pre>
  99:  *
 100:  * The default serialization can be overriden in two ways.
 101:  *
 102:  * By defining a method <code>private void
 103:  * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
 104:  * how information about itself is written.
 105:  * <code>defaultWriteObject ()</code> may be called from this method to
 106:  * carry out default serialization.  This method is not
 107:  * responsible for dealing with fields of super-classes or subclasses.
 108:  *
 109:  * By implementing <code>java.io.Externalizable</code>.  This gives
 110:  * the class complete control over the way it is written to the
 111:  * stream.  If this approach is used the burden of writing superclass
 112:  * and subclass data is transfered to the class implementing
 113:  * <code>java.io.Externalizable</code>.
 114:  *
 115:  * @see java.io.DataOutputStream
 116:  * @see java.io.Externalizable
 117:  * @see java.io.ObjectInputStream
 118:  * @see java.io.Serializable
 119:  * @author Tom Tromey (tromey@redhat.com)
 120:  * @author Jeroen Frijters (jeroen@frijters.net)
 121:  * @author Guilhem Lavaux (guilhem@kaffe.org)
 122:  * @author Michael Koch (konqueror@gmx.de)
 123:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 124:  */
 125: public class ObjectOutputStream extends OutputStream
 126:   implements ObjectOutput, ObjectStreamConstants
 127: {
 128:   /**
 129:    * Creates a new <code>ObjectOutputStream</code> that will do all of
 130:    * its writing onto <code>out</code>.  This method also initializes
 131:    * the stream by writing the header information (stream magic number
 132:    * and stream version).
 133:    *
 134:    * @exception IOException Writing stream header to underlying
 135:    * stream cannot be completed.
 136:    *
 137:    * @see #writeStreamHeader()
 138:    */
 139:   public ObjectOutputStream (OutputStream out) throws IOException
 140:   {
 141:     SecurityManager secMan = System.getSecurityManager();
 142:     if (secMan != null && overridesMethods(getClass()))
 143:       secMan.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
 144: 
 145:     realOutput = new DataOutputStream(out);
 146:     blockData = new byte[ BUFFER_SIZE ];
 147:     blockDataCount = 0;
 148:     blockDataOutput = new DataOutputStream(this);
 149:     setBlockDataMode(true);
 150:     replacementEnabled = false;
 151:     isSerializing = false;
 152:     nextOID = baseWireHandle;
 153:     OIDLookupTable = new ObjectIdentityMap2Int();
 154:     protocolVersion = defaultProtocolVersion;
 155:     useSubclassMethod = false;
 156:     writeStreamHeader();
 157: 
 158:     if (DEBUG)
 159:       {
 160:         String val = System.getProperty("gcj.dumpobjects");
 161:         if (val != null && !val.equals(""))
 162:           dump = true;
 163:       }
 164:   }
 165: 
 166:   /**
 167:    * Writes a representation of <code>obj</code> to the underlying
 168:    * output stream by writing out information about its class, then
 169:    * writing out each of the objects non-transient, non-static
 170:    * fields.  If any of these fields are other objects,
 171:    * they are written out in the same manner.
 172:    *
 173:    * This method can be overriden by a class by implementing
 174:    * <code>private void writeObject (ObjectOutputStream)</code>.
 175:    *
 176:    * If an exception is thrown from this method, the stream is left in
 177:    * an undefined state.
 178:    *
 179:    * @param obj the object to serialize.
 180:    * @exception NotSerializableException An attempt was made to
 181:    * serialize an <code>Object</code> that is not serializable.
 182:    *
 183:    * @exception InvalidClassException Somebody tried to serialize
 184:    * an object which is wrongly formatted.
 185:    *
 186:    * @exception IOException Exception from underlying
 187:    * <code>OutputStream</code>.
 188:    * @see #writeUnshared(Object)
 189:    */
 190:   public final void writeObject(Object obj) throws IOException
 191:   {
 192:     writeObject(obj, true);
 193:   }
 194: 
 195:   /**
 196:    * Writes an object to the stream in the same manner as
 197:    * {@link #writeObject(Object)}, but without the use of
 198:    * references.  As a result, the object is always written
 199:    * to the stream in full.  Likewise, if an object is written
 200:    * by this method and is then later written again by
 201:    * {@link #writeObject(Object)}, both calls will write out
 202:    * the object in full, as the later call to
 203:    * {@link #writeObject(Object)} will know nothing of the
 204:    * earlier use of {@link #writeUnshared(Object)}.
 205:    *
 206:    * @param obj the object to serialize.
 207:    * @throws NotSerializableException if the object being
 208:    *                                  serialized does not implement
 209:    *                                  {@link Serializable}.
 210:    * @throws InvalidClassException if a problem occurs with
 211:    *                               the class of the object being
 212:    *                               serialized.
 213:    * @throws IOException if an I/O error occurs on the underlying
 214:    *                     <code>OutputStream</code>.
 215:    * @since 1.4
 216:    * @see #writeObject(Object)
 217:    */
 218:   public void writeUnshared(Object obj)
 219:     throws IOException
 220:   {
 221:     writeObject(obj, false);
 222:   }
 223: 
 224:   /**
 225:    * Writes a representation of <code>obj</code> to the underlying
 226:    * output stream by writing out information about its class, then
 227:    * writing out each of the objects non-transient, non-static
 228:    * fields.  If any of these fields are other objects,
 229:    * they are written out in the same manner.
 230:    *
 231:    * This method can be overriden by a class by implementing
 232:    * <code>private void writeObject (ObjectOutputStream)</code>.
 233:    *
 234:    * If an exception is thrown from this method, the stream is left in
 235:    * an undefined state.
 236:    *
 237:    * @param obj the object to serialize.
 238:    * @param shared true if the serialized object should be
 239:    *               shared with later calls.
 240:    * @exception NotSerializableException An attempt was made to
 241:    * serialize an <code>Object</code> that is not serializable.
 242:    *
 243:    * @exception InvalidClassException Somebody tried to serialize
 244:    * an object which is wrongly formatted.
 245:    *
 246:    * @exception IOException Exception from underlying
 247:    * <code>OutputStream</code>.
 248:    * @see #writeUnshared(Object)
 249:    */
 250:   private final void writeObject(Object obj, boolean shared)
 251:     throws IOException
 252:   {
 253:     if (useSubclassMethod)
 254:       {
 255:         if (dump)
 256:           dumpElementln ("WRITE OVERRIDE: " + obj);
 257: 
 258:         writeObjectOverride(obj);
 259:         return;
 260:       }
 261: 
 262:     if (dump)
 263:       dumpElementln ("WRITE: ", obj);
 264: 
 265:     depth += 2;
 266: 
 267:     boolean was_serializing = isSerializing;
 268:     boolean old_mode = setBlockDataMode(false);
 269:     try
 270:       {
 271:         isSerializing = true;
 272:         boolean replaceDone = false;
 273:         Object replacedObject = null;
 274: 
 275:         while (true)
 276:           {
 277:             if (obj == null)
 278:               {
 279:                 realOutput.writeByte(TC_NULL);
 280:                 break;
 281:               }
 282: 
 283:             int handle = findHandle(obj);
 284:             if (handle >= 0 && shared)
 285:               {
 286:                 realOutput.writeByte(TC_REFERENCE);
 287:                 realOutput.writeInt(handle);
 288:                 break;
 289:               }
 290: 
 291:             if (obj instanceof Class)
 292:               {
 293:                 Class cl = (Class)obj;
 294:                 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
 295:                 realOutput.writeByte(TC_CLASS);
 296:                 if (!osc.isProxyClass)
 297:                   {
 298:                     writeObject (osc);
 299:                   }
 300:                 else
 301:                   {System.err.println("1");
 302:                     realOutput.writeByte(TC_PROXYCLASSDESC);
 303:                     Class[] intfs = cl.getInterfaces();
 304:                     realOutput.writeInt(intfs.length);
 305:                     for (int i = 0; i < intfs.length; i++)
 306:                       realOutput.writeUTF(intfs[i].getName());
 307: 
 308:                     boolean oldmode = setBlockDataMode(true);
 309:                     annotateProxyClass(cl);
 310:                     setBlockDataMode(oldmode);
 311:                     realOutput.writeByte(TC_ENDBLOCKDATA);
 312: 
 313:                     writeObject(osc.getSuper());
 314:                   }
 315:                 if (shared)
 316:                   assignNewHandle(obj);
 317:                 break;
 318:               }
 319: 
 320:             if (obj instanceof ObjectStreamClass)
 321:               {
 322:                 writeClassDescriptor((ObjectStreamClass) obj);
 323:                 break;
 324:               }
 325: 
 326:             Class clazz = obj.getClass();
 327:             ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
 328:             if (osc == null)
 329:               throw new NotSerializableException(clazz.getName());
 330: 
 331:             if (osc.isEnum())
 332:               {
 333:                 /* TC_ENUM classDesc newHandle enumConstantName */
 334:                 realOutput.writeByte(TC_ENUM);
 335:                 writeObject(osc);
 336:                 if (shared)
 337:                   assignNewHandle(obj);
 338:                 writeObject(((Enum) obj).name());
 339:                 break;
 340:               }
 341: 
 342:             if ((replacementEnabled || obj instanceof Serializable)
 343:                 && ! replaceDone)
 344:               {
 345:                 replacedObject = obj;
 346: 
 347:                 if (obj instanceof Serializable)
 348:                   {
 349:                     try
 350:                       {
 351:                         Method m = osc.writeReplaceMethod;
 352:                         if (m != null)
 353:                             obj = m.invoke(obj, new Object[0]);
 354:                       }
 355:                     catch (IllegalAccessException ignore)
 356:                       {
 357:                       }
 358:                     catch (InvocationTargetException ignore)
 359:                       {
 360:                       }
 361:                   }
 362: 
 363:                 if (replacementEnabled)
 364:                   obj = replaceObject(obj);
 365: 
 366:                 replaceDone = true;
 367:                 continue;
 368:               }
 369: 
 370:             if (obj instanceof String)
 371:               {
 372:                 String s = (String)obj;
 373:                 long l = realOutput.getUTFlength(s, 0, 0);
 374:                 if (l <= 65535)
 375:                   {
 376:                     realOutput.writeByte(TC_STRING);
 377:                     if (shared)
 378:                       assignNewHandle(obj);
 379:                     realOutput.writeUTFShort(s, (int)l);
 380:                   }
 381:                 else
 382:                   {
 383:                     realOutput.writeByte(TC_LONGSTRING);
 384:                     if (shared)
 385:                       assignNewHandle(obj);
 386:                     realOutput.writeUTFLong(s, l);
 387:                   }
 388:                 break;
 389:               }
 390: 
 391:             if (clazz.isArray ())
 392:               {
 393:                 realOutput.writeByte(TC_ARRAY);
 394:                 writeObject(osc);
 395:                 if (shared)
 396:                   assignNewHandle(obj);
 397:                 writeArraySizeAndElements(obj, clazz.getComponentType());
 398:                 break;
 399:               }
 400: 
 401:             realOutput.writeByte(TC_OBJECT);
 402:             writeObject(osc);
 403: 
 404:             if (shared)
 405:               if (replaceDone)
 406:                 assignNewHandle(replacedObject);
 407:               else
 408:                 assignNewHandle(obj);
 409: 
 410:             if (obj instanceof Externalizable)
 411:               {
 412:                 if (protocolVersion == PROTOCOL_VERSION_2)
 413:                   setBlockDataMode(true);
 414: 
 415:                 ((Externalizable)obj).writeExternal(this);
 416: 
 417:                 if (protocolVersion == PROTOCOL_VERSION_2)
 418:                   {
 419:                     setBlockDataMode(false);
 420:                     realOutput.writeByte(TC_ENDBLOCKDATA);
 421:                   }
 422: 
 423:                 break;
 424:               }
 425: 
 426:             if (obj instanceof Serializable)
 427:               {
 428:                 Object prevObject = this.currentObject;
 429:                 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
 430:                 currentObject = obj;
 431:                 ObjectStreamClass[] hierarchy = osc.hierarchy();
 432: 
 433:                 for (int i = 0; i < hierarchy.length; i++)
 434:                   {
 435:                     currentObjectStreamClass = hierarchy[i];
 436: 
 437:                     fieldsAlreadyWritten = false;
 438:                     if (currentObjectStreamClass.hasWriteMethod())
 439:                       {
 440:                         if (dump)
 441:                           dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
 442:                         setBlockDataMode(true);
 443:                         callWriteMethod(obj, currentObjectStreamClass);
 444:                         setBlockDataMode(false);
 445:                         realOutput.writeByte(TC_ENDBLOCKDATA);
 446:                         if (dump)
 447:                           dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
 448:                       }
 449:                     else
 450:                       {
 451:                         if (dump)
 452:                           dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
 453:                         writeFields(obj, currentObjectStreamClass);
 454:                       }
 455:                   }
 456: 
 457:                 this.currentObject = prevObject;
 458:                 this.currentObjectStreamClass = prevObjectStreamClass;
 459:                 currentPutField = null;
 460:                 break;
 461:               }
 462: 
 463:             throw new NotSerializableException(clazz.getName()
 464:                                                + " in "
 465:                                                + obj.getClass());
 466:           } // end pseudo-loop
 467:       }
 468:     catch (ObjectStreamException ose)
 469:       {
 470:         // Rethrow these are fatal.
 471:         throw ose;
 472:       }
 473:     catch (IOException e)
 474:       {
 475:         realOutput.writeByte(TC_EXCEPTION);
 476:         reset(true);
 477: 
 478:         setBlockDataMode(false);
 479:         try
 480:           {
 481:             if (DEBUG)
 482:               {
 483:                 e.printStackTrace(System.out);
 484:               }
 485:             writeObject(e);
 486:           }
 487:         catch (IOException ioe)
 488:           {
 489:             StreamCorruptedException ex =
 490:               new StreamCorruptedException
 491:               (ioe + " thrown while exception was being written to stream.");
 492:             if (DEBUG)
 493:               {
 494:                 ex.printStackTrace(System.out);
 495:               }
 496:             throw ex;
 497:           }
 498: 
 499:         reset (true);
 500: 
 501:       }
 502:     finally
 503:       {
 504:         isSerializing = was_serializing;
 505:         setBlockDataMode(old_mode);
 506:         depth -= 2;
 507: 
 508:         if (dump)
 509:           dumpElementln ("END: ", obj);
 510:       }
 511:   }
 512: 
 513:   protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
 514:   {
 515:     if (osc.isProxyClass)
 516:       {
 517:         realOutput.writeByte(TC_PROXYCLASSDESC);
 518:         Class[] intfs = osc.forClass().getInterfaces();
 519:         realOutput.writeInt(intfs.length);
 520:         for (int i = 0; i < intfs.length; i++)
 521:           realOutput.writeUTF(intfs[i].getName());
 522: 
 523:         assignNewHandle(osc);
 524: 
 525:         boolean oldmode = setBlockDataMode(true);
 526:         annotateProxyClass(osc.forClass());
 527:         setBlockDataMode(oldmode);
 528:         realOutput.writeByte(TC_ENDBLOCKDATA);
 529:       }
 530:     else
 531:       {
 532:         realOutput.writeByte(TC_CLASSDESC);
 533:         realOutput.writeUTF(osc.getName());
 534:         if (osc.isEnum())
 535:           realOutput.writeLong(0L);
 536:         else
 537:           realOutput.writeLong(osc.getSerialVersionUID());
 538:         assignNewHandle(osc);
 539: 
 540:         int flags = osc.getFlags();
 541: 
 542:         if (protocolVersion == PROTOCOL_VERSION_2
 543:             && osc.isExternalizable())
 544:         flags |= SC_BLOCK_DATA;
 545: 
 546:         realOutput.writeByte(flags);
 547: 
 548:         ObjectStreamField[] fields = osc.fields;
 549: 
 550:         if (fields == ObjectStreamClass.INVALID_FIELDS)
 551:           throw new InvalidClassException
 552:                   (osc.getName(), "serialPersistentFields is invalid");
 553: 
 554:         realOutput.writeShort(fields.length);
 555: 
 556:         ObjectStreamField field;
 557:         for (int i = 0; i < fields.length; i++)
 558:           {
 559:             field = fields[i];
 560:             realOutput.writeByte(field.getTypeCode ());
 561:             realOutput.writeUTF(field.getName ());
 562: 
 563:             if (! field.isPrimitive())
 564:               writeObject(field.getTypeString());
 565:           }
 566: 
 567:         boolean oldmode = setBlockDataMode(true);
 568:         annotateClass(osc.forClass());
 569:         setBlockDataMode(oldmode);
 570:         realOutput.writeByte(TC_ENDBLOCKDATA);
 571:       }
 572: 
 573:     if (osc.isSerializable() || osc.isExternalizable())
 574:       writeObject(osc.getSuper());
 575:     else
 576:       writeObject(null);
 577:   }
 578: 
 579:   /**
 580:    * Writes the current objects non-transient, non-static fields from
 581:    * the current class to the underlying output stream.
 582:    *
 583:    * This method is intended to be called from within a object's
 584:    * <code>private void writeObject (ObjectOutputStream)</code>
 585:    * method.
 586:    *
 587:    * @exception NotActiveException This method was called from a
 588:    * context other than from the current object's and current class's
 589:    * <code>private void writeObject (ObjectOutputStream)</code>
 590:    * method.
 591:    *
 592:    * @exception IOException Exception from underlying
 593:    * <code>OutputStream</code>.
 594:    */
 595:   public void defaultWriteObject()
 596:     throws IOException, NotActiveException
 597:   {
 598:     markFieldsWritten();
 599:     writeFields(currentObject, currentObjectStreamClass);
 600:   }
 601: 
 602: 
 603:   private void markFieldsWritten() throws IOException
 604:   {
 605:     if (currentObject == null || currentObjectStreamClass == null)
 606:       throw new NotActiveException
 607:         ("defaultWriteObject called by non-active class and/or object");
 608: 
 609:     if (fieldsAlreadyWritten)
 610:       throw new IOException
 611:         ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
 612: 
 613:     fieldsAlreadyWritten = true;
 614:   }
 615: 
 616:   /**
 617:    * Resets stream to state equivalent to the state just after it was
 618:    * constructed.
 619:    *
 620:    * Causes all objects previously written to the stream to be
 621:    * forgotten.  A notification of this reset is also written to the
 622:    * underlying stream.
 623:    *
 624:    * @exception IOException Exception from underlying
 625:    * <code>OutputStream</code> or reset called while serialization is
 626:    * in progress.
 627:    */
 628:   public void reset() throws IOException
 629:   {
 630:     reset(false);
 631:   }
 632: 
 633: 
 634:   private void reset(boolean internal) throws IOException
 635:   {
 636:     if (!internal)
 637:       {
 638:         if (isSerializing)
 639:           throw new IOException("Reset called while serialization in progress");
 640: 
 641:         realOutput.writeByte(TC_RESET);
 642:       }
 643: 
 644:     clearHandles();
 645:   }
 646: 
 647: 
 648:   /**
 649:    * Informs this <code>ObjectOutputStream</code> to write data
 650:    * according to the specified protocol.  There are currently two
 651:    * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
 652:    * and <code>PROTOCOL_VERSION_2</code>.  This implementation writes
 653:    * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
 654:    * since the JDK 1.2.
 655:    * <p>
 656:    * For an explanation of the differences between the two protocols
 657:    * see the Java Object Serialization Specification.
 658:    * </p>
 659:    *
 660:    * @param version the version to use.
 661:    *
 662:    * @throws IllegalArgumentException if <code>version</code> is not a valid
 663:    * protocol.
 664:    * @throws IllegalStateException if called after the first the first object
 665:    * was serialized.
 666:    * @throws IOException if an I/O error occurs.
 667:    *
 668:    * @see ObjectStreamConstants#PROTOCOL_VERSION_1
 669:    * @see ObjectStreamConstants#PROTOCOL_VERSION_2
 670:    *
 671:    * @since 1.2
 672:    */
 673:   public void useProtocolVersion(int version) throws IOException
 674:   {
 675:     if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
 676:       throw new IllegalArgumentException("Invalid protocol version requested.");
 677: 
 678:     if (nextOID != baseWireHandle)
 679:       throw new IllegalStateException("Protocol version cannot be changed "
 680:                                       + "after serialization started.");
 681: 
 682:     protocolVersion = version;
 683:   }
 684: 
 685:   /**
 686:    * An empty hook that allows subclasses to write extra information
 687:    * about classes to the stream.  This method is called the first
 688:    * time each class is seen, and after all of the standard
 689:    * information about the class has been written.
 690:    *
 691:    * @exception IOException Exception from underlying
 692:    * <code>OutputStream</code>.
 693:    *
 694:    * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
 695:    */
 696:   protected void annotateClass(Class<?> cl) throws IOException
 697:   {
 698:   }
 699: 
 700:   protected void annotateProxyClass(Class<?> cl) throws IOException
 701:   {
 702:   }
 703: 
 704:   /**
 705:    * Allows subclasses to replace objects that are written to the
 706:    * stream with other objects to be written in their place.  This
 707:    * method is called the first time each object is encountered
 708:    * (modulo reseting of the stream).
 709:    *
 710:    * This method must be enabled before it will be called in the
 711:    * serialization process.
 712:    *
 713:    * @exception IOException Exception from underlying
 714:    * <code>OutputStream</code>.
 715:    *
 716:    * @see #enableReplaceObject(boolean)
 717:    */
 718:   protected Object replaceObject(Object obj) throws IOException
 719:   {
 720:     return obj;
 721:   }
 722: 
 723: 
 724:   /**
 725:    * If <code>enable</code> is <code>true</code> and this object is
 726:    * trusted, then <code>replaceObject (Object)</code> will be called
 727:    * in subsequent calls to <code>writeObject (Object)</code>.
 728:    * Otherwise, <code>replaceObject (Object)</code> will not be called.
 729:    *
 730:    * @exception SecurityException This class is not trusted.
 731:    */
 732:   protected boolean enableReplaceObject(boolean enable)
 733:     throws SecurityException
 734:   {
 735:     if (enable)
 736:       {
 737:         SecurityManager sm = System.getSecurityManager();
 738:         if (sm != null)
 739:           sm.checkPermission(new SerializablePermission("enableSubstitution"));
 740:       }
 741: 
 742:     boolean old_val = replacementEnabled;
 743:     replacementEnabled = enable;
 744:     return old_val;
 745:   }
 746: 
 747: 
 748:   /**
 749:    * Writes stream magic and stream version information to the
 750:    * underlying stream.
 751:    *
 752:    * @exception IOException Exception from underlying
 753:    * <code>OutputStream</code>.
 754:    */
 755:   protected void writeStreamHeader() throws IOException
 756:   {
 757:     realOutput.writeShort(STREAM_MAGIC);
 758:     realOutput.writeShort(STREAM_VERSION);
 759:   }
 760: 
 761:   /**
 762:    * Protected constructor that allows subclasses to override
 763:    * serialization.  This constructor should be called by subclasses
 764:    * that wish to override <code>writeObject (Object)</code>.  This
 765:    * method does a security check <i>NOTE: currently not
 766:    * implemented</i>, then sets a flag that informs
 767:    * <code>writeObject (Object)</code> to call the subclasses
 768:    * <code>writeObjectOverride (Object)</code> method.
 769:    *
 770:    * @see #writeObjectOverride(Object)
 771:    */
 772:   protected ObjectOutputStream() throws IOException, SecurityException
 773:   {
 774:     SecurityManager sec_man = System.getSecurityManager ();
 775:     if (sec_man != null)
 776:       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
 777:     useSubclassMethod = true;
 778:   }
 779: 
 780: 
 781:   /**
 782:    * This method allows subclasses to override the default
 783:    * serialization mechanism provided by
 784:    * <code>ObjectOutputStream</code>.  To make this method be used for
 785:    * writing objects, subclasses must invoke the 0-argument
 786:    * constructor on this class from there constructor.
 787:    *
 788:    * @see #ObjectOutputStream()
 789:    *
 790:    * @exception NotActiveException Subclass has arranged for this
 791:    * method to be called, but did not implement this method.
 792:    */
 793:   protected void writeObjectOverride(Object obj) throws NotActiveException,
 794:     IOException
 795:   {
 796:     throw new NotActiveException
 797:       ("Subclass of ObjectOutputStream must implement writeObjectOverride");
 798:   }
 799: 
 800: 
 801:   /**
 802:    * @see DataOutputStream#write(int)
 803:    */
 804:   public void write (int data) throws IOException
 805:   {
 806:     if (writeDataAsBlocks)
 807:       {
 808:         if (blockDataCount == BUFFER_SIZE)
 809:           drain();
 810: 
 811:         blockData[ blockDataCount++ ] = (byte)data;
 812:       }
 813:     else
 814:       realOutput.write(data);
 815:   }
 816: 
 817: 
 818:   /**
 819:    * @see DataOutputStream#write(byte[])
 820:    */
 821:   public void write(byte[] b) throws IOException
 822:   {
 823:     write(b, 0, b.length);
 824:   }
 825: 
 826: 
 827:   /**
 828:    * @see DataOutputStream#write(byte[],int,int)
 829:    */
 830:   public void write(byte[] b, int off, int len) throws IOException
 831:   {
 832:     if (writeDataAsBlocks)
 833:       {
 834:         if (len < 0)
 835:           throw new IndexOutOfBoundsException();
 836: 
 837:         if (blockDataCount + len < BUFFER_SIZE)
 838:           {
 839:             System.arraycopy(b, off, blockData, blockDataCount, len);
 840:             blockDataCount += len;
 841:           }
 842:         else
 843:           {
 844:             drain();
 845:             writeBlockDataHeader(len);
 846:             realOutput.write(b, off, len);
 847:           }
 848:       }
 849:     else
 850:       realOutput.write(b, off, len);
 851:   }
 852: 
 853: 
 854:   /**
 855:    * @see DataOutputStream#flush()
 856:    */
 857:   public void flush () throws IOException
 858:   {
 859:     drain();
 860:     realOutput.flush();
 861:   }
 862: 
 863: 
 864:   /**
 865:    * Causes the block-data buffer to be written to the underlying
 866:    * stream, but does not flush underlying stream.
 867:    *
 868:    * @exception IOException Exception from underlying
 869:    * <code>OutputStream</code>.
 870:    */
 871:   protected void drain() throws IOException
 872:   {
 873:     if (blockDataCount == 0)
 874:       return;
 875: 
 876:     if (writeDataAsBlocks)
 877:       writeBlockDataHeader(blockDataCount);
 878:     realOutput.write(blockData, 0, blockDataCount);
 879:     blockDataCount = 0;
 880:   }
 881: 
 882: 
 883:   /**
 884:    * @see java.io.DataOutputStream#close ()
 885:    */
 886:   public void close() throws IOException
 887:   {
 888:     flush();
 889:     realOutput.close();
 890:   }
 891: 
 892: 
 893:   /**
 894:    * @see java.io.DataOutputStream#writeBoolean (boolean)
 895:    */
 896:   public void writeBoolean(boolean data) throws IOException
 897:   {
 898:     blockDataOutput.writeBoolean(data);
 899:   }
 900: 
 901: 
 902:   /**
 903:    * @see java.io.DataOutputStream#writeByte (int)
 904:    */
 905:   public void writeByte(int data) throws IOException
 906:   {
 907:     blockDataOutput.writeByte(data);
 908:   }
 909: 
 910: 
 911:   /**
 912:    * @see java.io.DataOutputStream#writeShort (int)
 913:    */
 914:   public void writeShort (int data) throws IOException
 915:   {
 916:     blockDataOutput.writeShort(data);
 917:   }
 918: 
 919: 
 920:   /**
 921:    * @see java.io.DataOutputStream#writeChar (int)
 922:    */
 923:   public void writeChar(int data) throws IOException
 924:   {
 925:     blockDataOutput.writeChar(data);
 926:   }
 927: 
 928: 
 929:   /**
 930:    * @see java.io.DataOutputStream#writeInt (int)
 931:    */
 932:   public void writeInt(int data) throws IOException
 933:   {
 934:     blockDataOutput.writeInt(data);
 935:   }
 936: 
 937: 
 938:   /**
 939:    * @see java.io.DataOutputStream#writeLong (long)
 940:    */
 941:   public void writeLong(long data) throws IOException
 942:   {
 943:     blockDataOutput.writeLong(data);
 944:   }
 945: 
 946: 
 947:   /**
 948:    * @see java.io.DataOutputStream#writeFloat (float)
 949:    */
 950:   public void writeFloat(float data) throws IOException
 951:   {
 952:     blockDataOutput.writeFloat(data);
 953:   }
 954: 
 955: 
 956:   /**
 957:    * @see java.io.DataOutputStream#writeDouble (double)
 958:    */
 959:   public void writeDouble(double data) throws IOException
 960:   {
 961:     blockDataOutput.writeDouble(data);
 962:   }
 963: 
 964: 
 965:   /**
 966:    * @see java.io.DataOutputStream#writeBytes (java.lang.String)
 967:    */
 968:   public void writeBytes(String data) throws IOException
 969:   {
 970:     blockDataOutput.writeBytes(data);
 971:   }
 972: 
 973: 
 974:   /**
 975:    * @see java.io.DataOutputStream#writeChars (java.lang.String)
 976:    */
 977:   public void writeChars(String data) throws IOException
 978:   {
 979:     dataOutput.writeChars(data);
 980:   }
 981: 
 982: 
 983:   /**
 984:    * @see java.io.DataOutputStream#writeUTF (java.lang.String)
 985:    */
 986:   public void writeUTF(String data) throws IOException
 987:   {
 988:     dataOutput.writeUTF(data);
 989:   }
 990: 
 991: 
 992:   /**
 993:    * This class allows a class to specify exactly which fields should
 994:    * be written, and what values should be written for these fields.
 995:    *
 996:    * XXX: finish up comments
 997:    */
 998:   public abstract static class PutField
 999:   {
1000:     public abstract void put (String name, boolean value);
1001:     public abstract void put (String name, byte value);
1002:     public abstract void put (String name, char value);
1003:     public abstract void put (String name, double value);
1004:     public abstract void put (String name, float value);
1005:     public abstract void put (String name, int value);
1006:     public abstract void put (String name, long value);
1007:     public abstract void put (String name, short value);
1008:     public abstract void put (String name, Object value);
1009: 
1010:     /**
1011:      * @deprecated
1012:      */
1013:     public abstract void write (ObjectOutput out) throws IOException;
1014:   }
1015: 
1016:   public PutField putFields() throws IOException
1017:   {
1018:     if (currentPutField != null)
1019:       return currentPutField;
1020: 
1021:     currentPutField = new PutField()
1022:       {
1023:         private byte[] prim_field_data
1024:           = new byte[currentObjectStreamClass.primFieldSize];
1025:         private Object[] objs
1026:           = new Object[currentObjectStreamClass.objectFieldCount];
1027: 
1028:         private ObjectStreamField getField (String name)
1029:         {
1030:           ObjectStreamField field
1031:             = currentObjectStreamClass.getField(name);
1032: 
1033:           if (field == null)
1034:             throw new IllegalArgumentException("no such serializable field " + name);
1035: 
1036:           return field;
1037:         }
1038: 
1039:         public void put(String name, boolean value)
1040:         {
1041:           ObjectStreamField field = getField(name);
1042: 
1043:           checkType(field, 'Z');
1044:           prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
1045:         }
1046: 
1047:         public void put(String name, byte value)
1048:         {
1049:           ObjectStreamField field = getField(name);
1050: 
1051:           checkType(field, 'B');
1052:           prim_field_data[field.getOffset()] = value;
1053:         }
1054: 
1055:         public void put(String name, char value)
1056:         {
1057:           ObjectStreamField field = getField(name);
1058: 
1059:           checkType(field, 'C');
1060:           int off = field.getOffset();
1061:           prim_field_data[off++] = (byte)(value >>> 8);
1062:           prim_field_data[off] = (byte)value;
1063:         }
1064: 
1065:         public void put(String name, double value)
1066:         {
1067:           ObjectStreamField field = getField (name);
1068: 
1069:           checkType(field, 'D');
1070:           int off = field.getOffset();
1071:           long l_value = Double.doubleToLongBits (value);
1072:           prim_field_data[off++] = (byte)(l_value >>> 52);
1073:           prim_field_data[off++] = (byte)(l_value >>> 48);
1074:           prim_field_data[off++] = (byte)(l_value >>> 40);
1075:           prim_field_data[off++] = (byte)(l_value >>> 32);
1076:           prim_field_data[off++] = (byte)(l_value >>> 24);
1077:           prim_field_data[off++] = (byte)(l_value >>> 16);
1078:           prim_field_data[off++] = (byte)(l_value >>> 8);
1079:           prim_field_data[off] = (byte)l_value;
1080:         }
1081: 
1082:         public void put(String name, float value)
1083:         {
1084:           ObjectStreamField field = getField(name);
1085: 
1086:           checkType(field, 'F');
1087:           int off = field.getOffset();
1088:           int i_value = Float.floatToIntBits(value);
1089:           prim_field_data[off++] = (byte)(i_value >>> 24);
1090:           prim_field_data[off++] = (byte)(i_value >>> 16);
1091:           prim_field_data[off++] = (byte)(i_value >>> 8);
1092:           prim_field_data[off] = (byte)i_value;
1093:         }
1094: 
1095:         public void put(String name, int value)
1096:         {
1097:           ObjectStreamField field = getField(name);
1098:           checkType(field, 'I');
1099:           int off = field.getOffset();
1100:           prim_field_data[off++] = (byte)(value >>> 24);
1101:           prim_field_data[off++] = (byte)(value >>> 16);
1102:           prim_field_data[off++] = (byte)(value >>> 8);
1103:           prim_field_data[off] = (byte)value;
1104:         }
1105: 
1106:         public void put(String name, long value)
1107:         {
1108:           ObjectStreamField field = getField(name);
1109:           checkType(field, 'J');
1110:           int off = field.getOffset();
1111:           prim_field_data[off++] = (byte)(value >>> 52);
1112:           prim_field_data[off++] = (byte)(value >>> 48);
1113:           prim_field_data[off++] = (byte)(value >>> 40);
1114:           prim_field_data[off++] = (byte)(value >>> 32);
1115:           prim_field_data[off++] = (byte)(value >>> 24);
1116:           prim_field_data[off++] = (byte)(value >>> 16);
1117:           prim_field_data[off++] = (byte)(value >>> 8);
1118:           prim_field_data[off] = (byte)value;
1119:         }
1120: 
1121:         public void put(String name, short value)
1122:         {
1123:           ObjectStreamField field = getField(name);
1124:           checkType(field, 'S');
1125:           int off = field.getOffset();
1126:           prim_field_data[off++] = (byte)(value >>> 8);
1127:           prim_field_data[off] = (byte)value;
1128:         }
1129: 
1130:         public void put(String name, Object value)
1131:         {
1132:           ObjectStreamField field = getField(name);
1133: 
1134:           if (value != null &&
1135:               ! field.getType().isAssignableFrom(value.getClass ()))
1136:             throw new IllegalArgumentException("Class " + value.getClass() +
1137:                                                " cannot be cast to " + field.getType());
1138:           objs[field.getOffset()] = value;
1139:         }
1140: 
1141:         public void write(ObjectOutput out) throws IOException
1142:         {
1143:           // Apparently Block data is not used with PutField as per
1144:           // empirical evidence against JDK 1.2.  Also see Mauve test
1145:           // java.io.ObjectInputOutput.Test.GetPutField.
1146:           boolean oldmode = setBlockDataMode(false);
1147:           out.write(prim_field_data);
1148:           for (int i = 0; i < objs.length; ++ i)
1149:             out.writeObject(objs[i]);
1150:           setBlockDataMode(oldmode);
1151:         }
1152: 
1153:         private void checkType(ObjectStreamField field, char type)
1154:           throws IllegalArgumentException
1155:         {
1156:           if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1157:               != type)
1158:             throw new IllegalArgumentException();
1159:         }
1160:       };
1161:     // end PutFieldImpl
1162: 
1163:     return currentPutField;
1164:   }
1165: 
1166: 
1167:   public void writeFields() throws IOException
1168:   {
1169:     if (currentPutField == null)
1170:       throw new NotActiveException("writeFields can only be called after putFields has been called");
1171: 
1172:     markFieldsWritten();
1173:     currentPutField.write(this);
1174:   }
1175: 
1176: 
1177:   // write out the block-data buffer, picking the correct header
1178:   // depending on the size of the buffer
1179:   private void writeBlockDataHeader(int size) throws IOException
1180:   {
1181:     if (size < 256)
1182:       {
1183:         realOutput.writeByte(TC_BLOCKDATA);
1184:         realOutput.write(size);
1185:       }
1186:     else
1187:       {
1188:         realOutput.writeByte(TC_BLOCKDATALONG);
1189:         realOutput.writeInt(size);
1190:       }
1191:   }
1192: 
1193: 
1194:   // lookup the handle for OBJ, return null if OBJ doesn't have a
1195:   // handle yet
1196:   private int findHandle(Object obj)
1197:   {
1198:     return OIDLookupTable.get(obj);
1199:   }
1200: 
1201: 
1202:   // assigns the next availible handle to OBJ
1203:   private int assignNewHandle(Object obj)
1204:   {
1205:     OIDLookupTable.put(obj, nextOID);
1206:     return nextOID++;
1207:   }
1208: 
1209: 
1210:   // resets mapping from objects to handles
1211:   private void clearHandles()
1212:   {
1213:     nextOID = baseWireHandle;
1214:     OIDLookupTable.clear();
1215:   }
1216: 
1217: 
1218:   // write out array size followed by each element of the array
1219:   private void writeArraySizeAndElements(Object array, Class clazz)
1220:     throws IOException
1221:   {
1222:     int length = Array.getLength(array);
1223: 
1224:     if (clazz.isPrimitive())
1225:       {
1226:         if (clazz == Boolean.TYPE)
1227:           {
1228:             boolean[] cast_array = (boolean[])array;
1229:             realOutput.writeInt (length);
1230:             for (int i = 0; i < length; i++)
1231:               realOutput.writeBoolean(cast_array[i]);
1232:             return;
1233:           }
1234:         if (clazz == Byte.TYPE)
1235:           {
1236:             byte[] cast_array = (byte[])array;
1237:             realOutput.writeInt(length);
1238:             realOutput.write(cast_array, 0, length);
1239:             return;
1240:           }
1241:         if (clazz == Character.TYPE)
1242:           {
1243:             char[] cast_array = (char[])array;
1244:             realOutput.writeInt(length);
1245:             for (int i = 0; i < length; i++)
1246:               realOutput.writeChar(cast_array[i]);
1247:             return;
1248:           }
1249:         if (clazz == Double.TYPE)
1250:           {
1251:             double[] cast_array = (double[])array;
1252:             realOutput.writeInt(length);
1253:             for (int i = 0; i < length; i++)
1254:               realOutput.writeDouble(cast_array[i]);
1255:             return;
1256:           }
1257:         if (clazz == Float.TYPE)
1258:           {
1259:             float[] cast_array = (float[])array;
1260:             realOutput.writeInt(length);
1261:             for (int i = 0; i < length; i++)
1262:               realOutput.writeFloat(cast_array[i]);
1263:             return;
1264:           }
1265:         if (clazz == Integer.TYPE)
1266:           {
1267:             int[] cast_array = (int[])array;
1268:             realOutput.writeInt(length);
1269:             for (int i = 0; i < length; i++)
1270:               realOutput.writeInt(cast_array[i]);
1271:             return;
1272:           }
1273:         if (clazz == Long.TYPE)
1274:           {
1275:             long[] cast_array = (long[])array;
1276:             realOutput.writeInt (length);
1277:             for (int i = 0; i < length; i++)
1278:               realOutput.writeLong(cast_array[i]);
1279:             return;
1280:           }
1281:         if (clazz == Short.TYPE)
1282:           {
1283:             short[] cast_array = (short[])array;
1284:             realOutput.writeInt (length);
1285:             for (int i = 0; i < length; i++)
1286:               realOutput.writeShort(cast_array[i]);
1287:             return;
1288:           }
1289:       }
1290:     else
1291:       {
1292:         Object[] cast_array = (Object[])array;
1293:         realOutput.writeInt(length);
1294:         for (int i = 0; i < length; i++)
1295:           writeObject(cast_array[i]);
1296:       }
1297:   }
1298: 
1299: 
1300: /* GCJ LOCAL */
1301:   // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1302:   // FIELDS are already supposed already to be in canonical order, but
1303:   // under some circumstances (to do with Proxies) this isn't the
1304:   // case, so we call ensureFieldsSet().
1305:   private void writeFields(Object obj, ObjectStreamClass osc)
1306:     throws IOException
1307:   {
1308:     osc.ensureFieldsSet(osc.forClass());
1309: /* END GCJ LOCAL */
1310: 
1311:     ObjectStreamField[] fields = osc.fields;
1312:     boolean oldmode = setBlockDataMode(false);
1313: 
1314:     try
1315:       {
1316:         writeFields(obj,fields);
1317:       }
1318:     catch (IllegalArgumentException _)
1319:       {
1320:         InvalidClassException e = new InvalidClassException
1321:           ("writing fields of class " + osc.forClass().getName());
1322:         e.initCause(_);
1323:         throw e;
1324:       }
1325:     catch (IOException e)
1326:       {
1327:         throw e;
1328:       }
1329:     catch (Exception _)
1330:       {
1331:         IOException e = new IOException("Unexpected exception " + _);
1332:         e.initCause(_);
1333:         throw(e);
1334:       }
1335: 
1336:     setBlockDataMode(oldmode);
1337:   }
1338: 
1339: 
1340:   /**
1341:    * Helper function for writeFields(Object,ObjectStreamClass): write
1342:    * fields from given fields array.  Pass exception on.
1343:    *
1344:    * @param obj the object to be written
1345:    *
1346:    * @param fields the fields of obj to be written.
1347:    */
1348:   private void writeFields(Object obj, ObjectStreamField[] fields)
1349:     throws
1350:       IllegalArgumentException, IllegalAccessException, IOException
1351:   {
1352:     for (int i = 0; i < fields.length; i++)
1353:       {
1354:         ObjectStreamField osf = fields[i];
1355:         Field field = osf.field;
1356: 
1357:         if (DEBUG && dump)
1358:           dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
1359: 
1360:         switch (osf.getTypeCode())
1361:           {
1362:           case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
1363:           case 'B': realOutput.writeByte   (field.getByte   (obj)); break;
1364:           case 'S': realOutput.writeShort  (field.getShort  (obj)); break;
1365:           case 'C': realOutput.writeChar   (field.getChar   (obj)); break;
1366:           case 'I': realOutput.writeInt    (field.getInt    (obj)); break;
1367:           case 'F': realOutput.writeFloat  (field.getFloat  (obj)); break;
1368:           case 'J': realOutput.writeLong   (field.getLong   (obj)); break;
1369:           case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
1370:           case 'L':
1371:           case '[':            writeObject (field.get       (obj)); break;
1372:           default:
1373:             throw new IOException("Unexpected type code " + osf.getTypeCode());
1374:           }
1375:       }
1376:   }
1377: 
1378: 
1379:   // Toggles writing primitive data to block-data buffer.
1380:   // Package-private to avoid a trampoline constructor.
1381:   boolean setBlockDataMode(boolean on) throws IOException
1382:   {
1383:     if (on == writeDataAsBlocks)
1384:       return on;
1385: 
1386:     drain();
1387:     boolean oldmode = writeDataAsBlocks;
1388:     writeDataAsBlocks = on;
1389: 
1390:     if (on)
1391:       dataOutput = blockDataOutput;
1392:     else
1393:       dataOutput = realOutput;
1394: 
1395:     return oldmode;
1396:   }
1397: 
1398: 
1399:   private void callWriteMethod(Object obj, ObjectStreamClass osc)
1400:     throws IOException
1401:   {
1402:     currentPutField = null;
1403:     try
1404:       {
1405:         Object args[] = {this};
1406:         osc.writeObjectMethod.invoke(obj, args);
1407:       }
1408:     catch (InvocationTargetException x)
1409:       {
1410:         /* Rethrow if possible. */
1411:         Throwable exception = x.getTargetException();
1412:         if (exception instanceof RuntimeException)
1413:           throw (RuntimeException) exception;
1414:         if (exception instanceof IOException)
1415:           throw (IOException) exception;
1416: 
1417:         IOException ioe
1418:           = new IOException("Exception thrown from writeObject() on " +
1419:                             osc.forClass().getName() + ": " +
1420:                             exception.getClass().getName());
1421:         ioe.initCause(exception);
1422:         throw ioe;
1423:       }
1424:     catch (Exception x)
1425:       {
1426:         IOException ioe
1427:           = new IOException("Failure invoking writeObject() on " +
1428:                             osc.forClass().getName() + ": " +
1429:                             x.getClass().getName());
1430:         ioe.initCause(x);
1431:         throw ioe;
1432:       }
1433:   }
1434: 
1435:   private void dumpElementln (String msg, Object obj)
1436:   {
1437:     try
1438:       {
1439:         for (int i = 0; i < depth; i++)
1440:           System.out.print (" ");
1441:         System.out.print (Thread.currentThread() + ": ");
1442:         System.out.print (msg);
1443:         if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
1444:           System.out.print (obj.getClass());
1445:         else
1446:           System.out.print (obj);
1447:       }
1448:     catch (Exception _)
1449:       {
1450:       }
1451:     finally
1452:       {
1453:         System.out.println ();
1454:       }
1455:   }
1456: 
1457:   private void dumpElementln (String msg)
1458:   {
1459:     for (int i = 0; i < depth; i++)
1460:       System.out.print (" ");
1461:     System.out.print (Thread.currentThread() + ": ");
1462:     System.out.println(msg);
1463:   }
1464: 
1465:   // this value comes from 1.2 spec, but is used in 1.1 as well
1466:   private static final int BUFFER_SIZE = 1024;
1467: 
1468:   private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1469: 
1470:   private DataOutputStream dataOutput;
1471:   private boolean writeDataAsBlocks;
1472:   private DataOutputStream realOutput;
1473:   private DataOutputStream blockDataOutput;
1474:   private byte[] blockData;
1475:   private int blockDataCount;
1476:   private Object currentObject;
1477:   // Package-private to avoid a trampoline.
1478:   ObjectStreamClass currentObjectStreamClass;
1479:   private PutField currentPutField;
1480:   private boolean fieldsAlreadyWritten;
1481:   private boolean replacementEnabled;
1482:   private boolean isSerializing;
1483:   private int nextOID;
1484:   private ObjectIdentityMap2Int OIDLookupTable;
1485:   private int protocolVersion;
1486:   private boolean useSubclassMethod;
1487:   private SetAccessibleAction setAccessible = new SetAccessibleAction();
1488: 
1489:   // The nesting depth for debugging output
1490:   private int depth = 0;
1491: 
1492:   // Set if we're generating debugging dumps
1493:   private boolean dump = false;
1494: 
1495:   private static final boolean DEBUG = false;
1496: 
1497:   /**
1498:    * Returns true if the given class overrides either of the
1499:    * methods <code>putFields</code> or <code>writeUnshared</code>.
1500:    *
1501:    * @param clazz the class to check.
1502:    * @return true if the class overrides one of the methods.
1503:    */
1504:   private static boolean overridesMethods(final Class<?> clazz)
1505:   {
1506:     if (clazz == ObjectOutputStream.class)
1507:       return false;
1508: 
1509:     return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
1510:         public Boolean run()
1511:         {
1512:           Method[] methods = clazz.getDeclaredMethods();
1513:           for (int a = 0; a < methods.length; ++a)
1514:             {
1515:               String name = methods[a].getName();
1516:               if (name.equals("writeUnshared"))
1517:                 {
1518:                   Class<?>[] paramTypes = methods[a].getParameterTypes();
1519:                   if (paramTypes.length == 1 &&
1520:                       paramTypes[0] == Object.class &&
1521:                       methods[a].getReturnType() == Void.class)
1522:                     return true;
1523:                 }
1524:               else if (name.equals("putFields"))
1525:                 {
1526:                   if (methods[a].getParameterTypes().length == 0 &&
1527:                       methods[a].getReturnType() == PutField.class)
1528:                     return true;
1529:                 }
1530:             }
1531:           return false;
1532:         }
1533:       });
1534:   }
1535: 
1536: }