001    /* ObjectOutputStream.java -- Class used to write serialized objects
002       Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.io;
041    
042    import gnu.java.io.ObjectIdentityMap2Int;
043    import gnu.java.lang.reflect.TypeSignature;
044    import gnu.java.security.action.SetAccessibleAction;
045    
046    import java.lang.reflect.Array;
047    import java.lang.reflect.Field;
048    import java.lang.reflect.InvocationTargetException;
049    import java.lang.reflect.Method;
050    
051    
052    /**
053     * An <code>ObjectOutputStream</code> can be used to write objects
054     * as well as primitive data in a platform-independent manner to an
055     * <code>OutputStream</code>.
056     *
057     * The data produced by an <code>ObjectOutputStream</code> can be read
058     * and reconstituted by an <code>ObjectInputStream</code>.
059     *
060     * <code>writeObject (Object)</code> is used to write Objects, the
061     * <code>write&lt;type&gt;</code> methods are used to write primitive
062     * data (as in <code>DataOutputStream</code>). Strings can be written
063     * as objects or as primitive data.
064     *
065     * Not all objects can be written out using an
066     * <code>ObjectOutputStream</code>.  Only those objects that are an
067     * instance of <code>java.io.Serializable</code> can be written.
068     *
069     * Using default serialization, information about the class of an
070     * object is written, all of the non-transient, non-static fields of
071     * the object are written, if any of these fields are objects, they are
072     * written out in the same manner.
073     *
074     * An object is only written out the first time it is encountered.  If
075     * the object is encountered later, a reference to it is written to
076     * the underlying stream.  Thus writing circular object graphs
077     * does not present a problem, nor are relationships between objects
078     * in a graph lost.
079     *
080     * Example usage:
081     * <pre>
082     * Hashtable map = new Hashtable ();
083     * map.put ("one", new Integer (1));
084     * map.put ("two", new Integer (2));
085     *
086     * ObjectOutputStream oos =
087     * new ObjectOutputStream (new FileOutputStream ("numbers"));
088     * oos.writeObject (map);
089     * oos.close ();
090     *
091     * ObjectInputStream ois =
092     * new ObjectInputStream (new FileInputStream ("numbers"));
093     * Hashtable newmap = (Hashtable)ois.readObject ();
094     *
095     * System.out.println (newmap);
096     * </pre>
097     *
098     * The default serialization can be overriden in two ways.
099     *
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                    String s = (String)obj;
367                    long l = realOutput.getUTFlength(s, 0, 0);
368                    if (l <= 65535)
369                      {
370                        realOutput.writeByte(TC_STRING);
371                        if (shared)
372                          assignNewHandle(obj);
373                        realOutput.writeUTFShort(s, (int)l);
374                      }
375                    else
376                      {
377                        realOutput.writeByte(TC_LONGSTRING);
378                        if (shared)
379                          assignNewHandle(obj);
380                        realOutput.writeUTFLong(s, l);
381                      }
382                    break;
383                  }
384    
385                if (clazz.isArray ())
386                  {
387                    realOutput.writeByte(TC_ARRAY);
388                    writeObject(osc);
389                    if (shared)
390                      assignNewHandle(obj);
391                    writeArraySizeAndElements(obj, clazz.getComponentType());
392                    break;
393                  }
394                
395                realOutput.writeByte(TC_OBJECT);
396                writeObject(osc);
397    
398                if (shared)
399                  if (replaceDone)
400                    assignNewHandle(replacedObject);
401                  else
402                    assignNewHandle(obj);
403                
404                if (obj instanceof Externalizable)
405                  {
406                    if (protocolVersion == PROTOCOL_VERSION_2)
407                      setBlockDataMode(true);
408                    
409                    ((Externalizable)obj).writeExternal(this);
410                    
411                    if (protocolVersion == PROTOCOL_VERSION_2)
412                      {
413                        setBlockDataMode(false);
414                        realOutput.writeByte(TC_ENDBLOCKDATA);
415                      }
416    
417                    break;
418                  }
419    
420                if (obj instanceof Serializable)
421                  {
422                    Object prevObject = this.currentObject;
423                    ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
424                    currentObject = obj;
425                    ObjectStreamClass[] hierarchy = osc.hierarchy();
426                    
427                    for (int i = 0; i < hierarchy.length; i++)
428                      {
429                        currentObjectStreamClass = hierarchy[i];
430                        
431                        fieldsAlreadyWritten = false;
432                        if (currentObjectStreamClass.hasWriteMethod())
433                          {
434                            if (dump)
435                              dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
436                            setBlockDataMode(true);
437                            callWriteMethod(obj, currentObjectStreamClass);
438                            setBlockDataMode(false);
439                            realOutput.writeByte(TC_ENDBLOCKDATA);
440                            if (dump)
441                              dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
442                          }
443                        else
444                          {
445                            if (dump)
446                              dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
447                            writeFields(obj, currentObjectStreamClass);
448                          }
449                      }
450    
451                    this.currentObject = prevObject;
452                    this.currentObjectStreamClass = prevObjectStreamClass;
453                    currentPutField = null;
454                    break;
455                  }
456    
457                throw new NotSerializableException(clazz.getName()
458                                                   + " in "
459                                                   + obj.getClass());
460              } // end pseudo-loop
461          }
462        catch (ObjectStreamException ose)
463          {
464            // Rethrow these are fatal.
465            throw ose;
466          }
467        catch (IOException e)
468          {
469            realOutput.writeByte(TC_EXCEPTION);
470            reset(true);
471    
472            setBlockDataMode(false);
473            try
474              {
475                if (DEBUG)
476                  {
477                    e.printStackTrace(System.out);
478                  }
479                writeObject(e);
480              }
481            catch (IOException ioe)
482              {
483                StreamCorruptedException ex = 
484                  new StreamCorruptedException
485                  (ioe + " thrown while exception was being written to stream.");
486                if (DEBUG)
487                  {
488                    ex.printStackTrace(System.out);
489                  }
490                throw ex;
491              }
492    
493            reset (true);
494            
495          }
496        finally
497          {
498            isSerializing = was_serializing;
499            setBlockDataMode(old_mode);
500            depth -= 2;
501    
502            if (dump)
503              dumpElementln ("END: ", obj);
504          }
505      }
506    
507      protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
508      {
509        if (osc.isProxyClass)
510          {
511            realOutput.writeByte(TC_PROXYCLASSDESC);
512            Class[] intfs = osc.forClass().getInterfaces();
513            realOutput.writeInt(intfs.length);
514            for (int i = 0; i < intfs.length; i++)
515              realOutput.writeUTF(intfs[i].getName());
516    
517            assignNewHandle(osc);
518        
519            boolean oldmode = setBlockDataMode(true);
520            annotateProxyClass(osc.forClass());
521            setBlockDataMode(oldmode);
522            realOutput.writeByte(TC_ENDBLOCKDATA);
523          }
524        else
525          {
526            realOutput.writeByte(TC_CLASSDESC);
527            realOutput.writeUTF(osc.getName());
528            if (osc.isEnum())
529              realOutput.writeLong(0L);
530            else
531              realOutput.writeLong(osc.getSerialVersionUID());
532            assignNewHandle(osc);
533    
534            int flags = osc.getFlags();
535    
536            if (protocolVersion == PROTOCOL_VERSION_2
537                && osc.isExternalizable())
538            flags |= SC_BLOCK_DATA;
539    
540            realOutput.writeByte(flags);
541    
542            ObjectStreamField[] fields = osc.fields;
543    
544            if (fields == ObjectStreamClass.INVALID_FIELDS)
545              throw new InvalidClassException
546                      (osc.getName(), "serialPersistentFields is invalid");
547    
548            realOutput.writeShort(fields.length);
549    
550            ObjectStreamField field;
551            for (int i = 0; i < fields.length; i++)
552              {
553                field = fields[i];
554                realOutput.writeByte(field.getTypeCode ());
555                realOutput.writeUTF(field.getName ());
556    
557                if (! field.isPrimitive())
558                  writeObject(field.getTypeString());
559              }
560    
561            boolean oldmode = setBlockDataMode(true);
562            annotateClass(osc.forClass());
563            setBlockDataMode(oldmode);
564            realOutput.writeByte(TC_ENDBLOCKDATA);
565          }
566    
567        if (osc.isSerializable() || osc.isExternalizable())
568          writeObject(osc.getSuper());
569        else
570          writeObject(null);
571      }
572      
573      /**
574       * Writes the current objects non-transient, non-static fields from
575       * the current class to the underlying output stream.
576       *
577       * This method is intended to be called from within a object's
578       * <code>private void writeObject (ObjectOutputStream)</code>
579       * method.
580       *
581       * @exception NotActiveException This method was called from a
582       * context other than from the current object's and current class's
583       * <code>private void writeObject (ObjectOutputStream)</code>
584       * method.
585       *
586       * @exception IOException Exception from underlying
587       * <code>OutputStream</code>.
588       */
589      public void defaultWriteObject()
590        throws IOException, NotActiveException
591      {
592        markFieldsWritten();
593        writeFields(currentObject, currentObjectStreamClass);
594      }
595    
596    
597      private void markFieldsWritten() throws IOException
598      {
599        if (currentObject == null || currentObjectStreamClass == null)
600          throw new NotActiveException
601            ("defaultWriteObject called by non-active class and/or object");
602    
603        if (fieldsAlreadyWritten)
604          throw new IOException
605            ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
606    
607        fieldsAlreadyWritten = true;
608      }
609    
610      /**
611       * Resets stream to state equivalent to the state just after it was
612       * constructed.
613       *
614       * Causes all objects previously written to the stream to be
615       * forgotten.  A notification of this reset is also written to the
616       * underlying stream.
617       *
618       * @exception IOException Exception from underlying
619       * <code>OutputStream</code> or reset called while serialization is
620       * in progress.
621       */
622      public void reset() throws IOException
623      {
624        reset(false);
625      }
626    
627    
628      private void reset(boolean internal) throws IOException
629      {
630        if (!internal)
631          {
632            if (isSerializing)
633              throw new IOException("Reset called while serialization in progress");
634    
635            realOutput.writeByte(TC_RESET);
636          }
637        
638        clearHandles();
639      }
640    
641    
642      /**
643       * Informs this <code>ObjectOutputStream</code> to write data
644       * according to the specified protocol.  There are currently two
645       * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
646       * and <code>PROTOCOL_VERSION_2</code>.  This implementation writes
647       * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
648       * since the JDK 1.2.
649       * <p>
650       * For an explanation of the differences between the two protocols
651       * see the Java Object Serialization Specification.
652       * </p>
653       * 
654       * @param version the version to use.
655       * 
656       * @throws IllegalArgumentException if <code>version</code> is not a valid 
657       * protocol.
658       * @throws IllegalStateException if called after the first the first object
659       * was serialized.
660       * @throws IOException if an I/O error occurs.
661       * 
662       * @see ObjectStreamConstants#PROTOCOL_VERSION_1
663       * @see ObjectStreamConstants#PROTOCOL_VERSION_2
664       * 
665       * @since 1.2
666       */
667      public void useProtocolVersion(int version) throws IOException
668      {
669        if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
670          throw new IllegalArgumentException("Invalid protocol version requested.");
671        
672        if (nextOID != baseWireHandle)
673          throw new IllegalStateException("Protocol version cannot be changed " 
674                                          + "after serialization started.");
675        
676        protocolVersion = version;
677      }
678    
679      /**
680       * An empty hook that allows subclasses to write extra information
681       * about classes to the stream.  This method is called the first
682       * time each class is seen, and after all of the standard
683       * information about the class has been written.
684       *
685       * @exception IOException Exception from underlying
686       * <code>OutputStream</code>.
687       *
688       * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
689       */
690      protected void annotateClass(Class<?> cl) throws IOException
691      {
692      }
693    
694      protected void annotateProxyClass(Class<?> cl) throws IOException
695      {
696      }
697    
698      /**
699       * Allows subclasses to replace objects that are written to the
700       * stream with other objects to be written in their place.  This
701       * method is called the first time each object is encountered
702       * (modulo reseting of the stream).
703       *
704       * This method must be enabled before it will be called in the
705       * serialization process.
706       *
707       * @exception IOException Exception from underlying
708       * <code>OutputStream</code>.
709       *
710       * @see #enableReplaceObject(boolean)
711       */
712      protected Object replaceObject(Object obj) throws IOException
713      {
714        return obj;
715      }
716    
717    
718      /**
719       * If <code>enable</code> is <code>true</code> and this object is
720       * trusted, then <code>replaceObject (Object)</code> will be called
721       * in subsequent calls to <code>writeObject (Object)</code>.
722       * Otherwise, <code>replaceObject (Object)</code> will not be called.
723       *
724       * @exception SecurityException This class is not trusted.
725       */
726      protected boolean enableReplaceObject(boolean enable)
727        throws SecurityException
728      {
729        if (enable)
730          {
731            SecurityManager sm = System.getSecurityManager();
732            if (sm != null)
733              sm.checkPermission(new SerializablePermission("enableSubstitution"));
734          }
735    
736        boolean old_val = replacementEnabled;
737        replacementEnabled = enable;
738        return old_val;
739      }
740    
741    
742      /**
743       * Writes stream magic and stream version information to the
744       * underlying stream.
745       *
746       * @exception IOException Exception from underlying
747       * <code>OutputStream</code>.
748       */
749      protected void writeStreamHeader() throws IOException
750      {
751        realOutput.writeShort(STREAM_MAGIC);
752        realOutput.writeShort(STREAM_VERSION);
753      }
754    
755      /**
756       * Protected constructor that allows subclasses to override
757       * serialization.  This constructor should be called by subclasses
758       * that wish to override <code>writeObject (Object)</code>.  This
759       * method does a security check <i>NOTE: currently not
760       * implemented</i>, then sets a flag that informs
761       * <code>writeObject (Object)</code> to call the subclasses
762       * <code>writeObjectOverride (Object)</code> method.
763       *
764       * @see #writeObjectOverride(Object)
765       */
766      protected ObjectOutputStream() throws IOException, SecurityException
767      {
768        SecurityManager sec_man = System.getSecurityManager ();
769        if (sec_man != null)
770          sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
771        useSubclassMethod = true;
772      }
773    
774    
775      /**
776       * This method allows subclasses to override the default
777       * serialization mechanism provided by
778       * <code>ObjectOutputStream</code>.  To make this method be used for
779       * writing objects, subclasses must invoke the 0-argument
780       * constructor on this class from there constructor.
781       *
782       * @see #ObjectOutputStream()
783       *
784       * @exception NotActiveException Subclass has arranged for this
785       * method to be called, but did not implement this method.
786       */
787      protected void writeObjectOverride(Object obj) throws NotActiveException,
788        IOException
789      {
790        throw new NotActiveException
791          ("Subclass of ObjectOutputStream must implement writeObjectOverride");
792      }
793    
794    
795      /**
796       * @see DataOutputStream#write(int)
797       */
798      public void write (int data) throws IOException
799      {
800        if (writeDataAsBlocks)
801          {
802            if (blockDataCount == BUFFER_SIZE)
803              drain();
804    
805            blockData[ blockDataCount++ ] = (byte)data;
806          }
807        else
808          realOutput.write(data);
809      }
810    
811    
812      /**
813       * @see DataOutputStream#write(byte[])
814       */
815      public void write(byte[] b) throws IOException
816      {
817        write(b, 0, b.length);
818      }
819    
820    
821      /**
822       * @see DataOutputStream#write(byte[],int,int)
823       */
824      public void write(byte[] b, int off, int len) throws IOException
825      {
826        if (writeDataAsBlocks)
827          {
828            if (len < 0)
829              throw new IndexOutOfBoundsException();
830    
831            if (blockDataCount + len < BUFFER_SIZE)
832              {
833                System.arraycopy(b, off, blockData, blockDataCount, len);
834                blockDataCount += len;
835              }
836            else
837              {
838                drain();
839                writeBlockDataHeader(len);
840                realOutput.write(b, off, len);
841              }
842          }
843        else
844          realOutput.write(b, off, len);
845      }
846    
847    
848      /**
849       * @see DataOutputStream#flush()
850       */
851      public void flush () throws IOException
852      {
853        drain();
854        realOutput.flush();
855      }
856    
857    
858      /**
859       * Causes the block-data buffer to be written to the underlying
860       * stream, but does not flush underlying stream.
861       *
862       * @exception IOException Exception from underlying
863       * <code>OutputStream</code>.
864       */
865      protected void drain() throws IOException
866      {
867        if (blockDataCount == 0)
868          return;
869    
870        if (writeDataAsBlocks)
871          writeBlockDataHeader(blockDataCount);
872        realOutput.write(blockData, 0, blockDataCount);
873        blockDataCount = 0;
874      }
875    
876    
877      /**
878       * @see java.io.DataOutputStream#close ()
879       */
880      public void close() throws IOException
881      {
882        flush();
883        realOutput.close();
884      }
885    
886    
887      /**
888       * @see java.io.DataOutputStream#writeBoolean (boolean)
889       */
890      public void writeBoolean(boolean data) throws IOException
891      {
892        blockDataOutput.writeBoolean(data);
893      }
894    
895    
896      /**
897       * @see java.io.DataOutputStream#writeByte (int)
898       */
899      public void writeByte(int data) throws IOException
900      {
901        blockDataOutput.writeByte(data);
902      }
903    
904    
905      /**
906       * @see java.io.DataOutputStream#writeShort (int)
907       */
908      public void writeShort (int data) throws IOException
909      {
910        blockDataOutput.writeShort(data);
911      }
912    
913    
914      /**
915       * @see java.io.DataOutputStream#writeChar (int)
916       */
917      public void writeChar(int data) throws IOException
918      {
919        blockDataOutput.writeChar(data);
920      }
921    
922    
923      /**
924       * @see java.io.DataOutputStream#writeInt (int)
925       */
926      public void writeInt(int data) throws IOException
927      {
928        blockDataOutput.writeInt(data);
929      }
930    
931    
932      /**
933       * @see java.io.DataOutputStream#writeLong (long)
934       */
935      public void writeLong(long data) throws IOException
936      {
937        blockDataOutput.writeLong(data);
938      }
939    
940    
941      /**
942       * @see java.io.DataOutputStream#writeFloat (float)
943       */
944      public void writeFloat(float data) throws IOException
945      {
946        blockDataOutput.writeFloat(data);
947      }
948    
949    
950      /**
951       * @see java.io.DataOutputStream#writeDouble (double)
952       */
953      public void writeDouble(double data) throws IOException
954      {
955        blockDataOutput.writeDouble(data);
956      }
957    
958    
959      /**
960       * @see java.io.DataOutputStream#writeBytes (java.lang.String)
961       */
962      public void writeBytes(String data) throws IOException
963      {
964        blockDataOutput.writeBytes(data);
965      }
966    
967    
968      /**
969       * @see java.io.DataOutputStream#writeChars (java.lang.String)
970       */
971      public void writeChars(String data) throws IOException
972      {
973        dataOutput.writeChars(data);
974      }
975    
976    
977      /**
978       * @see java.io.DataOutputStream#writeUTF (java.lang.String)
979       */
980      public void writeUTF(String data) throws IOException
981      {
982        dataOutput.writeUTF(data);
983      }
984    
985    
986      /**
987       * This class allows a class to specify exactly which fields should
988       * be written, and what values should be written for these fields.
989       *
990       * XXX: finish up comments
991       */
992      public abstract static class PutField
993      {
994        public abstract void put (String name, boolean value);
995        public abstract void put (String name, byte value);
996        public abstract void put (String name, char value);
997        public abstract void put (String name, double value);
998        public abstract void put (String name, float value);
999        public abstract void put (String name, int value);
1000        public abstract void put (String name, long value);
1001        public abstract void put (String name, short value);
1002        public abstract void put (String name, Object value);
1003    
1004        /**
1005         * @deprecated
1006         */
1007        public abstract void write (ObjectOutput out) throws IOException;
1008      }
1009    
1010      public PutField putFields() throws IOException
1011      {
1012        if (currentPutField != null)
1013          return currentPutField;
1014    
1015        currentPutField = new PutField()
1016          {
1017            private byte[] prim_field_data
1018              = new byte[currentObjectStreamClass.primFieldSize];
1019            private Object[] objs
1020              = new Object[currentObjectStreamClass.objectFieldCount];
1021    
1022            private ObjectStreamField getField (String name)
1023            {
1024              ObjectStreamField field
1025                = currentObjectStreamClass.getField(name);
1026              
1027              if (field == null)
1028                throw new IllegalArgumentException("no such serializable field " + name);
1029              
1030              return field;
1031            }
1032            
1033            public void put(String name, boolean value)
1034            {
1035              ObjectStreamField field = getField(name);
1036    
1037              checkType(field, 'Z');
1038              prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
1039            }
1040    
1041            public void put(String name, byte value)
1042            {
1043              ObjectStreamField field = getField(name);
1044    
1045              checkType(field, 'B');
1046              prim_field_data[field.getOffset()] = value;
1047            }
1048    
1049            public void put(String name, char value)
1050            {
1051              ObjectStreamField field = getField(name);
1052    
1053              checkType(field, 'C');
1054              int off = field.getOffset();
1055              prim_field_data[off++] = (byte)(value >>> 8);
1056              prim_field_data[off] = (byte)value;
1057            }
1058    
1059            public void put(String name, double value)
1060            {
1061              ObjectStreamField field = getField (name);
1062    
1063              checkType(field, 'D');
1064              int off = field.getOffset();
1065              long l_value = Double.doubleToLongBits (value);
1066              prim_field_data[off++] = (byte)(l_value >>> 52);
1067              prim_field_data[off++] = (byte)(l_value >>> 48);
1068              prim_field_data[off++] = (byte)(l_value >>> 40);
1069              prim_field_data[off++] = (byte)(l_value >>> 32);
1070              prim_field_data[off++] = (byte)(l_value >>> 24);
1071              prim_field_data[off++] = (byte)(l_value >>> 16);
1072              prim_field_data[off++] = (byte)(l_value >>> 8);
1073              prim_field_data[off] = (byte)l_value;
1074            }
1075    
1076            public void put(String name, float value)
1077            {
1078              ObjectStreamField field = getField(name);
1079    
1080              checkType(field, 'F');
1081              int off = field.getOffset();
1082              int i_value = Float.floatToIntBits(value);
1083              prim_field_data[off++] = (byte)(i_value >>> 24);
1084              prim_field_data[off++] = (byte)(i_value >>> 16);
1085              prim_field_data[off++] = (byte)(i_value >>> 8);
1086              prim_field_data[off] = (byte)i_value;
1087            }
1088    
1089            public void put(String name, int value)
1090            {
1091              ObjectStreamField field = getField(name);
1092              checkType(field, 'I');
1093              int off = field.getOffset();
1094              prim_field_data[off++] = (byte)(value >>> 24);
1095              prim_field_data[off++] = (byte)(value >>> 16);
1096              prim_field_data[off++] = (byte)(value >>> 8);
1097              prim_field_data[off] = (byte)value;
1098            }
1099    
1100            public void put(String name, long value)
1101            {
1102              ObjectStreamField field = getField(name);
1103              checkType(field, 'J');
1104              int off = field.getOffset();
1105              prim_field_data[off++] = (byte)(value >>> 52);
1106              prim_field_data[off++] = (byte)(value >>> 48);
1107              prim_field_data[off++] = (byte)(value >>> 40);
1108              prim_field_data[off++] = (byte)(value >>> 32);
1109              prim_field_data[off++] = (byte)(value >>> 24);
1110              prim_field_data[off++] = (byte)(value >>> 16);
1111              prim_field_data[off++] = (byte)(value >>> 8);
1112              prim_field_data[off] = (byte)value;
1113            }
1114    
1115            public void put(String name, short value)
1116            {
1117              ObjectStreamField field = getField(name);
1118              checkType(field, 'S');
1119              int off = field.getOffset();
1120              prim_field_data[off++] = (byte)(value >>> 8);
1121              prim_field_data[off] = (byte)value;
1122            }
1123    
1124            public void put(String name, Object value)
1125            {
1126              ObjectStreamField field = getField(name);
1127    
1128              if (value != null &&
1129                  ! field.getType().isAssignableFrom(value.getClass ()))        
1130                throw new IllegalArgumentException("Class " + value.getClass() +
1131                                                   " cannot be cast to " + field.getType());
1132              objs[field.getOffset()] = value;
1133            }
1134    
1135            public void write(ObjectOutput out) throws IOException
1136            {
1137              // Apparently Block data is not used with PutField as per
1138              // empirical evidence against JDK 1.2.  Also see Mauve test
1139              // java.io.ObjectInputOutput.Test.GetPutField.
1140              boolean oldmode = setBlockDataMode(false);
1141              out.write(prim_field_data);
1142              for (int i = 0; i < objs.length; ++ i)
1143                out.writeObject(objs[i]);
1144              setBlockDataMode(oldmode);
1145            }
1146    
1147            private void checkType(ObjectStreamField field, char type)
1148              throws IllegalArgumentException
1149            {
1150              if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1151                  != type)
1152                throw new IllegalArgumentException();
1153            }
1154          };
1155        // end PutFieldImpl
1156    
1157        return currentPutField;
1158      }
1159    
1160    
1161      public void writeFields() throws IOException
1162      {
1163        if (currentPutField == null)
1164          throw new NotActiveException("writeFields can only be called after putFields has been called");
1165    
1166        markFieldsWritten();
1167        currentPutField.write(this);
1168      }
1169    
1170    
1171      // write out the block-data buffer, picking the correct header
1172      // depending on the size of the buffer
1173      private void writeBlockDataHeader(int size) throws IOException
1174      {
1175        if (size < 256)
1176          {
1177            realOutput.writeByte(TC_BLOCKDATA);
1178            realOutput.write(size);
1179          }
1180        else
1181          {
1182            realOutput.writeByte(TC_BLOCKDATALONG);
1183            realOutput.writeInt(size);
1184          }
1185      }
1186    
1187    
1188      // lookup the handle for OBJ, return null if OBJ doesn't have a
1189      // handle yet
1190      private int findHandle(Object obj)
1191      {
1192        return OIDLookupTable.get(obj);
1193      }
1194    
1195    
1196      // assigns the next availible handle to OBJ
1197      private int assignNewHandle(Object obj)
1198      {
1199        OIDLookupTable.put(obj, nextOID);
1200        return nextOID++;
1201      }
1202    
1203    
1204      // resets mapping from objects to handles
1205      private void clearHandles()
1206      {
1207        nextOID = baseWireHandle;
1208        OIDLookupTable.clear();
1209      }
1210    
1211    
1212      // write out array size followed by each element of the array
1213      private void writeArraySizeAndElements(Object array, Class clazz)
1214        throws IOException
1215      {
1216        int length = Array.getLength(array);
1217    
1218        if (clazz.isPrimitive())
1219          {
1220            if (clazz == Boolean.TYPE)
1221              {
1222                boolean[] cast_array = (boolean[])array;
1223                realOutput.writeInt (length);
1224                for (int i = 0; i < length; i++)
1225                  realOutput.writeBoolean(cast_array[i]);
1226                return;
1227              }
1228            if (clazz == Byte.TYPE)
1229              {
1230                byte[] cast_array = (byte[])array;
1231                realOutput.writeInt(length);
1232                realOutput.write(cast_array, 0, length);
1233                return;
1234              }
1235            if (clazz == Character.TYPE)
1236              {
1237                char[] cast_array = (char[])array;
1238                realOutput.writeInt(length);
1239                for (int i = 0; i < length; i++)
1240                  realOutput.writeChar(cast_array[i]);
1241                return;
1242              }
1243            if (clazz == Double.TYPE)
1244              {
1245                double[] cast_array = (double[])array;
1246                realOutput.writeInt(length);
1247                for (int i = 0; i < length; i++)
1248                  realOutput.writeDouble(cast_array[i]);
1249                return;
1250              }
1251            if (clazz == Float.TYPE)
1252              {
1253                float[] cast_array = (float[])array;
1254                realOutput.writeInt(length);
1255                for (int i = 0; i < length; i++)
1256                  realOutput.writeFloat(cast_array[i]);
1257                return;
1258              }
1259            if (clazz == Integer.TYPE)
1260              {
1261                int[] cast_array = (int[])array;
1262                realOutput.writeInt(length);
1263                for (int i = 0; i < length; i++)
1264                  realOutput.writeInt(cast_array[i]);
1265                return;
1266              }
1267            if (clazz == Long.TYPE)
1268              {
1269                long[] cast_array = (long[])array;
1270                realOutput.writeInt (length);
1271                for (int i = 0; i < length; i++)
1272                  realOutput.writeLong(cast_array[i]);
1273                return;
1274              }
1275            if (clazz == Short.TYPE)
1276              {
1277                short[] cast_array = (short[])array;
1278                realOutput.writeInt (length);
1279                for (int i = 0; i < length; i++)
1280                  realOutput.writeShort(cast_array[i]);
1281                return;
1282              }
1283          }
1284        else
1285          {
1286            Object[] cast_array = (Object[])array;
1287            realOutput.writeInt(length);
1288            for (int i = 0; i < length; i++)
1289              writeObject(cast_array[i]);
1290          }
1291      }
1292    
1293    
1294    /* GCJ LOCAL */
1295      // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1296      // FIELDS are already supposed already to be in canonical order, but
1297      // under some circumstances (to do with Proxies) this isn't the
1298      // case, so we call ensureFieldsSet().
1299      private void writeFields(Object obj, ObjectStreamClass osc)
1300        throws IOException
1301      {
1302        osc.ensureFieldsSet(osc.forClass());
1303    /* END GCJ LOCAL */
1304    
1305        ObjectStreamField[] fields = osc.fields;
1306        boolean oldmode = setBlockDataMode(false);
1307    
1308        try
1309          {
1310            writeFields(obj,fields);
1311          }
1312        catch (IllegalArgumentException _)
1313          {
1314            InvalidClassException e = new InvalidClassException
1315              ("writing fields of class " + osc.forClass().getName());
1316            e.initCause(_);
1317            throw e;
1318          }
1319        catch (IOException e)
1320          {
1321            throw e;
1322          }
1323        catch (Exception _)
1324          {
1325            IOException e = new IOException("Unexpected exception " + _);
1326            e.initCause(_);
1327            throw(e);
1328          }    
1329    
1330        setBlockDataMode(oldmode);
1331      }
1332            
1333    
1334      /**
1335       * Helper function for writeFields(Object,ObjectStreamClass): write
1336       * fields from given fields array.  Pass exception on.
1337       *
1338       * @param obj the object to be written
1339       *
1340       * @param fields the fields of obj to be written.
1341       */
1342      private void writeFields(Object obj, ObjectStreamField[] fields)
1343        throws
1344          IllegalArgumentException, IllegalAccessException, IOException
1345      {
1346        for (int i = 0; i < fields.length; i++)
1347          {
1348            ObjectStreamField osf = fields[i];
1349            Field field = osf.field;
1350            
1351            if (DEBUG && dump)
1352              dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
1353            
1354            switch (osf.getTypeCode())
1355              {
1356              case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
1357              case 'B': realOutput.writeByte   (field.getByte   (obj)); break;
1358              case 'S': realOutput.writeShort  (field.getShort  (obj)); break;
1359              case 'C': realOutput.writeChar   (field.getChar   (obj)); break;
1360              case 'I': realOutput.writeInt    (field.getInt    (obj)); break;
1361              case 'F': realOutput.writeFloat  (field.getFloat  (obj)); break;
1362              case 'J': realOutput.writeLong   (field.getLong   (obj)); break;
1363              case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
1364              case 'L': 
1365              case '[':            writeObject (field.get       (obj)); break;
1366              default: 
1367                throw new IOException("Unexpected type code " + osf.getTypeCode());
1368              }
1369          }
1370      }
1371    
1372    
1373      // Toggles writing primitive data to block-data buffer.
1374      // Package-private to avoid a trampoline constructor.
1375      boolean setBlockDataMode(boolean on) throws IOException
1376      {
1377        if (on == writeDataAsBlocks)
1378          return on;
1379    
1380        drain();
1381        boolean oldmode = writeDataAsBlocks;
1382        writeDataAsBlocks = on;
1383    
1384        if (on)
1385          dataOutput = blockDataOutput;
1386        else
1387          dataOutput = realOutput;
1388    
1389        return oldmode;
1390      }
1391    
1392    
1393      private void callWriteMethod(Object obj, ObjectStreamClass osc)
1394        throws IOException
1395      {
1396        currentPutField = null;
1397        try
1398          {
1399            Object args[] = {this};
1400            osc.writeObjectMethod.invoke(obj, args);
1401          }
1402        catch (InvocationTargetException x)
1403          {
1404            /* Rethrow if possible. */
1405            Throwable exception = x.getTargetException();
1406            if (exception instanceof RuntimeException)
1407              throw (RuntimeException) exception;
1408            if (exception instanceof IOException)
1409              throw (IOException) exception;
1410    
1411            IOException ioe
1412              = new IOException("Exception thrown from writeObject() on " +
1413                                osc.forClass().getName() + ": " +
1414                                exception.getClass().getName());
1415            ioe.initCause(exception);
1416            throw ioe;
1417          }
1418        catch (Exception x)
1419          {
1420            IOException ioe
1421              = new IOException("Failure invoking writeObject() on " +
1422                                osc.forClass().getName() + ": " +
1423                                x.getClass().getName());
1424            ioe.initCause(x);
1425            throw ioe;
1426          }
1427      }
1428    
1429      private void dumpElementln (String msg, Object obj)
1430      {
1431        try
1432          {
1433            for (int i = 0; i < depth; i++)
1434              System.out.print (" ");
1435            System.out.print (Thread.currentThread() + ": ");
1436            System.out.print (msg);
1437            if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
1438              System.out.print (obj.getClass());
1439            else
1440              System.out.print (obj);
1441          }
1442        catch (Exception _)
1443          {
1444          }
1445        finally
1446          {
1447            System.out.println ();
1448          }
1449      }
1450    
1451      private void dumpElementln (String msg)
1452      {
1453        for (int i = 0; i < depth; i++)
1454          System.out.print (" ");
1455        System.out.print (Thread.currentThread() + ": ");
1456        System.out.println(msg);
1457      }
1458    
1459      // this value comes from 1.2 spec, but is used in 1.1 as well
1460      private static final int BUFFER_SIZE = 1024;
1461    
1462      private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1463    
1464      private DataOutputStream dataOutput;
1465      private boolean writeDataAsBlocks;
1466      private DataOutputStream realOutput;
1467      private DataOutputStream blockDataOutput;
1468      private byte[] blockData;
1469      private int blockDataCount;
1470      private Object currentObject;
1471      // Package-private to avoid a trampoline.
1472      ObjectStreamClass currentObjectStreamClass;
1473      private PutField currentPutField;
1474      private boolean fieldsAlreadyWritten;
1475      private boolean replacementEnabled;
1476      private boolean isSerializing;
1477      private int nextOID;
1478      private ObjectIdentityMap2Int OIDLookupTable;
1479      private int protocolVersion;
1480      private boolean useSubclassMethod;
1481      private SetAccessibleAction setAccessible = new SetAccessibleAction();
1482    
1483      // The nesting depth for debugging output
1484      private int depth = 0;
1485    
1486      // Set if we're generating debugging dumps
1487      private boolean dump = false;
1488    
1489      private static final boolean DEBUG = false;
1490    }