- /*
- * @(#)ObjectStreamClass.java 1.35 03/01/23
- *
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- /*
- * Licensed Materials - Property of IBM
- * RMI-IIOP v1.0
- * Copyright IBM Corp. 1998 1999 All Rights Reserved
- *
- * US Government Users Restricted Rights - Use, duplication or
- * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
-
- package com.sun.corba.se.internal.io;
-
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import java.security.DigestOutputStream;
- import java.security.AccessController;
- import java.security.PrivilegedExceptionAction;
- import java.security.PrivilegedActionException;
- import java.security.PrivilegedAction;
-
- import java.lang.reflect.Modifier;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.Member;
- import java.lang.reflect.Method;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.InvocationTargetException;
-
- import java.io.IOException;
- import java.io.DataOutputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.InvalidClassException;
- import java.io.Serializable;
-
- import java.util.Arrays;
- import java.util.Comparator;
- import java.util.Hashtable;
-
-
- import org.omg.CORBA.ValueMember;
-
-
- /**
- * A ObjectStreamClass describes a class that can be serialized to a stream
- * or a class that was serialized to a stream. It contains the name
- * and the serialVersionUID of the class.
- * <br>
- * The ObjectStreamClass for a specific class loaded in this Java VM can
- * be found using the lookup method.
- *
- * @author Roger Riggs
- * @(#)ObjectStreamClass.java 1.17 99/06/07
- * @since JDK1.1
- */
- public class ObjectStreamClass implements java.io.Serializable {
-
- public static final long kDefaultUID = -1;
-
- private static Object noArgsList[] = {};
- private static Class noTypesList[] = {};
-
- private static Hashtable translatedFields;
-
- static {
- try{
- Class.forName("com.sun.corba.se.internal.io.LibraryManager").getDeclaredMethod("load",null).invoke(null,null);
- }
- catch(Throwable t){}
- }
-
- /** Find the descriptor for a class that can be serialized. Null
- * is returned if the specified class does not implement
- * java.io.Serializable or java.io.Externalizable.
- */
- static final ObjectStreamClass lookup(Class cl)
- {
- ObjectStreamClass desc = lookupInternal(cl);
- if (desc.isSerializable() || desc.isExternalizable())
- return desc;
- return null;
- }
-
- /*
- * Find the class descriptor for the specified class.
- * Package access only so it can be called from ObjectIn/OutStream.
- */
- static ObjectStreamClass lookupInternal(Class cl)
- {
- /* Synchronize on the hashtable so no two threads will do
- * this at the same time.
- */
- ObjectStreamClass desc = null;
- synchronized (descriptorFor) {
- /* Find the matching descriptor if it already known */
- desc = findDescriptorFor(cl);
- if (desc == null) {
- /* Check if it's serializable */
- boolean serializable = classSerializable.isAssignableFrom(cl);
-
- /* If the class is only Serializable,
- * lookup the descriptor for the superclass.
- */
- ObjectStreamClass superdesc = null;
- if (serializable) {
- Class superclass = cl.getSuperclass();
- if (superclass != null)
- superdesc = lookup(superclass);
- }
-
- /* Check if its' externalizable.
- * If it's Externalizable, clear the serializable flag.
- * Only one or the other may be set in the protocol.
- */
- boolean externalizable = false;
- if (serializable) {
- externalizable =
- ((superdesc != null) && superdesc.isExternalizable()) ||
- classExternalizable.isAssignableFrom(cl);
- if (externalizable) {
- serializable = false;
- }
- }
-
- /* Create a new version descriptor,
- * it put itself in the known table.
- */
- desc = new ObjectStreamClass(cl, superdesc,
- serializable, externalizable);
- }
- }
-
- // Must always call init. See bug 4488137. This code was
- // incorrectly changed to return immediately on a non-null
- // cache result. That allowed threads to gain access to
- // unintialized instances.
- //
- // All threads must sync on the member variable lock
- // and check the initialization state.
- //
- // An alternative is to continue to synchronize on the
- // descriptorFor array, but that leads to poor performance
- // (see bug 4165204 "ObjectStreamClass can hold global lock
- // for a very long time").
- desc.init();
-
- return desc;
- }
-
- /**
- * The name of the class described by this descriptor.
- */
- public final String getName() {
- return name;
- }
-
- /**
- * Return the serialVersionUID for this class.
- * The serialVersionUID defines a set of classes all with the same name
- * that have evolved from a common root class and agree to be serialized
- * and deserialized using a common format.
- */
- public static final long getSerialVersionUID( java.lang.Class clazz) {
- ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz );
- if( theosc != null )
- {
- return theosc.getSerialVersionUID( );
- }
- return 0;
- }
-
- /**
- * Return the serialVersionUID for this class.
- * The serialVersionUID defines a set of classes all with the same name
- * that have evolved from a common root class and agree to be serialized
- * and deserialized using a common format.
- */
- public final long getSerialVersionUID() {
- return suid;
- }
-
- /**
- * Return the serialVersionUID string for this class.
- * The serialVersionUID defines a set of classes all with the same name
- * that have evolved from a common root class and agree to be serialized
- * and deserialized using a common format.
- */
- public final String getSerialVersionUIDStr() {
- if (suidStr == null)
- suidStr = Long.toHexString(suid).toUpperCase();
- return suidStr;
- }
-
- /**
- * Return the actual (computed) serialVersionUID for this class.
- */
- public static final long getActualSerialVersionUID( java.lang.Class clazz )
- {
- ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz );
- if( theosc != null )
- {
- return theosc.getActualSerialVersionUID( );
- }
- return 0;
- }
-
- /**
- * Return the actual (computed) serialVersionUID for this class.
- */
- public final long getActualSerialVersionUID() {
- return actualSuid;
- }
-
- /**
- * Return the actual (computed) serialVersionUID for this class.
- */
- public final String getActualSerialVersionUIDStr() {
- if (actualSuidStr == null)
- actualSuidStr = Long.toHexString(actualSuid).toUpperCase();
- return actualSuidStr;
- }
-
- /**
- * Return the class in the local VM that this version is mapped to.
- * Null is returned if there is no corresponding local class.
- */
- public final Class forClass() {
- return ofClass;
- }
-
- /**
- * Return an array of the fields of this serializable class.
- * @return an array containing an element for each persistent
- * field of this class. Returns an array of length zero if
- * there are no fields.
- * @since JDK1.2
- */
- public ObjectStreamField[] getFields() {
- // Return a copy so the caller can't change the fields.
- if (fields.length > 0) {
- ObjectStreamField[] dup = new ObjectStreamField[fields.length];
- System.arraycopy(fields, 0, dup, 0, fields.length);
- return dup;
- } else {
- return fields;
- }
- }
-
- public boolean hasField(ValueMember field){
-
- for (int i = 0; i < fields.length; i++){
- try{
- if (fields[i].getName().equals(field.name)) {
-
- if (fields[i].getSignature().equals(ValueUtility.getSignature(field)))
- return true;
- }
- }
- catch(Throwable t){}
- }
- return false;
- }
-
- /* Avoid unnecessary allocations. */
- final ObjectStreamField[] getFieldsNoCopy() {
- return fields;
- }
-
- /**
- * Get the field of this class by name.
- * @return The ObjectStreamField object of the named field or null if there
- * is no such named field.
- */
- public final ObjectStreamField getField(String name) {
- /* Binary search of fields by name.
- */
- for (int i = fields.length-1; i >= 0; i--) {
- if (name.equals(fields[i].getName())) {
- return fields[i];
- }
- }
- return null;
- }
-
- public Serializable writeReplace(Serializable value) {
- if (writeReplaceObjectMethod != null) {
- try {
- return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList);
- }
- catch(Throwable t) {
- throw new RuntimeException(t.getMessage());
- }
- }
- else return value;
- }
-
- public Object readResolve(Object value) {
- if (readResolveObjectMethod != null) {
- try {
- return readResolveObjectMethod.invoke(value,noArgsList);
- }
- catch(Throwable t) {
- throw new RuntimeException(t.getMessage());
- }
- }
- else return value;
- }
-
- /**
- * Return a string describing this ObjectStreamClass.
- */
- public final String toString() {
- StringBuffer sb = new StringBuffer();
-
- sb.append(name);
- sb.append(": static final long serialVersionUID = ");
- sb.append(Long.toString(suid));
- sb.append("L;");
- return sb.toString();
- }
-
- /*
- * Create a new ObjectStreamClass from a loaded class.
- * Don't call this directly, call lookup instead.
- */
- private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc,
- boolean serial, boolean extern)
- {
- ofClass = cl; /* created from this class */
-
- if (Proxy.isProxyClass(cl)) {
- forProxyClass = true;
- }
-
- name = cl.getName();
- superclass = superdesc;
- serializable = serial;
- if (!forProxyClass) {
- // proxy classes are never externalizable
- externalizable = extern;
- }
-
- /*
- * Enter this class in the table of known descriptors.
- * Otherwise, when the fields are read it may recurse
- * trying to find the descriptor for itself.
- */
- insertDescriptorFor(this);
-
- /*
- * The remainder of initialization occurs in init(), which is called
- * after the lock on the global class descriptor table has been
- * released.
- */
- }
-
- /*
- * Initialize class descriptor. This method is only invoked on class
- * descriptors created via calls to lookupInternal(). This method is kept
- * separate from the ObjectStreamClass constructor so that lookupInternal
- * does not have to hold onto a global class descriptor table lock while the
- * class descriptor is being initialized (see bug 4165204).
- */
-
-
- private void init() {
- synchronized (lock) {
-
- // See description at definition of initialized.
- if (initialized)
- return;
-
- final Class cl = ofClass;
-
- if (!serializable ||
- externalizable ||
- forProxyClass ||
- name.equals("java.lang.String")) {
- fields = NO_FIELDS;
- } else if (serializable) {
-
- /* Ask for permission to override field access checks.
- */
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- /* Fill in the list of persistent fields.
- * If it is declared, use the declared serialPersistentFields.
- * Otherwise, extract the fields from the class itself.
- */
- try {
- Field pf = cl.getDeclaredField("serialPersistentFields");
- // serial bug 7; the serialPersistentFields were not
- // being read and stored as Accessible bit was not set
- pf.setAccessible(true);
- // serial bug 7; need to find if the field is of type
- // java.io.ObjectStreamField
- java.io.ObjectStreamField[] f =
- (java.io.ObjectStreamField[])pf.get(cl);
- int mods = pf.getModifiers();
- if ((Modifier.isPrivate(mods)) &&
- (Modifier.isStatic(mods)) &&
- (Modifier.isFinal(mods)))
- {
- fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl));
- }
- } catch (NoSuchFieldException e) {
- fields = null;
- } catch (IllegalAccessException e) {
- fields = null;
- } catch (IllegalArgumentException e) {
- fields = null;
- } catch (ClassCastException e) {
- /* Thrown if a field serialPersistentField exists
- * but it is not of type ObjectStreamField.
- */
- fields = null;
- }
-
-
- if (fields == null) {
- /* Get all of the declared fields for this
- * Class. setAccessible on all fields so they
- * can be accessed later. Create a temporary
- * ObjectStreamField array to hold each
- * non-static, non-transient field. Then copy the
- * temporary array into an array of the correct
- * size once the number of fields is known.
- */
- Field[] actualfields = cl.getDeclaredFields();
-
- int numFields = 0;
- ObjectStreamField[] tempFields =
- new ObjectStreamField[actualfields.length];
- for (int i = 0; i < actualfields.length; i++) {
- int modifiers = actualfields[i].getModifiers();
- if (!Modifier.isStatic(modifiers) &&
- !Modifier.isTransient(modifiers)) {
- tempFields[numFields++] =
- new com.sun.corba.se.internal.io.ObjectStreamField(actualfields[i]);
- }
- }
-
- fields = new ObjectStreamField[numFields];
- System.arraycopy(tempFields, 0, fields, 0, numFields);
-
- } else {
- // For each declared persistent field, look for an actual
- // reflected Field. If there is one, make sure it's the correct
- // type and cache it in the ObjectStreamClass for that field.
- for (int j = fields.length-1; j >= 0; j--) {
- try {
- Field reflField = cl.getDeclaredField(fields[j].getName());
- if (fields[j].getType() == reflField.getType()) {
- // reflField.setAccessible(true);
- fields[j].setField(reflField);
- }
- } catch (NoSuchFieldException e) {
- // Nothing to do
- }
- }
- }
- return null;
- }
- });
-
- if (fields.length > 1)
- Arrays.sort(fields);
-
- /* Set up field data for use while writing using the API api. */
- computeFieldInfo();
- }
-
- /* Get the serialVersionUID from the class.
- * It uses the access override mechanism so make sure
- * the field objects is only used here.
- *
- * NonSerializable classes have a serialVerisonUID of 0L.
- */
- if (isNonSerializable()) {
- suid = 0L;
- } else {
- // Lookup special Serializable members using reflection.
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- if (forProxyClass) {
- // proxy classes always have serialVersionUID of 0L
- suid = 0L;
- } else {
- try {
- final Field f = cl.getDeclaredField("serialVersionUID");
- int mods = f.getModifiers();
- // SerialBug 5: static final SUID should be read
- if (Modifier.isStatic(mods) &&
- Modifier.isFinal(mods) ) {
- f.setAccessible(true);
- suid = f.getLong(cl);
- // get rid of native code
- // suid = getSerialVersionUIDField(cl);
- // SerialBug 2: should be computed after writeObject
- // actualSuid = computeStructuralUID(cl);
- } else {
- suid = _computeSerialVersionUID(cl);
- // SerialBug 2: should be computed after writeObject
- // actualSuid = computeStructuralUID(cl);
- }
- } catch (NoSuchFieldException ex) {
- suid = _computeSerialVersionUID(cl);
- // SerialBug 2: should be computed after writeObject
- // actualSuid = computeStructuralUID(cl);
- } catch (IllegalAccessException ex) {
- suid = _computeSerialVersionUID(cl);
- }
- }
-
- writeReplaceObjectMethod
- = ObjectStreamClass.getInheritableMethod(cl,
- "writeReplace",
- noTypesList,
- Object.class);
-
- readResolveObjectMethod
- = ObjectStreamClass.getInheritableMethod(cl,
- "readResolve",
- noTypesList,
- Object.class);
-
- /* Cache lookup of writeObject and readObject for
- * Serializable classes. (Do not lookup for
- * Externalizable)
- */
-
- if (serializable && !forProxyClass) {
-
- /* Look for the writeObject method
- * Set the accessible flag on it here. ObjectOutputStream
- * will call it as necessary.
- */
- try {
- Class[] args = {java.io.ObjectOutputStream.class};
- writeObjectMethod = cl.getDeclaredMethod("writeObject", args);
- hasWriteObjectMethod = true;
- int mods = writeObjectMethod.getModifiers();
-
- // Method must be private and non-static
- if (!Modifier.isPrivate(mods) ||
- Modifier.isStatic(mods)) {
- writeObjectMethod = null;
- hasWriteObjectMethod = false;
- }
-
- } catch (NoSuchMethodException e) {
- }
-
- /* Look for the readObject method
- * set the access override and save the reference for
- * ObjectInputStream so it can all the method directly.
- */
- try {
- Class[] args = {java.io.ObjectInputStream.class};
- readObjectMethod = cl.getDeclaredMethod("readObject", args);
- int mods = readObjectMethod.getModifiers();
-
- // Method must be private and non-static
- if (!Modifier.isPrivate(mods) ||
- Modifier.isStatic(mods)) {
- readObjectMethod = null;
- }
- } catch (NoSuchMethodException e) {
- }
- // Compute the structural UID. This must be done after the
- // calculation for writeObject. Fixed 4/20/2000, eea1
- // SerialBug 2: to have correct value in RepId
- }
- return null;
- }
- });
- }
-
- // This call depends on a lot of information computed above!
- actualSuid = ObjectStreamClass.computeStructuralUID(this, cl);
-
- // This must be done last.
- initialized = true;
- }
- }
-
- /*
- * Create an empty ObjectStreamClass for a class about to be read.
- * This is separate from read so ObjectInputStream can assign the
- * wire handle early, before any nested ObjectStreamClass might
- * be read.
- */
- ObjectStreamClass(String n, long s) {
- name = n;
- suid = s;
- superclass = null;
- }
-
- private static Object[] translateFields(Object objs[])
- throws NoSuchFieldException {
- try{
- java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs;
- Object translation[] = null;
-
- if (translatedFields == null)
- translatedFields = new Hashtable();
-
- translation = (Object[])translatedFields.get(fields);
-
- if (translation != null)
- return translation;
- else {
- Class osfClass = Class.forName("com.sun.corba.se.internal.io.ObjectStreamField");
- translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length);
- Object arg[] = new Object[2];
- Class types[] = {String.class, Class.class};
- Constructor constructor = osfClass.getDeclaredConstructor(types);
- for (int i = fields.length -1; i >= 0; i--){
- arg[0] = fields[i].getName();
- arg[1] = fields[i].getType();
-
- translation[i] = constructor.newInstance(arg);
- }
- translatedFields.put(fields, translation);
-
- }
-
- return (Object[])translation;
- }
- catch(Throwable t){
- throw new NoSuchFieldException();
- }
- }
-
- /*
- * Set the class this version descriptor matches.
- * The base class name and serializable hash must match.
- * Fill in the reflected Fields that will be used
- * for reading.
- */
- final void setClass(Class cl) throws InvalidClassException {
-
- if (cl == null) {
- localClassDesc = null;
- ofClass = null;
- computeFieldInfo();
- return;
- }
-
- localClassDesc = lookupInternal(cl);
- if (localClassDesc == null)
- throw new InvalidClassException(cl.getName(),
- "Local class not compatible");
- if (suid != localClassDesc.suid) {
-
- /* Check for exceptional cases that allow mismatched suid. */
-
- /* Allow adding Serializable or Externalizable
- * to a later release of the class.
- */
- boolean addedSerialOrExtern =
- isNonSerializable() || localClassDesc.isNonSerializable();
-
- /* Disregard the serialVersionUID of an array
- * when name and cl.Name differ. If resolveClass() returns
- * an array with a different package name,
- * the serialVersionUIDs will not match since the fully
- * qualified array class is used in the
- * computation of the array's serialVersionUID. There is
- * no way to set a permanent serialVersionUID for an array type.
- */
-
- boolean arraySUID = (cl.isArray() && ! cl.getName().equals(name));
-
- if (! arraySUID && ! addedSerialOrExtern ) {
- throw new InvalidClassException(cl.getName(),
- "Local class not compatible:" +
- " stream classdesc serialVersionUID=" + suid +
- " local class serialVersionUID=" + localClassDesc.suid);
- }
- }
-
- /* compare the class names, stripping off package names. */
- if (! compareClassNames(name, cl.getName(), '.'))
- throw new InvalidClassException(cl.getName(),
- "Incompatible local class name. " +
- "Expected class name compatible with " +
- name);
-
- /*
- * Test that both implement either serializable or externalizable.
- */
-
- // The next check is more generic, since it covers the
- // Proxy case, the JDK 1.3 serialization code has
- // both checks
- //if ((serializable && localClassDesc.externalizable) ||
- // (externalizable && localClassDesc.serializable))
- // throw new InvalidClassException(localCl.getName(),
- // "Serializable is incompatible with Externalizable");
-
- if ((serializable != localClassDesc.serializable) ||
- (externalizable != localClassDesc.externalizable) ||
- (!serializable && !externalizable))
-
- throw new InvalidClassException(cl.getName(),
- "Serialization incompatible with Externalization");
-
- /* Set up the reflected Fields in the class where the value of each
- * field in this descriptor should be stored.
- * Each field in this ObjectStreamClass (the source) is located (by
- * name) in the ObjectStreamClass of the class(the destination).
- * In the usual (non-versioned case) the field is in both
- * descriptors and the types match, so the reflected Field is copied.
- * If the type does not match, a InvalidClass exception is thrown.
- * If the field is not present in the class, the reflected Field
- * remains null so the field will be read but discarded.
- * If extra fields are present in the class they are ignored. Their
- * values will be set to the default value by the object allocator.
- * Both the src and dest field list are sorted by type and name.
- */
-
- ObjectStreamField[] destfield =
- (ObjectStreamField[])localClassDesc.fields;
- ObjectStreamField[] srcfield =
- (ObjectStreamField[])fields;
-
- int j = 0;
- nextsrc:
- for (int i = 0; i < srcfield.length; i++ ) {
- /* Find this field in the dest*/
- for (int k = j; k < destfield.length; k++) {
- if (srcfield[i].getName().equals(destfield[k].getName())) {
- /* found match */
- if (srcfield[i].isPrimitive() &&
- !srcfield[i].typeEquals(destfield[k])) {
- throw new InvalidClassException(cl.getName(),
- "The type of field " +
- srcfield[i].getName() +
- " of class " + name +
- " is incompatible.");
- }
-
- /* Skip over any fields in the dest that are not in the src */
- j = k;
-
- srcfield[i].setField(destfield[j].getField());
- // go on to the next source field
- continue nextsrc;
- }
- }
- }
-
- /* Set up field data for use while reading from the input stream. */
- computeFieldInfo();
-
- /* Remember the class this represents */
- ofClass = cl;
-
- /* get the cache of these methods from the local class
- * implementation.
- */
- readObjectMethod = localClassDesc.readObjectMethod;
- readResolveObjectMethod = localClassDesc.readResolveObjectMethod;
-
- /* Look for the readObject method
- * set the access override and save the reference for
- * ObjectInputStream so it can all the method directly.
- */
- /*****
- try {
- Class[] args = {java.io.ObjectInputStream.class};
- readObjectMethod = ObjectStreamClass.getDeclaredMethod(cl,"readObject", args);
- int mods = readObjectMethod.getModifiers();
-
- // Method must be private and non-static
- if (!Modifier.isPrivate(mods) ||
- Modifier.isStatic(mods)) {
- readObjectMethod = null;
- }
- } catch (NoSuchMethodException e) {
- }
- *****/
- }
-
- /* Compare the base class names of streamName and localName.
- *
- * @return Return true iff the base class name compare.
- * @parameter streamName Fully qualified class name.
- * @parameter localName Fully qualified class name.
- * @parameter pkgSeparator class names use either '.' or '/'.
- *
- * Only compare base class name to allow package renaming.
- */
- static boolean compareClassNames(String streamName,
- String localName,
- char pkgSeparator) {
- /* compare the class names, stripping off package names. */
- int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
- if (streamNameIndex < 0)
- streamNameIndex = 0;
-
- int localNameIndex = localName.lastIndexOf(pkgSeparator);
- if (localNameIndex < 0)
- localNameIndex = 0;
-
- return streamName.regionMatches(false, streamNameIndex,
- localName, localNameIndex,
- streamName.length() - streamNameIndex);
- }
-
- /*
- * Compare the types of two class descriptors.
- * They match if they have the same class name and suid
- */
- final boolean typeEquals(ObjectStreamClass other) {
- return (suid == other.suid) &&
- compareClassNames(name, other.name, '.');
- }
-
- /*
- * Return the superclass descriptor of this descriptor.
- */
- final void setSuperclass(ObjectStreamClass s) {
- superclass = s;
- }
-
- /*
- * Return the superclass descriptor of this descriptor.
- */
- final ObjectStreamClass getSuperclass() {
- return superclass;
- }
-
- /*
- * Return whether the class has a writeObject method
- */
- final boolean hasWriteObject() {
- return hasWriteObjectMethod;
- }
-
- final boolean isCustomMarshaled() {
- return (hasWriteObject() || isExternalizable());
- }
-
- /*
- * Return true if all instances of 'this' Externalizable class
- * are written in block-data mode from the stream that 'this' was read
- * from. <p>
- *
- * In JDK 1.1, all Externalizable instances are not written
- * in block-data mode.
- * In JDK 1.2, all Externalizable instances, by default, are written
- * in block-data mode and the Externalizable instance is terminated with
- * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
- * instances.
- *
- * IMPLEMENTATION NOTE:
- * This should have been a mode maintained per stream; however,
- * for compatibility reasons, it was only possible to record
- * this change per class. All Externalizable classes within
- * a given stream should either have this mode enabled or
- * disabled. This is enforced by not allowing the PROTOCOL_VERSION
- * of a stream to he changed after any objects have been written.
- *
- * @see ObjectOuputStream#useProtocolVersion
- * @see ObjectStreamConstants#PROTOCOL_VERSION_1
- * @see ObjectStreamConstants#PROTOCOL_VERSION_2
- *
- * @since JDK 1.2
- */
- boolean hasExternalizableBlockDataMode() {
- return hasExternalizableBlockData;
- }
-
- /*
- * Return the ObjectStreamClass of the local class this one is based on.
- */
- final ObjectStreamClass localClassDescriptor() {
- return localClassDesc;
- }
-
- /*
- * Get the Serializability of the class.
- */
- boolean isSerializable() {
- return serializable;
- }
-
- /*
- * Get the externalizability of the class.
- */
- boolean isExternalizable() {
- return externalizable;
- }
-
- boolean isNonSerializable() {
- return ! (externalizable || serializable);
- }
-
- /*
- * Calculate the size of the array needed to store primitive data and the
- * number of object references to read when reading from the input
- * stream.
- */
- private void computeFieldInfo() {
- primBytes = 0;
- objFields = 0;
-
- for (int i = 0; i < fields.length; i++ ) {
- switch (fields[i].getTypeCode()) {
- case 'B':
- case 'Z':
- primBytes += 1;
- break;
- case 'C':
- case 'S':
- primBytes += 2;
- break;
-
- case 'I':
- case 'F':
- primBytes += 4;
- break;
- case 'J':
- case 'D' :
- primBytes += 8;
- break;
-
- case 'L':
- case '[':
- objFields += 1;
- break;
- }
- }
- }
-
- /*
- * Compute a hash for the specified class. Incrementally add
- * items to the hash accumulating in the digest stream.
- * Fold the hash into a long. Use the SHA secure hash function.
- */
- private static long _computeSerialVersionUID(Class cl) {
- ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
-
- long h = 0;
- try {
- MessageDigest md = MessageDigest.getInstance("SHA");
- DigestOutputStream mdo = new DigestOutputStream(devnull, md);
- DataOutputStream data = new DataOutputStream(mdo);
-
-
- data.writeUTF(cl.getName());
-
- int classaccess = cl.getModifiers();
- classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
- Modifier.INTERFACE | Modifier.ABSTRACT);
-
- /* Workaround for javac bug that only set ABSTRACT for
- * interfaces if the interface had some methods.
- * The ABSTRACT bit reflects that the number of methods > 0.
- * This is required so correct hashes can be computed
- * for existing class files.
- * Previously this hack was previously present in the VM.
- */
- Method[] method = cl.getDeclaredMethods();
- if ((classaccess & Modifier.INTERFACE) != 0) {
- classaccess &= (~Modifier.ABSTRACT);
- if (method.length > 0) {
- classaccess |= Modifier.ABSTRACT;
- }
- }
-
- data.writeInt(classaccess);
-
- /*
- * Get the list of interfaces supported,
- * Accumulate their names their names in Lexical order
- * and add them to the hash
- */
- if (!cl.isArray()) {
- /* In 1.2fcs, getInterfaces() was modified to return
- * {java.lang.Cloneable, java.io.Serializable} when
- * called on array classes. These values would upset
- * the computation of the hash, so we explicitly omit
- * them from its computation.
- */
-
- Class interfaces[] = cl.getInterfaces();
- Arrays.sort(interfaces, compareClassByName);
-
- for (int i = 0; i < interfaces.length; i++) {
- data.writeUTF(interfaces[i].getName());
- }
- }
-
- /* Sort the field names to get a deterministic order */
- Field[] field = cl.getDeclaredFields();
- Arrays.sort(field, compareMemberByName);
-
- for (int i = 0; i < field.length; i++) {
- Field f = field[i];
-
- /* Include in the hash all fields except those that are
- * private transient and private static.
- */
- int m = f.getModifiers();
- if (Modifier.isPrivate(m) &&
- (Modifier.isTransient(m) || Modifier.isStatic(m)))
- continue;
-
- data.writeUTF(f.getName());
- data.writeInt(m);
- data.writeUTF(getSignature(f.getType()));
- }
-
- if (hasStaticInitializer(cl)) {
- data.writeUTF("<clinit>");
- data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have
- data.writeUTF("()V");
- }
-
- /*
- * Get the list of constructors including name and signature
- * Sort lexically, add all except the private constructors
- * to the hash with their access flags
- */
-
- MethodSignature[] constructors =
- MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors());
- for (int i = 0; i < constructors.length; i++) {
- MethodSignature c = constructors[i];
- String mname = "<init>";
- String desc = c.signature;
- desc = desc.replace('/', '.');
- data.writeUTF(mname);
- data.writeInt(c.member.getModifiers());
- data.writeUTF(desc);
- }
-
- /* Include in the hash all methods except those that are
- * private transient and private static.
- */
- MethodSignature[] methods =
- MethodSignature.removePrivateAndSort(method);
- for (int i = 0; i < methods.length; i++ ) {
- MethodSignature m = methods[i];
- String desc = m.signature;
- desc = desc.replace('/', '.');
- data.writeUTF(m.member.getName());
- data.writeInt(m.member.getModifiers());
- data.writeUTF(desc);
- }
-
- /* Compute the hash value for this class.
- * Use only the first 64 bits of the hash.
- */
- data.flush();
- byte hasharray[] = md.digest();
- for (int i = 0; i < Math.min(8, hasharray.length); i++) {
- h += (long)(hasharray[i] & 255) << (i * 8);
- }
- } catch (IOException ignore) {
- /* can't happen, but be deterministic anyway. */
- h = -1;
- } catch (NoSuchAlgorithmException complain) {
- throw new SecurityException(complain.getMessage());
- }
- return h;
- }
-
- private static long computeStructuralUID(com.sun.corba.se.internal.io.ObjectStreamClass osc, Class cl) {
- ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
-
- long h = 0;
- try {
-
- if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
- (cl.isInterface())){
- return 0;
- }
-
- if (java.io.Externalizable.class.isAssignableFrom(cl)) {
- return 1;
- }
-
- MessageDigest md = MessageDigest.getInstance("SHA");
- DigestOutputStream mdo = new DigestOutputStream(devnull, md);
- DataOutputStream data = new DataOutputStream(mdo);
-
- // Get SUID of parent
- Class parent = cl.getSuperclass();
- if ((parent != null))
- // SerialBug 1; acc. to spec the one for
- // java.lang.object
- // should be computed and put
- // && (parent != java.lang.Object.class))
- {
- //data.writeLong(computeSerialVersionUID(null,parent));
- data.writeLong(computeStructuralUID(lookup(parent), parent));
- }
-
- if (osc.hasWriteObject())
- data.writeInt(2);
- else
- data.writeInt(1);
-
- // CORBA formal 00-11-03 10.6.2: For each field of the
- // class that is mapped to IDL, sorted lexicographically
- // by Java field name, in increasing order...
- ObjectStreamField[] field = osc.getFields();
- if (field.length > 1) {
- Arrays.sort(field, compareObjStrFieldsByName);
- }
-
- // ...Java field name in UTF encoding, field
- // descriptor, as defined by the JVM spec...
- for (int i = 0; i < field.length; i++) {
- data.writeUTF(field[i].getName());
- data.writeUTF(field[i].getSignature());
- }
-
- /* Compute the hash value for this class.
- * Use only the first 64 bits of the hash.
- */
- data.flush();
- byte hasharray[] = md.digest();
- // int minimum = Math.min(8, hasharray.length);
- // SerialBug 3: SHA computation is wrong; for loop reversed
- //for (int i = minimum; i > 0; i--)
- for (int i = 0; i < Math.min(8, hasharray.length); i++) {
- h += (long)(hasharray[i] & 255) << (i * 8);
- }
- } catch (IOException ignore) {
- /* can't happen, but be deterministic anyway. */
- h = -1;
- } catch (NoSuchAlgorithmException complain) {
- throw new SecurityException(complain.getMessage());
- }
- return h;
- }
-
- /**
- * Compute the JVM signature for the class.
- */
- static String getSignature(Class clazz) {
- String type = null;
- if (clazz.isArray()) {
- Class cl = clazz;
- int dimensions = 0;
- while (cl.isArray()) {
- dimensions++;
- cl = cl.getComponentType();
- }
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < dimensions; i++) {
- sb.append("[");
- }
- sb.append(getSignature(cl));
- type = sb.toString();
- } else if (clazz.isPrimitive()) {
- if (clazz == Integer.TYPE) {
- type = "I";
- } else if (clazz == Byte.TYPE) {
- type = "B";
- } else if (clazz == Long.TYPE) {
- type = "J";
- } else if (clazz == Float.TYPE) {
- type = "F";
- } else if (clazz == Double.TYPE) {
- type = "D";
- } else if (clazz == Short.TYPE) {
- type = "S";
- } else if (clazz == Character.TYPE) {
- type = "C";
- } else if (clazz == Boolean.TYPE) {
- type = "Z";
- } else if (clazz == Void.TYPE) {
- type = "V";
- }
- } else {
- type = "L" + clazz.getName().replace('.', '/') + ";";
- }
- return type;
- }
-
- /*
- * Compute the JVM method descriptor for the method.
- */
- static String getSignature(Method meth) {
- StringBuffer sb = new StringBuffer();
-
- sb.append("(");
-
- Class[] params = meth.getParameterTypes(); // avoid clone
- for (int j = 0; j < params.length; j++) {
- sb.append(getSignature(params[j]));
- }
- sb.append(")");
- sb.append(getSignature(meth.getReturnType()));
- return sb.toString();
- }
-
- /*
- * Compute the JVM constructor descriptor for the constructor.
- */
- static String getSignature(Constructor cons) {
- StringBuffer sb = new StringBuffer();
-
- sb.append("(");
-
- Class[] params = cons.getParameterTypes(); // avoid clone
- for (int j = 0; j < params.length; j++) {
- sb.append(getSignature(params[j]));
- }
- sb.append(")V");
- return sb.toString();
- }
-
- /*
- * Cache of Class -> ClassDescriptor Mappings.
- */
- static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
-
- /*
- * findDescriptorFor a Class. This looks in the cache for a
- * mapping from Class -> ObjectStreamClass mappings. The hashCode
- * of the Class is used for the lookup since the Class is the key.
- * The entries are extended from java.lang.ref.SoftReference so the
- * gc will be able to free them if needed.
- */
- private static ObjectStreamClass findDescriptorFor(Class cl) {
-
- int hash = cl.hashCode();
- int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
- ObjectStreamClassEntry e;
- ObjectStreamClassEntry prev;
-
- /* Free any initial entries whose refs have been cleared */
- while ((e = descriptorFor[index]) != null && e.get() == null) {
- descriptorFor[index] = e.next;
- }
-
- /* Traverse the chain looking for a descriptor with ofClass == cl.
- * unlink entries that are unresolved.
- */
- prev = e;
- while (e != null ) {
- ObjectStreamClass desc = (ObjectStreamClass)(e.get());
- if (desc == null) {
- // This entry has been cleared, unlink it
- prev.next = e.next;
- } else {
- if (desc.ofClass == cl)
- return desc;
- prev = e;
- }
- e = e.next;
- }
- return null;
- }
-
- /*
- * insertDescriptorFor a Class -> ObjectStreamClass mapping.
- */
- private static void insertDescriptorFor(ObjectStreamClass desc) {
- // Make sure not already present
- if (findDescriptorFor(desc.ofClass) != null) {
- return;
- }
-
- int hash = desc.ofClass.hashCode();
- int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
- ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
- e.next = descriptorFor[index];
- descriptorFor[index] = e;
- }
-
- private static Field[] getDeclaredFields(final Class clz) {
- return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- return clz.getDeclaredFields();
- }
- });
- }
-
-
- /*
- * The name of this descriptor
- */
- private String name;
-
- /*
- * The descriptor of the supertype.
- */
- private ObjectStreamClass superclass;
-
- /*
- * Flags for Serializable and Externalizable.
- */
- private boolean serializable;
- private boolean externalizable;
-
- /*
- * Array of persistent fields of this class, sorted by
- * type and name.
- */
- private ObjectStreamField[] fields;
-
- /*
- * Class that is a descriptor for in this virtual machine.
- */
- private Class ofClass;
-
- /*
- * True if descriptor for a proxy class.
- */
- boolean forProxyClass;
-
-
- /*
- * SerialVersionUID for this class.
- */
- private long suid = kDefaultUID;
- private String suidStr = null;
-
- /*
- * Actual (computed) SerialVersionUID for this class.
- */
- private long actualSuid = kDefaultUID;
- private String actualSuidStr = null;
-
- /*
- * The total number of bytes of primitive fields.
- * The total number of object fields.
- */
- int primBytes;
- int objFields;
-
- /**
- * Flag indicating whether or not this instance has
- * successfully completed initialization. This is to
- * try to fix bug 4373844. Working to move to
- * reusing java.io.ObjectStreamClass for JDK 1.5.
- */
- private boolean initialized = false;
-
- /* Internal lock object. */
- private Object lock = new Object();
-
- /* True if this class has/had a writeObject method */
- private boolean hasWriteObjectMethod;
-
- /* In JDK 1.1, external data was not written in block mode.
- * As of JDK 1.2, external data is written in block data mode. This
- * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
- *
- * @since JDK 1.2
- */
- private boolean hasExternalizableBlockData;
- Method writeObjectMethod;
- Method readObjectMethod;
- private Method writeReplaceObjectMethod;
- private Method readResolveObjectMethod;
-
- /*
- * ObjectStreamClass that this one was built from.
- */
- private ObjectStreamClass localClassDesc;
-
- /* Find out if the class has a static class initializer <clinit> */
- private static native boolean hasStaticInitializer(Class cl);
-
- /* Get the private static final field for serial version UID */
- // private static native long getSerialVersionUIDField(Class cl);
-
- /* The Class Object for java.io.Serializable */
- private static Class classSerializable = null;
- private static Class classExternalizable = null;
-
- /*
- * Resolve java.io.Serializable at load time.
- */
- static {
- try {
- classSerializable = Class.forName("java.io.Serializable");
- classExternalizable = Class.forName("java.io.Externalizable");
- } catch (Throwable e) {
- System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
- }
- }
-
- /** use serialVersionUID from JDK 1.1. for interoperability */
- private static final long serialVersionUID = -6120832682080437368L;
-
- /**
- * Set serialPersistentFields of a Serializable class to this value to
- * denote that the class has no Serializable fields.
- */
- public static final ObjectStreamField[] NO_FIELDS =
- new ObjectStreamField[0];
-
- /*
- * Entries held in the Cache of known ObjectStreamClass objects.
- * Entries are chained together with the same hash value (modulo array size).
- */
- private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference
- {
- ObjectStreamClassEntry(ObjectStreamClass c) {
- //super(c);
- this.c = c;
- }
- ObjectStreamClassEntry next;
-
- public Object get()
- {
- return c;
- }
- private ObjectStreamClass c;
- }
-
- /*
- * Comparator object for Classes and Interfaces
- */
- private static Comparator compareClassByName =
- new CompareClassByName();
-
- private static class CompareClassByName implements Comparator {
- public int compare(Object o1, Object o2) {
- Class c1 = (Class)o1;
- Class c2 = (Class)o2;
- return (c1.getName()).compareTo(c2.getName());
- }
- }
-
- /**
- * Comparator for ObjectStreamFields by name
- */
- private final static Comparator compareObjStrFieldsByName
- = new CompareObjStrFieldsByName();
-
- private static class CompareObjStrFieldsByName implements Comparator {
- public int compare(Object o1, Object o2) {
- ObjectStreamField osf1 = (ObjectStreamField)o1;
- ObjectStreamField osf2 = (ObjectStreamField)o2;
-
- return osf1.getName().compareTo(osf2.getName());
- }
- }
-
- /*
- * Comparator object for Members, Fields, and Methods
- */
- private static Comparator compareMemberByName =
- new CompareMemberByName();
-
- private static class CompareMemberByName implements Comparator {
- public int compare(Object o1, Object o2) {
- String s1 = ((Member)o1).getName();
- String s2 = ((Member)o2).getName();
-
- if (o1 instanceof Method) {
- s1 += getSignature((Method)o1);
- s2 += getSignature((Method)o2);
- } else if (o1 instanceof Constructor) {
- s1 += getSignature((Constructor)o1);
- s2 += getSignature((Constructor)o2);
- }
- return s1.compareTo(s2);
- }
- }
-
- /* It is expensive to recompute a method or constructor signature
- many times, so compute it only once using this data structure. */
- private static class MethodSignature implements Comparator {
- Member member;
- String signature; // cached parameter signature
-
- /* Given an array of Method or Constructor members,
- return a sorted array of the non-private members.*/
- /* A better implementation would be to implement the returned data
- structure as an insertion sorted link list.*/
- static MethodSignature[] removePrivateAndSort(Member[] m) {
- int numNonPrivate = 0;
- for (int i = 0; i < m.length; i++) {
- if (! Modifier.isPrivate(m[i].getModifiers())) {
- numNonPrivate++;
- }
- }
- MethodSignature[] cm = new MethodSignature[numNonPrivate];
- int cmi = 0;
- for (int i = 0; i < m.length; i++) {
- if (! Modifier.isPrivate(m[i].getModifiers())) {
- cm[cmi] = new MethodSignature(m[i]);
- cmi++;
- }
- }
- if (cmi > 0)
- Arrays.sort(cm, cm[0]);
- return cm;
- }
-
- /* Assumes that o1 and o2 are either both methods
- or both constructors.*/
- public int compare(Object o1, Object o2) {
- /* Arrays.sort calls compare when o1 and o2 are equal.*/
- if (o1 == o2)
- return 0;
-
- MethodSignature c1 = (MethodSignature)o1;
- MethodSignature c2 = (MethodSignature)o2;
-
- int result;
- if (isConstructor()) {
- result = c1.signature.compareTo(c2.signature);
- } else { // is a Method.
- result = c1.member.getName().compareTo(c2.member.getName());
- if (result == 0)
- result = c1.signature.compareTo(c2.signature);
- }
- return result;
- }
-
- final private boolean isConstructor() {
- return member instanceof Constructor;
- }
- private MethodSignature(Member m) {
- member = m;
- if (isConstructor()) {
- signature = ObjectStreamClass.getSignature((Constructor)m);
- } else {
- signature = ObjectStreamClass.getSignature((Method)m);
- }
- }
- }
-
- /**
- * Returns non-static, non-abstract method with given signature provided it
- * is defined by or accessible (via inheritance) by the given class, or
- * null if no match found. Access checks are disabled on the returned
- * method (if any).
- *
- * Copied from the Merlin java.io.ObjectStreamClass.
- */
- private static Method getInheritableMethod(Class cl, String name,
- Class[] argTypes,
- Class returnType)
- {
- Method meth = null;
- Class defCl = cl;
- while (defCl != null) {
- try {
- meth = defCl.getDeclaredMethod(name, argTypes);
- break;
- } catch (NoSuchMethodException ex) {
- defCl = defCl.getSuperclass();
- }
- }
-
- if ((meth == null) || (meth.getReturnType() != returnType)) {
- return null;
- }
- meth.setAccessible(true);
- int mods = meth.getModifiers();
- if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
- return null;
- } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
- return meth;
- } else if ((mods & Modifier.PRIVATE) != 0) {
- return (cl == defCl) ? meth : null;
- } else {
- return packageEquals(cl, defCl) ? meth : null;
- }
- }
-
- /**
- * Returns true if classes are defined in the same package, false
- * otherwise.
- *
- * Copied from the Merlin java.io.ObjectStreamClass.
- */
- private static boolean packageEquals(Class cl1, Class cl2) {
- Package pkg1 = cl1.getPackage(), pkg2 = cl2.getPackage();
- return ((pkg1 == pkg2) || ((pkg1 != null) && (pkg1.equals(pkg2))));
- }
- }