jaroslav@601: /* jaroslav@601: * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. jaroslav@601: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@601: * jaroslav@601: * This code is free software; you can redistribute it and/or modify it jaroslav@601: * under the terms of the GNU General Public License version 2 only, as jaroslav@601: * published by the Free Software Foundation. Oracle designates this jaroslav@601: * particular file as subject to the "Classpath" exception as provided jaroslav@601: * by Oracle in the LICENSE file that accompanied this code. jaroslav@601: * jaroslav@601: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@601: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@601: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@601: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@601: * accompanied this code). jaroslav@601: * jaroslav@601: * You should have received a copy of the GNU General Public License version jaroslav@601: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@601: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@601: * jaroslav@601: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@601: * or visit www.oracle.com if you need additional information or have any jaroslav@601: * questions. jaroslav@601: */ jaroslav@601: jaroslav@601: package java.io; jaroslav@601: jaroslav@601: import java.io.ObjectStreamClass.WeakClassKey; jaroslav@601: import java.lang.ref.ReferenceQueue; jaroslav@601: import java.security.AccessController; jaroslav@601: import java.security.PrivilegedAction; jaroslav@601: import java.util.ArrayList; jaroslav@601: import java.util.Arrays; jaroslav@601: import java.util.List; jaroslav@601: import java.util.concurrent.ConcurrentHashMap; jaroslav@601: import java.util.concurrent.ConcurrentMap; jaroslav@601: import static java.io.ObjectStreamClass.processQueue; jaroslav@601: import java.io.SerialCallbackContext; jaroslav@601: jaroslav@601: /** jaroslav@601: * An ObjectOutputStream writes primitive data types and graphs of Java objects jaroslav@601: * to an OutputStream. The objects can be read (reconstituted) using an jaroslav@601: * ObjectInputStream. Persistent storage of objects can be accomplished by jaroslav@601: * using a file for the stream. If the stream is a network socket stream, the jaroslav@601: * objects can be reconstituted on another host or in another process. jaroslav@601: * jaroslav@601: *
Only objects that support the java.io.Serializable interface can be jaroslav@601: * written to streams. The class of each serializable object is encoded jaroslav@601: * including the class name and signature of the class, the values of the jaroslav@601: * object's fields and arrays, and the closure of any other objects referenced jaroslav@601: * from the initial objects. jaroslav@601: * jaroslav@601: *
The method writeObject is used to write an object to the stream. Any jaroslav@601: * object, including Strings and arrays, is written with writeObject. Multiple jaroslav@601: * objects or primitives can be written to the stream. The objects must be jaroslav@601: * read back from the corresponding ObjectInputstream with the same types and jaroslav@601: * in the same order as they were written. jaroslav@601: * jaroslav@601: *
Primitive data types can also be written to the stream using the jaroslav@601: * appropriate methods from DataOutput. Strings can also be written using the jaroslav@601: * writeUTF method. jaroslav@601: * jaroslav@601: *
The default serialization mechanism for an object writes the class of the jaroslav@601: * object, the class signature, and the values of all non-transient and jaroslav@601: * non-static fields. References to other objects (except in transient or jaroslav@601: * static fields) cause those objects to be written also. Multiple references jaroslav@601: * to a single object are encoded using a reference sharing mechanism so that jaroslav@601: * graphs of objects can be restored to the same shape as when the original was jaroslav@601: * written. jaroslav@601: * jaroslav@601: *
For example to write an object that can be read by the example in
jaroslav@601: * ObjectInputStream:
jaroslav@601: *
jaroslav@601: *
jaroslav@601: * FileOutputStream fos = new FileOutputStream("t.tmp"); jaroslav@601: * ObjectOutputStream oos = new ObjectOutputStream(fos); jaroslav@601: * jaroslav@601: * oos.writeInt(12345); jaroslav@601: * oos.writeObject("Today"); jaroslav@601: * oos.writeObject(new Date()); jaroslav@601: * jaroslav@601: * oos.close(); jaroslav@601: *jaroslav@601: * jaroslav@601: *
Classes that require special handling during the serialization and
jaroslav@601: * deserialization process must implement special methods with these exact
jaroslav@601: * signatures:
jaroslav@601: *
jaroslav@601: *
jaroslav@601: * private void readObject(java.io.ObjectInputStream stream) jaroslav@601: * throws IOException, ClassNotFoundException; jaroslav@601: * private void writeObject(java.io.ObjectOutputStream stream) jaroslav@601: * throws IOException jaroslav@601: * private void readObjectNoData() jaroslav@601: * throws ObjectStreamException; jaroslav@601: *jaroslav@601: * jaroslav@601: *
The writeObject method is responsible for writing the state of the object jaroslav@601: * for its particular class so that the corresponding readObject method can jaroslav@601: * restore it. The method does not need to concern itself with the state jaroslav@601: * belonging to the object's superclasses or subclasses. State is saved by jaroslav@601: * writing the individual fields to the ObjectOutputStream using the jaroslav@601: * writeObject method or by using the methods for primitive data types jaroslav@601: * supported by DataOutput. jaroslav@601: * jaroslav@601: *
Serialization does not write out the fields of any object that does not jaroslav@601: * implement the java.io.Serializable interface. Subclasses of Objects that jaroslav@601: * are not serializable can be serializable. In this case the non-serializable jaroslav@601: * class must have a no-arg constructor to allow its fields to be initialized. jaroslav@601: * In this case it is the responsibility of the subclass to save and restore jaroslav@601: * the state of the non-serializable class. It is frequently the case that the jaroslav@601: * fields of that class are accessible (public, package, or protected) or that jaroslav@601: * there are get and set methods that can be used to restore the state. jaroslav@601: * jaroslav@601: *
Serialization of an object can be prevented by implementing writeObject jaroslav@601: * and readObject methods that throw the NotSerializableException. The jaroslav@601: * exception will be caught by the ObjectOutputStream and abort the jaroslav@601: * serialization process. jaroslav@601: * jaroslav@601: *
Implementing the Externalizable interface allows the object to assume jaroslav@601: * complete control over the contents and format of the object's serialized jaroslav@601: * form. The methods of the Externalizable interface, writeExternal and jaroslav@601: * readExternal, are called to save and restore the objects state. When jaroslav@601: * implemented by a class they can write and read their own state using all of jaroslav@601: * the methods of ObjectOutput and ObjectInput. It is the responsibility of jaroslav@601: * the objects to handle any versioning that occurs. jaroslav@601: * jaroslav@601: *
Enum constants are serialized differently than ordinary serializable or jaroslav@601: * externalizable objects. The serialized form of an enum constant consists jaroslav@601: * solely of its name; field values of the constant are not transmitted. To jaroslav@601: * serialize an enum constant, ObjectOutputStream writes the string returned by jaroslav@601: * the constant's name method. Like other serializable or externalizable jaroslav@601: * objects, enum constants can function as the targets of back references jaroslav@601: * appearing subsequently in the serialization stream. The process by which jaroslav@601: * enum constants are serialized cannot be customized; any class-specific jaroslav@601: * writeObject and writeReplace methods defined by enum types are ignored jaroslav@601: * during serialization. Similarly, any serialPersistentFields or jaroslav@601: * serialVersionUID field declarations are also ignored--all enum types have a jaroslav@601: * fixed serialVersionUID of 0L. jaroslav@601: * jaroslav@601: *
Primitive data, excluding serializable fields and externalizable data, is
jaroslav@601: * written to the ObjectOutputStream in block-data records. A block data record
jaroslav@601: * is composed of a header and data. The block data header consists of a marker
jaroslav@601: * and the number of bytes to follow the header. Consecutive primitive data
jaroslav@601: * writes are merged into one block-data record. The blocking factor used for
jaroslav@601: * a block-data record will be 1024 bytes. Each block-data record will be
jaroslav@601: * filled up to 1024 bytes, or be written whenever there is a termination of
jaroslav@601: * block-data mode. Calls to the ObjectOutputStream methods writeObject,
jaroslav@601: * defaultWriteObject and writeFields initially terminate any existing
jaroslav@601: * block-data record.
jaroslav@601: *
jaroslav@601: * @author Mike Warres
jaroslav@601: * @author Roger Riggs
jaroslav@601: * @see java.io.DataOutput
jaroslav@601: * @see java.io.ObjectInputStream
jaroslav@601: * @see java.io.Serializable
jaroslav@601: * @see java.io.Externalizable
jaroslav@601: * @see Object Serialization Specification, Section 2, Object Output Classes
jaroslav@601: * @since JDK1.1
jaroslav@601: */
jaroslav@601: public class ObjectOutputStream
jaroslav@601: extends OutputStream implements ObjectOutput, ObjectStreamConstants
jaroslav@601: {
jaroslav@601:
jaroslav@601: private static class Caches {
jaroslav@601: /** cache of subclass security audit results */
jaroslav@601: static final ConcurrentMap If a security manager is installed, this constructor will check for
jaroslav@601: * the "enableSubclassImplementation" SerializablePermission when invoked
jaroslav@601: * directly or indirectly by the constructor of a subclass which overrides
jaroslav@601: * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
jaroslav@601: * methods.
jaroslav@601: *
jaroslav@601: * @param out output stream to write to
jaroslav@601: * @throws IOException if an I/O error occurs while writing stream header
jaroslav@601: * @throws SecurityException if untrusted subclass illegally overrides
jaroslav@601: * security-sensitive methods
jaroslav@601: * @throws NullPointerException if If there is a security manager installed, this method first calls the
jaroslav@601: * security manager's This routine provides a hook to enable the current version of
jaroslav@601: * Serialization to write in a format that is backwards compatible to a
jaroslav@601: * previous version of the stream format.
jaroslav@601: *
jaroslav@601: * Every effort will be made to avoid introducing additional
jaroslav@601: * backwards incompatibilities; however, sometimes there is no
jaroslav@601: * other alternative.
jaroslav@601: *
jaroslav@601: * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
jaroslav@601: * @throws IllegalStateException if called after any objects
jaroslav@601: * have been serialized.
jaroslav@601: * @throws IllegalArgumentException if invalid version is passed in.
jaroslav@601: * @throws IOException if I/O errors occur
jaroslav@601: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
jaroslav@601: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
jaroslav@601: * @since 1.2
jaroslav@601: */
jaroslav@601: public void useProtocolVersion(int version) throws IOException {
jaroslav@601: if (handles.size() != 0) {
jaroslav@601: // REMIND: implement better check for pristine stream?
jaroslav@601: throw new IllegalStateException("stream non-empty");
jaroslav@601: }
jaroslav@601: switch (version) {
jaroslav@601: case PROTOCOL_VERSION_1:
jaroslav@601: case PROTOCOL_VERSION_2:
jaroslav@601: protocol = version;
jaroslav@601: break;
jaroslav@601:
jaroslav@601: default:
jaroslav@601: throw new IllegalArgumentException(
jaroslav@601: "unknown version: " + version);
jaroslav@601: }
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Write the specified object to the ObjectOutputStream. The class of the
jaroslav@601: * object, the signature of the class, and the values of the non-transient
jaroslav@601: * and non-static fields of the class and all of its supertypes are
jaroslav@601: * written. Default serialization for a class can be overridden using the
jaroslav@601: * writeObject and the readObject methods. Objects referenced by this
jaroslav@601: * object are written transitively so that a complete equivalent graph of
jaroslav@601: * objects can be reconstructed by an ObjectInputStream.
jaroslav@601: *
jaroslav@601: * Exceptions are thrown for problems with the OutputStream and for
jaroslav@601: * classes that should not be serialized. All exceptions are fatal to the
jaroslav@601: * OutputStream, which is left in an indeterminate state, and it is up to
jaroslav@601: * the caller to ignore or recover the stream state.
jaroslav@601: *
jaroslav@601: * @throws InvalidClassException Something is wrong with a class used by
jaroslav@601: * serialization.
jaroslav@601: * @throws NotSerializableException Some object to be serialized does not
jaroslav@601: * implement the java.io.Serializable interface.
jaroslav@601: * @throws IOException Any exception thrown by the underlying
jaroslav@601: * OutputStream.
jaroslav@601: */
jaroslav@601: public final void writeObject(Object obj) throws IOException {
jaroslav@601: if (enableOverride) {
jaroslav@601: writeObjectOverride(obj);
jaroslav@601: return;
jaroslav@601: }
jaroslav@601: try {
jaroslav@601: writeObject0(obj, false);
jaroslav@601: } catch (IOException ex) {
jaroslav@601: if (depth == 0) {
jaroslav@601: writeFatalException(ex);
jaroslav@601: }
jaroslav@601: throw ex;
jaroslav@601: }
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Method used by subclasses to override the default writeObject method.
jaroslav@601: * This method is called by trusted subclasses of ObjectInputStream that
jaroslav@601: * constructed ObjectInputStream using the protected no-arg constructor.
jaroslav@601: * The subclass is expected to provide an override method with the modifier
jaroslav@601: * "final".
jaroslav@601: *
jaroslav@601: * @param obj object to be written to the underlying stream
jaroslav@601: * @throws IOException if there are I/O errors while writing to the
jaroslav@601: * underlying stream
jaroslav@601: * @see #ObjectOutputStream()
jaroslav@601: * @see #writeObject(Object)
jaroslav@601: * @since 1.2
jaroslav@601: */
jaroslav@601: protected void writeObjectOverride(Object obj) throws IOException {
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes an "unshared" object to the ObjectOutputStream. This method is
jaroslav@601: * identical to writeObject, except that it always writes the given object
jaroslav@601: * as a new, unique object in the stream (as opposed to a back-reference
jaroslav@601: * pointing to a previously serialized instance). Specifically:
jaroslav@601: * ObjectOutputStream subclasses which override this method can only be
jaroslav@601: * constructed in security contexts possessing the
jaroslav@601: * "enableSubclassImplementation" SerializablePermission; any attempt to
jaroslav@601: * instantiate such a subclass without this permission will cause a
jaroslav@601: * SecurityException to be thrown.
jaroslav@601: *
jaroslav@601: * @param obj object to write to stream
jaroslav@601: * @throws NotSerializableException if an object in the graph to be
jaroslav@601: * serialized does not implement the Serializable interface
jaroslav@601: * @throws InvalidClassException if a problem exists with the class of an
jaroslav@601: * object to be serialized
jaroslav@601: * @throws IOException if an I/O error occurs during serialization
jaroslav@601: * @since 1.4
jaroslav@601: */
jaroslav@601: public void writeUnshared(Object obj) throws IOException {
jaroslav@601: try {
jaroslav@601: writeObject0(obj, true);
jaroslav@601: } catch (IOException ex) {
jaroslav@601: if (depth == 0) {
jaroslav@601: writeFatalException(ex);
jaroslav@601: }
jaroslav@601: throw ex;
jaroslav@601: }
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Write the non-static and non-transient fields of the current class to
jaroslav@601: * this stream. This may only be called from the writeObject method of the
jaroslav@601: * class being serialized. It will throw the NotActiveException if it is
jaroslav@601: * called otherwise.
jaroslav@601: *
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * This method is called exactly once for each unique proxy class
jaroslav@601: * descriptor in the stream. The default implementation of this method in
jaroslav@601: * The corresponding method in The ObjectOutputStream.writeObject method takes a parameter of type
jaroslav@601: * Object (as opposed to type Serializable) to allow for cases where
jaroslav@601: * non-serializable objects are replaced by serializable ones.
jaroslav@601: *
jaroslav@601: * When a subclass is replacing objects it must insure that either a
jaroslav@601: * complementary substitution must be made during deserialization or that
jaroslav@601: * the substituted object is compatible with every field where the
jaroslav@601: * reference will be stored. Objects whose type is not a subclass of the
jaroslav@601: * type of the field or array element abort the serialization by raising an
jaroslav@601: * exception and the object is not be stored.
jaroslav@601: *
jaroslav@601: * This method is called only once when each object is first
jaroslav@601: * encountered. All subsequent references to the object will be redirected
jaroslav@601: * to the new object. This method should return the object to be
jaroslav@601: * substituted or the original object.
jaroslav@601: *
jaroslav@601: * Null can be returned as the object to be substituted, but may cause
jaroslav@601: * NullReferenceException in classes that contain references to the
jaroslav@601: * original object since they may be expecting an object instead of
jaroslav@601: * null.
jaroslav@601: *
jaroslav@601: * @param obj the object to be replaced
jaroslav@601: * @return the alternate object that replaced the specified one
jaroslav@601: * @throws IOException Any exception thrown by the underlying
jaroslav@601: * OutputStream.
jaroslav@601: */
jaroslav@601: protected Object replaceObject(Object obj) throws IOException {
jaroslav@601: return obj;
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Enable the stream to do replacement of objects in the stream. When
jaroslav@601: * enabled, the replaceObject method is called for every object being
jaroslav@601: * serialized.
jaroslav@601: *
jaroslav@601: * If Note that this method will only be called if the ObjectOutputStream
jaroslav@601: * is not using the old serialization stream format (set by calling
jaroslav@601: * ObjectOutputStream's out
is null
jaroslav@601: * @since 1.4
jaroslav@601: * @see ObjectOutputStream#ObjectOutputStream()
jaroslav@601: * @see ObjectOutputStream#putFields()
jaroslav@601: * @see ObjectInputStream#ObjectInputStream(InputStream)
jaroslav@601: */
jaroslav@601: public ObjectOutputStream(OutputStream out) throws IOException {
jaroslav@601: verifySubclass();
jaroslav@601: bout = new BlockDataOutputStream(out);
jaroslav@601: handles = new HandleTable(10, (float) 3.00);
jaroslav@601: subs = new ReplaceTable(10, (float) 3.00);
jaroslav@601: enableOverride = false;
jaroslav@601: writeStreamHeader();
jaroslav@601: bout.setBlockDataMode(true);
jaroslav@601: if (extendedDebugInfo) {
jaroslav@601: debugInfoStack = new DebugTraceInfoStack();
jaroslav@601: } else {
jaroslav@601: debugInfoStack = null;
jaroslav@601: }
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Provide a way for subclasses that are completely reimplementing
jaroslav@601: * ObjectOutputStream to not have to allocate private data just used by
jaroslav@601: * this implementation of ObjectOutputStream.
jaroslav@601: *
jaroslav@601: * checkPermission
method with a
jaroslav@601: * SerializablePermission("enableSubclassImplementation")
jaroslav@601: * permission to ensure it's ok to enable subclassing.
jaroslav@601: *
jaroslav@601: * @throws SecurityException if a security manager exists and its
jaroslav@601: * checkPermission
method denies enabling
jaroslav@601: * subclassing.
jaroslav@601: * @see SecurityManager#checkPermission
jaroslav@601: * @see java.io.SerializablePermission
jaroslav@601: */
jaroslav@601: protected ObjectOutputStream() throws IOException, SecurityException {
jaroslav@601: SecurityManager sm = System.getSecurityManager();
jaroslav@601: if (sm != null) {
jaroslav@601: sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
jaroslav@601: }
jaroslav@601: bout = null;
jaroslav@601: handles = null;
jaroslav@601: subs = null;
jaroslav@601: enableOverride = true;
jaroslav@601: debugInfoStack = null;
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Specify stream protocol version to use when writing the stream.
jaroslav@601: *
jaroslav@601: *
jaroslav@601: *
jaroslav@601: * While writing an object via writeUnshared does not in itself guarantee a
jaroslav@601: * unique reference to the object when it is deserialized, it allows a
jaroslav@601: * single object to be defined multiple times in a stream, so that multiple
jaroslav@601: * calls to readUnshared by the receiver will not conflict. Note that the
jaroslav@601: * rules described above only apply to the base-level object written with
jaroslav@601: * writeUnshared, and not to any transitively referenced sub-objects in the
jaroslav@601: * object graph to be serialized.
jaroslav@601: *
jaroslav@601: * OutputStream
jaroslav@601: */
jaroslav@601: public void defaultWriteObject() throws IOException {
jaroslav@601: if ( curContext == null ) {
jaroslav@601: throw new NotActiveException("not in call to writeObject");
jaroslav@601: }
jaroslav@601: Object curObj = curContext.getObj();
jaroslav@601: ObjectStreamClass curDesc = curContext.getDesc();
jaroslav@601: bout.setBlockDataMode(false);
jaroslav@601: defaultWriteFields(curObj, curDesc);
jaroslav@601: bout.setBlockDataMode(true);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Retrieve the object used to buffer persistent fields to be written to
jaroslav@601: * the stream. The fields will be written to the stream when writeFields
jaroslav@601: * method is called.
jaroslav@601: *
jaroslav@601: * @return an instance of the class Putfield that holds the serializable
jaroslav@601: * fields
jaroslav@601: * @throws IOException if I/O errors occur
jaroslav@601: * @since 1.2
jaroslav@601: */
jaroslav@601: public ObjectOutputStream.PutField putFields() throws IOException {
jaroslav@601: if (curPut == null) {
jaroslav@601: if (curContext == null) {
jaroslav@601: throw new NotActiveException("not in call to writeObject");
jaroslav@601: }
jaroslav@601: Object curObj = curContext.getObj();
jaroslav@601: ObjectStreamClass curDesc = curContext.getDesc();
jaroslav@601: curPut = new PutFieldImpl(curDesc);
jaroslav@601: }
jaroslav@601: return curPut;
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Write the buffered fields to the stream.
jaroslav@601: *
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: * @throws NotActiveException Called when a classes writeObject method was
jaroslav@601: * not called to write the state of the object.
jaroslav@601: * @since 1.2
jaroslav@601: */
jaroslav@601: public void writeFields() throws IOException {
jaroslav@601: if (curPut == null) {
jaroslav@601: throw new NotActiveException("no current PutField object");
jaroslav@601: }
jaroslav@601: bout.setBlockDataMode(false);
jaroslav@601: curPut.writeFields();
jaroslav@601: bout.setBlockDataMode(true);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Reset will disregard the state of any objects already written to the
jaroslav@601: * stream. The state is reset to be the same as a new ObjectOutputStream.
jaroslav@601: * The current point in the stream is marked as reset so the corresponding
jaroslav@601: * ObjectInputStream will be reset at the same point. Objects previously
jaroslav@601: * written to the stream will not be refered to as already being in the
jaroslav@601: * stream. They will be written to the stream again.
jaroslav@601: *
jaroslav@601: * @throws IOException if reset() is invoked while serializing an object.
jaroslav@601: */
jaroslav@601: public void reset() throws IOException {
jaroslav@601: if (depth != 0) {
jaroslav@601: throw new IOException("stream active");
jaroslav@601: }
jaroslav@601: bout.setBlockDataMode(false);
jaroslav@601: bout.writeByte(TC_RESET);
jaroslav@601: clear();
jaroslav@601: bout.setBlockDataMode(true);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Subclasses may implement this method to allow class data to be stored in
jaroslav@601: * the stream. By default this method does nothing. The corresponding
jaroslav@601: * method in ObjectInputStream is resolveClass. This method is called
jaroslav@601: * exactly once for each unique class in the stream. The class name and
jaroslav@601: * signature will have already been written to the stream. This method may
jaroslav@601: * make free use of the ObjectOutputStream to save any representation of
jaroslav@601: * the class it deems suitable (for example, the bytes of the class file).
jaroslav@601: * The resolveClass method in the corresponding subclass of
jaroslav@601: * ObjectInputStream must read and use any data or objects written by
jaroslav@601: * annotateClass.
jaroslav@601: *
jaroslav@601: * @param cl the class to annotate custom data for
jaroslav@601: * @throws IOException Any exception thrown by the underlying
jaroslav@601: * OutputStream.
jaroslav@601: */
jaroslav@601: protected void annotateClass(Class> cl) throws IOException {
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Subclasses may implement this method to store custom data in the stream
jaroslav@601: * along with descriptors for dynamic proxy classes.
jaroslav@601: *
jaroslav@601: * ObjectOutputStream
does nothing.
jaroslav@601: *
jaroslav@601: * ObjectInputStream
is
jaroslav@601: * resolveProxyClass
. For a given subclass of
jaroslav@601: * ObjectOutputStream
that overrides this method, the
jaroslav@601: * resolveProxyClass
method in the corresponding subclass of
jaroslav@601: * ObjectInputStream
must read any data or objects written by
jaroslav@601: * annotateProxyClass
.
jaroslav@601: *
jaroslav@601: * @param cl the proxy class to annotate custom data for
jaroslav@601: * @throws IOException any exception thrown by the underlying
jaroslav@601: * OutputStream
jaroslav@601: * @see ObjectInputStream#resolveProxyClass(String[])
jaroslav@601: * @since 1.3
jaroslav@601: */
jaroslav@601: protected void annotateProxyClass(Class> cl) throws IOException {
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * This method will allow trusted subclasses of ObjectOutputStream to
jaroslav@601: * substitute one object for another during serialization. Replacing
jaroslav@601: * objects is disabled until enableReplaceObject is called. The
jaroslav@601: * enableReplaceObject method checks that the stream requesting to do
jaroslav@601: * replacement can be trusted. The first occurrence of each object written
jaroslav@601: * into the serialization stream is passed to replaceObject. Subsequent
jaroslav@601: * references to the object are replaced by the object returned by the
jaroslav@601: * original call to replaceObject. To ensure that the private state of
jaroslav@601: * objects is not unintentionally exposed, only trusted streams may use
jaroslav@601: * replaceObject.
jaroslav@601: *
jaroslav@601: * enable
is true, and there is a security manager
jaroslav@601: * installed, this method first calls the security manager's
jaroslav@601: * checkPermission
method with a
jaroslav@601: * SerializablePermission("enableSubstitution")
permission to
jaroslav@601: * ensure it's ok to enable the stream to do replacement of objects in the
jaroslav@601: * stream.
jaroslav@601: *
jaroslav@601: * @param enable boolean parameter to enable replacement of objects
jaroslav@601: * @return the previous setting before this method was invoked
jaroslav@601: * @throws SecurityException if a security manager exists and its
jaroslav@601: * checkPermission
method denies enabling the stream
jaroslav@601: * to do replacement of objects in the stream.
jaroslav@601: * @see SecurityManager#checkPermission
jaroslav@601: * @see java.io.SerializablePermission
jaroslav@601: */
jaroslav@601: protected boolean enableReplaceObject(boolean enable)
jaroslav@601: throws SecurityException
jaroslav@601: {
jaroslav@601: if (enable == enableReplace) {
jaroslav@601: return enable;
jaroslav@601: }
jaroslav@601: if (enable) {
jaroslav@601: SecurityManager sm = System.getSecurityManager();
jaroslav@601: if (sm != null) {
jaroslav@601: sm.checkPermission(SUBSTITUTION_PERMISSION);
jaroslav@601: }
jaroslav@601: }
jaroslav@601: enableReplace = enable;
jaroslav@601: return !enableReplace;
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * The writeStreamHeader method is provided so subclasses can append or
jaroslav@601: * prepend their own header to the stream. It writes the magic number and
jaroslav@601: * version to the stream.
jaroslav@601: *
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: protected void writeStreamHeader() throws IOException {
jaroslav@601: bout.writeShort(STREAM_MAGIC);
jaroslav@601: bout.writeShort(STREAM_VERSION);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Write the specified class descriptor to the ObjectOutputStream. Class
jaroslav@601: * descriptors are used to identify the classes of objects written to the
jaroslav@601: * stream. Subclasses of ObjectOutputStream may override this method to
jaroslav@601: * customize the way in which class descriptors are written to the
jaroslav@601: * serialization stream. The corresponding method in ObjectInputStream,
jaroslav@601: * readClassDescriptor
, should then be overridden to
jaroslav@601: * reconstitute the class descriptor from its custom stream representation.
jaroslav@601: * By default, this method writes class descriptors according to the format
jaroslav@601: * defined in the Object Serialization specification.
jaroslav@601: *
jaroslav@601: * useProtocolVersion
method). If this
jaroslav@601: * serialization stream is using the old format
jaroslav@601: * (PROTOCOL_VERSION_1
), the class descriptor will be written
jaroslav@601: * internally in a manner that cannot be overridden or customized.
jaroslav@601: *
jaroslav@601: * @param desc class descriptor to write to the stream
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: * @see java.io.ObjectInputStream#readClassDescriptor()
jaroslav@601: * @see #useProtocolVersion(int)
jaroslav@601: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
jaroslav@601: * @since 1.3
jaroslav@601: */
jaroslav@601: protected void writeClassDescriptor(ObjectStreamClass desc)
jaroslav@601: throws IOException
jaroslav@601: {
jaroslav@601: desc.writeNonProxy(this);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a byte. This method will block until the byte is actually
jaroslav@601: * written.
jaroslav@601: *
jaroslav@601: * @param val the byte to be written to the stream
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: */
jaroslav@601: public void write(int val) throws IOException {
jaroslav@601: bout.write(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes an array of bytes. This method will block until the bytes are
jaroslav@601: * actually written.
jaroslav@601: *
jaroslav@601: * @param buf the data to be written
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: */
jaroslav@601: public void write(byte[] buf) throws IOException {
jaroslav@601: bout.write(buf, 0, buf.length, false);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a sub array of bytes.
jaroslav@601: *
jaroslav@601: * @param buf the data to be written
jaroslav@601: * @param off the start offset in the data
jaroslav@601: * @param len the number of bytes that are written
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: */
jaroslav@601: public void write(byte[] buf, int off, int len) throws IOException {
jaroslav@601: if (buf == null) {
jaroslav@601: throw new NullPointerException();
jaroslav@601: }
jaroslav@601: int endoff = off + len;
jaroslav@601: if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
jaroslav@601: throw new IndexOutOfBoundsException();
jaroslav@601: }
jaroslav@601: bout.write(buf, off, len, false);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Flushes the stream. This will write any buffered output bytes and flush
jaroslav@601: * through to the underlying stream.
jaroslav@601: *
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: */
jaroslav@601: public void flush() throws IOException {
jaroslav@601: bout.flush();
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Drain any buffered data in ObjectOutputStream. Similar to flush but
jaroslav@601: * does not propagate the flush to the underlying stream.
jaroslav@601: *
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: protected void drain() throws IOException {
jaroslav@601: bout.drain();
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Closes the stream. This method must be called to release any resources
jaroslav@601: * associated with the stream.
jaroslav@601: *
jaroslav@601: * @throws IOException If an I/O error has occurred.
jaroslav@601: */
jaroslav@601: public void close() throws IOException {
jaroslav@601: flush();
jaroslav@601: clear();
jaroslav@601: bout.close();
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a boolean.
jaroslav@601: *
jaroslav@601: * @param val the boolean to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeBoolean(boolean val) throws IOException {
jaroslav@601: bout.writeBoolean(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes an 8 bit byte.
jaroslav@601: *
jaroslav@601: * @param val the byte value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeByte(int val) throws IOException {
jaroslav@601: bout.writeByte(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 16 bit short.
jaroslav@601: *
jaroslav@601: * @param val the short value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeShort(int val) throws IOException {
jaroslav@601: bout.writeShort(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 16 bit char.
jaroslav@601: *
jaroslav@601: * @param val the char value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeChar(int val) throws IOException {
jaroslav@601: bout.writeChar(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 32 bit int.
jaroslav@601: *
jaroslav@601: * @param val the integer value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeInt(int val) throws IOException {
jaroslav@601: bout.writeInt(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 64 bit long.
jaroslav@601: *
jaroslav@601: * @param val the long value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeLong(long val) throws IOException {
jaroslav@601: bout.writeLong(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 32 bit float.
jaroslav@601: *
jaroslav@601: * @param val the float value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeFloat(float val) throws IOException {
jaroslav@601: bout.writeFloat(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a 64 bit double.
jaroslav@601: *
jaroslav@601: * @param val the double value to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeDouble(double val) throws IOException {
jaroslav@601: bout.writeDouble(val);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a String as a sequence of bytes.
jaroslav@601: *
jaroslav@601: * @param str the String of bytes to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeBytes(String str) throws IOException {
jaroslav@601: bout.writeBytes(str);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes a String as a sequence of chars.
jaroslav@601: *
jaroslav@601: * @param str the String of chars to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeChars(String str) throws IOException {
jaroslav@601: bout.writeChars(str);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Primitive data write of this String in
jaroslav@601: * modified UTF-8
jaroslav@601: * format. Note that there is a
jaroslav@601: * significant difference between writing a String into the stream as
jaroslav@601: * primitive data or as an Object. A String instance written by writeObject
jaroslav@601: * is written into the stream as a String initially. Future writeObject()
jaroslav@601: * calls write references to the string into the stream.
jaroslav@601: *
jaroslav@601: * @param str the String to be written
jaroslav@601: * @throws IOException if I/O errors occur while writing to the underlying
jaroslav@601: * stream
jaroslav@601: */
jaroslav@601: public void writeUTF(String str) throws IOException {
jaroslav@601: bout.writeUTF(str);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Provide programmatic access to the persistent fields to be written
jaroslav@601: * to ObjectOutput.
jaroslav@601: *
jaroslav@601: * @since 1.2
jaroslav@601: */
jaroslav@601: public static abstract class PutField {
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named boolean field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * boolean
jaroslav@601: */
jaroslav@601: public abstract void put(String name, boolean val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named byte field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * byte
jaroslav@601: */
jaroslav@601: public abstract void put(String name, byte val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named char field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * char
jaroslav@601: */
jaroslav@601: public abstract void put(String name, char val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named short field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * short
jaroslav@601: */
jaroslav@601: public abstract void put(String name, short val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named int field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * int
jaroslav@601: */
jaroslav@601: public abstract void put(String name, int val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named long field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * long
jaroslav@601: */
jaroslav@601: public abstract void put(String name, long val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named float field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * float
jaroslav@601: */
jaroslav@601: public abstract void put(String name, float val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named double field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not
jaroslav@601: * double
jaroslav@601: */
jaroslav@601: public abstract void put(String name, double val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Put the value of the named Object field into the persistent field.
jaroslav@601: *
jaroslav@601: * @param name the name of the serializable field
jaroslav@601: * @param val the value to assign to the field
jaroslav@601: * (which may be null
)
jaroslav@601: * @throws IllegalArgumentException if name
does not
jaroslav@601: * match the name of a serializable field for the class whose fields
jaroslav@601: * are being written, or if the type of the named field is not a
jaroslav@601: * reference type
jaroslav@601: */
jaroslav@601: public abstract void put(String name, Object val);
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Write the data and fields to the specified ObjectOutput stream,
jaroslav@601: * which must be the same stream that produced this
jaroslav@601: * PutField
object.
jaroslav@601: *
jaroslav@601: * @param out the stream to write the data and fields to
jaroslav@601: * @throws IOException if I/O errors occur while writing to the
jaroslav@601: * underlying stream
jaroslav@601: * @throws IllegalArgumentException if the specified stream is not
jaroslav@601: * the same stream that produced this PutField
jaroslav@601: * object
jaroslav@601: * @deprecated This method does not write the values contained by this
jaroslav@601: * PutField
object in a proper format, and may
jaroslav@601: * result in corruption of the serialization stream. The
jaroslav@601: * correct way to write PutField
data is by
jaroslav@601: * calling the {@link java.io.ObjectOutputStream#writeFields()}
jaroslav@601: * method.
jaroslav@601: */
jaroslav@601: @Deprecated
jaroslav@601: public abstract void write(ObjectOutput out) throws IOException;
jaroslav@601: }
jaroslav@601:
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Returns protocol version in use.
jaroslav@601: */
jaroslav@601: int getProtocolVersion() {
jaroslav@601: return protocol;
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Writes string without allowing it to be replaced in stream. Used by
jaroslav@601: * ObjectStreamClass to write class descriptor type strings.
jaroslav@601: */
jaroslav@601: void writeTypeString(String str) throws IOException {
jaroslav@601: int handle;
jaroslav@601: if (str == null) {
jaroslav@601: writeNull();
jaroslav@601: } else if ((handle = handles.lookup(str)) != -1) {
jaroslav@601: writeHandle(handle);
jaroslav@601: } else {
jaroslav@601: writeString(str, false);
jaroslav@601: }
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Verifies that this (possibly subclass) instance can be constructed
jaroslav@601: * without violating security constraints: the subclass must not override
jaroslav@601: * security-sensitive non-final methods, or else the
jaroslav@601: * "enableSubclassImplementation" SerializablePermission is checked.
jaroslav@601: */
jaroslav@601: private void verifySubclass() {
jaroslav@601: Class cl = getClass();
jaroslav@601: if (cl == ObjectOutputStream.class) {
jaroslav@601: return;
jaroslav@601: }
jaroslav@601: SecurityManager sm = System.getSecurityManager();
jaroslav@601: if (sm == null) {
jaroslav@601: return;
jaroslav@601: }
jaroslav@601: processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
jaroslav@601: WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
jaroslav@601: Boolean result = Caches.subclassAudits.get(key);
jaroslav@601: if (result == null) {
jaroslav@601: result = Boolean.valueOf(auditSubclass(cl));
jaroslav@601: Caches.subclassAudits.putIfAbsent(key, result);
jaroslav@601: }
jaroslav@601: if (result.booleanValue()) {
jaroslav@601: return;
jaroslav@601: }
jaroslav@601: sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
jaroslav@601: }
jaroslav@601:
jaroslav@601: /**
jaroslav@601: * Performs reflective checks on given subclass to verify that it doesn't
jaroslav@601: * override security-sensitive non-final methods. Returns true if subclass
jaroslav@601: * is "safe", false otherwise.
jaroslav@601: */
jaroslav@601: private static boolean auditSubclass(final Class subcl) {
jaroslav@601: Boolean result = AccessController.doPrivileged(
jaroslav@601: new PrivilegedAction