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