2304 lines · 84.8 KB · Java
RawDownload
/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.io; import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import java.lang.ref.SoftReference;import java.lang.ref.WeakReference;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Member;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.lang.reflect.Proxy;import java.security.AccessController;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.PrivilegedAction;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Comparator;import java.util.HashSet;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import sun.misc.Unsafe;import sun.reflect.ReflectionFactory; /** * Serialization's descriptor for classes.  It contains the name and * serialVersionUID of the class.  The ObjectStreamClass for a specific class * loaded in this Java VM can be found/created using the lookup method. * * <p>The algorithm to compute the SerialVersionUID is described in * <a href="../../../platform/serialization/spec/class.html#4100">Object * Serialization Specification, Section 4.6, Stream Unique Identifiers</a>. * * @author      Mike Warres * @author      Roger Riggs * @see ObjectStreamField * @see <a href="../../../platform/serialization/spec/class.html">Object Serialization Specification, Section 4, Class Descriptors</a> * @since   JDK1.1 */public class ObjectStreamClass implements Serializable {     /** serialPersistentFields value indicating no serializable fields */    public static final ObjectStreamField[] NO_FIELDS =        new ObjectStreamField[0];     private static final long serialVersionUID = -6120832682080437368L;    private static final ObjectStreamField[] serialPersistentFields =        NO_FIELDS;     /** reflection factory for obtaining serialization constructors */    private static final ReflectionFactory reflFactory =        AccessController.doPrivileged(            new ReflectionFactory.GetReflectionFactoryAction());     private static class Caches {        /** cache mapping local classes -> descriptors */        static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs =            new ConcurrentHashMap<>();         /** cache mapping field group/local desc pairs -> field reflectors */        static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors =            new ConcurrentHashMap<>();         /** queue for WeakReferences to local classes */        private static final ReferenceQueue<Class<?>> localDescsQueue =            new ReferenceQueue<>();        /** queue for WeakReferences to field reflectors keys */        private static final ReferenceQueue<Class<?>> reflectorsQueue =            new ReferenceQueue<>();    }     /** class associated with this descriptor (if any) */    private Class<?> cl;    /** name of class represented by this descriptor */    private String name;    /** serialVersionUID of represented class (null if not computed yet) */    private volatile Long suid;     /** true if represents dynamic proxy class */    private boolean isProxy;    /** true if represents enum type */    private boolean isEnum;    /** true if represented class implements Serializable */    private boolean serializable;    /** true if represented class implements Externalizable */    private boolean externalizable;    /** true if desc has data written by class-defined writeObject method */    private boolean hasWriteObjectData;    /**     * true if desc has externalizable data written in block data format; this     * must be true by default to accommodate ObjectInputStream subclasses which     * override readClassDescriptor() to return class descriptors obtained from     * ObjectStreamClass.lookup() (see 4461737)     */    private boolean hasBlockExternalData = true;     /** exception (if any) thrown while attempting to resolve class */    private ClassNotFoundException resolveEx;    /** exception (if any) to throw if non-enum deserialization attempted */    private InvalidClassException deserializeEx;    /** exception (if any) to throw if non-enum serialization attempted */    private InvalidClassException serializeEx;    /** exception (if any) to throw if default serialization attempted */    private InvalidClassException defaultSerializeEx;     /** serializable fields */    private ObjectStreamField[] fields;    /** aggregate marshalled size of primitive fields */    private int primDataSize;    /** number of non-primitive fields */    private int numObjFields;    /** reflector for setting/getting serializable field values */    private FieldReflector fieldRefl;    /** data layout of serialized objects described by this class desc */    private volatile ClassDataSlot[] dataLayout;     /** serialization-appropriate constructor, or null if none */    private Constructor cons;    /** class-defined writeObject method, or null if none */    private Method writeObjectMethod;    /** class-defined readObject method, or null if none */    private Method readObjectMethod;    /** class-defined readObjectNoData method, or null if none */    private Method readObjectNoDataMethod;    /** class-defined writeReplace method, or null if none */    private Method writeReplaceMethod;    /** class-defined readResolve method, or null if none */    private Method readResolveMethod;     /** local class descriptor for represented class (may point to self) */    private ObjectStreamClass localDesc;    /** superclass descriptor appearing in stream */    private ObjectStreamClass superDesc;     /**     * Initializes native code.     */    private static native void initNative();    static {        initNative();    }     /**     * Find the descriptor for a class that can be serialized.  Creates an     * ObjectStreamClass instance if one does not exist yet for class. Null is     * returned if the specified class does not implement java.io.Serializable     * or java.io.Externalizable.     *     * @param   cl class for which to get the descriptor     * @return  the class descriptor for the specified class     */    public static ObjectStreamClass lookup(Class<?> cl) {        return lookup(cl, false);    }     /**     * Returns the descriptor for any class, regardless of whether it     * implements {@link Serializable}.     *     * @param        cl class for which to get the descriptor     * @return       the class descriptor for the specified class     * @since 1.6     */    public static ObjectStreamClass lookupAny(Class<?> cl) {        return lookup(cl, true);    }     /**     * Returns the name of the class described by this descriptor.     * This method returns the name of the class in the format that     * is used by the {@link Class#getName} method.     *     * @return a string representing the name of the class     */    public 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.  NonSerializable classes have a serialVersionUID of 0L.     *     * @return  the SUID of the class described by this descriptor     */    public long getSerialVersionUID() {        // REMIND: synchronize instead of relying on volatile?        if (suid == null) {            suid = AccessController.doPrivileged(                new PrivilegedAction<Long>() {                    public Long run() {                        return computeDefaultSUID(cl);                    }                }            );        }        return suid.longValue();    }     /**     * Return the class in the local VM that this version is mapped to.  Null     * is returned if there is no corresponding local class.     *     * @return  the <code>Class</code> instance that this descriptor represents     */    public Class<?> forClass() {        return cl;    }     /**     * 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 1.2     */    public ObjectStreamField[] getFields() {        return getFields(true);    }     /**     * Get the field of this class by name.     *     * @param   name the name of the data field to look for     * @return  The ObjectStreamField object of the named field or null if     *          there is no such named field.     */    public ObjectStreamField getField(String name) {        return getField(name, null);    }     /**     * Return a string describing this ObjectStreamClass.     */    public String toString() {        return name + ": static final long serialVersionUID = " +            getSerialVersionUID() + "L;";    }     /**     * Looks up and returns class descriptor for given class, or null if class     * is non-serializable and "all" is set to false.     *     * @param   cl class to look up     * @param   all if true, return descriptors for all classes; if false, only     *          return descriptors for serializable classes     */    static ObjectStreamClass lookup(Class<?> cl, boolean all) {        if (!(all || Serializable.class.isAssignableFrom(cl))) {            return null;        }        processQueue(Caches.localDescsQueue, Caches.localDescs);        WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);        Reference<?> ref = Caches.localDescs.get(key);        Object entry = null;        if (ref != null) {            entry = ref.get();        }        EntryFuture future = null;        if (entry == null) {            EntryFuture newEntry = new EntryFuture();            Reference<?> newRef = new SoftReference<>(newEntry);            do {                if (ref != null) {                    Caches.localDescs.remove(key, ref);                }                ref = Caches.localDescs.putIfAbsent(key, newRef);                if (ref != null) {                    entry = ref.get();                }            } while (ref != null && entry == null);            if (entry == null) {                future = newEntry;            }        }         if (entry instanceof ObjectStreamClass) {  // check common case first            return (ObjectStreamClass) entry;        }        if (entry instanceof EntryFuture) {            future = (EntryFuture) entry;            if (future.getOwner() == Thread.currentThread()) {                /*                 * Handle nested call situation described by 4803747: waiting                 * for future value to be set by a lookup() call further up the                 * stack will result in deadlock, so calculate and set the                 * future value here instead.                 */                entry = null;            } else {                entry = future.get();            }        }        if (entry == null) {            try {                entry = new ObjectStreamClass(cl);            } catch (Throwable th) {                entry = th;            }            if (future.set(entry)) {                Caches.localDescs.put(key, new SoftReference<Object>(entry));            } else {                // nested lookup call already set future                entry = future.get();            }        }         if (entry instanceof ObjectStreamClass) {            return (ObjectStreamClass) entry;        } else if (entry instanceof RuntimeException) {            throw (RuntimeException) entry;        } else if (entry instanceof Error) {            throw (Error) entry;        } else {            throw new InternalError("unexpected entry: " + entry);        }    }     /**     * Placeholder used in class descriptor and field reflector lookup tables     * for an entry in the process of being initialized.  (Internal) callers     * which receive an EntryFuture belonging to another thread as the result     * of a lookup should call the get() method of the EntryFuture; this will     * return the actual entry once it is ready for use and has been set().  To     * conserve objects, EntryFutures synchronize on themselves.     */    private static class EntryFuture {         private static final Object unset = new Object();        private final Thread owner = Thread.currentThread();        private Object entry = unset;         /**         * Attempts to set the value contained by this EntryFuture.  If the         * EntryFuture's value has not been set already, then the value is         * saved, any callers blocked in the get() method are notified, and         * true is returned.  If the value has already been set, then no saving         * or notification occurs, and false is returned.         */        synchronized boolean set(Object entry) {            if (this.entry != unset) {                return false;            }            this.entry = entry;            notifyAll();            return true;        }         /**         * Returns the value contained by this EntryFuture, blocking if         * necessary until a value is set.         */        synchronized Object get() {            boolean interrupted = false;            while (entry == unset) {                try {                    wait();                } catch (InterruptedException ex) {                    interrupted = true;                }            }            if (interrupted) {                AccessController.doPrivileged(                    new PrivilegedAction<Void>() {                        public Void run() {                            Thread.currentThread().interrupt();                            return null;                        }                    }                );            }            return entry;        }         /**         * Returns the thread that created this EntryFuture.         */        Thread getOwner() {            return owner;        }    }     /**     * Creates local class descriptor representing given class.     */    private ObjectStreamClass(final Class<?> cl) {        this.cl = cl;        name = cl.getName();        isProxy = Proxy.isProxyClass(cl);        isEnum = Enum.class.isAssignableFrom(cl);        serializable = Serializable.class.isAssignableFrom(cl);        externalizable = Externalizable.class.isAssignableFrom(cl);         Class<?> superCl = cl.getSuperclass();        superDesc = (superCl != null) ? lookup(superCl, false) : null;        localDesc = this;         if (serializable) {            AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                    if (isEnum) {                        suid = Long.valueOf(0);                        fields = NO_FIELDS;                        return null;                    }                    if (cl.isArray()) {                        fields = NO_FIELDS;                        return null;                    }                     suid = getDeclaredSUID(cl);                    try {                        fields = getSerialFields(cl);                        computeFieldOffsets();                    } catch (InvalidClassException e) {                        serializeEx = deserializeEx = e;                        fields = NO_FIELDS;                    }                     if (externalizable) {                        cons = getExternalizableConstructor(cl);                    } else {                        cons = getSerializableConstructor(cl);                        writeObjectMethod = getPrivateMethod(cl, "writeObject",                            new Class<?>[] { ObjectOutputStream.class },                            Void.TYPE);                        readObjectMethod = getPrivateMethod(cl, "readObject",                            new Class<?>[] { ObjectInputStream.class },                            Void.TYPE);                        readObjectNoDataMethod = getPrivateMethod(                            cl, "readObjectNoData", null, Void.TYPE);                        hasWriteObjectData = (writeObjectMethod != null);                    }                    writeReplaceMethod = getInheritableMethod(                        cl, "writeReplace", null, Object.class);                    readResolveMethod = getInheritableMethod(                        cl, "readResolve", null, Object.class);                    return null;                }            });        } else {            suid = Long.valueOf(0);            fields = NO_FIELDS;        }         try {            fieldRefl = getReflector(fields, this);        } catch (InvalidClassException ex) {            // field mismatches impossible when matching local fields vs. self            throw new InternalError();        }         if (deserializeEx == null) {            if (isEnum) {                deserializeEx = new InvalidClassException(name, "enum type");            } else if (cons == null) {                deserializeEx = new InvalidClassException(                    name, "no valid constructor");            }        }        for (int i = 0; i < fields.length; i++) {            if (fields[i].getField() == null) {                defaultSerializeEx = new InvalidClassException(                    name, "unmatched serializable field(s) declared");            }        }    }     /**

Lines 1–500 of 2304.