0001: /*
0002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package java.io;
0027:
0028: import java.io.ObjectStreamClass.WeakClassKey;
0029: import java.lang.ref.ReferenceQueue;
0030: import java.security.AccessController;
0031: import java.security.PrivilegedAction;
0032: import java.util.ArrayList;
0033: import java.util.Arrays;
0034: import java.util.List;
0035: import java.util.concurrent.ConcurrentHashMap;
0036: import java.util.concurrent.ConcurrentMap;
0037: import static java.io.ObjectStreamClass.processQueue;
0038:
0039: /**
0040: * An ObjectOutputStream writes primitive data types and graphs of Java objects
0041: * to an OutputStream. The objects can be read (reconstituted) using an
0042: * ObjectInputStream. Persistent storage of objects can be accomplished by
0043: * using a file for the stream. If the stream is a network socket stream, the
0044: * objects can be reconstituted on another host or in another process.
0045: *
0046: * <p>Only objects that support the java.io.Serializable interface can be
0047: * written to streams. The class of each serializable object is encoded
0048: * including the class name and signature of the class, the values of the
0049: * object's fields and arrays, and the closure of any other objects referenced
0050: * from the initial objects.
0051: *
0052: * <p>The method writeObject is used to write an object to the stream. Any
0053: * object, including Strings and arrays, is written with writeObject. Multiple
0054: * objects or primitives can be written to the stream. The objects must be
0055: * read back from the corresponding ObjectInputstream with the same types and
0056: * in the same order as they were written.
0057: *
0058: * <p>Primitive data types can also be written to the stream using the
0059: * appropriate methods from DataOutput. Strings can also be written using the
0060: * writeUTF method.
0061: *
0062: * <p>The default serialization mechanism for an object writes the class of the
0063: * object, the class signature, and the values of all non-transient and
0064: * non-static fields. References to other objects (except in transient or
0065: * static fields) cause those objects to be written also. Multiple references
0066: * to a single object are encoded using a reference sharing mechanism so that
0067: * graphs of objects can be restored to the same shape as when the original was
0068: * written.
0069: *
0070: * <p>For example to write an object that can be read by the example in
0071: * ObjectInputStream:
0072: * <br>
0073: * <pre>
0074: * FileOutputStream fos = new FileOutputStream("t.tmp");
0075: * ObjectOutputStream oos = new ObjectOutputStream(fos);
0076: *
0077: * oos.writeInt(12345);
0078: * oos.writeObject("Today");
0079: * oos.writeObject(new Date());
0080: *
0081: * oos.close();
0082: * </pre>
0083: *
0084: * <p>Classes that require special handling during the serialization and
0085: * deserialization process must implement special methods with these exact
0086: * signatures:
0087: * <br>
0088: * <pre>
0089: * private void readObject(java.io.ObjectInputStream stream)
0090: * throws IOException, ClassNotFoundException;
0091: * private void writeObject(java.io.ObjectOutputStream stream)
0092: * throws IOException
0093: * private void readObjectNoData()
0094: * throws ObjectStreamException;
0095: * </pre>
0096: *
0097: * <p>The writeObject method is responsible for writing the state of the object
0098: * for its particular class so that the corresponding readObject method can
0099: * restore it. The method does not need to concern itself with the state
0100: * belonging to the object's superclasses or subclasses. State is saved by
0101: * writing the individual fields to the ObjectOutputStream using the
0102: * writeObject method or by using the methods for primitive data types
0103: * supported by DataOutput.
0104: *
0105: * <p>Serialization does not write out the fields of any object that does not
0106: * implement the java.io.Serializable interface. Subclasses of Objects that
0107: * are not serializable can be serializable. In this case the non-serializable
0108: * class must have a no-arg constructor to allow its fields to be initialized.
0109: * In this case it is the responsibility of the subclass to save and restore
0110: * the state of the non-serializable class. It is frequently the case that the
0111: * fields of that class are accessible (public, package, or protected) or that
0112: * there are get and set methods that can be used to restore the state.
0113: *
0114: * <p>Serialization of an object can be prevented by implementing writeObject
0115: * and readObject methods that throw the NotSerializableException. The
0116: * exception will be caught by the ObjectOutputStream and abort the
0117: * serialization process.
0118: *
0119: * <p>Implementing the Externalizable interface allows the object to assume
0120: * complete control over the contents and format of the object's serialized
0121: * form. The methods of the Externalizable interface, writeExternal and
0122: * readExternal, are called to save and restore the objects state. When
0123: * implemented by a class they can write and read their own state using all of
0124: * the methods of ObjectOutput and ObjectInput. It is the responsibility of
0125: * the objects to handle any versioning that occurs.
0126: *
0127: * <p>Enum constants are serialized differently than ordinary serializable or
0128: * externalizable objects. The serialized form of an enum constant consists
0129: * solely of its name; field values of the constant are not transmitted. To
0130: * serialize an enum constant, ObjectOutputStream writes the string returned by
0131: * the constant's name method. Like other serializable or externalizable
0132: * objects, enum constants can function as the targets of back references
0133: * appearing subsequently in the serialization stream. The process by which
0134: * enum constants are serialized cannot be customized; any class-specific
0135: * writeObject and writeReplace methods defined by enum types are ignored
0136: * during serialization. Similarly, any serialPersistentFields or
0137: * serialVersionUID field declarations are also ignored--all enum types have a
0138: * fixed serialVersionUID of 0L.
0139: *
0140: * <p>Primitive data, excluding serializable fields and externalizable data, is
0141: * written to the ObjectOutputStream in block-data records. A block data record
0142: * is composed of a header and data. The block data header consists of a marker
0143: * and the number of bytes to follow the header. Consecutive primitive data
0144: * writes are merged into one block-data record. The blocking factor used for
0145: * a block-data record will be 1024 bytes. Each block-data record will be
0146: * filled up to 1024 bytes, or be written whenever there is a termination of
0147: * block-data mode. Calls to the ObjectOutputStream methods writeObject,
0148: * defaultWriteObject and writeFields initially terminate any existing
0149: * block-data record.
0150: *
0151: * @author Mike Warres
0152: * @author Roger Riggs
0153: * @version 1.165, 07/05/05
0154: * @see java.io.DataOutput
0155: * @see java.io.ObjectInputStream
0156: * @see java.io.Serializable
0157: * @see java.io.Externalizable
0158: * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
0159: * @since JDK1.1
0160: */
0161: public class ObjectOutputStream extends OutputStream implements
0162: ObjectOutput, ObjectStreamConstants {
0163:
0164: private static class Caches {
0165: /** cache of subclass security audit results */
0166: static final ConcurrentMap<WeakClassKey, Boolean> subclassAudits = new ConcurrentHashMap<WeakClassKey, Boolean>();
0167:
0168: /** queue for WeakReferences to audited subclasses */
0169: static final ReferenceQueue<Class<?>> subclassAuditsQueue = new ReferenceQueue<Class<?>>();
0170: }
0171:
0172: /** filter stream for handling block data conversion */
0173: private final BlockDataOutputStream bout;
0174: /** obj -> wire handle map */
0175: private final HandleTable handles;
0176: /** obj -> replacement obj map */
0177: private final ReplaceTable subs;
0178: /** stream protocol version */
0179: private int protocol = PROTOCOL_VERSION_2;
0180: /** recursion depth */
0181: private int depth;
0182:
0183: /** buffer for writing primitive field values */
0184: private byte[] primVals;
0185:
0186: /** if true, invoke writeObjectOverride() instead of writeObject() */
0187: private final boolean enableOverride;
0188: /** if true, invoke replaceObject() */
0189: private boolean enableReplace;
0190:
0191: // values below valid only during upcalls to writeObject()/writeExternal()
0192: /** object currently being serialized */
0193: private Object curObj;
0194: /** descriptor for current class (null if in writeExternal()) */
0195: private ObjectStreamClass curDesc;
0196: /** current PutField object */
0197: private PutFieldImpl curPut;
0198:
0199: /** custom storage for debug trace info */
0200: private final DebugTraceInfoStack debugInfoStack;
0201:
0202: /**
0203: * value of "sun.io.serialization.extendedDebugInfo" property,
0204: * as true or false for extended information about exception's place
0205: */
0206: private static final boolean extendedDebugInfo = ((Boolean) java.security.AccessController
0207: .doPrivileged(new sun.security.action.GetBooleanAction(
0208: "sun.io.serialization.extendedDebugInfo")))
0209: .booleanValue();
0210:
0211: /**
0212: * Creates an ObjectOutputStream that writes to the specified OutputStream.
0213: * This constructor writes the serialization stream header to the
0214: * underlying stream; callers may wish to flush the stream immediately to
0215: * ensure that constructors for receiving ObjectInputStreams will not block
0216: * when reading the header.
0217: *
0218: * <p>If a security manager is installed, this constructor will check for
0219: * the "enableSubclassImplementation" SerializablePermission when invoked
0220: * directly or indirectly by the constructor of a subclass which overrides
0221: * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
0222: * methods.
0223: *
0224: * @param out output stream to write to
0225: * @throws IOException if an I/O error occurs while writing stream header
0226: * @throws SecurityException if untrusted subclass illegally overrides
0227: * security-sensitive methods
0228: * @throws NullPointerException if <code>out</code> is <code>null</code>
0229: * @since 1.4
0230: * @see ObjectOutputStream#ObjectOutputStream()
0231: * @see ObjectOutputStream#putFields()
0232: * @see ObjectInputStream#ObjectInputStream(InputStream)
0233: */
0234: public ObjectOutputStream(OutputStream out) throws IOException {
0235: verifySubclass();
0236: bout = new BlockDataOutputStream(out);
0237: handles = new HandleTable(10, (float) 3.00);
0238: subs = new ReplaceTable(10, (float) 3.00);
0239: enableOverride = false;
0240: writeStreamHeader();
0241: bout.setBlockDataMode(true);
0242: if (extendedDebugInfo) {
0243: debugInfoStack = new DebugTraceInfoStack();
0244: } else {
0245: debugInfoStack = null;
0246: }
0247: }
0248:
0249: /**
0250: * Provide a way for subclasses that are completely reimplementing
0251: * ObjectOutputStream to not have to allocate private data just used by
0252: * this implementation of ObjectOutputStream.
0253: *
0254: * <p>If there is a security manager installed, this method first calls the
0255: * security manager's <code>checkPermission</code> method with a
0256: * <code>SerializablePermission("enableSubclassImplementation")</code>
0257: * permission to ensure it's ok to enable subclassing.
0258: *
0259: * @throws SecurityException if a security manager exists and its
0260: * <code>checkPermission</code> method denies enabling
0261: * subclassing.
0262: * @see SecurityManager#checkPermission
0263: * @see java.io.SerializablePermission
0264: */
0265: protected ObjectOutputStream() throws IOException,
0266: SecurityException {
0267: SecurityManager sm = System.getSecurityManager();
0268: if (sm != null) {
0269: sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
0270: }
0271: bout = null;
0272: handles = null;
0273: subs = null;
0274: enableOverride = true;
0275: debugInfoStack = null;
0276: }
0277:
0278: /**
0279: * Specify stream protocol version to use when writing the stream.
0280: *
0281: * <p>This routine provides a hook to enable the current version of
0282: * Serialization to write in a format that is backwards compatible to a
0283: * previous version of the stream format.
0284: *
0285: * <p>Every effort will be made to avoid introducing additional
0286: * backwards incompatibilities; however, sometimes there is no
0287: * other alternative.
0288: *
0289: * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
0290: * @throws IllegalStateException if called after any objects
0291: * have been serialized.
0292: * @throws IllegalArgumentException if invalid version is passed in.
0293: * @throws IOException if I/O errors occur
0294: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0295: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
0296: * @since 1.2
0297: */
0298: public void useProtocolVersion(int version) throws IOException {
0299: if (handles.size() != 0) {
0300: // REMIND: implement better check for pristine stream?
0301: throw new IllegalStateException("stream non-empty");
0302: }
0303: switch (version) {
0304: case PROTOCOL_VERSION_1:
0305: case PROTOCOL_VERSION_2:
0306: protocol = version;
0307: break;
0308:
0309: default:
0310: throw new IllegalArgumentException("unknown version: "
0311: + version);
0312: }
0313: }
0314:
0315: /**
0316: * Write the specified object to the ObjectOutputStream. The class of the
0317: * object, the signature of the class, and the values of the non-transient
0318: * and non-static fields of the class and all of its supertypes are
0319: * written. Default serialization for a class can be overridden using the
0320: * writeObject and the readObject methods. Objects referenced by this
0321: * object are written transitively so that a complete equivalent graph of
0322: * objects can be reconstructed by an ObjectInputStream.
0323: *
0324: * <p>Exceptions are thrown for problems with the OutputStream and for
0325: * classes that should not be serialized. All exceptions are fatal to the
0326: * OutputStream, which is left in an indeterminate state, and it is up to
0327: * the caller to ignore or recover the stream state.
0328: *
0329: * @throws InvalidClassException Something is wrong with a class used by
0330: * serialization.
0331: * @throws NotSerializableException Some object to be serialized does not
0332: * implement the java.io.Serializable interface.
0333: * @throws IOException Any exception thrown by the underlying
0334: * OutputStream.
0335: */
0336: public final void writeObject(Object obj) throws IOException {
0337: if (enableOverride) {
0338: writeObjectOverride(obj);
0339: return;
0340: }
0341: try {
0342: writeObject0(obj, false);
0343: } catch (IOException ex) {
0344: if (depth == 0) {
0345: writeFatalException(ex);
0346: }
0347: throw ex;
0348: }
0349: }
0350:
0351: /**
0352: * Method used by subclasses to override the default writeObject method.
0353: * This method is called by trusted subclasses of ObjectInputStream that
0354: * constructed ObjectInputStream using the protected no-arg constructor.
0355: * The subclass is expected to provide an override method with the modifier
0356: * "final".
0357: *
0358: * @param obj object to be written to the underlying stream
0359: * @throws IOException if there are I/O errors while writing to the
0360: * underlying stream
0361: * @see #ObjectOutputStream()
0362: * @see #writeObject(Object)
0363: * @since 1.2
0364: */
0365: protected void writeObjectOverride(Object obj) throws IOException {
0366: }
0367:
0368: /**
0369: * Writes an "unshared" object to the ObjectOutputStream. This method is
0370: * identical to writeObject, except that it always writes the given object
0371: * as a new, unique object in the stream (as opposed to a back-reference
0372: * pointing to a previously serialized instance). Specifically:
0373: * <ul>
0374: * <li>An object written via writeUnshared is always serialized in the
0375: * same manner as a newly appearing object (an object that has not
0376: * been written to the stream yet), regardless of whether or not the
0377: * object has been written previously.
0378: *
0379: * <li>If writeObject is used to write an object that has been previously
0380: * written with writeUnshared, the previous writeUnshared operation
0381: * is treated as if it were a write of a separate object. In other
0382: * words, ObjectOutputStream will never generate back-references to
0383: * object data written by calls to writeUnshared.
0384: * </ul>
0385: * While writing an object via writeUnshared does not in itself guarantee a
0386: * unique reference to the object when it is deserialized, it allows a
0387: * single object to be defined multiple times in a stream, so that multiple
0388: * calls to readUnshared by the receiver will not conflict. Note that the
0389: * rules described above only apply to the base-level object written with
0390: * writeUnshared, and not to any transitively referenced sub-objects in the
0391: * object graph to be serialized.
0392: *
0393: * <p>ObjectOutputStream subclasses which override this method can only be
0394: * constructed in security contexts possessing the
0395: * "enableSubclassImplementation" SerializablePermission; any attempt to
0396: * instantiate such a subclass without this permission will cause a
0397: * SecurityException to be thrown.
0398: *
0399: * @param obj object to write to stream
0400: * @throws NotSerializableException if an object in the graph to be
0401: * serialized does not implement the Serializable interface
0402: * @throws InvalidClassException if a problem exists with the class of an
0403: * object to be serialized
0404: * @throws IOException if an I/O error occurs during serialization
0405: * @since 1.4
0406: */
0407: public void writeUnshared(Object obj) throws IOException {
0408: try {
0409: writeObject0(obj, true);
0410: } catch (IOException ex) {
0411: if (depth == 0) {
0412: writeFatalException(ex);
0413: }
0414: throw ex;
0415: }
0416: }
0417:
0418: /**
0419: * Write the non-static and non-transient fields of the current class to
0420: * this stream. This may only be called from the writeObject method of the
0421: * class being serialized. It will throw the NotActiveException if it is
0422: * called otherwise.
0423: *
0424: * @throws IOException if I/O errors occur while writing to the underlying
0425: * <code>OutputStream</code>
0426: */
0427: public void defaultWriteObject() throws IOException {
0428: if (curObj == null || curDesc == null) {
0429: throw new NotActiveException("not in call to writeObject");
0430: }
0431: bout.setBlockDataMode(false);
0432: defaultWriteFields(curObj, curDesc);
0433: bout.setBlockDataMode(true);
0434: }
0435:
0436: /**
0437: * Retrieve the object used to buffer persistent fields to be written to
0438: * the stream. The fields will be written to the stream when writeFields
0439: * method is called.
0440: *
0441: * @return an instance of the class Putfield that holds the serializable
0442: * fields
0443: * @throws IOException if I/O errors occur
0444: * @since 1.2
0445: */
0446: public ObjectOutputStream.PutField putFields() throws IOException {
0447: if (curPut == null) {
0448: if (curObj == null || curDesc == null) {
0449: throw new NotActiveException(
0450: "not in call to writeObject");
0451: }
0452: curPut = new PutFieldImpl(curDesc);
0453: }
0454: return curPut;
0455: }
0456:
0457: /**
0458: * Write the buffered fields to the stream.
0459: *
0460: * @throws IOException if I/O errors occur while writing to the underlying
0461: * stream
0462: * @throws NotActiveException Called when a classes writeObject method was
0463: * not called to write the state of the object.
0464: * @since 1.2
0465: */
0466: public void writeFields() throws IOException {
0467: if (curPut == null) {
0468: throw new NotActiveException("no current PutField object");
0469: }
0470: bout.setBlockDataMode(false);
0471: curPut.writeFields();
0472: bout.setBlockDataMode(true);
0473: }
0474:
0475: /**
0476: * Reset will disregard the state of any objects already written to the
0477: * stream. The state is reset to be the same as a new ObjectOutputStream.
0478: * The current point in the stream is marked as reset so the corresponding
0479: * ObjectInputStream will be reset at the same point. Objects previously
0480: * written to the stream will not be refered to as already being in the
0481: * stream. They will be written to the stream again.
0482: *
0483: * @throws IOException if reset() is invoked while serializing an object.
0484: */
0485: public void reset() throws IOException {
0486: if (depth != 0) {
0487: throw new IOException("stream active");
0488: }
0489: bout.setBlockDataMode(false);
0490: bout.writeByte(TC_RESET);
0491: clear();
0492: bout.setBlockDataMode(true);
0493: }
0494:
0495: /**
0496: * Subclasses may implement this method to allow class data to be stored in
0497: * the stream. By default this method does nothing. The corresponding
0498: * method in ObjectInputStream is resolveClass. This method is called
0499: * exactly once for each unique class in the stream. The class name and
0500: * signature will have already been written to the stream. This method may
0501: * make free use of the ObjectOutputStream to save any representation of
0502: * the class it deems suitable (for example, the bytes of the class file).
0503: * The resolveClass method in the corresponding subclass of
0504: * ObjectInputStream must read and use any data or objects written by
0505: * annotateClass.
0506: *
0507: * @param cl the class to annotate custom data for
0508: * @throws IOException Any exception thrown by the underlying
0509: * OutputStream.
0510: */
0511: protected void annotateClass(Class<?> cl) throws IOException {
0512: }
0513:
0514: /**
0515: * Subclasses may implement this method to store custom data in the stream
0516: * along with descriptors for dynamic proxy classes.
0517: *
0518: * <p>This method is called exactly once for each unique proxy class
0519: * descriptor in the stream. The default implementation of this method in
0520: * <code>ObjectOutputStream</code> does nothing.
0521: *
0522: * <p>The corresponding method in <code>ObjectInputStream</code> is
0523: * <code>resolveProxyClass</code>. For a given subclass of
0524: * <code>ObjectOutputStream</code> that overrides this method, the
0525: * <code>resolveProxyClass</code> method in the corresponding subclass of
0526: * <code>ObjectInputStream</code> must read any data or objects written by
0527: * <code>annotateProxyClass</code>.
0528: *
0529: * @param cl the proxy class to annotate custom data for
0530: * @throws IOException any exception thrown by the underlying
0531: * <code>OutputStream</code>
0532: * @see ObjectInputStream#resolveProxyClass(String[])
0533: * @since 1.3
0534: */
0535: protected void annotateProxyClass(Class<?> cl) throws IOException {
0536: }
0537:
0538: /**
0539: * This method will allow trusted subclasses of ObjectOutputStream to
0540: * substitute one object for another during serialization. Replacing
0541: * objects is disabled until enableReplaceObject is called. The
0542: * enableReplaceObject method checks that the stream requesting to do
0543: * replacement can be trusted. The first occurrence of each object written
0544: * into the serialization stream is passed to replaceObject. Subsequent
0545: * references to the object are replaced by the object returned by the
0546: * original call to replaceObject. To ensure that the private state of
0547: * objects is not unintentionally exposed, only trusted streams may use
0548: * replaceObject.
0549: *
0550: * <p>The ObjectOutputStream.writeObject method takes a parameter of type
0551: * Object (as opposed to type Serializable) to allow for cases where
0552: * non-serializable objects are replaced by serializable ones.
0553: *
0554: * <p>When a subclass is replacing objects it must insure that either a
0555: * complementary substitution must be made during deserialization or that
0556: * the substituted object is compatible with every field where the
0557: * reference will be stored. Objects whose type is not a subclass of the
0558: * type of the field or array element abort the serialization by raising an
0559: * exception and the object is not be stored.
0560: *
0561: * <p>This method is called only once when each object is first
0562: * encountered. All subsequent references to the object will be redirected
0563: * to the new object. This method should return the object to be
0564: * substituted or the original object.
0565: *
0566: * <p>Null can be returned as the object to be substituted, but may cause
0567: * NullReferenceException in classes that contain references to the
0568: * original object since they may be expecting an object instead of
0569: * null.
0570: *
0571: * @param obj the object to be replaced
0572: * @return the alternate object that replaced the specified one
0573: * @throws IOException Any exception thrown by the underlying
0574: * OutputStream.
0575: */
0576: protected Object replaceObject(Object obj) throws IOException {
0577: return obj;
0578: }
0579:
0580: /**
0581: * Enable the stream to do replacement of objects in the stream. When
0582: * enabled, the replaceObject method is called for every object being
0583: * serialized.
0584: *
0585: * <p>If <code>enable</code> is true, and there is a security manager
0586: * installed, this method first calls the security manager's
0587: * <code>checkPermission</code> method with a
0588: * <code>SerializablePermission("enableSubstitution")</code> permission to
0589: * ensure it's ok to enable the stream to do replacement of objects in the
0590: * stream.
0591: *
0592: * @param enable boolean parameter to enable replacement of objects
0593: * @return the previous setting before this method was invoked
0594: * @throws SecurityException if a security manager exists and its
0595: * <code>checkPermission</code> method denies enabling the stream
0596: * to do replacement of objects in the stream.
0597: * @see SecurityManager#checkPermission
0598: * @see java.io.SerializablePermission
0599: */
0600: protected boolean enableReplaceObject(boolean enable)
0601: throws SecurityException {
0602: if (enable == enableReplace) {
0603: return enable;
0604: }
0605: if (enable) {
0606: SecurityManager sm = System.getSecurityManager();
0607: if (sm != null) {
0608: sm.checkPermission(SUBSTITUTION_PERMISSION);
0609: }
0610: }
0611: enableReplace = enable;
0612: return !enableReplace;
0613: }
0614:
0615: /**
0616: * The writeStreamHeader method is provided so subclasses can append or
0617: * prepend their own header to the stream. It writes the magic number and
0618: * version to the stream.
0619: *
0620: * @throws IOException if I/O errors occur while writing to the underlying
0621: * stream
0622: */
0623: protected void writeStreamHeader() throws IOException {
0624: bout.writeShort(STREAM_MAGIC);
0625: bout.writeShort(STREAM_VERSION);
0626: }
0627:
0628: /**
0629: * Write the specified class descriptor to the ObjectOutputStream. Class
0630: * descriptors are used to identify the classes of objects written to the
0631: * stream. Subclasses of ObjectOutputStream may override this method to
0632: * customize the way in which class descriptors are written to the
0633: * serialization stream. The corresponding method in ObjectInputStream,
0634: * <code>readClassDescriptor</code>, should then be overridden to
0635: * reconstitute the class descriptor from its custom stream representation.
0636: * By default, this method writes class descriptors according to the format
0637: * defined in the Object Serialization specification.
0638: *
0639: * <p>Note that this method will only be called if the ObjectOutputStream
0640: * is not using the old serialization stream format (set by calling
0641: * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
0642: * serialization stream is using the old format
0643: * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
0644: * internally in a manner that cannot be overridden or customized.
0645: *
0646: * @param desc class descriptor to write to the stream
0647: * @throws IOException If an I/O error has occurred.
0648: * @see java.io.ObjectInputStream#readClassDescriptor()
0649: * @see #useProtocolVersion(int)
0650: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0651: * @since 1.3
0652: */
0653: protected void writeClassDescriptor(ObjectStreamClass desc)
0654: throws IOException {
0655: desc.writeNonProxy(this );
0656: }
0657:
0658: /**
0659: * Writes a byte. This method will block until the byte is actually
0660: * written.
0661: *
0662: * @param val the byte to be written to the stream
0663: * @throws IOException If an I/O error has occurred.
0664: */
0665: public void write(int val) throws IOException {
0666: bout.write(val);
0667: }
0668:
0669: /**
0670: * Writes an array of bytes. This method will block until the bytes are
0671: * actually written.
0672: *
0673: * @param buf the data to be written
0674: * @throws IOException If an I/O error has occurred.
0675: */
0676: public void write(byte[] buf) throws IOException {
0677: bout.write(buf, 0, buf.length, false);
0678: }
0679:
0680: /**
0681: * Writes a sub array of bytes.
0682: *
0683: * @param buf the data to be written
0684: * @param off the start offset in the data
0685: * @param len the number of bytes that are written
0686: * @throws IOException If an I/O error has occurred.
0687: */
0688: public void write(byte[] buf, int off, int len) throws IOException {
0689: if (buf == null) {
0690: throw new NullPointerException();
0691: }
0692: int endoff = off + len;
0693: if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
0694: throw new IndexOutOfBoundsException();
0695: }
0696: bout.write(buf, off, len, false);
0697: }
0698:
0699: /**
0700: * Flushes the stream. This will write any buffered output bytes and flush
0701: * through to the underlying stream.
0702: *
0703: * @throws IOException If an I/O error has occurred.
0704: */
0705: public void flush() throws IOException {
0706: bout.flush();
0707: }
0708:
0709: /**
0710: * Drain any buffered data in ObjectOutputStream. Similar to flush but
0711: * does not propagate the flush to the underlying stream.
0712: *
0713: * @throws IOException if I/O errors occur while writing to the underlying
0714: * stream
0715: */
0716: protected void drain() throws IOException {
0717: bout.drain();
0718: }
0719:
0720: /**
0721: * Closes the stream. This method must be called to release any resources
0722: * associated with the stream.
0723: *
0724: * @throws IOException If an I/O error has occurred.
0725: */
0726: public void close() throws IOException {
0727: flush();
0728: clear();
0729: bout.close();
0730: }
0731:
0732: /**
0733: * Writes a boolean.
0734: *
0735: * @param val the boolean to be written
0736: * @throws IOException if I/O errors occur while writing to the underlying
0737: * stream
0738: */
0739: public void writeBoolean(boolean val) throws IOException {
0740: bout.writeBoolean(val);
0741: }
0742:
0743: /**
0744: * Writes an 8 bit byte.
0745: *
0746: * @param val the byte value to be written
0747: * @throws IOException if I/O errors occur while writing to the underlying
0748: * stream
0749: */
0750: public void writeByte(int val) throws IOException {
0751: bout.writeByte(val);
0752: }
0753:
0754: /**
0755: * Writes a 16 bit short.
0756: *
0757: * @param val the short value to be written
0758: * @throws IOException if I/O errors occur while writing to the underlying
0759: * stream
0760: */
0761: public void writeShort(int val) throws IOException {
0762: bout.writeShort(val);
0763: }
0764:
0765: /**
0766: * Writes a 16 bit char.
0767: *
0768: * @param val the char value to be written
0769: * @throws IOException if I/O errors occur while writing to the underlying
0770: * stream
0771: */
0772: public void writeChar(int val) throws IOException {
0773: bout.writeChar(val);
0774: }
0775:
0776: /**
0777: * Writes a 32 bit int.
0778: *
0779: * @param val the integer value to be written
0780: * @throws IOException if I/O errors occur while writing to the underlying
0781: * stream
0782: */
0783: public void writeInt(int val) throws IOException {
0784: bout.writeInt(val);
0785: }
0786:
0787: /**
0788: * Writes a 64 bit long.
0789: *
0790: * @param val the long value to be written
0791: * @throws IOException if I/O errors occur while writing to the underlying
0792: * stream
0793: */
0794: public void writeLong(long val) throws IOException {
0795: bout.writeLong(val);
0796: }
0797:
0798: /**
0799: * Writes a 32 bit float.
0800: *
0801: * @param val the float value to be written
0802: * @throws IOException if I/O errors occur while writing to the underlying
0803: * stream
0804: */
0805: public void writeFloat(float val) throws IOException {
0806: bout.writeFloat(val);
0807: }
0808:
0809: /**
0810: * Writes a 64 bit double.
0811: *
0812: * @param val the double value to be written
0813: * @throws IOException if I/O errors occur while writing to the underlying
0814: * stream
0815: */
0816: public void writeDouble(double val) throws IOException {
0817: bout.writeDouble(val);
0818: }
0819:
0820: /**
0821: * Writes a String as a sequence of bytes.
0822: *
0823: * @param str the String of bytes to be written
0824: * @throws IOException if I/O errors occur while writing to the underlying
0825: * stream
0826: */
0827: public void writeBytes(String str) throws IOException {
0828: bout.writeBytes(str);
0829: }
0830:
0831: /**
0832: * Writes a String as a sequence of chars.
0833: *
0834: * @param str the String of chars to be written
0835: * @throws IOException if I/O errors occur while writing to the underlying
0836: * stream
0837: */
0838: public void writeChars(String str) throws IOException {
0839: bout.writeChars(str);
0840: }
0841:
0842: /**
0843: * Primitive data write of this String in
0844: * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
0845: * format. Note that there is a
0846: * significant difference between writing a String into the stream as
0847: * primitive data or as an Object. A String instance written by writeObject
0848: * is written into the stream as a String initially. Future writeObject()
0849: * calls write references to the string into the stream.
0850: *
0851: * @param str the String to be written
0852: * @throws IOException if I/O errors occur while writing to the underlying
0853: * stream
0854: */
0855: public void writeUTF(String str) throws IOException {
0856: bout.writeUTF(str);
0857: }
0858:
0859: /**
0860: * Provide programmatic access to the persistent fields to be written
0861: * to ObjectOutput.
0862: *
0863: * @since 1.2
0864: */
0865: public static abstract class PutField {
0866:
0867: /**
0868: * Put the value of the named boolean field into the persistent field.
0869: *
0870: * @param name the name of the serializable field
0871: * @param val the value to assign to the field
0872: * @throws IllegalArgumentException if <code>name</code> does not
0873: * match the name of a serializable field for the class whose fields
0874: * are being written, or if the type of the named field is not
0875: * <code>boolean</code>
0876: */
0877: public abstract void put(String name, boolean val);
0878:
0879: /**
0880: * Put the value of the named byte field into the persistent field.
0881: *
0882: * @param name the name of the serializable field
0883: * @param val the value to assign to the field
0884: * @throws IllegalArgumentException if <code>name</code> does not
0885: * match the name of a serializable field for the class whose fields
0886: * are being written, or if the type of the named field is not
0887: * <code>byte</code>
0888: */
0889: public abstract void put(String name, byte val);
0890:
0891: /**
0892: * Put the value of the named char field into the persistent field.
0893: *
0894: * @param name the name of the serializable field
0895: * @param val the value to assign to the field
0896: * @throws IllegalArgumentException if <code>name</code> does not
0897: * match the name of a serializable field for the class whose fields
0898: * are being written, or if the type of the named field is not
0899: * <code>char</code>
0900: */
0901: public abstract void put(String name, char val);
0902:
0903: /**
0904: * Put the value of the named short field into the persistent field.
0905: *
0906: * @param name the name of the serializable field
0907: * @param val the value to assign to the field
0908: * @throws IllegalArgumentException if <code>name</code> does not
0909: * match the name of a serializable field for the class whose fields
0910: * are being written, or if the type of the named field is not
0911: * <code>short</code>
0912: */
0913: public abstract void put(String name, short val);
0914:
0915: /**
0916: * Put the value of the named int field into the persistent field.
0917: *
0918: * @param name the name of the serializable field
0919: * @param val the value to assign to the field
0920: * @throws IllegalArgumentException if <code>name</code> does not
0921: * match the name of a serializable field for the class whose fields
0922: * are being written, or if the type of the named field is not
0923: * <code>int</code>
0924: */
0925: public abstract void put(String name, int val);
0926:
0927: /**
0928: * Put the value of the named long field into the persistent field.
0929: *
0930: * @param name the name of the serializable field
0931: * @param val the value to assign to the field
0932: * @throws IllegalArgumentException if <code>name</code> does not
0933: * match the name of a serializable field for the class whose fields
0934: * are being written, or if the type of the named field is not
0935: * <code>long</code>
0936: */
0937: public abstract void put(String name, long val);
0938:
0939: /**
0940: * Put the value of the named float field into the persistent field.
0941: *
0942: * @param name the name of the serializable field
0943: * @param val the value to assign to the field
0944: * @throws IllegalArgumentException if <code>name</code> does not
0945: * match the name of a serializable field for the class whose fields
0946: * are being written, or if the type of the named field is not
0947: * <code>float</code>
0948: */
0949: public abstract void put(String name, float val);
0950:
0951: /**
0952: * Put the value of the named double field into the persistent field.
0953: *
0954: * @param name the name of the serializable field
0955: * @param val the value to assign to the field
0956: * @throws IllegalArgumentException if <code>name</code> does not
0957: * match the name of a serializable field for the class whose fields
0958: * are being written, or if the type of the named field is not
0959: * <code>double</code>
0960: */
0961: public abstract void put(String name, double val);
0962:
0963: /**
0964: * Put the value of the named Object field into the persistent field.
0965: *
0966: * @param name the name of the serializable field
0967: * @param val the value to assign to the field
0968: * (which may be <code>null</code>)
0969: * @throws IllegalArgumentException if <code>name</code> does not
0970: * match the name of a serializable field for the class whose fields
0971: * are being written, or if the type of the named field is not a
0972: * reference type
0973: */
0974: public abstract void put(String name, Object val);
0975:
0976: /**
0977: * Write the data and fields to the specified ObjectOutput stream,
0978: * which must be the same stream that produced this
0979: * <code>PutField</code> object.
0980: *
0981: * @param out the stream to write the data and fields to
0982: * @throws IOException if I/O errors occur while writing to the
0983: * underlying stream
0984: * @throws IllegalArgumentException if the specified stream is not
0985: * the same stream that produced this <code>PutField</code>
0986: * object
0987: * @deprecated This method does not write the values contained by this
0988: * <code>PutField</code> object in a proper format, and may
0989: * result in corruption of the serialization stream. The
0990: * correct way to write <code>PutField</code> data is by
0991: * calling the {@link java.io.ObjectOutputStream#writeFields()}
0992: * method.
0993: */
0994: @Deprecated
0995: public abstract void write(ObjectOutput out) throws IOException;
0996: }
0997:
0998: /**
0999: * Returns protocol version in use.
1000: */
1001: int getProtocolVersion() {
1002: return protocol;
1003: }
1004:
1005: /**
1006: * Writes string without allowing it to be replaced in stream. Used by
1007: * ObjectStreamClass to write class descriptor type strings.
1008: */
1009: void writeTypeString(String str) throws IOException {
1010: int handle;
1011: if (str == null) {
1012: writeNull();
1013: } else if ((handle = handles.lookup(str)) != -1) {
1014: writeHandle(handle);
1015: } else {
1016: writeString(str, false);
1017: }
1018: }
1019:
1020: /**
1021: * Verifies that this (possibly subclass) instance can be constructed
1022: * without violating security constraints: the subclass must not override
1023: * security-sensitive non-final methods, or else the
1024: * "enableSubclassImplementation" SerializablePermission is checked.
1025: */
1026: private void verifySubclass() {
1027: Class cl = getClass();
1028: if (cl == ObjectOutputStream.class) {
1029: return;
1030: }
1031: SecurityManager sm = System.getSecurityManager();
1032: if (sm == null) {
1033: return;
1034: }
1035: processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
1036: WeakClassKey key = new WeakClassKey(cl,
1037: Caches.subclassAuditsQueue);
1038: Boolean result = Caches.subclassAudits.get(key);
1039: if (result == null) {
1040: result = Boolean.valueOf(auditSubclass(cl));
1041: Caches.subclassAudits.putIfAbsent(key, result);
1042: }
1043: if (result.booleanValue()) {
1044: return;
1045: }
1046: sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1047: }
1048:
1049: /**
1050: * Performs reflective checks on given subclass to verify that it doesn't
1051: * override security-sensitive non-final methods. Returns true if subclass
1052: * is "safe", false otherwise.
1053: */
1054: private static boolean auditSubclass(final Class subcl) {
1055: Boolean result = (Boolean) AccessController
1056: .doPrivileged(new PrivilegedAction() {
1057: public Object run() {
1058: for (Class cl = subcl; cl != ObjectOutputStream.class; cl = cl
1059: .getSuperclass()) {
1060: try {
1061: cl.getDeclaredMethod("writeUnshared",
1062: new Class[] { Object.class });
1063: return Boolean.FALSE;
1064: } catch (NoSuchMethodException ex) {
1065: }
1066: try {
1067: cl.getDeclaredMethod("putFields",
1068: (Class[]) null);
1069: return Boolean.FALSE;
1070: } catch (NoSuchMethodException ex) {
1071: }
1072: }
1073: return Boolean.TRUE;
1074: }
1075: });
1076: return result.booleanValue();
1077: }
1078:
1079: /**
1080: * Clears internal data structures.
1081: */
1082: private void clear() {
1083: subs.clear();
1084: handles.clear();
1085: }
1086:
1087: /**
1088: * Underlying writeObject/writeUnshared implementation.
1089: */
1090: private void writeObject0(Object obj, boolean unshared)
1091: throws IOException {
1092: boolean oldMode = bout.setBlockDataMode(false);
1093: depth++;
1094: try {
1095: // handle previously written and non-replaceable objects
1096: int h;
1097: if ((obj = subs.lookup(obj)) == null) {
1098: writeNull();
1099: return;
1100: } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1101: writeHandle(h);
1102: return;
1103: } else if (obj instanceof Class) {
1104: writeClass((Class) obj, unshared);
1105: return;
1106: } else if (obj instanceof ObjectStreamClass) {
1107: writeClassDesc((ObjectStreamClass) obj, unshared);
1108: return;
1109: }
1110:
1111: // check for replacement object
1112: Object orig = obj;
1113: Class cl = obj.getClass();
1114: ObjectStreamClass desc;
1115: for (;;) {
1116: // REMIND: skip this check for strings/arrays?
1117: Class repCl;
1118: desc = ObjectStreamClass.lookup(cl, true);
1119: if (!desc.hasWriteReplaceMethod()
1120: || (obj = desc.invokeWriteReplace(obj)) == null
1121: || (repCl = obj.getClass()) == cl) {
1122: break;
1123: }
1124: cl = repCl;
1125: }
1126: if (enableReplace) {
1127: Object rep = replaceObject(obj);
1128: if (rep != obj && rep != null) {
1129: cl = rep.getClass();
1130: desc = ObjectStreamClass.lookup(cl, true);
1131: }
1132: obj = rep;
1133: }
1134:
1135: // if object replaced, run through original checks a second time
1136: if (obj != orig) {
1137: subs.assign(orig, obj);
1138: if (obj == null) {
1139: writeNull();
1140: return;
1141: } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1142: writeHandle(h);
1143: return;
1144: } else if (obj instanceof Class) {
1145: writeClass((Class) obj, unshared);
1146: return;
1147: } else if (obj instanceof ObjectStreamClass) {
1148: writeClassDesc((ObjectStreamClass) obj, unshared);
1149: return;
1150: }
1151: }
1152:
1153: // remaining cases
1154: if (obj instanceof String) {
1155: writeString((String) obj, unshared);
1156: } else if (cl.isArray()) {
1157: writeArray(obj, desc, unshared);
1158: } else if (obj instanceof Enum) {
1159: writeEnum((Enum) obj, desc, unshared);
1160: } else if (obj instanceof Serializable) {
1161: writeOrdinaryObject(obj, desc, unshared);
1162: } else {
1163: if (extendedDebugInfo) {
1164: throw new NotSerializableException(cl.getName()
1165: + "\n" + debugInfoStack.toString());
1166: } else {
1167: throw new NotSerializableException(cl.getName());
1168: }
1169: }
1170: } finally {
1171: depth--;
1172: bout.setBlockDataMode(oldMode);
1173: }
1174: }
1175:
1176: /**
1177: * Writes null code to stream.
1178: */
1179: private void writeNull() throws IOException {
1180: bout.writeByte(TC_NULL);
1181: }
1182:
1183: /**
1184: * Writes given object handle to stream.
1185: */
1186: private void writeHandle(int handle) throws IOException {
1187: bout.writeByte(TC_REFERENCE);
1188: bout.writeInt(baseWireHandle + handle);
1189: }
1190:
1191: /**
1192: * Writes representation of given class to stream.
1193: */
1194: private void writeClass(Class cl, boolean unshared)
1195: throws IOException {
1196: bout.writeByte(TC_CLASS);
1197: writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1198: handles.assign(unshared ? null : cl);
1199: }
1200:
1201: /**
1202: * Writes representation of given class descriptor to stream.
1203: */
1204: private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1205: throws IOException {
1206: int handle;
1207: if (desc == null) {
1208: writeNull();
1209: } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1210: writeHandle(handle);
1211: } else if (desc.isProxy()) {
1212: writeProxyDesc(desc, unshared);
1213: } else {
1214: writeNonProxyDesc(desc, unshared);
1215: }
1216: }
1217:
1218: /**
1219: * Writes class descriptor representing a dynamic proxy class to stream.
1220: */
1221: private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1222: throws IOException {
1223: bout.writeByte(TC_PROXYCLASSDESC);
1224: handles.assign(unshared ? null : desc);
1225:
1226: Class cl = desc.forClass();
1227: Class[] ifaces = cl.getInterfaces();
1228: bout.writeInt(ifaces.length);
1229: for (int i = 0; i < ifaces.length; i++) {
1230: bout.writeUTF(ifaces[i].getName());
1231: }
1232:
1233: bout.setBlockDataMode(true);
1234: annotateProxyClass(cl);
1235: bout.setBlockDataMode(false);
1236: bout.writeByte(TC_ENDBLOCKDATA);
1237:
1238: writeClassDesc(desc.getSuperDesc(), false);
1239: }
1240:
1241: /**
1242: * Writes class descriptor representing a standard (i.e., not a dynamic
1243: * proxy) class to stream.
1244: */
1245: private void writeNonProxyDesc(ObjectStreamClass desc,
1246: boolean unshared) throws IOException {
1247: bout.writeByte(TC_CLASSDESC);
1248: handles.assign(unshared ? null : desc);
1249:
1250: if (protocol == PROTOCOL_VERSION_1) {
1251: // do not invoke class descriptor write hook with old protocol
1252: desc.writeNonProxy(this );
1253: } else {
1254: writeClassDescriptor(desc);
1255: }
1256:
1257: Class cl = desc.forClass();
1258: bout.setBlockDataMode(true);
1259: annotateClass(cl);
1260: bout.setBlockDataMode(false);
1261: bout.writeByte(TC_ENDBLOCKDATA);
1262:
1263: writeClassDesc(desc.getSuperDesc(), false);
1264: }
1265:
1266: /**
1267: * Writes given string to stream, using standard or long UTF format
1268: * depending on string length.
1269: */
1270: private void writeString(String str, boolean unshared)
1271: throws IOException {
1272: handles.assign(unshared ? null : str);
1273: long utflen = bout.getUTFLength(str);
1274: if (utflen <= 0xFFFF) {
1275: bout.writeByte(TC_STRING);
1276: bout.writeUTF(str, utflen);
1277: } else {
1278: bout.writeByte(TC_LONGSTRING);
1279: bout.writeLongUTF(str, utflen);
1280: }
1281: }
1282:
1283: /**
1284: * Writes given array object to stream.
1285: */
1286: private void writeArray(Object array, ObjectStreamClass desc,
1287: boolean unshared) throws IOException {
1288: bout.writeByte(TC_ARRAY);
1289: writeClassDesc(desc, false);
1290: handles.assign(unshared ? null : array);
1291:
1292: Class ccl = desc.forClass().getComponentType();
1293: if (ccl.isPrimitive()) {
1294: if (ccl == Integer.TYPE) {
1295: int[] ia = (int[]) array;
1296: bout.writeInt(ia.length);
1297: bout.writeInts(ia, 0, ia.length);
1298: } else if (ccl == Byte.TYPE) {
1299: byte[] ba = (byte[]) array;
1300: bout.writeInt(ba.length);
1301: bout.write(ba, 0, ba.length, true);
1302: } else if (ccl == Long.TYPE) {
1303: long[] ja = (long[]) array;
1304: bout.writeInt(ja.length);
1305: bout.writeLongs(ja, 0, ja.length);
1306: } else if (ccl == Float.TYPE) {
1307: float[] fa = (float[]) array;
1308: bout.writeInt(fa.length);
1309: bout.writeFloats(fa, 0, fa.length);
1310: } else if (ccl == Double.TYPE) {
1311: double[] da = (double[]) array;
1312: bout.writeInt(da.length);
1313: bout.writeDoubles(da, 0, da.length);
1314: } else if (ccl == Short.TYPE) {
1315: short[] sa = (short[]) array;
1316: bout.writeInt(sa.length);
1317: bout.writeShorts(sa, 0, sa.length);
1318: } else if (ccl == Character.TYPE) {
1319: char[] ca = (char[]) array;
1320: bout.writeInt(ca.length);
1321: bout.writeChars(ca, 0, ca.length);
1322: } else if (ccl == Boolean.TYPE) {
1323: boolean[] za = (boolean[]) array;
1324: bout.writeInt(za.length);
1325: bout.writeBooleans(za, 0, za.length);
1326: } else {
1327: throw new InternalError();
1328: }
1329: } else {
1330: Object[] objs = (Object[]) array;
1331: int len = objs.length;
1332: bout.writeInt(len);
1333: if (extendedDebugInfo) {
1334: debugInfoStack.push("array (class \""
1335: + array.getClass().getName() + "\", size: "
1336: + len + ")");
1337: }
1338: try {
1339: for (int i = 0; i < len; i++) {
1340: if (extendedDebugInfo) {
1341: debugInfoStack.push("element of array (index: "
1342: + i + ")");
1343: }
1344: try {
1345: writeObject0(objs[i], false);
1346: } finally {
1347: if (extendedDebugInfo) {
1348: debugInfoStack.pop();
1349: }
1350: }
1351: }
1352: } finally {
1353: if (extendedDebugInfo) {
1354: debugInfoStack.pop();
1355: }
1356: }
1357: }
1358: }
1359:
1360: /**
1361: * Writes given enum constant to stream.
1362: */
1363: private void writeEnum(Enum en, ObjectStreamClass desc,
1364: boolean unshared) throws IOException {
1365: bout.writeByte(TC_ENUM);
1366: ObjectStreamClass sdesc = desc.getSuperDesc();
1367: writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc,
1368: false);
1369: handles.assign(unshared ? null : en);
1370: writeString(en.name(), false);
1371: }
1372:
1373: /**
1374: * Writes representation of a "ordinary" (i.e., not a String, Class,
1375: * ObjectStreamClass, array, or enum constant) serializable object to the
1376: * stream.
1377: */
1378: private void writeOrdinaryObject(Object obj,
1379: ObjectStreamClass desc, boolean unshared)
1380: throws IOException {
1381: if (extendedDebugInfo) {
1382: debugInfoStack.push((depth == 1 ? "root " : "")
1383: + "object (class \"" + obj.getClass().getName()
1384: + "\", " + obj.toString() + ")");
1385: }
1386: try {
1387: desc.checkSerialize();
1388:
1389: bout.writeByte(TC_OBJECT);
1390: writeClassDesc(desc, false);
1391: handles.assign(unshared ? null : obj);
1392: if (desc.isExternalizable() && !desc.isProxy()) {
1393: writeExternalData((Externalizable) obj);
1394: } else {
1395: writeSerialData(obj, desc);
1396: }
1397: } finally {
1398: if (extendedDebugInfo) {
1399: debugInfoStack.pop();
1400: }
1401: }
1402: }
1403:
1404: /**
1405: * Writes externalizable data of given object by invoking its
1406: * writeExternal() method.
1407: */
1408: private void writeExternalData(Externalizable obj)
1409: throws IOException {
1410: Object oldObj = curObj;
1411: ObjectStreamClass oldDesc = curDesc;
1412: PutFieldImpl oldPut = curPut;
1413: curObj = obj;
1414: curDesc = null;
1415: curPut = null;
1416:
1417: if (extendedDebugInfo) {
1418: debugInfoStack.push("writeExternal data");
1419: }
1420: try {
1421: if (protocol == PROTOCOL_VERSION_1) {
1422: obj.writeExternal(this );
1423: } else {
1424: bout.setBlockDataMode(true);
1425: obj.writeExternal(this );
1426: bout.setBlockDataMode(false);
1427: bout.writeByte(TC_ENDBLOCKDATA);
1428: }
1429: } finally {
1430: if (extendedDebugInfo) {
1431: debugInfoStack.pop();
1432: }
1433: }
1434:
1435: curObj = oldObj;
1436: curDesc = oldDesc;
1437: curPut = oldPut;
1438: }
1439:
1440: /**
1441: * Writes instance data for each serializable class of given object, from
1442: * superclass to subclass.
1443: */
1444: private void writeSerialData(Object obj, ObjectStreamClass desc)
1445: throws IOException {
1446: ObjectStreamClass.ClassDataSlot[] slots = desc
1447: .getClassDataLayout();
1448: for (int i = 0; i < slots.length; i++) {
1449: ObjectStreamClass slotDesc = slots[i].desc;
1450: if (slotDesc.hasWriteObjectMethod()) {
1451: Object oldObj = curObj;
1452: ObjectStreamClass oldDesc = curDesc;
1453: PutFieldImpl oldPut = curPut;
1454: curObj = obj;
1455: curDesc = slotDesc;
1456: curPut = null;
1457:
1458: if (extendedDebugInfo) {
1459: debugInfoStack
1460: .push("custom writeObject data (class \""
1461: + slotDesc.getName() + "\")");
1462: }
1463: try {
1464: bout.setBlockDataMode(true);
1465: slotDesc.invokeWriteObject(obj, this );
1466: bout.setBlockDataMode(false);
1467: bout.writeByte(TC_ENDBLOCKDATA);
1468: } finally {
1469: if (extendedDebugInfo) {
1470: debugInfoStack.pop();
1471: }
1472: }
1473:
1474: curObj = oldObj;
1475: curDesc = oldDesc;
1476: curPut = oldPut;
1477: } else {
1478: defaultWriteFields(obj, slotDesc);
1479: }
1480: }
1481: }
1482:
1483: /**
1484: * Fetches and writes values of serializable fields of given object to
1485: * stream. The given class descriptor specifies which field values to
1486: * write, and in which order they should be written.
1487: */
1488: private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1489: throws IOException {
1490: // REMIND: perform conservative isInstance check here?
1491: desc.checkDefaultSerialize();
1492:
1493: int primDataSize = desc.getPrimDataSize();
1494: if (primVals == null || primVals.length < primDataSize) {
1495: primVals = new byte[primDataSize];
1496: }
1497: desc.getPrimFieldValues(obj, primVals);
1498: bout.write(primVals, 0, primDataSize, false);
1499:
1500: ObjectStreamField[] fields = desc.getFields(false);
1501: Object[] objVals = new Object[desc.getNumObjFields()];
1502: int numPrimFields = fields.length - objVals.length;
1503: desc.getObjFieldValues(obj, objVals);
1504: for (int i = 0; i < objVals.length; i++) {
1505: if (extendedDebugInfo) {
1506: debugInfoStack.push("field (class \"" + desc.getName()
1507: + "\", name: \""
1508: + fields[numPrimFields + i].getName()
1509: + "\", type: \""
1510: + fields[numPrimFields + i].getType() + "\")");
1511: }
1512: try {
1513: writeObject0(objVals[i], fields[numPrimFields + i]
1514: .isUnshared());
1515: } finally {
1516: if (extendedDebugInfo) {
1517: debugInfoStack.pop();
1518: }
1519: }
1520: }
1521: }
1522:
1523: /**
1524: * Attempts to write to stream fatal IOException that has caused
1525: * serialization to abort.
1526: */
1527: private void writeFatalException(IOException ex) throws IOException {
1528: /*
1529: * Note: the serialization specification states that if a second
1530: * IOException occurs while attempting to serialize the original fatal
1531: * exception to the stream, then a StreamCorruptedException should be
1532: * thrown (section 2.1). However, due to a bug in previous
1533: * implementations of serialization, StreamCorruptedExceptions were
1534: * rarely (if ever) actually thrown--the "root" exceptions from
1535: * underlying streams were thrown instead. This historical behavior is
1536: * followed here for consistency.
1537: */
1538: clear();
1539: boolean oldMode = bout.setBlockDataMode(false);
1540: try {
1541: bout.writeByte(TC_EXCEPTION);
1542: writeObject0(ex, false);
1543: clear();
1544: } finally {
1545: bout.setBlockDataMode(oldMode);
1546: }
1547: }
1548:
1549: /**
1550: * Converts specified span of float values into byte values.
1551: */
1552: // REMIND: remove once hotspot inlines Float.floatToIntBits
1553: private static native void floatsToBytes(float[] src, int srcpos,
1554: byte[] dst, int dstpos, int nfloats);
1555:
1556: /**
1557: * Converts specified span of double values into byte values.
1558: */
1559: // REMIND: remove once hotspot inlines Double.doubleToLongBits
1560: private static native void doublesToBytes(double[] src, int srcpos,
1561: byte[] dst, int dstpos, int ndoubles);
1562:
1563: /**
1564: * Default PutField implementation.
1565: */
1566: private class PutFieldImpl extends PutField {
1567:
1568: /** class descriptor describing serializable fields */
1569: private final ObjectStreamClass desc;
1570: /** primitive field values */
1571: private final byte[] primVals;
1572: /** object field values */
1573: private final Object[] objVals;
1574:
1575: /**
1576: * Creates PutFieldImpl object for writing fields defined in given
1577: * class descriptor.
1578: */
1579: PutFieldImpl(ObjectStreamClass desc) {
1580: this .desc = desc;
1581: primVals = new byte[desc.getPrimDataSize()];
1582: objVals = new Object[desc.getNumObjFields()];
1583: }
1584:
1585: public void put(String name, boolean val) {
1586: Bits.putBoolean(primVals,
1587: getFieldOffset(name, Boolean.TYPE), val);
1588: }
1589:
1590: public void put(String name, byte val) {
1591: primVals[getFieldOffset(name, Byte.TYPE)] = val;
1592: }
1593:
1594: public void put(String name, char val) {
1595: Bits.putChar(primVals,
1596: getFieldOffset(name, Character.TYPE), val);
1597: }
1598:
1599: public void put(String name, short val) {
1600: Bits.putShort(primVals, getFieldOffset(name, Short.TYPE),
1601: val);
1602: }
1603:
1604: public void put(String name, int val) {
1605: Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE),
1606: val);
1607: }
1608:
1609: public void put(String name, float val) {
1610: Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE),
1611: val);
1612: }
1613:
1614: public void put(String name, long val) {
1615: Bits
1616: .putLong(primVals, getFieldOffset(name, Long.TYPE),
1617: val);
1618: }
1619:
1620: public void put(String name, double val) {
1621: Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE),
1622: val);
1623: }
1624:
1625: public void put(String name, Object val) {
1626: objVals[getFieldOffset(name, Object.class)] = val;
1627: }
1628:
1629: // deprecated in ObjectOutputStream.PutField
1630: public void write(ObjectOutput out) throws IOException {
1631: /*
1632: * Applications should *not* use this method to write PutField
1633: * data, as it will lead to stream corruption if the PutField
1634: * object writes any primitive data (since block data mode is not
1635: * unset/set properly, as is done in OOS.writeFields()). This
1636: * broken implementation is being retained solely for behavioral
1637: * compatibility, in order to support applications which use
1638: * OOS.PutField.write() for writing only non-primitive data.
1639: *
1640: * Serialization of unshared objects is not implemented here since
1641: * it is not necessary for backwards compatibility; also, unshared
1642: * semantics may not be supported by the given ObjectOutput
1643: * instance. Applications which write unshared objects using the
1644: * PutField API must use OOS.writeFields().
1645: */
1646: if (ObjectOutputStream.this != out) {
1647: throw new IllegalArgumentException("wrong stream");
1648: }
1649: out.write(primVals, 0, primVals.length);
1650:
1651: ObjectStreamField[] fields = desc.getFields(false);
1652: int numPrimFields = fields.length - objVals.length;
1653: // REMIND: warn if numPrimFields > 0?
1654: for (int i = 0; i < objVals.length; i++) {
1655: if (fields[numPrimFields + i].isUnshared()) {
1656: throw new IOException(
1657: "cannot write unshared object");
1658: }
1659: out.writeObject(objVals[i]);
1660: }
1661: }
1662:
1663: /**
1664: * Writes buffered primitive data and object fields to stream.
1665: */
1666: void writeFields() throws IOException {
1667: bout.write(primVals, 0, primVals.length, false);
1668:
1669: ObjectStreamField[] fields = desc.getFields(false);
1670: int numPrimFields = fields.length - objVals.length;
1671: for (int i = 0; i < objVals.length; i++) {
1672: if (extendedDebugInfo) {
1673: debugInfoStack.push("field (class \""
1674: + desc.getName() + "\", name: \""
1675: + fields[numPrimFields + i].getName()
1676: + "\", type: \""
1677: + fields[numPrimFields + i].getType()
1678: + "\")");
1679: }
1680: try {
1681: writeObject0(objVals[i], fields[numPrimFields + i]
1682: .isUnshared());
1683: } finally {
1684: if (extendedDebugInfo) {
1685: debugInfoStack.pop();
1686: }
1687: }
1688: }
1689: }
1690:
1691: /**
1692: * Returns offset of field with given name and type. A specified type
1693: * of null matches all types, Object.class matches all non-primitive
1694: * types, and any other non-null type matches assignable types only.
1695: * Throws IllegalArgumentException if no matching field found.
1696: */
1697: private int getFieldOffset(String name, Class type) {
1698: ObjectStreamField field = desc.getField(name, type);
1699: if (field == null) {
1700: throw new IllegalArgumentException("no such field "
1701: + name + " with type " + type);
1702: }
1703: return field.getOffset();
1704: }
1705: }
1706:
1707: /**
1708: * Buffered output stream with two modes: in default mode, outputs data in
1709: * same format as DataOutputStream; in "block data" mode, outputs data
1710: * bracketed by block data markers (see object serialization specification
1711: * for details).
1712: */
1713: private static class BlockDataOutputStream extends OutputStream
1714: implements DataOutput {
1715: /** maximum data block length */
1716: private static final int MAX_BLOCK_SIZE = 1024;
1717: /** maximum data block header length */
1718: private static final int MAX_HEADER_SIZE = 5;
1719: /** (tunable) length of char buffer (for writing strings) */
1720: private static final int CHAR_BUF_SIZE = 256;
1721:
1722: /** buffer for writing general/block data */
1723: private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1724: /** buffer for writing block data headers */
1725: private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1726: /** char buffer for fast string writes */
1727: private final char[] cbuf = new char[CHAR_BUF_SIZE];
1728:
1729: /** block data mode */
1730: private boolean blkmode = false;
1731: /** current offset into buf */
1732: private int pos = 0;
1733:
1734: /** underlying output stream */
1735: private final OutputStream out;
1736: /** loopback stream (for data writes that span data blocks) */
1737: private final DataOutputStream dout;
1738:
1739: /**
1740: * Creates new BlockDataOutputStream on top of given underlying stream.
1741: * Block data mode is turned off by default.
1742: */
1743: BlockDataOutputStream(OutputStream out) {
1744: this .out = out;
1745: dout = new DataOutputStream(this );
1746: }
1747:
1748: /**
1749: * Sets block data mode to the given mode (true == on, false == off)
1750: * and returns the previous mode value. If the new mode is the same as
1751: * the old mode, no action is taken. If the new mode differs from the
1752: * old mode, any buffered data is flushed before switching to the new
1753: * mode.
1754: */
1755: boolean setBlockDataMode(boolean mode) throws IOException {
1756: if (blkmode == mode) {
1757: return blkmode;
1758: }
1759: drain();
1760: blkmode = mode;
1761: return !blkmode;
1762: }
1763:
1764: /**
1765: * Returns true if the stream is currently in block data mode, false
1766: * otherwise.
1767: */
1768: boolean getBlockDataMode() {
1769: return blkmode;
1770: }
1771:
1772: /* ----------------- generic output stream methods ----------------- */
1773: /*
1774: * The following methods are equivalent to their counterparts in
1775: * OutputStream, except that they partition written data into data
1776: * blocks when in block data mode.
1777: */
1778:
1779: public void write(int b) throws IOException {
1780: if (pos >= MAX_BLOCK_SIZE) {
1781: drain();
1782: }
1783: buf[pos++] = (byte) b;
1784: }
1785:
1786: public void write(byte[] b) throws IOException {
1787: write(b, 0, b.length, false);
1788: }
1789:
1790: public void write(byte[] b, int off, int len)
1791: throws IOException {
1792: write(b, off, len, false);
1793: }
1794:
1795: public void flush() throws IOException {
1796: drain();
1797: out.flush();
1798: }
1799:
1800: public void close() throws IOException {
1801: flush();
1802: out.close();
1803: }
1804:
1805: /**
1806: * Writes specified span of byte values from given array. If copy is
1807: * true, copies the values to an intermediate buffer before writing
1808: * them to underlying stream (to avoid exposing a reference to the
1809: * original byte array).
1810: */
1811: void write(byte[] b, int off, int len, boolean copy)
1812: throws IOException {
1813: if (!(copy || blkmode)) { // write directly
1814: drain();
1815: out.write(b, off, len);
1816: return;
1817: }
1818:
1819: while (len > 0) {
1820: if (pos >= MAX_BLOCK_SIZE) {
1821: drain();
1822: }
1823: if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1824: // avoid unnecessary copy
1825: writeBlockHeader(MAX_BLOCK_SIZE);
1826: out.write(b, off, MAX_BLOCK_SIZE);
1827: off += MAX_BLOCK_SIZE;
1828: len -= MAX_BLOCK_SIZE;
1829: } else {
1830: int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1831: System.arraycopy(b, off, buf, pos, wlen);
1832: pos += wlen;
1833: off += wlen;
1834: len -= wlen;
1835: }
1836: }
1837: }
1838:
1839: /**
1840: * Writes all buffered data from this stream to the underlying stream,
1841: * but does not flush underlying stream.
1842: */
1843: void drain() throws IOException {
1844: if (pos == 0) {
1845: return;
1846: }
1847: if (blkmode) {
1848: writeBlockHeader(pos);
1849: }
1850: out.write(buf, 0, pos);
1851: pos = 0;
1852: }
1853:
1854: /**
1855: * Writes block data header. Data blocks shorter than 256 bytes are
1856: * prefixed with a 2-byte header; all others start with a 5-byte
1857: * header.
1858: */
1859: private void writeBlockHeader(int len) throws IOException {
1860: if (len <= 0xFF) {
1861: hbuf[0] = TC_BLOCKDATA;
1862: hbuf[1] = (byte) len;
1863: out.write(hbuf, 0, 2);
1864: } else {
1865: hbuf[0] = TC_BLOCKDATALONG;
1866: Bits.putInt(hbuf, 1, len);
1867: out.write(hbuf, 0, 5);
1868: }
1869: }
1870:
1871: /* ----------------- primitive data output methods ----------------- */
1872: /*
1873: * The following methods are equivalent to their counterparts in
1874: * DataOutputStream, except that they partition written data into data
1875: * blocks when in block data mode.
1876: */
1877:
1878: public void writeBoolean(boolean v) throws IOException {
1879: if (pos >= MAX_BLOCK_SIZE) {
1880: drain();
1881: }
1882: Bits.putBoolean(buf, pos++, v);
1883: }
1884:
1885: public void writeByte(int v) throws IOException {
1886: if (pos >= MAX_BLOCK_SIZE) {
1887: drain();
1888: }
1889: buf[pos++] = (byte) v;
1890: }
1891:
1892: public void writeChar(int v) throws IOException {
1893: if (pos + 2 <= MAX_BLOCK_SIZE) {
1894: Bits.putChar(buf, pos, (char) v);
1895: pos += 2;
1896: } else {
1897: dout.writeChar(v);
1898: }
1899: }
1900:
1901: public void writeShort(int v) throws IOException {
1902: if (pos + 2 <= MAX_BLOCK_SIZE) {
1903: Bits.putShort(buf, pos, (short) v);
1904: pos += 2;
1905: } else {
1906: dout.writeShort(v);
1907: }
1908: }
1909:
1910: public void writeInt(int v) throws IOException {
1911: if (pos + 4 <= MAX_BLOCK_SIZE) {
1912: Bits.putInt(buf, pos, v);
1913: pos += 4;
1914: } else {
1915: dout.writeInt(v);
1916: }
1917: }
1918:
1919: public void writeFloat(float v) throws IOException {
1920: if (pos + 4 <= MAX_BLOCK_SIZE) {
1921: Bits.putFloat(buf, pos, v);
1922: pos += 4;
1923: } else {
1924: dout.writeFloat(v);
1925: }
1926: }
1927:
1928: public void writeLong(long v) throws IOException {
1929: if (pos + 8 <= MAX_BLOCK_SIZE) {
1930: Bits.putLong(buf, pos, v);
1931: pos += 8;
1932: } else {
1933: dout.writeLong(v);
1934: }
1935: }
1936:
1937: public void writeDouble(double v) throws IOException {
1938: if (pos + 8 <= MAX_BLOCK_SIZE) {
1939: Bits.putDouble(buf, pos, v);
1940: pos += 8;
1941: } else {
1942: dout.writeDouble(v);
1943: }
1944: }
1945:
1946: public void writeBytes(String s) throws IOException {
1947: int endoff = s.length();
1948: int cpos = 0;
1949: int csize = 0;
1950: for (int off = 0; off < endoff;) {
1951: if (cpos >= csize) {
1952: cpos = 0;
1953: csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1954: s.getChars(off, off + csize, cbuf, 0);
1955: }
1956: if (pos >= MAX_BLOCK_SIZE) {
1957: drain();
1958: }
1959: int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1960: int stop = pos + n;
1961: while (pos < stop) {
1962: buf[pos++] = (byte) cbuf[cpos++];
1963: }
1964: off += n;
1965: }
1966: }
1967:
1968: public void writeChars(String s) throws IOException {
1969: int endoff = s.length();
1970: for (int off = 0; off < endoff;) {
1971: int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1972: s.getChars(off, off + csize, cbuf, 0);
1973: writeChars(cbuf, 0, csize);
1974: off += csize;
1975: }
1976: }
1977:
1978: public void writeUTF(String s) throws IOException {
1979: writeUTF(s, getUTFLength(s));
1980: }
1981:
1982: /* -------------- primitive data array output methods -------------- */
1983: /*
1984: * The following methods write out spans of primitive data values.
1985: * Though equivalent to calling the corresponding primitive write
1986: * methods repeatedly, these methods are optimized for writing groups
1987: * of primitive data values more efficiently.
1988: */
1989:
1990: void writeBooleans(boolean[] v, int off, int len)
1991: throws IOException {
1992: int endoff = off + len;
1993: while (off < endoff) {
1994: if (pos >= MAX_BLOCK_SIZE) {
1995: drain();
1996: }
1997: int stop = Math.min(endoff, off
1998: + (MAX_BLOCK_SIZE - pos));
1999: while (off < stop) {
2000: Bits.putBoolean(buf, pos++, v[off++]);
2001: }
2002: }
2003: }
2004:
2005: void writeChars(char[] v, int off, int len) throws IOException {
2006: int limit = MAX_BLOCK_SIZE - 2;
2007: int endoff = off + len;
2008: while (off < endoff) {
2009: if (pos <= limit) {
2010: int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2011: int stop = Math.min(endoff, off + avail);
2012: while (off < stop) {
2013: Bits.putChar(buf, pos, v[off++]);
2014: pos += 2;
2015: }
2016: } else {
2017: dout.writeChar(v[off++]);
2018: }
2019: }
2020: }
2021:
2022: void writeShorts(short[] v, int off, int len)
2023: throws IOException {
2024: int limit = MAX_BLOCK_SIZE - 2;
2025: int endoff = off + len;
2026: while (off < endoff) {
2027: if (pos <= limit) {
2028: int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2029: int stop = Math.min(endoff, off + avail);
2030: while (off < stop) {
2031: Bits.putShort(buf, pos, v[off++]);
2032: pos += 2;
2033: }
2034: } else {
2035: dout.writeShort(v[off++]);
2036: }
2037: }
2038: }
2039:
2040: void writeInts(int[] v, int off, int len) throws IOException {
2041: int limit = MAX_BLOCK_SIZE - 4;
2042: int endoff = off + len;
2043: while (off < endoff) {
2044: if (pos <= limit) {
2045: int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2046: int stop = Math.min(endoff, off + avail);
2047: while (off < stop) {
2048: Bits.putInt(buf, pos, v[off++]);
2049: pos += 4;
2050: }
2051: } else {
2052: dout.writeInt(v[off++]);
2053: }
2054: }
2055: }
2056:
2057: void writeFloats(float[] v, int off, int len)
2058: throws IOException {
2059: int limit = MAX_BLOCK_SIZE - 4;
2060: int endoff = off + len;
2061: while (off < endoff) {
2062: if (pos <= limit) {
2063: int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2064: int chunklen = Math.min(endoff - off, avail);
2065: floatsToBytes(v, off, buf, pos, chunklen);
2066: off += chunklen;
2067: pos += chunklen << 2;
2068: } else {
2069: dout.writeFloat(v[off++]);
2070: }
2071: }
2072: }
2073:
2074: void writeLongs(long[] v, int off, int len) throws IOException {
2075: int limit = MAX_BLOCK_SIZE - 8;
2076: int endoff = off + len;
2077: while (off < endoff) {
2078: if (pos <= limit) {
2079: int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2080: int stop = Math.min(endoff, off + avail);
2081: while (off < stop) {
2082: Bits.putLong(buf, pos, v[off++]);
2083: pos += 8;
2084: }
2085: } else {
2086: dout.writeLong(v[off++]);
2087: }
2088: }
2089: }
2090:
2091: void writeDoubles(double[] v, int off, int len)
2092: throws IOException {
2093: int limit = MAX_BLOCK_SIZE - 8;
2094: int endoff = off + len;
2095: while (off < endoff) {
2096: if (pos <= limit) {
2097: int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2098: int chunklen = Math.min(endoff - off, avail);
2099: doublesToBytes(v, off, buf, pos, chunklen);
2100: off += chunklen;
2101: pos += chunklen << 3;
2102: } else {
2103: dout.writeDouble(v[off++]);
2104: }
2105: }
2106: }
2107:
2108: /**
2109: * Returns the length in bytes of the UTF encoding of the given string.
2110: */
2111: long getUTFLength(String s) {
2112: int len = s.length();
2113: long utflen = 0;
2114: for (int off = 0; off < len;) {
2115: int csize = Math.min(len - off, CHAR_BUF_SIZE);
2116: s.getChars(off, off + csize, cbuf, 0);
2117: for (int cpos = 0; cpos < csize; cpos++) {
2118: char c = cbuf[cpos];
2119: if (c >= 0x0001 && c <= 0x007F) {
2120: utflen++;
2121: } else if (c > 0x07FF) {
2122: utflen += 3;
2123: } else {
2124: utflen += 2;
2125: }
2126: }
2127: off += csize;
2128: }
2129: return utflen;
2130: }
2131:
2132: /**
2133: * Writes the given string in UTF format. This method is used in
2134: * situations where the UTF encoding length of the string is already
2135: * known; specifying it explicitly avoids a prescan of the string to
2136: * determine its UTF length.
2137: */
2138: void writeUTF(String s, long utflen) throws IOException {
2139: if (utflen > 0xFFFFL) {
2140: throw new UTFDataFormatException();
2141: }
2142: writeShort((int) utflen);
2143: if (utflen == (long) s.length()) {
2144: writeBytes(s);
2145: } else {
2146: writeUTFBody(s);
2147: }
2148: }
2149:
2150: /**
2151: * Writes given string in "long" UTF format. "Long" UTF format is
2152: * identical to standard UTF, except that it uses an 8 byte header
2153: * (instead of the standard 2 bytes) to convey the UTF encoding length.
2154: */
2155: void writeLongUTF(String s) throws IOException {
2156: writeLongUTF(s, getUTFLength(s));
2157: }
2158:
2159: /**
2160: * Writes given string in "long" UTF format, where the UTF encoding
2161: * length of the string is already known.
2162: */
2163: void writeLongUTF(String s, long utflen) throws IOException {
2164: writeLong(utflen);
2165: if (utflen == (long) s.length()) {
2166: writeBytes(s);
2167: } else {
2168: writeUTFBody(s);
2169: }
2170: }
2171:
2172: /**
2173: * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2174: * 8-byte length header) of the UTF encoding for the given string.
2175: */
2176: private void writeUTFBody(String s) throws IOException {
2177: int limit = MAX_BLOCK_SIZE - 3;
2178: int len = s.length();
2179: for (int off = 0; off < len;) {
2180: int csize = Math.min(len - off, CHAR_BUF_SIZE);
2181: s.getChars(off, off + csize, cbuf, 0);
2182: for (int cpos = 0; cpos < csize; cpos++) {
2183: char c = cbuf[cpos];
2184: if (pos <= limit) {
2185: if (c <= 0x007F && c != 0) {
2186: buf[pos++] = (byte) c;
2187: } else if (c > 0x07FF) {
2188: buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2189: buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2190: buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2191: pos += 3;
2192: } else {
2193: buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2194: buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2195: pos += 2;
2196: }
2197: } else { // write one byte at a time to normalize block
2198: if (c <= 0x007F && c != 0) {
2199: write(c);
2200: } else if (c > 0x07FF) {
2201: write(0xE0 | ((c >> 12) & 0x0F));
2202: write(0x80 | ((c >> 6) & 0x3F));
2203: write(0x80 | ((c >> 0) & 0x3F));
2204: } else {
2205: write(0xC0 | ((c >> 6) & 0x1F));
2206: write(0x80 | ((c >> 0) & 0x3F));
2207: }
2208: }
2209: }
2210: off += csize;
2211: }
2212: }
2213: }
2214:
2215: /**
2216: * Lightweight identity hash table which maps objects to integer handles,
2217: * assigned in ascending order.
2218: */
2219: private static class HandleTable {
2220:
2221: /* number of mappings in table/next available handle */
2222: private int size;
2223: /* size threshold determining when to expand hash spine */
2224: private int threshold;
2225: /* factor for computing size threshold */
2226: private final float loadFactor;
2227: /* maps hash value -> candidate handle value */
2228: private int[] spine;
2229: /* maps handle value -> next candidate handle value */
2230: private int[] next;
2231: /* maps handle value -> associated object */
2232: private Object[] objs;
2233:
2234: /**
2235: * Creates new HandleTable with given capacity and load factor.
2236: */
2237: HandleTable(int initialCapacity, float loadFactor) {
2238: this .loadFactor = loadFactor;
2239: spine = new int[initialCapacity];
2240: next = new int[initialCapacity];
2241: objs = new Object[initialCapacity];
2242: threshold = (int) (initialCapacity * loadFactor);
2243: clear();
2244: }
2245:
2246: /**
2247: * Assigns next available handle to given object, and returns handle
2248: * value. Handles are assigned in ascending order starting at 0.
2249: */
2250: int assign(Object obj) {
2251: if (size >= next.length) {
2252: growEntries();
2253: }
2254: if (size >= threshold) {
2255: growSpine();
2256: }
2257: insert(obj, size);
2258: return size++;
2259: }
2260:
2261: /**
2262: * Looks up and returns handle associated with given object, or -1 if
2263: * no mapping found.
2264: */
2265: int lookup(Object obj) {
2266: if (size == 0) {
2267: return -1;
2268: }
2269: int index = hash(obj) % spine.length;
2270: for (int i = spine[index]; i >= 0; i = next[i]) {
2271: if (objs[i] == obj) {
2272: return i;
2273: }
2274: }
2275: return -1;
2276: }
2277:
2278: /**
2279: * Resets table to its initial (empty) state.
2280: */
2281: void clear() {
2282: Arrays.fill(spine, -1);
2283: Arrays.fill(objs, 0, size, null);
2284: size = 0;
2285: }
2286:
2287: /**
2288: * Returns the number of mappings currently in table.
2289: */
2290: int size() {
2291: return size;
2292: }
2293:
2294: /**
2295: * Inserts mapping object -> handle mapping into table. Assumes table
2296: * is large enough to accommodate new mapping.
2297: */
2298: private void insert(Object obj, int handle) {
2299: int index = hash(obj) % spine.length;
2300: objs[handle] = obj;
2301: next[handle] = spine[index];
2302: spine[index] = handle;
2303: }
2304:
2305: /**
2306: * Expands the hash "spine" -- equivalent to increasing the number of
2307: * buckets in a conventional hash table.
2308: */
2309: private void growSpine() {
2310: spine = new int[(spine.length << 1) + 1];
2311: threshold = (int) (spine.length * loadFactor);
2312: Arrays.fill(spine, -1);
2313: for (int i = 0; i < size; i++) {
2314: insert(objs[i], i);
2315: }
2316: }
2317:
2318: /**
2319: * Increases hash table capacity by lengthening entry arrays.
2320: */
2321: private void growEntries() {
2322: int newLength = (next.length << 1) + 1;
2323: int[] newNext = new int[newLength];
2324: System.arraycopy(next, 0, newNext, 0, size);
2325: next = newNext;
2326:
2327: Object[] newObjs = new Object[newLength];
2328: System.arraycopy(objs, 0, newObjs, 0, size);
2329: objs = newObjs;
2330: }
2331:
2332: /**
2333: * Returns hash value for given object.
2334: */
2335: private int hash(Object obj) {
2336: return System.identityHashCode(obj) & 0x7FFFFFFF;
2337: }
2338: }
2339:
2340: /**
2341: * Lightweight identity hash table which maps objects to replacement
2342: * objects.
2343: */
2344: private static class ReplaceTable {
2345:
2346: /* maps object -> index */
2347: private final HandleTable htab;
2348: /* maps index -> replacement object */
2349: private Object[] reps;
2350:
2351: /**
2352: * Creates new ReplaceTable with given capacity and load factor.
2353: */
2354: ReplaceTable(int initialCapacity, float loadFactor) {
2355: htab = new HandleTable(initialCapacity, loadFactor);
2356: reps = new Object[initialCapacity];
2357: }
2358:
2359: /**
2360: * Enters mapping from object to replacement object.
2361: */
2362: void assign(Object obj, Object rep) {
2363: int index = htab.assign(obj);
2364: while (index >= reps.length) {
2365: grow();
2366: }
2367: reps[index] = rep;
2368: }
2369:
2370: /**
2371: * Looks up and returns replacement for given object. If no
2372: * replacement is found, returns the lookup object itself.
2373: */
2374: Object lookup(Object obj) {
2375: int index = htab.lookup(obj);
2376: return (index >= 0) ? reps[index] : obj;
2377: }
2378:
2379: /**
2380: * Resets table to its initial (empty) state.
2381: */
2382: void clear() {
2383: Arrays.fill(reps, 0, htab.size(), null);
2384: htab.clear();
2385: }
2386:
2387: /**
2388: * Returns the number of mappings currently in table.
2389: */
2390: int size() {
2391: return htab.size();
2392: }
2393:
2394: /**
2395: * Increases table capacity.
2396: */
2397: private void grow() {
2398: Object[] newReps = new Object[(reps.length << 1) + 1];
2399: System.arraycopy(reps, 0, newReps, 0, reps.length);
2400: reps = newReps;
2401: }
2402: }
2403:
2404: /**
2405: * Stack to keep debug information about the state of the
2406: * serialization process, for embedding in exception messages.
2407: */
2408: private static class DebugTraceInfoStack {
2409: private final List<String> stack;
2410:
2411: DebugTraceInfoStack() {
2412: stack = new ArrayList<String>();
2413: }
2414:
2415: /**
2416: * Removes all of the elements from enclosed list.
2417: */
2418: void clear() {
2419: stack.clear();
2420: }
2421:
2422: /**
2423: * Removes the object at the top of enclosed list.
2424: */
2425: void pop() {
2426: stack.remove(stack.size() - 1);
2427: }
2428:
2429: /**
2430: * Pushes a String onto the top of enclosed list.
2431: */
2432: void push(String entry) {
2433: stack.add("\t- " + entry);
2434: }
2435:
2436: /**
2437: * Returns a string representation of this object
2438: */
2439: public String toString() {
2440: StringBuilder buffer = new StringBuilder();
2441: if (!stack.isEmpty()) {
2442: for (int i = stack.size(); i > 0; i--) {
2443: buffer.append(stack.get(i - 1)
2444: + ((i != 1) ? "\n" : ""));
2445: }
2446: }
2447: return buffer.toString();
2448: }
2449: }
2450:
2451: }
|