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
   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: 
  52: /**
  53:  * An <code>ObjectOutputStream</code> can be used to write objects
  54:  * as well as primitive data in a platform-independent manner to an
  55:  * <code>OutputStream</code>.
  56:  *
  57:  * The data produced by an <code>ObjectOutputStream</code> can be read
  58:  * and reconstituted by an <code>ObjectInputStream</code>.
  59:  *
  60:  * <code>writeObject (Object)</code> is used to write Objects, the
  61:  * <code>write&lt;type&gt;</code> methods are used to write primitive
  62:  * data (as in <code>DataOutputStream</code>). Strings can be written
  63:  * as objects or as primitive data.
  64:  *
  65:  * Not all objects can be written out using an
  66:  * <code>ObjectOutputStream</code>.  Only those objects that are an
  67:  * instance of <code>java.io.Serializable</code> can be written.
  68:  *
  69:  * Using default serialization, information about the class of an
  70:  * object is written, all of the non-transient, non-static fields of
  71:  * the object are written, if any of these fields are objects, they are
  72:  * written out in the same manner.
  73:  *
  74:  * An object is only written out the first time it is encountered.  If
  75:  * the object is encountered later, a reference to it is written to
  76:  * the underlying stream.  Thus writing circular object graphs
  77:  * does not present a problem, nor are relationships between objects
  78:  * in a graph lost.
  79:  *
  80:  * Example usage:
  81:  * <pre>
  82:  * Hashtable map = new Hashtable ();
  83:  * map.put ("one", new Integer (1));
  84:  * map.put ("two", new Integer (2));
  85:  *
  86:  * ObjectOutputStream oos =
  87:  * new ObjectOutputStream (new FileOutputStream ("numbers"));
  88:  * oos.writeObject (map);
  89:  * oos.close ();
  90:  *
  91:  * ObjectInputStream ois =
  92:  * new ObjectInputStream (new FileInputStream ("numbers"));
  93:  * Hashtable newmap = (Hashtable)ois.readObject ();
  94:  *
  95:  * System.out.println (newmap);
  96:  * </pre>
  97:  *
  98:  * The default serialization can be overriden in two ways.
  99:  *
 100:  * By defining a method <code>private void
 101:  * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
 102:  * how information about itself is written.
 103:  * <code>defaultWriteObject ()</code> may be called from this method to
 104:  * carry out default serialization.  This method is not
 105:  * responsible for dealing with fields of super-classes or subclasses.
 106:  *
 107:  * By implementing <code>java.io.Externalizable</code>.  This gives
 108:  * the class complete control over the way it is written to the
 109:  * stream.  If this approach is used the burden of writing superclass
 110:  * and subclass data is transfered to the class implementing
 111:  * <code>java.io.Externalizable</code>.
 112:  *
 113:  * @see java.io.DataOutputStream
 114:  * @see java.io.Externalizable
 115:  * @see java.io.ObjectInputStream
 116:  * @see java.io.Serializable
 117:  * @author Tom Tromey (tromey@redhat.com)
 118:  * @author Jeroen Frijters (jeroen@frijters.net)
 119:  * @author Guilhem Lavaux (guilhem@kaffe.org)
 120:  * @author Michael Koch (konqueror@gmx.de)
 121:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 122:  */
 123: public class ObjectOutputStream extends OutputStream
 124:   implements ObjectOutput, ObjectStreamConstants
 125: {
 126:   /**
 127:    * Creates a new <code>ObjectOutputStream</code> that will do all of
 128:    * its writing onto <code>out</code>.  This method also initializes
 129:    * the stream by writing the header information (stream magic number
 130:    * and stream version).
 131:    *
 132:    * @exception IOException Writing stream header to underlying
 133:    * stream cannot be completed.
 134:    *
 135:    * @see #writeStreamHeader()
 136:    */
 137:   public ObjectOutputStream (OutputStream out) throws IOException
 138:   {
 139:     realOutput = new DataOutputStream(out);
 140:     blockData = new byte[ BUFFER_SIZE ];
 141:     blockDataCount = 0;
 142:     blockDataOutput = new DataOutputStream(this);
 143:     setBlockDataMode(true);
 144:     replacementEnabled = false;
 145:     isSerializing = false;
 146:     nextOID = baseWireHandle;
 147:     OIDLookupTable = new ObjectIdentityMap2Int();
 148:     protocolVersion = defaultProtocolVersion;
 149:     useSubclassMethod = false;
 150:     writeStreamHeader();
 151: 
 152:     if (DEBUG)
 153:       {
 154:     String val = System.getProperty("gcj.dumpobjects");
 155:     if (val != null && !val.equals(""))
 156:       dump = true;
 157:       }
 158:   }
 159: 
 160:   /**
 161:    * Writes a representation of <code>obj</code> to the underlying
 162:    * output stream by writing out information about its class, then
 163:    * writing out each of the objects non-transient, non-static
 164:    * fields.  If any of these fields are other objects,
 165:    * they are written out in the same manner.
 166:    *
 167:    * This method can be overriden by a class by implementing
 168:    * <code>private void writeObject (ObjectOutputStream)</code>.
 169:    *
 170:    * If an exception is thrown from this method, the stream is left in
 171:    * an undefined state.
 172:    *
 173:    * @param obj the object to serialize.
 174:    * @exception NotSerializableException An attempt was made to
 175:    * serialize an <code>Object</code> that is not serializable.
 176:    *
 177:    * @exception InvalidClassException Somebody tried to serialize
 178:    * an object which is wrongly formatted.
 179:    *
 180:    * @exception IOException Exception from underlying
 181:    * <code>OutputStream</code>.
 182:    * @see #writeUnshared(Object)
 183:    */
 184:   public final void writeObject(Object obj) throws IOException
 185:   {
 186:     writeObject(obj, true);
 187:   }
 188: 
 189:   /**
 190:    * Writes an object to the stream in the same manner as
 191:    * {@link #writeObject(Object)}, but without the use of
 192:    * references.  As a result, the object is always written
 193:    * to the stream in full.  Likewise, if an object is written
 194:    * by this method and is then later written again by
 195:    * {@link #writeObject(Object)}, both calls will write out
 196:    * the object in full, as the later call to
 197:    * {@link #writeObject(Object)} will know nothing of the
 198:    * earlier use of {@link #writeUnshared(Object)}.
 199:    *
 200:    * @param obj the object to serialize.
 201:    * @throws NotSerializableException if the object being
 202:    *                                  serialized does not implement
 203:    *                                  {@link Serializable}.
 204:    * @throws InvalidClassException if a problem occurs with
 205:    *                               the class of the object being
 206:    *                               serialized.
 207:    * @throws IOException if an I/O error occurs on the underlying
 208:    *                     <code>OutputStream</code>.
 209:    * @since 1.4
 210:    * @see #writeObject(Object)
 211:    */
 212:   public void writeUnshared(Object obj)
 213:     throws IOException
 214:   {
 215:     writeObject(obj, false);
 216:   }
 217: 
 218:   /**
 219:    * Writes a representation of <code>obj</code> to the underlying
 220:    * output stream by writing out information about its class, then
 221:    * writing out each of the objects non-transient, non-static
 222:    * fields.  If any of these fields are other objects,
 223:    * they are written out in the same manner.
 224:    *
 225:    * This method can be overriden by a class by implementing
 226:    * <code>private void writeObject (ObjectOutputStream)</code>.
 227:    *
 228:    * If an exception is thrown from this method, the stream is left in
 229:    * an undefined state.
 230:    *
 231:    * @param obj the object to serialize.
 232:    * @param shared true if the serialized object should be
 233:    *               shared with later calls.
 234:    * @exception NotSerializableException An attempt was made to
 235:    * serialize an <code>Object</code> that is not serializable.
 236:    *
 237:    * @exception InvalidClassException Somebody tried to serialize
 238:    * an object which is wrongly formatted.
 239:    *
 240:    * @exception IOException Exception from underlying
 241:    * <code>OutputStream</code>.
 242:    * @see #writeUnshared(Object)
 243:    */
 244:   private final void writeObject(Object obj, boolean shared)
 245:     throws IOException
 246:   {
 247:     if (useSubclassMethod)
 248:       {
 249:     if (dump)
 250:       dumpElementln ("WRITE OVERRIDE: " + obj);
 251:       
 252:     writeObjectOverride(obj);
 253:     return;
 254:       }
 255: 
 256:     if (dump)
 257:       dumpElementln ("WRITE: " + obj);
 258:     
 259:     depth += 2;    
 260: 
 261:     boolean was_serializing = isSerializing;
 262:     boolean old_mode = setBlockDataMode(false);
 263:     try
 264:       {
 265:     isSerializing = true;
 266:     boolean replaceDone = false;
 267:     Object replacedObject = null;
 268:     
 269:     while (true)
 270:       {
 271:         if (obj == null)
 272:           {
 273:         realOutput.writeByte(TC_NULL);
 274:         break;
 275:           }
 276: 
 277:         int handle = findHandle(obj);
 278:         if (handle >= 0 && shared)
 279:           {
 280:         realOutput.writeByte(TC_REFERENCE);
 281:         realOutput.writeInt(handle);
 282:         break;
 283:           }
 284: 
 285:         if (obj instanceof Class)
 286:           {
 287:         Class cl = (Class)obj;
 288:         ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
 289:         realOutput.writeByte(TC_CLASS);
 290:         if (!osc.isProxyClass)
 291:           {
 292:             writeObject (osc);
 293:           }
 294:         else
 295:           {System.err.println("1");
 296:             realOutput.writeByte(TC_PROXYCLASSDESC);
 297:             Class[] intfs = cl.getInterfaces();
 298:             realOutput.writeInt(intfs.length);
 299:             for (int i = 0; i < intfs.length; i++)
 300:               realOutput.writeUTF(intfs[i].getName());
 301:             
 302:             boolean oldmode = setBlockDataMode(true);
 303:             annotateProxyClass(cl);
 304:             setBlockDataMode(oldmode);
 305:             realOutput.writeByte(TC_ENDBLOCKDATA);
 306:             
 307:             writeObject(osc.getSuper());
 308:           }
 309:         if (shared)
 310:           assignNewHandle(obj);
 311:         break;
 312:           }
 313: 
 314:         if (obj instanceof ObjectStreamClass)
 315:           {
 316:         writeClassDescriptor((ObjectStreamClass) obj);
 317:         break;
 318:           }
 319: 
 320:         Class clazz = obj.getClass();
 321:         ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
 322:         if (osc == null)
 323:           throw new NotSerializableException(clazz.getName());
 324: 
 325:         if (osc.isEnum())
 326:           {
 327:         /* TC_ENUM classDesc newHandle enumConstantName */
 328:         realOutput.writeByte(TC_ENUM);
 329:         writeObject(osc);
 330:         if (shared)
 331:           assignNewHandle(obj);
 332:         writeObject(((Enum) obj).name());
 333:         break;
 334:           }
 335: 
 336:         if ((replacementEnabled || obj instanceof Serializable)
 337:         && ! replaceDone)
 338:           {
 339:         replacedObject = obj;
 340:         
 341:         if (obj instanceof Serializable)
 342:           {
 343:             try
 344:               {
 345:                         Method m = osc.writeReplaceMethod;
 346:                         if (m != null)
 347:                             obj = m.invoke(obj, new Object[0]);
 348:               }
 349:             catch (IllegalAccessException ignore)
 350:               {
 351:               }
 352:             catch (InvocationTargetException ignore)
 353:               {
 354:               }
 355:           }
 356:         
 357:         if (replacementEnabled)
 358:           obj = replaceObject(obj);
 359:         
 360:         replaceDone = true;
 361:         continue;
 362:           }
 363: 
 364:         if (obj instanceof String)
 365:           {
 366:         realOutput.writeByte(TC_STRING);
 367:         if (shared)
 368:           assignNewHandle(obj);
 369:         realOutput.writeUTF((String)obj);
 370:         break;
 371:           }
 372: 
 373:         if (clazz.isArray ())
 374:           {
 375:         realOutput.writeByte(TC_ARRAY);
 376:         writeObject(osc);
 377:         if (shared)
 378:           assignNewHandle(obj);
 379:         writeArraySizeAndElements(obj, clazz.getComponentType());
 380:         break;
 381:           }
 382:         
 383:         realOutput.writeByte(TC_OBJECT);
 384:         writeObject(osc);
 385: 
 386:         if (shared)
 387:           if (replaceDone)
 388:         assignNewHandle(replacedObject);
 389:           else
 390:         assignNewHandle(obj);
 391:         
 392:         if (obj instanceof Externalizable)
 393:           {
 394:         if (protocolVersion == PROTOCOL_VERSION_2)
 395:           setBlockDataMode(true);
 396:         
 397:         ((Externalizable)obj).writeExternal(this);
 398:         
 399:         if (protocolVersion == PROTOCOL_VERSION_2)
 400:           {
 401:             setBlockDataMode(false);
 402:             realOutput.writeByte(TC_ENDBLOCKDATA);
 403:           }
 404: 
 405:         break;
 406:           }
 407: 
 408:         if (obj instanceof Serializable)
 409:           {
 410:         Object prevObject = this.currentObject;
 411:         ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
 412:         currentObject = obj;
 413:         ObjectStreamClass[] hierarchy = osc.hierarchy();
 414:         
 415:         for (int i = 0; i < hierarchy.length; i++)
 416:           {
 417:             currentObjectStreamClass = hierarchy[i];
 418:             
 419:             fieldsAlreadyWritten = false;
 420:             if (currentObjectStreamClass.hasWriteMethod())
 421:               {
 422:             if (dump)
 423:               dumpElementln ("WRITE METHOD CALLED FOR: " + obj);
 424:             setBlockDataMode(true);
 425:             callWriteMethod(obj, currentObjectStreamClass);
 426:             setBlockDataMode(false);
 427:             realOutput.writeByte(TC_ENDBLOCKDATA);
 428:             if (dump)
 429:               dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj);
 430:               }
 431:             else
 432:               {
 433:             if (dump)
 434:               dumpElementln ("WRITE FIELDS CALLED FOR: " + obj);
 435:             writeFields(obj, currentObjectStreamClass);
 436:               }
 437:           }
 438: 
 439:         this.currentObject = prevObject;
 440:         this.currentObjectStreamClass = prevObjectStreamClass;
 441:         currentPutField = null;
 442:         break;
 443:           }
 444: 
 445:         throw new NotSerializableException(clazz.getName()
 446:                            + " in "
 447:                            + obj.getClass());
 448:       } // end pseudo-loop
 449:       }
 450:     catch (ObjectStreamException ose)
 451:       {
 452:     // Rethrow these are fatal.
 453:     throw ose;
 454:       }
 455:     catch (IOException e)
 456:       {
 457:     realOutput.writeByte(TC_EXCEPTION);
 458:     reset(true);
 459: 
 460:     setBlockDataMode(false);
 461:     try
 462:       {
 463:         if (DEBUG)
 464:           {
 465:         e.printStackTrace(System.out);
 466:           }
 467:         writeObject(e);
 468:       }
 469:     catch (IOException ioe)
 470:       {
 471:         StreamCorruptedException ex = 
 472:           new StreamCorruptedException
 473:           (ioe + " thrown while exception was being written to stream.");
 474:         if (DEBUG)
 475:           {
 476:         ex.printStackTrace(System.out);
 477:           }
 478:         throw ex;
 479:       }
 480: 
 481:     reset (true);
 482:     
 483:       }
 484:     finally
 485:       {
 486:     isSerializing = was_serializing;
 487:     setBlockDataMode(old_mode);
 488:     depth -= 2;
 489: 
 490:     if (dump)
 491:       dumpElementln ("END: " + obj);
 492:       }
 493:   }
 494: 
 495:   protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
 496:   {
 497:     if (osc.isProxyClass)
 498:       {
 499:         realOutput.writeByte(TC_PROXYCLASSDESC);
 500:     Class[] intfs = osc.forClass().getInterfaces();
 501:     realOutput.writeInt(intfs.length);
 502:     for (int i = 0; i < intfs.length; i++)
 503:       realOutput.writeUTF(intfs[i].getName());
 504: 
 505:         assignNewHandle(osc);
 506:     
 507:         boolean oldmode = setBlockDataMode(true);
 508:         annotateProxyClass(osc.forClass());
 509:         setBlockDataMode(oldmode);
 510:         realOutput.writeByte(TC_ENDBLOCKDATA);
 511:       }
 512:     else
 513:       {
 514:         realOutput.writeByte(TC_CLASSDESC);
 515:         realOutput.writeUTF(osc.getName());
 516:     if (osc.isEnum())
 517:       realOutput.writeLong(0L);
 518:     else
 519:       realOutput.writeLong(osc.getSerialVersionUID());
 520:         assignNewHandle(osc);
 521: 
 522:         int flags = osc.getFlags();
 523: 
 524:         if (protocolVersion == PROTOCOL_VERSION_2
 525:         && osc.isExternalizable())
 526:         flags |= SC_BLOCK_DATA;
 527: 
 528:         realOutput.writeByte(flags);
 529: 
 530:         ObjectStreamField[] fields = osc.fields;
 531: 
 532:     if (fields == ObjectStreamClass.INVALID_FIELDS)
 533:       throw new InvalidClassException
 534:           (osc.getName(), "serialPersistentFields is invalid");
 535: 
 536:         realOutput.writeShort(fields.length);
 537: 
 538:         ObjectStreamField field;
 539:         for (int i = 0; i < fields.length; i++)
 540:           {
 541:         field = fields[i];
 542:         realOutput.writeByte(field.getTypeCode ());
 543:         realOutput.writeUTF(field.getName ());
 544: 
 545:         if (! field.isPrimitive())
 546:           writeObject(field.getTypeString());
 547:           }
 548: 
 549:         boolean oldmode = setBlockDataMode(true);
 550:         annotateClass(osc.forClass());
 551:         setBlockDataMode(oldmode);
 552:         realOutput.writeByte(TC_ENDBLOCKDATA);
 553:       }
 554: 
 555:     if (osc.isSerializable() || osc.isExternalizable())
 556:       writeObject(osc.getSuper());
 557:     else
 558:       writeObject(null);
 559:   }
 560:   
 561:   /**
 562:    * Writes the current objects non-transient, non-static fields from
 563:    * the current class to the underlying output stream.
 564:    *
 565:    * This method is intended to be called from within a object's
 566:    * <code>private void writeObject (ObjectOutputStream)</code>
 567:    * method.
 568:    *
 569:    * @exception NotActiveException This method was called from a
 570:    * context other than from the current object's and current class's
 571:    * <code>private void writeObject (ObjectOutputStream)</code>
 572:    * method.
 573:    *
 574:    * @exception IOException Exception from underlying
 575:    * <code>OutputStream</code>.
 576:    */
 577:   public void defaultWriteObject()
 578:     throws IOException, NotActiveException
 579:   {
 580:     markFieldsWritten();
 581:     writeFields(currentObject, currentObjectStreamClass);
 582:   }
 583: 
 584: 
 585:   private void markFieldsWritten() throws IOException
 586:   {
 587:     if (currentObject == null || currentObjectStreamClass == null)
 588:       throw new NotActiveException
 589:     ("defaultWriteObject called by non-active class and/or object");
 590: 
 591:     if (fieldsAlreadyWritten)
 592:       throw new IOException
 593:     ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
 594: 
 595:     fieldsAlreadyWritten = true;
 596:   }
 597: 
 598:   /**
 599:    * Resets stream to state equivalent to the state just after it was
 600:    * constructed.
 601:    *
 602:    * Causes all objects previously written to the stream to be
 603:    * forgotten.  A notification of this reset is also written to the
 604:    * underlying stream.
 605:    *
 606:    * @exception IOException Exception from underlying
 607:    * <code>OutputStream</code> or reset called while serialization is
 608:    * in progress.
 609:    */
 610:   public void reset() throws IOException
 611:   {
 612:     reset(false);
 613:   }
 614: 
 615: 
 616:   private void reset(boolean internal) throws IOException
 617:   {
 618:     if (!internal)
 619:       {
 620:     if (isSerializing)
 621:       throw new IOException("Reset called while serialization in progress");
 622: 
 623:     realOutput.writeByte(TC_RESET);
 624:       }
 625:     
 626:     clearHandles();
 627:   }
 628: 
 629: 
 630:   /**
 631:    * Informs this <code>ObjectOutputStream</code> to write data
 632:    * according to the specified protocol.  There are currently two
 633:    * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
 634:    * and <code>PROTOCOL_VERSION_2</code>.  This implementation writes
 635:    * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
 636:    * since the JDK 1.2.
 637:    * <p>
 638:    * For an explanation of the differences between the two protocols
 639:    * see the Java Object Serialization Specification.
 640:    * </p>
 641:    * 
 642:    * @param version the version to use.
 643:    * 
 644:    * @throws IllegalArgumentException if <code>version</code> is not a valid 
 645:    * protocol.
 646:    * @throws IllegalStateException if called after the first the first object
 647:    * was serialized.
 648:    * @throws IOException if an I/O error occurs.
 649:    * 
 650:    * @see ObjectStreamConstants#PROTOCOL_VERSION_1
 651:    * @see ObjectStreamConstants#PROTOCOL_VERSION_2
 652:    * 
 653:    * @since 1.2
 654:    */
 655:   public void useProtocolVersion(int version) throws IOException
 656:   {
 657:     if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
 658:       throw new IllegalArgumentException("Invalid protocol version requested.");
 659:     
 660:     if (nextOID != baseWireHandle)
 661:       throw new IllegalStateException("Protocol version cannot be changed " 
 662:                                       + "after serialization started.");
 663:     
 664:     protocolVersion = version;
 665:   }
 666: 
 667:   /**
 668:    * An empty hook that allows subclasses to write extra information
 669:    * about classes to the stream.  This method is called the first
 670:    * time each class is seen, and after all of the standard
 671:    * information about the class has been written.
 672:    *
 673:    * @exception IOException Exception from underlying
 674:    * <code>OutputStream</code>.
 675:    *
 676:    * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
 677:    */
 678:   protected void annotateClass(Class<?> cl) throws IOException
 679:   {
 680:   }
 681: 
 682:   protected void annotateProxyClass(Class<?> cl) throws IOException
 683:   {
 684:   }
 685: 
 686:   /**
 687:    * Allows subclasses to replace objects that are written to the
 688:    * stream with other objects to be written in their place.  This
 689:    * method is called the first time each object is encountered
 690:    * (modulo reseting of the stream).
 691:    *
 692:    * This method must be enabled before it will be called in the
 693:    * serialization process.
 694:    *
 695:    * @exception IOException Exception from underlying
 696:    * <code>OutputStream</code>.
 697:    *
 698:    * @see #enableReplaceObject(boolean)
 699:    */
 700:   protected Object replaceObject(Object obj) throws IOException
 701:   {
 702:     return obj;
 703:   }
 704: 
 705: 
 706:   /**
 707:    * If <code>enable</code> is <code>true</code> and this object is
 708:    * trusted, then <code>replaceObject (Object)</code> will be called
 709:    * in subsequent calls to <code>writeObject (Object)</code>.
 710:    * Otherwise, <code>replaceObject (Object)</code> will not be called.
 711:    *
 712:    * @exception SecurityException This class is not trusted.
 713:    */
 714:   protected boolean enableReplaceObject(boolean enable)
 715:     throws SecurityException
 716:   {
 717:     if (enable)
 718:       {
 719:     SecurityManager sm = System.getSecurityManager();
 720:     if (sm != null)
 721:       sm.checkPermission(new SerializablePermission("enableSubstitution"));
 722:       }
 723: 
 724:     boolean old_val = replacementEnabled;
 725:     replacementEnabled = enable;
 726:     return old_val;
 727:   }
 728: 
 729: 
 730:   /**
 731:    * Writes stream magic and stream version information to the
 732:    * underlying stream.
 733:    *
 734:    * @exception IOException Exception from underlying
 735:    * <code>OutputStream</code>.
 736:    */
 737:   protected void writeStreamHeader() throws IOException
 738:   {
 739:     realOutput.writeShort(STREAM_MAGIC);
 740:     realOutput.writeShort(STREAM_VERSION);
 741:   }
 742: 
 743:   /**
 744:    * Protected constructor that allows subclasses to override
 745:    * serialization.  This constructor should be called by subclasses
 746:    * that wish to override <code>writeObject (Object)</code>.  This
 747:    * method does a security check <i>NOTE: currently not
 748:    * implemented</i>, then sets a flag that informs
 749:    * <code>writeObject (Object)</code> to call the subclasses
 750:    * <code>writeObjectOverride (Object)</code> method.
 751:    *
 752:    * @see #writeObjectOverride(Object)
 753:    */
 754:   protected ObjectOutputStream() throws IOException, SecurityException
 755:   {
 756:     SecurityManager sec_man = System.getSecurityManager ();
 757:     if (sec_man != null)
 758:       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
 759:     useSubclassMethod = true;
 760:   }
 761: 
 762: 
 763:   /**
 764:    * This method allows subclasses to override the default
 765:    * serialization mechanism provided by
 766:    * <code>ObjectOutputStream</code>.  To make this method be used for
 767:    * writing objects, subclasses must invoke the 0-argument
 768:    * constructor on this class from there constructor.
 769:    *
 770:    * @see #ObjectOutputStream()
 771:    *
 772:    * @exception NotActiveException Subclass has arranged for this
 773:    * method to be called, but did not implement this method.
 774:    */
 775:   protected void writeObjectOverride(Object obj) throws NotActiveException,
 776:     IOException
 777:   {
 778:     throw new NotActiveException
 779:       ("Subclass of ObjectOutputStream must implement writeObjectOverride");
 780:   }
 781: 
 782: 
 783:   /**
 784:    * @see DataOutputStream#write(int)
 785:    */
 786:   public void write (int data) throws IOException
 787:   {
 788:     if (writeDataAsBlocks)
 789:       {
 790:     if (blockDataCount == BUFFER_SIZE)
 791:       drain();
 792: 
 793:     blockData[ blockDataCount++ ] = (byte)data;
 794:       }
 795:     else
 796:       realOutput.write(data);
 797:   }
 798: 
 799: 
 800:   /**
 801:    * @see DataOutputStream#write(byte[])
 802:    */
 803:   public void write(byte[] b) throws IOException
 804:   {
 805:     write(b, 0, b.length);
 806:   }
 807: 
 808: 
 809:   /**
 810:    * @see DataOutputStream#write(byte[],int,int)
 811:    */
 812:   public void write(byte[] b, int off, int len) throws IOException
 813:   {
 814:     if (writeDataAsBlocks)
 815:       {
 816:     if (len < 0)
 817:       throw new IndexOutOfBoundsException();
 818: 
 819:     if (blockDataCount + len < BUFFER_SIZE)
 820:       {
 821:         System.arraycopy(b, off, blockData, blockDataCount, len);
 822:         blockDataCount += len;
 823:       }
 824:     else
 825:       {
 826:         drain();
 827:         writeBlockDataHeader(len);
 828:         realOutput.write(b, off, len);
 829:       }
 830:       }
 831:     else
 832:       realOutput.write(b, off, len);
 833:   }
 834: 
 835: 
 836:   /**
 837:    * @see DataOutputStream#flush()
 838:    */
 839:   public void flush () throws IOException
 840:   {
 841:     drain();
 842:     realOutput.flush();
 843:   }
 844: 
 845: 
 846:   /**
 847:    * Causes the block-data buffer to be written to the underlying
 848:    * stream, but does not flush underlying stream.
 849:    *
 850:    * @exception IOException Exception from underlying
 851:    * <code>OutputStream</code>.
 852:    */
 853:   protected void drain() throws IOException
 854:   {
 855:     if (blockDataCount == 0)
 856:       return;
 857: 
 858:     if (writeDataAsBlocks)
 859:       writeBlockDataHeader(blockDataCount);
 860:     realOutput.write(blockData, 0, blockDataCount);
 861:     blockDataCount = 0;
 862:   }
 863: 
 864: 
 865:   /**
 866:    * @see java.io.DataOutputStream#close ()
 867:    */
 868:   public void close() throws IOException
 869:   {
 870:     flush();
 871:     realOutput.close();
 872:   }
 873: 
 874: 
 875:   /**
 876:    * @see java.io.DataOutputStream#writeBoolean (boolean)
 877:    */
 878:   public void writeBoolean(boolean data) throws IOException
 879:   {
 880:     blockDataOutput.writeBoolean(data);
 881:   }
 882: 
 883: 
 884:   /**
 885:    * @see java.io.DataOutputStream#writeByte (int)
 886:    */
 887:   public void writeByte(int data) throws IOException
 888:   {
 889:     blockDataOutput.writeByte(data);
 890:   }
 891: 
 892: 
 893:   /**
 894:    * @see java.io.DataOutputStream#writeShort (int)
 895:    */
 896:   public void writeShort (int data) throws IOException
 897:   {
 898:     blockDataOutput.writeShort(data);
 899:   }
 900: 
 901: 
 902:   /**
 903:    * @see java.io.DataOutputStream#writeChar (int)
 904:    */
 905:   public void writeChar(int data) throws IOException
 906:   {
 907:     blockDataOutput.writeChar(data);
 908:   }
 909: 
 910: 
 911:   /**
 912:    * @see java.io.DataOutputStream#writeInt (int)
 913:    */
 914:   public void writeInt(int data) throws IOException
 915:   {
 916:     blockDataOutput.writeInt(data);
 917:   }
 918: 
 919: 
 920:   /**
 921:    * @see java.io.DataOutputStream#writeLong (long)
 922:    */
 923:   public void writeLong(long data) throws IOException
 924:   {
 925:     blockDataOutput.writeLong(data);
 926:   }
 927: 
 928: 
 929:   /**
 930:    * @see java.io.DataOutputStream#writeFloat (float)
 931:    */
 932:   public void writeFloat(float data) throws IOException
 933:   {
 934:     blockDataOutput.writeFloat(data);
 935:   }
 936: 
 937: 
 938:   /**
 939:    * @see java.io.DataOutputStream#writeDouble (double)
 940:    */
 941:   public void writeDouble(