1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537 |
- /* ObjectOutputStream.java -- Class used to write serialized objects
- Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
- Free Software Foundation, Inc.
- This file is part of GNU Classpath.
- GNU Classpath is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- GNU Classpath is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GNU Classpath; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 USA.
- Linking this library statically or dynamically with other modules is
- making a combined work based on this library. Thus, the terms and
- conditions of the GNU General Public License cover the whole
- combination.
- As a special exception, the copyright holders of this library give you
- permission to link this library with independent modules to produce an
- executable, regardless of the license terms of these independent
- modules, and to copy and distribute the resulting executable under
- terms of your choice, provided that you also meet, for each linked
- independent module, the terms and conditions of the license of that
- module. An independent module is a module which is not derived from
- or based on this library. If you modify this library, you may extend
- this exception to your version of the library, but you are not
- obligated to do so. If you do not wish to do so, delete this
- exception statement from your version. */
- package java.io;
- import gnu.java.io.ObjectIdentityMap2Int;
- import gnu.java.lang.reflect.TypeSignature;
- import gnu.java.security.action.SetAccessibleAction;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- /**
- * An <code>ObjectOutputStream</code> can be used to write objects
- * as well as primitive data in a platform-independent manner to an
- * <code>OutputStream</code>.
- *
- * The data produced by an <code>ObjectOutputStream</code> can be read
- * and reconstituted by an <code>ObjectInputStream</code>.
- *
- * <code>writeObject (Object)</code> is used to write Objects, the
- * <code>write<type></code> methods are used to write primitive
- * data (as in <code>DataOutputStream</code>). Strings can be written
- * as objects or as primitive data.
- *
- * Not all objects can be written out using an
- * <code>ObjectOutputStream</code>. Only those objects that are an
- * instance of <code>java.io.Serializable</code> can be written.
- *
- * Using default serialization, information about the class of an
- * object is written, all of the non-transient, non-static fields of
- * the object are written, if any of these fields are objects, they are
- * written out in the same manner.
- *
- * An object is only written out the first time it is encountered. If
- * the object is encountered later, a reference to it is written to
- * the underlying stream. Thus writing circular object graphs
- * does not present a problem, nor are relationships between objects
- * in a graph lost.
- *
- * Example usage:
- * <pre>
- * Hashtable map = new Hashtable ();
- * map.put ("one", new Integer (1));
- * map.put ("two", new Integer (2));
- *
- * ObjectOutputStream oos =
- * new ObjectOutputStream (new FileOutputStream ("numbers"));
- * oos.writeObject (map);
- * oos.close ();
- *
- * ObjectInputStream ois =
- * new ObjectInputStream (new FileInputStream ("numbers"));
- * Hashtable newmap = (Hashtable)ois.readObject ();
- *
- * System.out.println (newmap);
- * </pre>
- *
- * The default serialization can be overriden in two ways.
- *
- * By defining a method <code>private void
- * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
- * how information about itself is written.
- * <code>defaultWriteObject ()</code> may be called from this method to
- * carry out default serialization. This method is not
- * responsible for dealing with fields of super-classes or subclasses.
- *
- * By implementing <code>java.io.Externalizable</code>. This gives
- * the class complete control over the way it is written to the
- * stream. If this approach is used the burden of writing superclass
- * and subclass data is transfered to the class implementing
- * <code>java.io.Externalizable</code>.
- *
- * @see java.io.DataOutputStream
- * @see java.io.Externalizable
- * @see java.io.ObjectInputStream
- * @see java.io.Serializable
- * @author Tom Tromey (tromey@redhat.com)
- * @author Jeroen Frijters (jeroen@frijters.net)
- * @author Guilhem Lavaux (guilhem@kaffe.org)
- * @author Michael Koch (konqueror@gmx.de)
- * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
- */
- public class ObjectOutputStream extends OutputStream
- implements ObjectOutput, ObjectStreamConstants
- {
- /**
- * Creates a new <code>ObjectOutputStream</code> that will do all of
- * its writing onto <code>out</code>. This method also initializes
- * the stream by writing the header information (stream magic number
- * and stream version).
- *
- * @exception IOException Writing stream header to underlying
- * stream cannot be completed.
- *
- * @see #writeStreamHeader()
- */
- public ObjectOutputStream (OutputStream out) throws IOException
- {
- SecurityManager secMan = System.getSecurityManager();
- if (secMan != null && overridesMethods(getClass()))
- secMan.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- realOutput = new DataOutputStream(out);
- blockData = new byte[ BUFFER_SIZE ];
- blockDataCount = 0;
- blockDataOutput = new DataOutputStream(this);
- setBlockDataMode(true);
- replacementEnabled = false;
- isSerializing = false;
- nextOID = baseWireHandle;
- OIDLookupTable = new ObjectIdentityMap2Int();
- protocolVersion = defaultProtocolVersion;
- useSubclassMethod = false;
- writeStreamHeader();
- if (DEBUG)
- {
- String val = System.getProperty("gcj.dumpobjects");
- if (val != null && !val.equals(""))
- dump = true;
- }
- }
- /**
- * Writes a representation of <code>obj</code> to the underlying
- * output stream by writing out information about its class, then
- * writing out each of the objects non-transient, non-static
- * fields. If any of these fields are other objects,
- * they are written out in the same manner.
- *
- * This method can be overriden by a class by implementing
- * <code>private void writeObject (ObjectOutputStream)</code>.
- *
- * If an exception is thrown from this method, the stream is left in
- * an undefined state.
- *
- * @param obj the object to serialize.
- * @exception NotSerializableException An attempt was made to
- * serialize an <code>Object</code> that is not serializable.
- *
- * @exception InvalidClassException Somebody tried to serialize
- * an object which is wrongly formatted.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- * @see #writeUnshared(Object)
- */
- public final void writeObject(Object obj) throws IOException
- {
- writeObject(obj, true);
- }
- /**
- * Writes an object to the stream in the same manner as
- * {@link #writeObject(Object)}, but without the use of
- * references. As a result, the object is always written
- * to the stream in full. Likewise, if an object is written
- * by this method and is then later written again by
- * {@link #writeObject(Object)}, both calls will write out
- * the object in full, as the later call to
- * {@link #writeObject(Object)} will know nothing of the
- * earlier use of {@link #writeUnshared(Object)}.
- *
- * @param obj the object to serialize.
- * @throws NotSerializableException if the object being
- * serialized does not implement
- * {@link Serializable}.
- * @throws InvalidClassException if a problem occurs with
- * the class of the object being
- * serialized.
- * @throws IOException if an I/O error occurs on the underlying
- * <code>OutputStream</code>.
- * @since 1.4
- * @see #writeObject(Object)
- */
- public void writeUnshared(Object obj)
- throws IOException
- {
- writeObject(obj, false);
- }
- /**
- * Writes a representation of <code>obj</code> to the underlying
- * output stream by writing out information about its class, then
- * writing out each of the objects non-transient, non-static
- * fields. If any of these fields are other objects,
- * they are written out in the same manner.
- *
- * This method can be overriden by a class by implementing
- * <code>private void writeObject (ObjectOutputStream)</code>.
- *
- * If an exception is thrown from this method, the stream is left in
- * an undefined state.
- *
- * @param obj the object to serialize.
- * @param shared true if the serialized object should be
- * shared with later calls.
- * @exception NotSerializableException An attempt was made to
- * serialize an <code>Object</code> that is not serializable.
- *
- * @exception InvalidClassException Somebody tried to serialize
- * an object which is wrongly formatted.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- * @see #writeUnshared(Object)
- */
- private final void writeObject(Object obj, boolean shared)
- throws IOException
- {
- if (useSubclassMethod)
- {
- if (dump)
- dumpElementln ("WRITE OVERRIDE: " + obj);
- writeObjectOverride(obj);
- return;
- }
- if (dump)
- dumpElementln ("WRITE: ", obj);
- depth += 2;
- boolean was_serializing = isSerializing;
- boolean old_mode = setBlockDataMode(false);
- try
- {
- isSerializing = true;
- boolean replaceDone = false;
- Object replacedObject = null;
- while (true)
- {
- if (obj == null)
- {
- realOutput.writeByte(TC_NULL);
- break;
- }
- int handle = findHandle(obj);
- if (handle >= 0 && shared)
- {
- realOutput.writeByte(TC_REFERENCE);
- realOutput.writeInt(handle);
- break;
- }
- if (obj instanceof Class)
- {
- Class cl = (Class)obj;
- ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
- realOutput.writeByte(TC_CLASS);
- if (!osc.isProxyClass)
- {
- writeObject (osc);
- }
- else
- {System.err.println("1");
- realOutput.writeByte(TC_PROXYCLASSDESC);
- Class[] intfs = cl.getInterfaces();
- realOutput.writeInt(intfs.length);
- for (int i = 0; i < intfs.length; i++)
- realOutput.writeUTF(intfs[i].getName());
- boolean oldmode = setBlockDataMode(true);
- annotateProxyClass(cl);
- setBlockDataMode(oldmode);
- realOutput.writeByte(TC_ENDBLOCKDATA);
- writeObject(osc.getSuper());
- }
- if (shared)
- assignNewHandle(obj);
- break;
- }
- if (obj instanceof ObjectStreamClass)
- {
- writeClassDescriptor((ObjectStreamClass) obj);
- break;
- }
- Class clazz = obj.getClass();
- ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
- if (osc == null)
- throw new NotSerializableException(clazz.getName());
- if (osc.isEnum())
- {
- /* TC_ENUM classDesc newHandle enumConstantName */
- realOutput.writeByte(TC_ENUM);
- writeObject(osc);
- if (shared)
- assignNewHandle(obj);
- writeObject(((Enum) obj).name());
- break;
- }
- if ((replacementEnabled || obj instanceof Serializable)
- && ! replaceDone)
- {
- replacedObject = obj;
- if (obj instanceof Serializable)
- {
- try
- {
- Method m = osc.writeReplaceMethod;
- if (m != null)
- obj = m.invoke(obj, new Object[0]);
- }
- catch (IllegalAccessException ignore)
- {
- }
- catch (InvocationTargetException ignore)
- {
- }
- }
- if (replacementEnabled)
- obj = replaceObject(obj);
- replaceDone = true;
- continue;
- }
- if (obj instanceof String)
- {
- String s = (String)obj;
- long l = realOutput.getUTFlength(s, 0, 0);
- if (l <= 65535)
- {
- realOutput.writeByte(TC_STRING);
- if (shared)
- assignNewHandle(obj);
- realOutput.writeUTFShort(s, (int)l);
- }
- else
- {
- realOutput.writeByte(TC_LONGSTRING);
- if (shared)
- assignNewHandle(obj);
- realOutput.writeUTFLong(s, l);
- }
- break;
- }
- if (clazz.isArray ())
- {
- realOutput.writeByte(TC_ARRAY);
- writeObject(osc);
- if (shared)
- assignNewHandle(obj);
- writeArraySizeAndElements(obj, clazz.getComponentType());
- break;
- }
- realOutput.writeByte(TC_OBJECT);
- writeObject(osc);
- if (shared)
- if (replaceDone)
- assignNewHandle(replacedObject);
- else
- assignNewHandle(obj);
- if (obj instanceof Externalizable)
- {
- if (protocolVersion == PROTOCOL_VERSION_2)
- setBlockDataMode(true);
- ((Externalizable)obj).writeExternal(this);
- if (protocolVersion == PROTOCOL_VERSION_2)
- {
- setBlockDataMode(false);
- realOutput.writeByte(TC_ENDBLOCKDATA);
- }
- break;
- }
- if (obj instanceof Serializable)
- {
- Object prevObject = this.currentObject;
- ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
- currentObject = obj;
- ObjectStreamClass[] hierarchy = osc.hierarchy();
- for (int i = 0; i < hierarchy.length; i++)
- {
- currentObjectStreamClass = hierarchy[i];
- fieldsAlreadyWritten = false;
- if (currentObjectStreamClass.hasWriteMethod())
- {
- if (dump)
- dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
- setBlockDataMode(true);
- callWriteMethod(obj, currentObjectStreamClass);
- setBlockDataMode(false);
- realOutput.writeByte(TC_ENDBLOCKDATA);
- if (dump)
- dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
- }
- else
- {
- if (dump)
- dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
- writeFields(obj, currentObjectStreamClass);
- }
- }
- this.currentObject = prevObject;
- this.currentObjectStreamClass = prevObjectStreamClass;
- currentPutField = null;
- break;
- }
- throw new NotSerializableException(clazz.getName()
- + " in "
- + obj.getClass());
- } // end pseudo-loop
- }
- catch (ObjectStreamException ose)
- {
- // Rethrow these are fatal.
- throw ose;
- }
- catch (IOException e)
- {
- realOutput.writeByte(TC_EXCEPTION);
- reset(true);
- setBlockDataMode(false);
- try
- {
- if (DEBUG)
- {
- e.printStackTrace(System.out);
- }
- writeObject(e);
- }
- catch (IOException ioe)
- {
- StreamCorruptedException ex =
- new StreamCorruptedException
- (ioe + " thrown while exception was being written to stream.");
- if (DEBUG)
- {
- ex.printStackTrace(System.out);
- }
- throw ex;
- }
- reset (true);
- }
- finally
- {
- isSerializing = was_serializing;
- setBlockDataMode(old_mode);
- depth -= 2;
- if (dump)
- dumpElementln ("END: ", obj);
- }
- }
- protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
- {
- if (osc.isProxyClass)
- {
- realOutput.writeByte(TC_PROXYCLASSDESC);
- Class[] intfs = osc.forClass().getInterfaces();
- realOutput.writeInt(intfs.length);
- for (int i = 0; i < intfs.length; i++)
- realOutput.writeUTF(intfs[i].getName());
- assignNewHandle(osc);
- boolean oldmode = setBlockDataMode(true);
- annotateProxyClass(osc.forClass());
- setBlockDataMode(oldmode);
- realOutput.writeByte(TC_ENDBLOCKDATA);
- }
- else
- {
- realOutput.writeByte(TC_CLASSDESC);
- realOutput.writeUTF(osc.getName());
- if (osc.isEnum())
- realOutput.writeLong(0L);
- else
- realOutput.writeLong(osc.getSerialVersionUID());
- assignNewHandle(osc);
- int flags = osc.getFlags();
- if (protocolVersion == PROTOCOL_VERSION_2
- && osc.isExternalizable())
- flags |= SC_BLOCK_DATA;
- realOutput.writeByte(flags);
- ObjectStreamField[] fields = osc.fields;
- if (fields == ObjectStreamClass.INVALID_FIELDS)
- throw new InvalidClassException
- (osc.getName(), "serialPersistentFields is invalid");
- realOutput.writeShort(fields.length);
- ObjectStreamField field;
- for (int i = 0; i < fields.length; i++)
- {
- field = fields[i];
- realOutput.writeByte(field.getTypeCode ());
- realOutput.writeUTF(field.getName ());
- if (! field.isPrimitive())
- writeObject(field.getTypeString());
- }
- boolean oldmode = setBlockDataMode(true);
- annotateClass(osc.forClass());
- setBlockDataMode(oldmode);
- realOutput.writeByte(TC_ENDBLOCKDATA);
- }
- if (osc.isSerializable() || osc.isExternalizable())
- writeObject(osc.getSuper());
- else
- writeObject(null);
- }
- /**
- * Writes the current objects non-transient, non-static fields from
- * the current class to the underlying output stream.
- *
- * This method is intended to be called from within a object's
- * <code>private void writeObject (ObjectOutputStream)</code>
- * method.
- *
- * @exception NotActiveException This method was called from a
- * context other than from the current object's and current class's
- * <code>private void writeObject (ObjectOutputStream)</code>
- * method.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- */
- public void defaultWriteObject()
- throws IOException, NotActiveException
- {
- markFieldsWritten();
- writeFields(currentObject, currentObjectStreamClass);
- }
- private void markFieldsWritten() throws IOException
- {
- if (currentObject == null || currentObjectStreamClass == null)
- throw new NotActiveException
- ("defaultWriteObject called by non-active class and/or object");
- if (fieldsAlreadyWritten)
- throw new IOException
- ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
- fieldsAlreadyWritten = true;
- }
- /**
- * Resets stream to state equivalent to the state just after it was
- * constructed.
- *
- * Causes all objects previously written to the stream to be
- * forgotten. A notification of this reset is also written to the
- * underlying stream.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code> or reset called while serialization is
- * in progress.
- */
- public void reset() throws IOException
- {
- reset(false);
- }
- private void reset(boolean internal) throws IOException
- {
- if (!internal)
- {
- if (isSerializing)
- throw new IOException("Reset called while serialization in progress");
- realOutput.writeByte(TC_RESET);
- }
- clearHandles();
- }
- /**
- * Informs this <code>ObjectOutputStream</code> to write data
- * according to the specified protocol. There are currently two
- * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
- * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
- * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
- * since the JDK 1.2.
- * <p>
- * For an explanation of the differences between the two protocols
- * see the Java Object Serialization Specification.
- * </p>
- *
- * @param version the version to use.
- *
- * @throws IllegalArgumentException if <code>version</code> is not a valid
- * protocol.
- * @throws IllegalStateException if called after the first the first object
- * was serialized.
- * @throws IOException if an I/O error occurs.
- *
- * @see ObjectStreamConstants#PROTOCOL_VERSION_1
- * @see ObjectStreamConstants#PROTOCOL_VERSION_2
- *
- * @since 1.2
- */
- public void useProtocolVersion(int version) throws IOException
- {
- if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
- throw new IllegalArgumentException("Invalid protocol version requested.");
- if (nextOID != baseWireHandle)
- throw new IllegalStateException("Protocol version cannot be changed "
- + "after serialization started.");
- protocolVersion = version;
- }
- /**
- * An empty hook that allows subclasses to write extra information
- * about classes to the stream. This method is called the first
- * time each class is seen, and after all of the standard
- * information about the class has been written.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- *
- * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
- */
- protected void annotateClass(Class<?> cl) throws IOException
- {
- }
- protected void annotateProxyClass(Class<?> cl) throws IOException
- {
- }
- /**
- * Allows subclasses to replace objects that are written to the
- * stream with other objects to be written in their place. This
- * method is called the first time each object is encountered
- * (modulo reseting of the stream).
- *
- * This method must be enabled before it will be called in the
- * serialization process.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- *
- * @see #enableReplaceObject(boolean)
- */
- protected Object replaceObject(Object obj) throws IOException
- {
- return obj;
- }
- /**
- * If <code>enable</code> is <code>true</code> and this object is
- * trusted, then <code>replaceObject (Object)</code> will be called
- * in subsequent calls to <code>writeObject (Object)</code>.
- * Otherwise, <code>replaceObject (Object)</code> will not be called.
- *
- * @exception SecurityException This class is not trusted.
- */
- protected boolean enableReplaceObject(boolean enable)
- throws SecurityException
- {
- if (enable)
- {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null)
- sm.checkPermission(new SerializablePermission("enableSubstitution"));
- }
- boolean old_val = replacementEnabled;
- replacementEnabled = enable;
- return old_val;
- }
- /**
- * Writes stream magic and stream version information to the
- * underlying stream.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- */
- protected void writeStreamHeader() throws IOException
- {
- realOutput.writeShort(STREAM_MAGIC);
- realOutput.writeShort(STREAM_VERSION);
- }
- /**
- * Protected constructor that allows subclasses to override
- * serialization. This constructor should be called by subclasses
- * that wish to override <code>writeObject (Object)</code>. This
- * method does a security check <i>NOTE: currently not
- * implemented</i>, then sets a flag that informs
- * <code>writeObject (Object)</code> to call the subclasses
- * <code>writeObjectOverride (Object)</code> method.
- *
- * @see #writeObjectOverride(Object)
- */
- protected ObjectOutputStream() throws IOException, SecurityException
- {
- SecurityManager sec_man = System.getSecurityManager ();
- if (sec_man != null)
- sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- useSubclassMethod = true;
- }
- /**
- * This method allows subclasses to override the default
- * serialization mechanism provided by
- * <code>ObjectOutputStream</code>. To make this method be used for
- * writing objects, subclasses must invoke the 0-argument
- * constructor on this class from there constructor.
- *
- * @see #ObjectOutputStream()
- *
- * @exception NotActiveException Subclass has arranged for this
- * method to be called, but did not implement this method.
- */
- protected void writeObjectOverride(Object obj) throws NotActiveException,
- IOException
- {
- throw new NotActiveException
- ("Subclass of ObjectOutputStream must implement writeObjectOverride");
- }
- /**
- * @see DataOutputStream#write(int)
- */
- public void write (int data) throws IOException
- {
- if (writeDataAsBlocks)
- {
- if (blockDataCount == BUFFER_SIZE)
- drain();
- blockData[ blockDataCount++ ] = (byte)data;
- }
- else
- realOutput.write(data);
- }
- /**
- * @see DataOutputStream#write(byte[])
- */
- public void write(byte[] b) throws IOException
- {
- write(b, 0, b.length);
- }
- /**
- * @see DataOutputStream#write(byte[],int,int)
- */
- public void write(byte[] b, int off, int len) throws IOException
- {
- if (writeDataAsBlocks)
- {
- if (len < 0)
- throw new IndexOutOfBoundsException();
- if (blockDataCount + len < BUFFER_SIZE)
- {
- System.arraycopy(b, off, blockData, blockDataCount, len);
- blockDataCount += len;
- }
- else
- {
- drain();
- writeBlockDataHeader(len);
- realOutput.write(b, off, len);
- }
- }
- else
- realOutput.write(b, off, len);
- }
- /**
- * @see DataOutputStream#flush()
- */
- public void flush () throws IOException
- {
- drain();
- realOutput.flush();
- }
- /**
- * Causes the block-data buffer to be written to the underlying
- * stream, but does not flush underlying stream.
- *
- * @exception IOException Exception from underlying
- * <code>OutputStream</code>.
- */
- protected void drain() throws IOException
- {
- if (blockDataCount == 0)
- return;
- if (writeDataAsBlocks)
- writeBlockDataHeader(blockDataCount);
- realOutput.write(blockData, 0, blockDataCount);
- blockDataCount = 0;
- }
- /**
- * @see java.io.DataOutputStream#close ()
- */
- public void close() throws IOException
- {
- flush();
- realOutput.close();
- }
- /**
- * @see java.io.DataOutputStream#writeBoolean (boolean)
- */
- public void writeBoolean(boolean data) throws IOException
- {
- blockDataOutput.writeBoolean(data);
- }
- /**
- * @see java.io.DataOutputStream#writeByte (int)
- */
- public void writeByte(int data) throws IOException
- {
- blockDataOutput.writeByte(data);
- }
- /**
- * @see java.io.DataOutputStream#writeShort (int)
- */
- public void writeShort (int data) throws IOException
- {
- blockDataOutput.writeShort(data);
- }
- /**
- * @see java.io.DataOutputStream#writeChar (int)
- */
- public void writeChar(int data) throws IOException
- {
- blockDataOutput.writeChar(data);
- }
- /**
- * @see java.io.DataOutputStream#writeInt (int)
- */
- public void writeInt(int data) throws IOException
- {
- blockDataOutput.writeInt(data);
- }
- /**
- * @see java.io.DataOutputStream#writeLong (long)
- */
- public void writeLong(long data) throws IOException
- {
- blockDataOutput.writeLong(data);
- }
- /**
- * @see java.io.DataOutputStream#writeFloat (float)
- */
- public void writeFloat(float data) throws IOException
- {
- blockDataOutput.writeFloat(data);
- }
- /**
- * @see java.io.DataOutputStream#writeDouble (double)
- */
- public void writeDouble(double data) throws IOException
- {
- blockDataOutput.writeDouble(data);
- }
- /**
- * @see java.io.DataOutputStream#writeBytes (java.lang.String)
- */
- public void writeBytes(String data) throws IOException
- {
- blockDataOutput.writeBytes(data);
- }
- /**
- * @see java.io.DataOutputStream#writeChars (java.lang.String)
- */
- public void writeChars(String data) throws IOException
- {
- dataOutput.writeChars(data);
- }
- /**
- * @see java.io.DataOutputStream#writeUTF (java.lang.String)
- */
- public void writeUTF(String data) throws IOException
- {
- dataOutput.writeUTF(data);
- }
- /**
- * This class allows a class to specify exactly which fields should
- * be written, and what values should be written for these fields.
- *
- * XXX: finish up comments
- */
- public abstract static class PutField
- {
- public abstract void put (String name, boolean value);
- public abstract void put (String name, byte value);
- public abstract void put (String name, char value);
- public abstract void put (String name, double value);
- public abstract void put (String name, float value);
- public abstract void put (String name, int value);
- public abstract void put (String name, long value);
- public abstract void put (String name, short value);
- public abstract void put (String name, Object value);
- /**
- * @deprecated
- */
- public abstract void write (ObjectOutput out) throws IOException;
- }
- public PutField putFields() throws IOException
- {
- if (currentPutField != null)
- return currentPutField;
- currentPutField = new PutField()
- {
- private byte[] prim_field_data
- = new byte[currentObjectStreamClass.primFieldSize];
- private Object[] objs
- = new Object[currentObjectStreamClass.objectFieldCount];
- private ObjectStreamField getField (String name)
- {
- ObjectStreamField field
- = currentObjectStreamClass.getField(name);
- if (field == null)
- throw new IllegalArgumentException("no such serializable field " + name);
- return field;
- }
- public void put(String name, boolean value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'Z');
- prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
- }
- public void put(String name, byte value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'B');
- prim_field_data[field.getOffset()] = value;
- }
- public void put(String name, char value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'C');
- int off = field.getOffset();
- prim_field_data[off++] = (byte)(value >>> 8);
- prim_field_data[off] = (byte)value;
- }
- public void put(String name, double value)
- {
- ObjectStreamField field = getField (name);
- checkType(field, 'D');
- int off = field.getOffset();
- long l_value = Double.doubleToLongBits (value);
- prim_field_data[off++] = (byte)(l_value >>> 52);
- prim_field_data[off++] = (byte)(l_value >>> 48);
- prim_field_data[off++] = (byte)(l_value >>> 40);
- prim_field_data[off++] = (byte)(l_value >>> 32);
- prim_field_data[off++] = (byte)(l_value >>> 24);
- prim_field_data[off++] = (byte)(l_value >>> 16);
- prim_field_data[off++] = (byte)(l_value >>> 8);
- prim_field_data[off] = (byte)l_value;
- }
- public void put(String name, float value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'F');
- int off = field.getOffset();
- int i_value = Float.floatToIntBits(value);
- prim_field_data[off++] = (byte)(i_value >>> 24);
- prim_field_data[off++] = (byte)(i_value >>> 16);
- prim_field_data[off++] = (byte)(i_value >>> 8);
- prim_field_data[off] = (byte)i_value;
- }
- public void put(String name, int value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'I');
- int off = field.getOffset();
- prim_field_data[off++] = (byte)(value >>> 24);
- prim_field_data[off++] = (byte)(value >>> 16);
- prim_field_data[off++] = (byte)(value >>> 8);
- prim_field_data[off] = (byte)value;
- }
- public void put(String name, long value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'J');
- int off = field.getOffset();
- prim_field_data[off++] = (byte)(value >>> 52);
- prim_field_data[off++] = (byte)(value >>> 48);
- prim_field_data[off++] = (byte)(value >>> 40);
- prim_field_data[off++] = (byte)(value >>> 32);
- prim_field_data[off++] = (byte)(value >>> 24);
- prim_field_data[off++] = (byte)(value >>> 16);
- prim_field_data[off++] = (byte)(value >>> 8);
- prim_field_data[off] = (byte)value;
- }
- public void put(String name, short value)
- {
- ObjectStreamField field = getField(name);
- checkType(field, 'S');
- int off = field.getOffset();
- prim_field_data[off++] = (byte)(value >>> 8);
- prim_field_data[off] = (byte)value;
- }
- public void put(String name, Object value)
- {
- ObjectStreamField field = getField(name);
- if (value != null &&
- ! field.getType().isAssignableFrom(value.getClass ()))
- throw new IllegalArgumentException("Class " + value.getClass() +
- " cannot be cast to " + field.getType());
- objs[field.getOffset()] = value;
- }
- public void write(ObjectOutput out) throws IOException
- {
- // Apparently Block data is not used with PutField as per
- // empirical evidence against JDK 1.2. Also see Mauve test
- // java.io.ObjectInputOutput.Test.GetPutField.
- boolean oldmode = setBlockDataMode(false);
- out.write(prim_field_data);
- for (int i = 0; i < objs.length; ++ i)
- out.writeObject(objs[i]);
- setBlockDataMode(oldmode);
- }
- private void checkType(ObjectStreamField field, char type)
- throws IllegalArgumentException
- {
- if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
- != type)
- throw new IllegalArgumentException();
- }
- };
- // end PutFieldImpl
- return currentPutField;
- }
- public void writeFields() throws IOException
- {
- if (currentPutField == null)
- throw new NotActiveException("writeFields can only be called after putFields has been called");
- markFieldsWritten();
- currentPutField.write(this);
- }
- // write out the block-data buffer, picking the correct header
- // depending on the size of the buffer
- private void writeBlockDataHeader(int size) throws IOException
- {
- if (size < 256)
- {
- realOutput.writeByte(TC_BLOCKDATA);
- realOutput.write(size);
- }
- else
- {
- realOutput.writeByte(TC_BLOCKDATALONG);
- realOutput.writeInt(size);
- }
- }
- // lookup the handle for OBJ, return null if OBJ doesn't have a
- // handle yet
- private int findHandle(Object obj)
- {
- return OIDLookupTable.get(obj);
- }
- // assigns the next availible handle to OBJ
- private int assignNewHandle(Object obj)
- {
- OIDLookupTable.put(obj, nextOID);
- return nextOID++;
- }
- // resets mapping from objects to handles
- private void clearHandles()
- {
- nextOID = baseWireHandle;
- OIDLookupTable.clear();
- }
- // write out array size followed by each element of the array
- private void writeArraySizeAndElements(Object array, Class clazz)
- throws IOException
- {
- int length = Array.getLength(array);
- if (clazz.isPrimitive())
- {
- if (clazz == Boolean.TYPE)
- {
- boolean[] cast_array = (boolean[])array;
- realOutput.writeInt (length);
- for (int i = 0; i < length; i++)
- realOutput.writeBoolean(cast_array[i]);
- return;
- }
- if (clazz == Byte.TYPE)
- {
- byte[] cast_array = (byte[])array;
- realOutput.writeInt(length);
- realOutput.write(cast_array, 0, length);
- return;
- }
- if (clazz == Character.TYPE)
- {
- char[] cast_array = (char[])array;
- realOutput.writeInt(length);
- for (int i = 0; i < length; i++)
- realOutput.writeChar(cast_array[i]);
- return;
- }
- if (clazz == Double.TYPE)
- {
- double[] cast_array = (double[])array;
- realOutput.writeInt(length);
- for (int i = 0; i < length; i++)
- realOutput.writeDouble(cast_array[i]);
- return;
- }
- if (clazz == Float.TYPE)
- {
- float[] cast_array = (float[])array;
- realOutput.writeInt(length);
- for (int i = 0; i < length; i++)
- realOutput.writeFloat(cast_array[i]);
- return;
- }
- if (clazz == Integer.TYPE)
- {
- int[] cast_array = (int[])array;
- realOutput.writeInt(length);
- for (int i = 0; i < length; i++)
- realOutput.writeInt(cast_array[i]);
- return;
- }
- if (clazz == Long.TYPE)
- {
- long[] cast_array = (long[])array;
- realOutput.writeInt (length);
- for (int i = 0; i < length; i++)
- realOutput.writeLong(cast_array[i]);
- return;
- }
- if (clazz == Short.TYPE)
- {
- short[] cast_array = (short[])array;
- realOutput.writeInt (length);
- for (int i = 0; i < length; i++)
- realOutput.writeShort(cast_array[i]);
- return;
- }
- }
- else
- {
- Object[] cast_array = (Object[])array;
- realOutput.writeInt(length);
- for (int i = 0; i < length; i++)
- writeObject(cast_array[i]);
- }
- }
- /* GCJ LOCAL */
- // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
- // FIELDS are already supposed already to be in canonical order, but
- // under some circumstances (to do with Proxies) this isn't the
- // case, so we call ensureFieldsSet().
- private void writeFields(Object obj, ObjectStreamClass osc)
- throws IOException
- {
- osc.ensureFieldsSet(osc.forClass());
- /* END GCJ LOCAL */
- ObjectStreamField[] fields = osc.fields;
- boolean oldmode = setBlockDataMode(false);
- try
- {
- writeFields(obj,fields);
- }
- catch (IllegalArgumentException _)
- {
- InvalidClassException e = new InvalidClassException
- ("writing fields of class " + osc.forClass().getName());
- e.initCause(_);
- throw e;
- }
- catch (IOException e)
- {
- throw e;
- }
- catch (Exception _)
- {
- IOException e = new IOException("Unexpected exception " + _);
- e.initCause(_);
- throw(e);
- }
- setBlockDataMode(oldmode);
- }
- /**
- * Helper function for writeFields(Object,ObjectStreamClass): write
- * fields from given fields array. Pass exception on.
- *
- * @param obj the object to be written
- *
- * @param fields the fields of obj to be written.
- */
- private void writeFields(Object obj, ObjectStreamField[] fields)
- throws
- IllegalArgumentException, IllegalAccessException, IOException
- {
- for (int i = 0; i < fields.length; i++)
- {
- ObjectStreamField osf = fields[i];
- Field field = osf.field;
- if (DEBUG && dump)
- dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
- switch (osf.getTypeCode())
- {
- case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
- case 'B': realOutput.writeByte (field.getByte (obj)); break;
- case 'S': realOutput.writeShort (field.getShort (obj)); break;
- case 'C': realOutput.writeChar (field.getChar (obj)); break;
- case 'I': realOutput.writeInt (field.getInt (obj)); break;
- case 'F': realOutput.writeFloat (field.getFloat (obj)); break;
- case 'J': realOutput.writeLong (field.getLong (obj)); break;
- case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
- case 'L':
- case '[': writeObject (field.get (obj)); break;
- default:
- throw new IOException("Unexpected type code " + osf.getTypeCode());
- }
- }
- }
- // Toggles writing primitive data to block-data buffer.
- // Package-private to avoid a trampoline constructor.
- boolean setBlockDataMode(boolean on) throws IOException
- {
- if (on == writeDataAsBlocks)
- return on;
- drain();
- boolean oldmode = writeDataAsBlocks;
- writeDataAsBlocks = on;
- if (on)
- dataOutput = blockDataOutput;
- else
- dataOutput = realOutput;
- return oldmode;
- }
- private void callWriteMethod(Object obj, ObjectStreamClass osc)
- throws IOException
- {
- currentPutField = null;
- try
- {
- Object args[] = {this};
- osc.writeObjectMethod.invoke(obj, args);
- }
- catch (InvocationTargetException x)
- {
- /* Rethrow if possible. */
- Throwable exception = x.getTargetException();
- if (exception instanceof RuntimeException)
- throw (RuntimeException) exception;
- if (exception instanceof IOException)
- throw (IOException) exception;
- IOException ioe
- = new IOException("Exception thrown from writeObject() on " +
- osc.forClass().getName() + ": " +
- exception.getClass().getName());
- ioe.initCause(exception);
- throw ioe;
- }
- catch (Exception x)
- {
- IOException ioe
- = new IOException("Failure invoking writeObject() on " +
- osc.forClass().getName() + ": " +
- x.getClass().getName());
- ioe.initCause(x);
- throw ioe;
- }
- }
- private void dumpElementln (String msg, Object obj)
- {
- try
- {
- for (int i = 0; i < depth; i++)
- System.out.print (" ");
- System.out.print (Thread.currentThread() + ": ");
- System.out.print (msg);
- if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
- System.out.print (obj.getClass());
- else
- System.out.print (obj);
- }
- catch (Exception _)
- {
- }
- finally
- {
- System.out.println ();
- }
- }
- private void dumpElementln (String msg)
- {
- for (int i = 0; i < depth; i++)
- System.out.print (" ");
- System.out.print (Thread.currentThread() + ": ");
- System.out.println(msg);
- }
- // this value comes from 1.2 spec, but is used in 1.1 as well
- private static final int BUFFER_SIZE = 1024;
- private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
- private DataOutputStream dataOutput;
- private boolean writeDataAsBlocks;
- private DataOutputStream realOutput;
- private DataOutputStream blockDataOutput;
- private byte[] blockData;
- private int blockDataCount;
- private Object currentObject;
- // Package-private to avoid a trampoline.
- ObjectStreamClass currentObjectStreamClass;
- private PutField currentPutField;
- private boolean fieldsAlreadyWritten;
- private boolean replacementEnabled;
- private boolean isSerializing;
- private int nextOID;
- private ObjectIdentityMap2Int OIDLookupTable;
- private int protocolVersion;
- private boolean useSubclassMethod;
- private SetAccessibleAction setAccessible = new SetAccessibleAction();
- // The nesting depth for debugging output
- private int depth = 0;
- // Set if we're generating debugging dumps
- private boolean dump = false;
- private static final boolean DEBUG = false;
- /**
- * Returns true if the given class overrides either of the
- * methods <code>putFields</code> or <code>writeUnshared</code>.
- *
- * @param clazz the class to check.
- * @return true if the class overrides one of the methods.
- */
- private static boolean overridesMethods(final Class<?> clazz)
- {
- if (clazz == ObjectOutputStream.class)
- return false;
- return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
- public Boolean run()
- {
- Method[] methods = clazz.getDeclaredMethods();
- for (int a = 0; a < methods.length; ++a)
- {
- String name = methods[a].getName();
- if (name.equals("writeUnshared"))
- {
- Class<?>[] paramTypes = methods[a].getParameterTypes();
- if (paramTypes.length == 1 &&
- paramTypes[0] == Object.class &&
- methods[a].getReturnType() == Void.class)
- return true;
- }
- else if (name.equals("putFields"))
- {
- if (methods[a].getParameterTypes().length == 0 &&
- methods[a].getReturnType() == PutField.class)
- return true;
- }
- }
- return false;
- }
- });
- }
- }
|