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<type></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 }