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.lang.ref.Reference;
0029: import java.lang.ref.ReferenceQueue;
0030: import java.lang.ref.SoftReference;
0031: import java.lang.ref.WeakReference;
0032: import java.lang.reflect.Constructor;
0033: import java.lang.reflect.Field;
0034: import java.lang.reflect.InvocationTargetException;
0035: import java.lang.reflect.Member;
0036: import java.lang.reflect.Method;
0037: import java.lang.reflect.Modifier;
0038: import java.lang.reflect.Proxy;
0039: import java.security.AccessController;
0040: import java.security.MessageDigest;
0041: import java.security.NoSuchAlgorithmException;
0042: import java.security.PrivilegedAction;
0043: import java.util.ArrayList;
0044: import java.util.Arrays;
0045: import java.util.Collections;
0046: import java.util.Comparator;
0047: import java.util.HashSet;
0048: import java.util.Set;
0049: import java.util.concurrent.ConcurrentHashMap;
0050: import java.util.concurrent.ConcurrentMap;
0051: import sun.misc.Unsafe;
0052: import sun.reflect.ReflectionFactory;
0053:
0054: /**
0055: * Serialization's descriptor for classes. It contains the name and
0056: * serialVersionUID of the class. The ObjectStreamClass for a specific class
0057: * loaded in this Java VM can be found/created using the lookup method.
0058: *
0059: * <p>The algorithm to compute the SerialVersionUID is described in
0060: * <a href="../../../platform/serialization/spec/class.html#4100">Object
0061: * Serialization Specification, Section 4.6, Stream Unique Identifiers</a>.
0062: *
0063: * @author Mike Warres
0064: * @author Roger Riggs
0065: * @version 1.158, 05/05/07
0066: * @see ObjectStreamField
0067: * @see <a href="../../../platform/serialization/spec/class.html">Object Serialization Specification, Section 4, Class Descriptors</a>
0068: * @since JDK1.1
0069: */
0070: public class ObjectStreamClass implements Serializable {
0071:
0072: /** serialPersistentFields value indicating no serializable fields */
0073: public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0];
0074:
0075: private static final long serialVersionUID = -6120832682080437368L;
0076: private static final ObjectStreamField[] serialPersistentFields = NO_FIELDS;
0077:
0078: /** reflection factory for obtaining serialization constructors */
0079: private static final ReflectionFactory reflFactory = (ReflectionFactory) AccessController
0080: .doPrivileged(new ReflectionFactory.GetReflectionFactoryAction());
0081:
0082: private static class Caches {
0083: /** cache mapping local classes -> descriptors */
0084: static final ConcurrentMap<WeakClassKey, Reference<?>> localDescs = new ConcurrentHashMap<WeakClassKey, Reference<?>>();
0085:
0086: /** cache mapping field group/local desc pairs -> field reflectors */
0087: static final ConcurrentMap<FieldReflectorKey, Reference<?>> reflectors = new ConcurrentHashMap<FieldReflectorKey, Reference<?>>();
0088:
0089: /** queue for WeakReferences to local classes */
0090: private static final ReferenceQueue<Class<?>> localDescsQueue = new ReferenceQueue<Class<?>>();
0091: /** queue for WeakReferences to field reflectors keys */
0092: private static final ReferenceQueue<Class<?>> reflectorsQueue = new ReferenceQueue<Class<?>>();
0093: }
0094:
0095: /** class associated with this descriptor (if any) */
0096: private Class cl;
0097: /** name of class represented by this descriptor */
0098: private String name;
0099: /** serialVersionUID of represented class (null if not computed yet) */
0100: private volatile Long suid;
0101:
0102: /** true if represents dynamic proxy class */
0103: private boolean isProxy;
0104: /** true if represents enum type */
0105: private boolean isEnum;
0106: /** true if represented class implements Serializable */
0107: private boolean serializable;
0108: /** true if represented class implements Externalizable */
0109: private boolean externalizable;
0110: /** true if desc has data written by class-defined writeObject method */
0111: private boolean hasWriteObjectData;
0112: /**
0113: * true if desc has externalizable data written in block data format; this
0114: * must be true by default to accommodate ObjectInputStream subclasses which
0115: * override readClassDescriptor() to return class descriptors obtained from
0116: * ObjectStreamClass.lookup() (see 4461737)
0117: */
0118: private boolean hasBlockExternalData = true;
0119:
0120: /** exception (if any) thrown while attempting to resolve class */
0121: private ClassNotFoundException resolveEx;
0122: /** exception (if any) to throw if non-enum deserialization attempted */
0123: private InvalidClassException deserializeEx;
0124: /** exception (if any) to throw if non-enum serialization attempted */
0125: private InvalidClassException serializeEx;
0126: /** exception (if any) to throw if default serialization attempted */
0127: private InvalidClassException defaultSerializeEx;
0128:
0129: /** serializable fields */
0130: private ObjectStreamField[] fields;
0131: /** aggregate marshalled size of primitive fields */
0132: private int primDataSize;
0133: /** number of non-primitive fields */
0134: private int numObjFields;
0135: /** reflector for setting/getting serializable field values */
0136: private FieldReflector fieldRefl;
0137: /** data layout of serialized objects described by this class desc */
0138: private volatile ClassDataSlot[] dataLayout;
0139:
0140: /** serialization-appropriate constructor, or null if none */
0141: private Constructor cons;
0142: /** class-defined writeObject method, or null if none */
0143: private Method writeObjectMethod;
0144: /** class-defined readObject method, or null if none */
0145: private Method readObjectMethod;
0146: /** class-defined readObjectNoData method, or null if none */
0147: private Method readObjectNoDataMethod;
0148: /** class-defined writeReplace method, or null if none */
0149: private Method writeReplaceMethod;
0150: /** class-defined readResolve method, or null if none */
0151: private Method readResolveMethod;
0152:
0153: /** local class descriptor for represented class (may point to self) */
0154: private ObjectStreamClass localDesc;
0155: /** superclass descriptor appearing in stream */
0156: private ObjectStreamClass super Desc;
0157:
0158: /**
0159: * Initializes native code.
0160: */
0161: private static native void initNative();
0162:
0163: static {
0164: initNative();
0165: }
0166:
0167: /**
0168: * Find the descriptor for a class that can be serialized. Creates an
0169: * ObjectStreamClass instance if one does not exist yet for class. Null is
0170: * returned if the specified class does not implement java.io.Serializable
0171: * or java.io.Externalizable.
0172: *
0173: * @param cl class for which to get the descriptor
0174: * @return the class descriptor for the specified class
0175: */
0176: public static ObjectStreamClass lookup(Class<?> cl) {
0177: return lookup(cl, false);
0178: }
0179:
0180: /**
0181: * Returns the descriptor for any class, regardless of whether it
0182: * implements {@link Serializable}.
0183: *
0184: * @param cl class for which to get the descriptor
0185: * @return the class descriptor for the specified class
0186: * @since 1.6
0187: */
0188: public static ObjectStreamClass lookupAny(Class<?> cl) {
0189: return lookup(cl, true);
0190: }
0191:
0192: /**
0193: * Returns the name of the class described by this descriptor.
0194: * This method returns the name of the class in the format that
0195: * is used by the {@link Class#getName} method.
0196: *
0197: * @return a string representing the name of the class
0198: */
0199: public String getName() {
0200: return name;
0201: }
0202:
0203: /**
0204: * Return the serialVersionUID for this class. The serialVersionUID
0205: * defines a set of classes all with the same name that have evolved from a
0206: * common root class and agree to be serialized and deserialized using a
0207: * common format. NonSerializable classes have a serialVersionUID of 0L.
0208: *
0209: * @return the SUID of the class described by this descriptor
0210: */
0211: public long getSerialVersionUID() {
0212: // REMIND: synchronize instead of relying on volatile?
0213: if (suid == null) {
0214: suid = (Long) AccessController
0215: .doPrivileged(new PrivilegedAction() {
0216: public Object run() {
0217: return Long.valueOf(computeDefaultSUID(cl));
0218: }
0219: });
0220: }
0221: return suid.longValue();
0222: }
0223:
0224: /**
0225: * Return the class in the local VM that this version is mapped to. Null
0226: * is returned if there is no corresponding local class.
0227: *
0228: * @return the <code>Class</code> instance that this descriptor represents
0229: */
0230: public Class<?> forClass() {
0231: return cl;
0232: }
0233:
0234: /**
0235: * Return an array of the fields of this serializable class.
0236: *
0237: * @return an array containing an element for each persistent field of
0238: * this class. Returns an array of length zero if there are no
0239: * fields.
0240: * @since 1.2
0241: */
0242: public ObjectStreamField[] getFields() {
0243: return getFields(true);
0244: }
0245:
0246: /**
0247: * Get the field of this class by name.
0248: *
0249: * @param name the name of the data field to look for
0250: * @return The ObjectStreamField object of the named field or null if
0251: * there is no such named field.
0252: */
0253: public ObjectStreamField getField(String name) {
0254: return getField(name, null);
0255: }
0256:
0257: /**
0258: * Return a string describing this ObjectStreamClass.
0259: */
0260: public String toString() {
0261: return name + ": static final long serialVersionUID = "
0262: + getSerialVersionUID() + "L;";
0263: }
0264:
0265: /**
0266: * Looks up and returns class descriptor for given class, or null if class
0267: * is non-serializable and "all" is set to false.
0268: *
0269: * @param cl class to look up
0270: * @param all if true, return descriptors for all classes; if false, only
0271: * return descriptors for serializable classes
0272: */
0273: static ObjectStreamClass lookup(Class cl, boolean all) {
0274: if (!(all || Serializable.class.isAssignableFrom(cl))) {
0275: return null;
0276: }
0277: processQueue(Caches.localDescsQueue, Caches.localDescs);
0278: WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
0279: Reference<?> ref = Caches.localDescs.get(key);
0280: Object entry = null;
0281: if (ref != null) {
0282: entry = ref.get();
0283: }
0284: EntryFuture future = null;
0285: if (entry == null) {
0286: EntryFuture newEntry = new EntryFuture();
0287: Reference<?> newRef = new SoftReference<EntryFuture>(
0288: newEntry);
0289: do {
0290: if (ref != null) {
0291: Caches.localDescs.remove(key, ref);
0292: }
0293: ref = Caches.localDescs.putIfAbsent(key, newRef);
0294: if (ref != null) {
0295: entry = ref.get();
0296: }
0297: } while (ref != null && entry == null);
0298: if (entry == null) {
0299: future = newEntry;
0300: }
0301: }
0302:
0303: if (entry instanceof ObjectStreamClass) { // check common case first
0304: return (ObjectStreamClass) entry;
0305: }
0306: if (entry instanceof EntryFuture) {
0307: future = (EntryFuture) entry;
0308: if (future.getOwner() == Thread.currentThread()) {
0309: /*
0310: * Handle nested call situation described by 4803747: waiting
0311: * for future value to be set by a lookup() call further up the
0312: * stack will result in deadlock, so calculate and set the
0313: * future value here instead.
0314: */
0315: entry = null;
0316: } else {
0317: entry = future.get();
0318: }
0319: }
0320: if (entry == null) {
0321: try {
0322: entry = new ObjectStreamClass(cl);
0323: } catch (Throwable th) {
0324: entry = th;
0325: }
0326: if (future.set(entry)) {
0327: Caches.localDescs.put(key, new SoftReference<Object>(
0328: entry));
0329: } else {
0330: // nested lookup call already set future
0331: entry = future.get();
0332: }
0333: }
0334:
0335: if (entry instanceof ObjectStreamClass) {
0336: return (ObjectStreamClass) entry;
0337: } else if (entry instanceof RuntimeException) {
0338: throw (RuntimeException) entry;
0339: } else if (entry instanceof Error) {
0340: throw (Error) entry;
0341: } else {
0342: throw new InternalError("unexpected entry: " + entry);
0343: }
0344: }
0345:
0346: /**
0347: * Placeholder used in class descriptor and field reflector lookup tables
0348: * for an entry in the process of being initialized. (Internal) callers
0349: * which receive an EntryFuture belonging to another thread as the result
0350: * of a lookup should call the get() method of the EntryFuture; this will
0351: * return the actual entry once it is ready for use and has been set(). To
0352: * conserve objects, EntryFutures synchronize on themselves.
0353: */
0354: private static class EntryFuture {
0355:
0356: private static final Object unset = new Object();
0357: private final Thread owner = Thread.currentThread();
0358: private Object entry = unset;
0359:
0360: /**
0361: * Attempts to set the value contained by this EntryFuture. If the
0362: * EntryFuture's value has not been set already, then the value is
0363: * saved, any callers blocked in the get() method are notified, and
0364: * true is returned. If the value has already been set, then no saving
0365: * or notification occurs, and false is returned.
0366: */
0367: synchronized boolean set(Object entry) {
0368: if (this .entry != unset) {
0369: return false;
0370: }
0371: this .entry = entry;
0372: notifyAll();
0373: return true;
0374: }
0375:
0376: /**
0377: * Returns the value contained by this EntryFuture, blocking if
0378: * necessary until a value is set.
0379: */
0380: synchronized Object get() {
0381: boolean interrupted = false;
0382: while (entry == unset) {
0383: try {
0384: wait();
0385: } catch (InterruptedException ex) {
0386: interrupted = true;
0387: }
0388: }
0389: if (interrupted) {
0390: AccessController.doPrivileged(new PrivilegedAction() {
0391: public Object run() {
0392: Thread.currentThread().interrupt();
0393: return null;
0394: }
0395: });
0396: }
0397: return entry;
0398: }
0399:
0400: /**
0401: * Returns the thread that created this EntryFuture.
0402: */
0403: Thread getOwner() {
0404: return owner;
0405: }
0406: }
0407:
0408: /**
0409: * Creates local class descriptor representing given class.
0410: */
0411: private ObjectStreamClass(final Class cl) {
0412: this .cl = cl;
0413: name = cl.getName();
0414: isProxy = Proxy.isProxyClass(cl);
0415: isEnum = Enum.class.isAssignableFrom(cl);
0416: serializable = Serializable.class.isAssignableFrom(cl);
0417: externalizable = Externalizable.class.isAssignableFrom(cl);
0418:
0419: Class super Cl = cl.getSuperclass();
0420: super Desc = (super Cl != null) ? lookup(super Cl, false) : null;
0421: localDesc = this ;
0422:
0423: if (serializable) {
0424: AccessController.doPrivileged(new PrivilegedAction() {
0425: public Object run() {
0426: if (isEnum) {
0427: suid = Long.valueOf(0);
0428: fields = NO_FIELDS;
0429: return null;
0430: }
0431: if (cl.isArray()) {
0432: fields = NO_FIELDS;
0433: return null;
0434: }
0435:
0436: suid = getDeclaredSUID(cl);
0437: try {
0438: fields = getSerialFields(cl);
0439: computeFieldOffsets();
0440: } catch (InvalidClassException e) {
0441: serializeEx = deserializeEx = e;
0442: fields = NO_FIELDS;
0443: }
0444:
0445: if (externalizable) {
0446: cons = getExternalizableConstructor(cl);
0447: } else {
0448: cons = getSerializableConstructor(cl);
0449: writeObjectMethod = getPrivateMethod(
0450: cl,
0451: "writeObject",
0452: new Class[] { ObjectOutputStream.class },
0453: Void.TYPE);
0454: readObjectMethod = getPrivateMethod(
0455: cl,
0456: "readObject",
0457: new Class[] { ObjectInputStream.class },
0458: Void.TYPE);
0459: readObjectNoDataMethod = getPrivateMethod(cl,
0460: "readObjectNoData", null, Void.TYPE);
0461: hasWriteObjectData = (writeObjectMethod != null);
0462: }
0463: writeReplaceMethod = getInheritableMethod(cl,
0464: "writeReplace", null, Object.class);
0465: readResolveMethod = getInheritableMethod(cl,
0466: "readResolve", null, Object.class);
0467: return null;
0468: }
0469: });
0470: } else {
0471: suid = Long.valueOf(0);
0472: fields = NO_FIELDS;
0473: }
0474:
0475: try {
0476: fieldRefl = getReflector(fields, this );
0477: } catch (InvalidClassException ex) {
0478: // field mismatches impossible when matching local fields vs. self
0479: throw new InternalError();
0480: }
0481:
0482: if (deserializeEx == null) {
0483: if (isEnum) {
0484: deserializeEx = new InvalidClassException(name,
0485: "enum type");
0486: } else if (cons == null) {
0487: deserializeEx = new InvalidClassException(name,
0488: "no valid constructor");
0489: }
0490: }
0491: for (int i = 0; i < fields.length; i++) {
0492: if (fields[i].getField() == null) {
0493: defaultSerializeEx = new InvalidClassException(name,
0494: "unmatched serializable field(s) declared");
0495: }
0496: }
0497: }
0498:
0499: /**
0500: * Creates blank class descriptor which should be initialized via a
0501: * subsequent call to initProxy(), initNonProxy() or readNonProxy().
0502: */
0503: ObjectStreamClass() {
0504: }
0505:
0506: /**
0507: * Initializes class descriptor representing a proxy class.
0508: */
0509: void initProxy(Class cl, ClassNotFoundException resolveEx,
0510: ObjectStreamClass super Desc) throws InvalidClassException {
0511: this .cl = cl;
0512: this .resolveEx = resolveEx;
0513: this .super Desc = super Desc;
0514: isProxy = true;
0515: serializable = true;
0516: suid = Long.valueOf(0);
0517: fields = NO_FIELDS;
0518:
0519: if (cl != null) {
0520: localDesc = lookup(cl, true);
0521: if (!localDesc.isProxy) {
0522: throw new InvalidClassException(
0523: "cannot bind proxy descriptor to a non-proxy class");
0524: }
0525: name = localDesc.name;
0526: externalizable = localDesc.externalizable;
0527: cons = localDesc.cons;
0528: writeReplaceMethod = localDesc.writeReplaceMethod;
0529: readResolveMethod = localDesc.readResolveMethod;
0530: deserializeEx = localDesc.deserializeEx;
0531: }
0532: fieldRefl = getReflector(fields, localDesc);
0533: }
0534:
0535: /**
0536: * Initializes class descriptor representing a non-proxy class.
0537: */
0538: void initNonProxy(ObjectStreamClass model, Class cl,
0539: ClassNotFoundException resolveEx,
0540: ObjectStreamClass super Desc) throws InvalidClassException {
0541: this .cl = cl;
0542: this .resolveEx = resolveEx;
0543: this .super Desc = super Desc;
0544: name = model.name;
0545: suid = Long.valueOf(model.getSerialVersionUID());
0546: isProxy = false;
0547: isEnum = model.isEnum;
0548: serializable = model.serializable;
0549: externalizable = model.externalizable;
0550: hasBlockExternalData = model.hasBlockExternalData;
0551: hasWriteObjectData = model.hasWriteObjectData;
0552: fields = model.fields;
0553: primDataSize = model.primDataSize;
0554: numObjFields = model.numObjFields;
0555:
0556: if (cl != null) {
0557: localDesc = lookup(cl, true);
0558: if (localDesc.isProxy) {
0559: throw new InvalidClassException(
0560: "cannot bind non-proxy descriptor to a proxy class");
0561: }
0562: if (isEnum != localDesc.isEnum) {
0563: throw new InvalidClassException(
0564: isEnum ? "cannot bind enum descriptor to a non-enum class"
0565: : "cannot bind non-enum descriptor to an enum class");
0566: }
0567:
0568: if (serializable == localDesc.serializable
0569: && !cl.isArray()
0570: && suid.longValue() != localDesc
0571: .getSerialVersionUID()) {
0572: throw new InvalidClassException(
0573: localDesc.name,
0574: "local class incompatible: "
0575: + "stream classdesc serialVersionUID = "
0576: + suid
0577: + ", local class serialVersionUID = "
0578: + localDesc.getSerialVersionUID());
0579: }
0580:
0581: if (!classNamesEqual(name, localDesc.name)) {
0582: throw new InvalidClassException(localDesc.name,
0583: "local class name incompatible with stream class "
0584: + "name \"" + name + "\"");
0585: }
0586:
0587: if (!isEnum) {
0588: if ((serializable == localDesc.serializable)
0589: && (externalizable != localDesc.externalizable)) {
0590: throw new InvalidClassException(localDesc.name,
0591: "Serializable incompatible with Externalizable");
0592: }
0593:
0594: if ((serializable != localDesc.serializable)
0595: || (externalizable != localDesc.externalizable)
0596: || !(serializable || externalizable)) {
0597: deserializeEx = new InvalidClassException(
0598: localDesc.name,
0599: "class invalid for deserialization");
0600: }
0601: }
0602:
0603: cons = localDesc.cons;
0604: writeObjectMethod = localDesc.writeObjectMethod;
0605: readObjectMethod = localDesc.readObjectMethod;
0606: readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
0607: writeReplaceMethod = localDesc.writeReplaceMethod;
0608: readResolveMethod = localDesc.readResolveMethod;
0609: if (deserializeEx == null) {
0610: deserializeEx = localDesc.deserializeEx;
0611: }
0612: }
0613: fieldRefl = getReflector(fields, localDesc);
0614: // reassign to matched fields so as to reflect local unshared settings
0615: fields = fieldRefl.getFields();
0616: }
0617:
0618: /**
0619: * Reads non-proxy class descriptor information from given input stream.
0620: * The resulting class descriptor is not fully functional; it can only be
0621: * used as input to the ObjectInputStream.resolveClass() and
0622: * ObjectStreamClass.initNonProxy() methods.
0623: */
0624: void readNonProxy(ObjectInputStream in) throws IOException,
0625: ClassNotFoundException {
0626: name = in.readUTF();
0627: suid = Long.valueOf(in.readLong());
0628: isProxy = false;
0629:
0630: byte flags = in.readByte();
0631: hasWriteObjectData = ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
0632: hasBlockExternalData = ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
0633: externalizable = ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
0634: boolean sflag = ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
0635: if (externalizable && sflag) {
0636: throw new InvalidClassException(name,
0637: "serializable and externalizable flags conflict");
0638: }
0639: serializable = externalizable || sflag;
0640: isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
0641: if (isEnum && suid.longValue() != 0L) {
0642: throw new InvalidClassException(name,
0643: "enum descriptor has non-zero serialVersionUID: "
0644: + suid);
0645: }
0646:
0647: int numFields = in.readShort();
0648: if (isEnum && numFields != 0) {
0649: throw new InvalidClassException(name,
0650: "enum descriptor has non-zero field count: "
0651: + numFields);
0652: }
0653: fields = (numFields > 0) ? new ObjectStreamField[numFields]
0654: : NO_FIELDS;
0655: for (int i = 0; i < numFields; i++) {
0656: char tcode = (char) in.readByte();
0657: String fname = in.readUTF();
0658: String signature = ((tcode == 'L') || (tcode == '[')) ? in
0659: .readTypeString()
0660: : new String(new char[] { tcode });
0661: try {
0662: fields[i] = new ObjectStreamField(fname, signature,
0663: false);
0664: } catch (RuntimeException e) {
0665: throw (IOException) new InvalidClassException(name,
0666: "invalid descriptor for field " + fname)
0667: .initCause(e);
0668: }
0669: }
0670: computeFieldOffsets();
0671: }
0672:
0673: /**
0674: * Writes non-proxy class descriptor information to given output stream.
0675: */
0676: void writeNonProxy(ObjectOutputStream out) throws IOException {
0677: out.writeUTF(name);
0678: out.writeLong(getSerialVersionUID());
0679:
0680: byte flags = 0;
0681: if (externalizable) {
0682: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
0683: int protocol = out.getProtocolVersion();
0684: if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
0685: flags |= ObjectStreamConstants.SC_BLOCK_DATA;
0686: }
0687: } else if (serializable) {
0688: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
0689: }
0690: if (hasWriteObjectData) {
0691: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
0692: }
0693: if (isEnum) {
0694: flags |= ObjectStreamConstants.SC_ENUM;
0695: }
0696: out.writeByte(flags);
0697:
0698: out.writeShort(fields.length);
0699: for (int i = 0; i < fields.length; i++) {
0700: ObjectStreamField f = fields[i];
0701: out.writeByte(f.getTypeCode());
0702: out.writeUTF(f.getName());
0703: if (!f.isPrimitive()) {
0704: out.writeTypeString(f.getTypeString());
0705: }
0706: }
0707: }
0708:
0709: /**
0710: * Returns ClassNotFoundException (if any) thrown while attempting to
0711: * resolve local class corresponding to this class descriptor.
0712: */
0713: ClassNotFoundException getResolveException() {
0714: return resolveEx;
0715: }
0716:
0717: /**
0718: * Throws an InvalidClassException if object instances referencing this
0719: * class descriptor should not be allowed to deserialize. This method does
0720: * not apply to deserialization of enum constants.
0721: */
0722: void checkDeserialize() throws InvalidClassException {
0723: if (deserializeEx != null) {
0724: InvalidClassException ice = new InvalidClassException(
0725: deserializeEx.classname, deserializeEx.getMessage());
0726: ice.initCause(deserializeEx);
0727: throw ice;
0728: }
0729: }
0730:
0731: /**
0732: * Throws an InvalidClassException if objects whose class is represented by
0733: * this descriptor should not be allowed to serialize. This method does
0734: * not apply to serialization of enum constants.
0735: */
0736: void checkSerialize() throws InvalidClassException {
0737: if (serializeEx != null) {
0738: InvalidClassException ice = new InvalidClassException(
0739: serializeEx.classname, serializeEx.getMessage());
0740: ice.initCause(serializeEx);
0741: throw ice;
0742: }
0743: }
0744:
0745: /**
0746: * Throws an InvalidClassException if objects whose class is represented by
0747: * this descriptor should not be permitted to use default serialization
0748: * (e.g., if the class declares serializable fields that do not correspond
0749: * to actual fields, and hence must use the GetField API). This method
0750: * does not apply to deserialization of enum constants.
0751: */
0752: void checkDefaultSerialize() throws InvalidClassException {
0753: if (defaultSerializeEx != null) {
0754: InvalidClassException ice = new InvalidClassException(
0755: defaultSerializeEx.classname, defaultSerializeEx
0756: .getMessage());
0757: ice.initCause(defaultSerializeEx);
0758: throw ice;
0759: }
0760: }
0761:
0762: /**
0763: * Returns superclass descriptor. Note that on the receiving side, the
0764: * superclass descriptor may be bound to a class that is not a superclass
0765: * of the subclass descriptor's bound class.
0766: */
0767: ObjectStreamClass getSuperDesc() {
0768: return super Desc;
0769: }
0770:
0771: /**
0772: * Returns the "local" class descriptor for the class associated with this
0773: * class descriptor (i.e., the result of
0774: * ObjectStreamClass.lookup(this.forClass())) or null if there is no class
0775: * associated with this descriptor.
0776: */
0777: ObjectStreamClass getLocalDesc() {
0778: return localDesc;
0779: }
0780:
0781: /**
0782: * Returns arrays of ObjectStreamFields representing the serializable
0783: * fields of the represented class. If copy is true, a clone of this class
0784: * descriptor's field array is returned, otherwise the array itself is
0785: * returned.
0786: */
0787: ObjectStreamField[] getFields(boolean copy) {
0788: return copy ? (ObjectStreamField[]) fields.clone() : fields;
0789: }
0790:
0791: /**
0792: * Looks up a serializable field of the represented class by name and type.
0793: * A specified type of null matches all types, Object.class matches all
0794: * non-primitive types, and any other non-null type matches assignable
0795: * types only. Returns matching field, or null if no match found.
0796: */
0797: ObjectStreamField getField(String name, Class type) {
0798: for (int i = 0; i < fields.length; i++) {
0799: ObjectStreamField f = fields[i];
0800: if (f.getName().equals(name)) {
0801: if (type == null
0802: || (type == Object.class && !f.isPrimitive())) {
0803: return f;
0804: }
0805: Class ftype = f.getType();
0806: if (ftype != null && type.isAssignableFrom(ftype)) {
0807: return f;
0808: }
0809: }
0810: }
0811: return null;
0812: }
0813:
0814: /**
0815: * Returns true if class descriptor represents a dynamic proxy class, false
0816: * otherwise.
0817: */
0818: boolean isProxy() {
0819: return isProxy;
0820: }
0821:
0822: /**
0823: * Returns true if class descriptor represents an enum type, false
0824: * otherwise.
0825: */
0826: boolean isEnum() {
0827: return isEnum;
0828: }
0829:
0830: /**
0831: * Returns true if represented class implements Externalizable, false
0832: * otherwise.
0833: */
0834: boolean isExternalizable() {
0835: return externalizable;
0836: }
0837:
0838: /**
0839: * Returns true if represented class implements Serializable, false
0840: * otherwise.
0841: */
0842: boolean isSerializable() {
0843: return serializable;
0844: }
0845:
0846: /**
0847: * Returns true if class descriptor represents externalizable class that
0848: * has written its data in 1.2 (block data) format, false otherwise.
0849: */
0850: boolean hasBlockExternalData() {
0851: return hasBlockExternalData;
0852: }
0853:
0854: /**
0855: * Returns true if class descriptor represents serializable (but not
0856: * externalizable) class which has written its data via a custom
0857: * writeObject() method, false otherwise.
0858: */
0859: boolean hasWriteObjectData() {
0860: return hasWriteObjectData;
0861: }
0862:
0863: /**
0864: * Returns true if represented class is serializable/externalizable and can
0865: * be instantiated by the serialization runtime--i.e., if it is
0866: * externalizable and defines a public no-arg constructor, or if it is
0867: * non-externalizable and its first non-serializable superclass defines an
0868: * accessible no-arg constructor. Otherwise, returns false.
0869: */
0870: boolean isInstantiable() {
0871: return (cons != null);
0872: }
0873:
0874: /**
0875: * Returns true if represented class is serializable (but not
0876: * externalizable) and defines a conformant writeObject method. Otherwise,
0877: * returns false.
0878: */
0879: boolean hasWriteObjectMethod() {
0880: return (writeObjectMethod != null);
0881: }
0882:
0883: /**
0884: * Returns true if represented class is serializable (but not
0885: * externalizable) and defines a conformant readObject method. Otherwise,
0886: * returns false.
0887: */
0888: boolean hasReadObjectMethod() {
0889: return (readObjectMethod != null);
0890: }
0891:
0892: /**
0893: * Returns true if represented class is serializable (but not
0894: * externalizable) and defines a conformant readObjectNoData method.
0895: * Otherwise, returns false.
0896: */
0897: boolean hasReadObjectNoDataMethod() {
0898: return (readObjectNoDataMethod != null);
0899: }
0900:
0901: /**
0902: * Returns true if represented class is serializable or externalizable and
0903: * defines a conformant writeReplace method. Otherwise, returns false.
0904: */
0905: boolean hasWriteReplaceMethod() {
0906: return (writeReplaceMethod != null);
0907: }
0908:
0909: /**
0910: * Returns true if represented class is serializable or externalizable and
0911: * defines a conformant readResolve method. Otherwise, returns false.
0912: */
0913: boolean hasReadResolveMethod() {
0914: return (readResolveMethod != null);
0915: }
0916:
0917: /**
0918: * Creates a new instance of the represented class. If the class is
0919: * externalizable, invokes its public no-arg constructor; otherwise, if the
0920: * class is serializable, invokes the no-arg constructor of the first
0921: * non-serializable superclass. Throws UnsupportedOperationException if
0922: * this class descriptor is not associated with a class, if the associated
0923: * class is non-serializable or if the appropriate no-arg constructor is
0924: * inaccessible/unavailable.
0925: */
0926: Object newInstance() throws InstantiationException,
0927: InvocationTargetException, UnsupportedOperationException {
0928: if (cons != null) {
0929: try {
0930: return cons.newInstance();
0931: } catch (IllegalAccessException ex) {
0932: // should not occur, as access checks have been suppressed
0933: throw new InternalError();
0934: }
0935: } else {
0936: throw new UnsupportedOperationException();
0937: }
0938: }
0939:
0940: /**
0941: * Invokes the writeObject method of the represented serializable class.
0942: * Throws UnsupportedOperationException if this class descriptor is not
0943: * associated with a class, or if the class is externalizable,
0944: * non-serializable or does not define writeObject.
0945: */
0946: void invokeWriteObject(Object obj, ObjectOutputStream out)
0947: throws IOException, UnsupportedOperationException {
0948: if (writeObjectMethod != null) {
0949: try {
0950: writeObjectMethod.invoke(obj, new Object[] { out });
0951: } catch (InvocationTargetException ex) {
0952: Throwable th = ex.getTargetException();
0953: if (th instanceof IOException) {
0954: throw (IOException) th;
0955: } else {
0956: throwMiscException(th);
0957: }
0958: } catch (IllegalAccessException ex) {
0959: // should not occur, as access checks have been suppressed
0960: throw new InternalError();
0961: }
0962: } else {
0963: throw new UnsupportedOperationException();
0964: }
0965: }
0966:
0967: /**
0968: * Invokes the readObject method of the represented serializable class.
0969: * Throws UnsupportedOperationException if this class descriptor is not
0970: * associated with a class, or if the class is externalizable,
0971: * non-serializable or does not define readObject.
0972: */
0973: void invokeReadObject(Object obj, ObjectInputStream in)
0974: throws ClassNotFoundException, IOException,
0975: UnsupportedOperationException {
0976: if (readObjectMethod != null) {
0977: try {
0978: readObjectMethod.invoke(obj, new Object[] { in });
0979: } catch (InvocationTargetException ex) {
0980: Throwable th = ex.getTargetException();
0981: if (th instanceof ClassNotFoundException) {
0982: throw (ClassNotFoundException) th;
0983: } else if (th instanceof IOException) {
0984: throw (IOException) th;
0985: } else {
0986: throwMiscException(th);
0987: }
0988: } catch (IllegalAccessException ex) {
0989: // should not occur, as access checks have been suppressed
0990: throw new InternalError();
0991: }
0992: } else {
0993: throw new UnsupportedOperationException();
0994: }
0995: }
0996:
0997: /**
0998: * Invokes the readObjectNoData method of the represented serializable
0999: * class. Throws UnsupportedOperationException if this class descriptor is
1000: * not associated with a class, or if the class is externalizable,
1001: * non-serializable or does not define readObjectNoData.
1002: */
1003: void invokeReadObjectNoData(Object obj) throws IOException,
1004: UnsupportedOperationException {
1005: if (readObjectNoDataMethod != null) {
1006: try {
1007: readObjectNoDataMethod.invoke(obj, (Object[]) null);
1008: } catch (InvocationTargetException ex) {
1009: Throwable th = ex.getTargetException();
1010: if (th instanceof ObjectStreamException) {
1011: throw (ObjectStreamException) th;
1012: } else {
1013: throwMiscException(th);
1014: }
1015: } catch (IllegalAccessException ex) {
1016: // should not occur, as access checks have been suppressed
1017: throw new InternalError();
1018: }
1019: } else {
1020: throw new UnsupportedOperationException();
1021: }
1022: }
1023:
1024: /**
1025: * Invokes the writeReplace method of the represented serializable class and
1026: * returns the result. Throws UnsupportedOperationException if this class
1027: * descriptor is not associated with a class, or if the class is
1028: * non-serializable or does not define writeReplace.
1029: */
1030: Object invokeWriteReplace(Object obj) throws IOException,
1031: UnsupportedOperationException {
1032: if (writeReplaceMethod != null) {
1033: try {
1034: return writeReplaceMethod.invoke(obj, (Object[]) null);
1035: } catch (InvocationTargetException ex) {
1036: Throwable th = ex.getTargetException();
1037: if (th instanceof ObjectStreamException) {
1038: throw (ObjectStreamException) th;
1039: } else {
1040: throwMiscException(th);
1041: throw new InternalError(); // never reached
1042: }
1043: } catch (IllegalAccessException ex) {
1044: // should not occur, as access checks have been suppressed
1045: throw new InternalError();
1046: }
1047: } else {
1048: throw new UnsupportedOperationException();
1049: }
1050: }
1051:
1052: /**
1053: * Invokes the readResolve method of the represented serializable class and
1054: * returns the result. Throws UnsupportedOperationException if this class
1055: * descriptor is not associated with a class, or if the class is
1056: * non-serializable or does not define readResolve.
1057: */
1058: Object invokeReadResolve(Object obj) throws IOException,
1059: UnsupportedOperationException {
1060: if (readResolveMethod != null) {
1061: try {
1062: return readResolveMethod.invoke(obj, (Object[]) null);
1063: } catch (InvocationTargetException ex) {
1064: Throwable th = ex.getTargetException();
1065: if (th instanceof ObjectStreamException) {
1066: throw (ObjectStreamException) th;
1067: } else {
1068: throwMiscException(th);
1069: throw new InternalError(); // never reached
1070: }
1071: } catch (IllegalAccessException ex) {
1072: // should not occur, as access checks have been suppressed
1073: throw new InternalError();
1074: }
1075: } else {
1076: throw new UnsupportedOperationException();
1077: }
1078: }
1079:
1080: /**
1081: * Class representing the portion of an object's serialized form allotted
1082: * to data described by a given class descriptor. If "hasData" is false,
1083: * the object's serialized form does not contain data associated with the
1084: * class descriptor.
1085: */
1086: static class ClassDataSlot {
1087:
1088: /** class descriptor "occupying" this slot */
1089: final ObjectStreamClass desc;
1090: /** true if serialized form includes data for this slot's descriptor */
1091: final boolean hasData;
1092:
1093: ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1094: this .desc = desc;
1095: this .hasData = hasData;
1096: }
1097: }
1098:
1099: /**
1100: * Returns array of ClassDataSlot instances representing the data layout
1101: * (including superclass data) for serialized objects described by this
1102: * class descriptor. ClassDataSlots are ordered by inheritance with those
1103: * containing "higher" superclasses appearing first. The final
1104: * ClassDataSlot contains a reference to this descriptor.
1105: */
1106: ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1107: // REMIND: synchronize instead of relying on volatile?
1108: if (dataLayout == null) {
1109: dataLayout = getClassDataLayout0();
1110: }
1111: return dataLayout;
1112: }
1113:
1114: private ClassDataSlot[] getClassDataLayout0()
1115: throws InvalidClassException {
1116: ArrayList slots = new ArrayList();
1117: Class start = cl, end = cl;
1118:
1119: // locate closest non-serializable superclass
1120: while (end != null && Serializable.class.isAssignableFrom(end)) {
1121: end = end.getSuperclass();
1122: }
1123:
1124: for (ObjectStreamClass d = this ; d != null; d = d.super Desc) {
1125:
1126: // search up inheritance hierarchy for class with matching name
1127: String searchName = (d.cl != null) ? d.cl.getName()
1128: : d.name;
1129: Class match = null;
1130: for (Class c = start; c != end; c = c.getSuperclass()) {
1131: if (searchName.equals(c.getName())) {
1132: match = c;
1133: break;
1134: }
1135: }
1136:
1137: // add "no data" slot for each unmatched class below match
1138: if (match != null) {
1139: for (Class c = start; c != match; c = c.getSuperclass()) {
1140: slots.add(new ClassDataSlot(ObjectStreamClass
1141: .lookup(c, true), false));
1142: }
1143: start = match.getSuperclass();
1144: }
1145:
1146: // record descriptor/class pairing
1147: slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1148: }
1149:
1150: // add "no data" slot for any leftover unmatched classes
1151: for (Class c = start; c != end; c = c.getSuperclass()) {
1152: slots.add(new ClassDataSlot(ObjectStreamClass.lookup(c,
1153: true), false));
1154: }
1155:
1156: // order slots from superclass -> subclass
1157: Collections.reverse(slots);
1158: return (ClassDataSlot[]) slots.toArray(new ClassDataSlot[slots
1159: .size()]);
1160: }
1161:
1162: /**
1163: * Returns aggregate size (in bytes) of marshalled primitive field values
1164: * for represented class.
1165: */
1166: int getPrimDataSize() {
1167: return primDataSize;
1168: }
1169:
1170: /**
1171: * Returns number of non-primitive serializable fields of represented
1172: * class.
1173: */
1174: int getNumObjFields() {
1175: return numObjFields;
1176: }
1177:
1178: /**
1179: * Fetches the serializable primitive field values of object obj and
1180: * marshals them into byte array buf starting at offset 0. It is the
1181: * responsibility of the caller to ensure that obj is of the proper type if
1182: * non-null.
1183: */
1184: void getPrimFieldValues(Object obj, byte[] buf) {
1185: fieldRefl.getPrimFieldValues(obj, buf);
1186: }
1187:
1188: /**
1189: * Sets the serializable primitive fields of object obj using values
1190: * unmarshalled from byte array buf starting at offset 0. It is the
1191: * responsibility of the caller to ensure that obj is of the proper type if
1192: * non-null.
1193: */
1194: void setPrimFieldValues(Object obj, byte[] buf) {
1195: fieldRefl.setPrimFieldValues(obj, buf);
1196: }
1197:
1198: /**
1199: * Fetches the serializable object field values of object obj and stores
1200: * them in array vals starting at offset 0. It is the responsibility of
1201: * the caller to ensure that obj is of the proper type if non-null.
1202: */
1203: void getObjFieldValues(Object obj, Object[] vals) {
1204: fieldRefl.getObjFieldValues(obj, vals);
1205: }
1206:
1207: /**
1208: * Sets the serializable object fields of object obj using values from
1209: * array vals starting at offset 0. It is the responsibility of the caller
1210: * to ensure that obj is of the proper type if non-null.
1211: */
1212: void setObjFieldValues(Object obj, Object[] vals) {
1213: fieldRefl.setObjFieldValues(obj, vals);
1214: }
1215:
1216: /**
1217: * Calculates and sets serializable field offsets, as well as primitive
1218: * data size and object field count totals. Throws InvalidClassException
1219: * if fields are illegally ordered.
1220: */
1221: private void computeFieldOffsets() throws InvalidClassException {
1222: primDataSize = 0;
1223: numObjFields = 0;
1224: int firstObjIndex = -1;
1225:
1226: for (int i = 0; i < fields.length; i++) {
1227: ObjectStreamField f = fields[i];
1228: switch (f.getTypeCode()) {
1229: case 'Z':
1230: case 'B':
1231: f.setOffset(primDataSize++);
1232: break;
1233:
1234: case 'C':
1235: case 'S':
1236: f.setOffset(primDataSize);
1237: primDataSize += 2;
1238: break;
1239:
1240: case 'I':
1241: case 'F':
1242: f.setOffset(primDataSize);
1243: primDataSize += 4;
1244: break;
1245:
1246: case 'J':
1247: case 'D':
1248: f.setOffset(primDataSize);
1249: primDataSize += 8;
1250: break;
1251:
1252: case '[':
1253: case 'L':
1254: f.setOffset(numObjFields++);
1255: if (firstObjIndex == -1) {
1256: firstObjIndex = i;
1257: }
1258: break;
1259:
1260: default:
1261: throw new InternalError();
1262: }
1263: }
1264: if (firstObjIndex != -1
1265: && firstObjIndex + numObjFields != fields.length) {
1266: throw new InvalidClassException(name, "illegal field order");
1267: }
1268: }
1269:
1270: /**
1271: * If given class is the same as the class associated with this class
1272: * descriptor, returns reference to this class descriptor. Otherwise,
1273: * returns variant of this class descriptor bound to given class.
1274: */
1275: private ObjectStreamClass getVariantFor(Class cl)
1276: throws InvalidClassException {
1277: if (this .cl == cl) {
1278: return this ;
1279: }
1280: ObjectStreamClass desc = new ObjectStreamClass();
1281: if (isProxy) {
1282: desc.initProxy(cl, null, super Desc);
1283: } else {
1284: desc.initNonProxy(this , cl, null, super Desc);
1285: }
1286: return desc;
1287: }
1288:
1289: /**
1290: * Returns public no-arg constructor of given class, or null if none found.
1291: * Access checks are disabled on the returned constructor (if any), since
1292: * the defining class may still be non-public.
1293: */
1294: private static Constructor getExternalizableConstructor(Class cl) {
1295: try {
1296: Constructor cons = cl
1297: .getDeclaredConstructor((Class[]) null);
1298: cons.setAccessible(true);
1299: return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ? cons
1300: : null;
1301: } catch (NoSuchMethodException ex) {
1302: return null;
1303: }
1304: }
1305:
1306: /**
1307: * Returns subclass-accessible no-arg constructor of first non-serializable
1308: * superclass, or null if none found. Access checks are disabled on the
1309: * returned constructor (if any).
1310: */
1311: private static Constructor getSerializableConstructor(Class cl) {
1312: Class initCl = cl;
1313: while (Serializable.class.isAssignableFrom(initCl)) {
1314: if ((initCl = initCl.getSuperclass()) == null) {
1315: return null;
1316: }
1317: }
1318: try {
1319: Constructor cons = initCl
1320: .getDeclaredConstructor((Class[]) null);
1321: int mods = cons.getModifiers();
1322: if ((mods & Modifier.PRIVATE) != 0
1323: || ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && !packageEquals(
1324: cl, initCl))) {
1325: return null;
1326: }
1327: cons = reflFactory.newConstructorForSerialization(cl, cons);
1328: cons.setAccessible(true);
1329: return cons;
1330: } catch (NoSuchMethodException ex) {
1331: return null;
1332: }
1333: }
1334:
1335: /**
1336: * Returns non-static, non-abstract method with given signature provided it
1337: * is defined by or accessible (via inheritance) by the given class, or
1338: * null if no match found. Access checks are disabled on the returned
1339: * method (if any).
1340: */
1341: private static Method getInheritableMethod(Class cl, String name,
1342: Class[] argTypes, Class returnType) {
1343: Method meth = null;
1344: Class defCl = cl;
1345: while (defCl != null) {
1346: try {
1347: meth = defCl.getDeclaredMethod(name, argTypes);
1348: break;
1349: } catch (NoSuchMethodException ex) {
1350: defCl = defCl.getSuperclass();
1351: }
1352: }
1353:
1354: if ((meth == null) || (meth.getReturnType() != returnType)) {
1355: return null;
1356: }
1357: meth.setAccessible(true);
1358: int mods = meth.getModifiers();
1359: if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
1360: return null;
1361: } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
1362: return meth;
1363: } else if ((mods & Modifier.PRIVATE) != 0) {
1364: return (cl == defCl) ? meth : null;
1365: } else {
1366: return packageEquals(cl, defCl) ? meth : null;
1367: }
1368: }
1369:
1370: /**
1371: * Returns non-static private method with given signature defined by given
1372: * class, or null if none found. Access checks are disabled on the
1373: * returned method (if any).
1374: */
1375: private static Method getPrivateMethod(Class cl, String name,
1376: Class[] argTypes, Class returnType) {
1377: try {
1378: Method meth = cl.getDeclaredMethod(name, argTypes);
1379: meth.setAccessible(true);
1380: int mods = meth.getModifiers();
1381: return ((meth.getReturnType() == returnType)
1382: && ((mods & Modifier.STATIC) == 0) && ((mods & Modifier.PRIVATE) != 0)) ? meth
1383: : null;
1384: } catch (NoSuchMethodException ex) {
1385: return null;
1386: }
1387: }
1388:
1389: /**
1390: * Returns true if classes are defined in the same runtime package, false
1391: * otherwise.
1392: */
1393: private static boolean packageEquals(Class cl1, Class cl2) {
1394: return (cl1.getClassLoader() == cl2.getClassLoader() && getPackageName(
1395: cl1).equals(getPackageName(cl2)));
1396: }
1397:
1398: /**
1399: * Returns package name of given class.
1400: */
1401: private static String getPackageName(Class cl) {
1402: String s = cl.getName();
1403: int i = s.lastIndexOf('[');
1404: if (i >= 0) {
1405: s = s.substring(i + 2);
1406: }
1407: i = s.lastIndexOf('.');
1408: return (i >= 0) ? s.substring(0, i) : "";
1409: }
1410:
1411: /**
1412: * Compares class names for equality, ignoring package names. Returns true
1413: * if class names equal, false otherwise.
1414: */
1415: private static boolean classNamesEqual(String name1, String name2) {
1416: name1 = name1.substring(name1.lastIndexOf('.') + 1);
1417: name2 = name2.substring(name2.lastIndexOf('.') + 1);
1418: return name1.equals(name2);
1419: }
1420:
1421: /**
1422: * Returns JVM type signature for given class.
1423: */
1424: static String getClassSignature(Class cl) {
1425: StringBuilder sbuf = new StringBuilder();
1426: while (cl.isArray()) {
1427: sbuf.append('[');
1428: cl = cl.getComponentType();
1429: }
1430: if (cl.isPrimitive()) {
1431: if (cl == Integer.TYPE) {
1432: sbuf.append('I');
1433: } else if (cl == Byte.TYPE) {
1434: sbuf.append('B');
1435: } else if (cl == Long.TYPE) {
1436: sbuf.append('J');
1437: } else if (cl == Float.TYPE) {
1438: sbuf.append('F');
1439: } else if (cl == Double.TYPE) {
1440: sbuf.append('D');
1441: } else if (cl == Short.TYPE) {
1442: sbuf.append('S');
1443: } else if (cl == Character.TYPE) {
1444: sbuf.append('C');
1445: } else if (cl == Boolean.TYPE) {
1446: sbuf.append('Z');
1447: } else if (cl == Void.TYPE) {
1448: sbuf.append('V');
1449: } else {
1450: throw new InternalError();
1451: }
1452: } else {
1453: sbuf.append('L' + cl.getName().replace('.', '/') + ';');
1454: }
1455: return sbuf.toString();
1456: }
1457:
1458: /**
1459: * Returns JVM type signature for given list of parameters and return type.
1460: */
1461: private static String getMethodSignature(Class[] paramTypes,
1462: Class retType) {
1463: StringBuilder sbuf = new StringBuilder();
1464: sbuf.append('(');
1465: for (int i = 0; i < paramTypes.length; i++) {
1466: sbuf.append(getClassSignature(paramTypes[i]));
1467: }
1468: sbuf.append(')');
1469: sbuf.append(getClassSignature(retType));
1470: return sbuf.toString();
1471: }
1472:
1473: /**
1474: * Convenience method for throwing an exception that is either a
1475: * RuntimeException, Error, or of some unexpected type (in which case it is
1476: * wrapped inside an IOException).
1477: */
1478: private static void throwMiscException(Throwable th)
1479: throws IOException {
1480: if (th instanceof RuntimeException) {
1481: throw (RuntimeException) th;
1482: } else if (th instanceof Error) {
1483: throw (Error) th;
1484: } else {
1485: IOException ex = new IOException(
1486: "unexpected exception type");
1487: ex.initCause(th);
1488: throw ex;
1489: }
1490: }
1491:
1492: /**
1493: * Returns ObjectStreamField array describing the serializable fields of
1494: * the given class. Serializable fields backed by an actual field of the
1495: * class are represented by ObjectStreamFields with corresponding non-null
1496: * Field objects. Throws InvalidClassException if the (explicitly
1497: * declared) serializable fields are invalid.
1498: */
1499: private static ObjectStreamField[] getSerialFields(Class cl)
1500: throws InvalidClassException {
1501: ObjectStreamField[] fields;
1502: if (Serializable.class.isAssignableFrom(cl)
1503: && !Externalizable.class.isAssignableFrom(cl)
1504: && !Proxy.isProxyClass(cl) && !cl.isInterface()) {
1505: if ((fields = getDeclaredSerialFields(cl)) == null) {
1506: fields = getDefaultSerialFields(cl);
1507: }
1508: Arrays.sort(fields);
1509: } else {
1510: fields = NO_FIELDS;
1511: }
1512: return fields;
1513: }
1514:
1515: /**
1516: * Returns serializable fields of given class as defined explicitly by a
1517: * "serialPersistentFields" field, or null if no appropriate
1518: * "serialPersistentFields" field is defined. Serializable fields backed
1519: * by an actual field of the class are represented by ObjectStreamFields
1520: * with corresponding non-null Field objects. For compatibility with past
1521: * releases, a "serialPersistentFields" field with a null value is
1522: * considered equivalent to not declaring "serialPersistentFields". Throws
1523: * InvalidClassException if the declared serializable fields are
1524: * invalid--e.g., if multiple fields share the same name.
1525: */
1526: private static ObjectStreamField[] getDeclaredSerialFields(Class cl)
1527: throws InvalidClassException {
1528: ObjectStreamField[] serialPersistentFields = null;
1529: try {
1530: Field f = cl.getDeclaredField("serialPersistentFields");
1531: int mask = Modifier.PRIVATE | Modifier.STATIC
1532: | Modifier.FINAL;
1533: if ((f.getModifiers() & mask) == mask) {
1534: f.setAccessible(true);
1535: serialPersistentFields = (ObjectStreamField[]) f
1536: .get(null);
1537: }
1538: } catch (Exception ex) {
1539: }
1540: if (serialPersistentFields == null) {
1541: return null;
1542: } else if (serialPersistentFields.length == 0) {
1543: return NO_FIELDS;
1544: }
1545:
1546: ObjectStreamField[] boundFields = new ObjectStreamField[serialPersistentFields.length];
1547: Set fieldNames = new HashSet(serialPersistentFields.length);
1548:
1549: for (int i = 0; i < serialPersistentFields.length; i++) {
1550: ObjectStreamField spf = serialPersistentFields[i];
1551:
1552: String fname = spf.getName();
1553: if (fieldNames.contains(fname)) {
1554: throw new InvalidClassException(
1555: "multiple serializable fields named " + fname);
1556: }
1557: fieldNames.add(fname);
1558:
1559: try {
1560: Field f = cl.getDeclaredField(fname);
1561: if ((f.getType() == spf.getType())
1562: && ((f.getModifiers() & Modifier.STATIC) == 0)) {
1563: boundFields[i] = new ObjectStreamField(f, spf
1564: .isUnshared(), true);
1565: }
1566: } catch (NoSuchFieldException ex) {
1567: }
1568: if (boundFields[i] == null) {
1569: boundFields[i] = new ObjectStreamField(fname, spf
1570: .getType(), spf.isUnshared());
1571: }
1572: }
1573: return boundFields;
1574: }
1575:
1576: /**
1577: * Returns array of ObjectStreamFields corresponding to all non-static
1578: * non-transient fields declared by given class. Each ObjectStreamField
1579: * contains a Field object for the field it represents. If no default
1580: * serializable fields exist, NO_FIELDS is returned.
1581: */
1582: private static ObjectStreamField[] getDefaultSerialFields(Class cl) {
1583: Field[] clFields = cl.getDeclaredFields();
1584: ArrayList list = new ArrayList();
1585: int mask = Modifier.STATIC | Modifier.TRANSIENT;
1586:
1587: for (int i = 0; i < clFields.length; i++) {
1588: if ((clFields[i].getModifiers() & mask) == 0) {
1589: list
1590: .add(new ObjectStreamField(clFields[i], false,
1591: true));
1592: }
1593: }
1594: int size = list.size();
1595: return (size == 0) ? NO_FIELDS : (ObjectStreamField[]) list
1596: .toArray(new ObjectStreamField[size]);
1597: }
1598:
1599: /**
1600: * Returns explicit serial version UID value declared by given class, or
1601: * null if none.
1602: */
1603: private static Long getDeclaredSUID(Class cl) {
1604: try {
1605: Field f = cl.getDeclaredField("serialVersionUID");
1606: int mask = Modifier.STATIC | Modifier.FINAL;
1607: if ((f.getModifiers() & mask) == mask) {
1608: f.setAccessible(true);
1609: return Long.valueOf(f.getLong(null));
1610: }
1611: } catch (Exception ex) {
1612: }
1613: return null;
1614: }
1615:
1616: /**
1617: * Computes the default serial version UID value for the given class.
1618: */
1619: private static long computeDefaultSUID(Class cl) {
1620: if (!Serializable.class.isAssignableFrom(cl)
1621: || Proxy.isProxyClass(cl)) {
1622: return 0L;
1623: }
1624:
1625: try {
1626: ByteArrayOutputStream bout = new ByteArrayOutputStream();
1627: DataOutputStream dout = new DataOutputStream(bout);
1628:
1629: dout.writeUTF(cl.getName());
1630:
1631: int classMods = cl.getModifiers()
1632: & (Modifier.PUBLIC | Modifier.FINAL
1633: | Modifier.INTERFACE | Modifier.ABSTRACT);
1634:
1635: /*
1636: * compensate for javac bug in which ABSTRACT bit was set for an
1637: * interface only if the interface declared methods
1638: */
1639: Method[] methods = cl.getDeclaredMethods();
1640: if ((classMods & Modifier.INTERFACE) != 0) {
1641: classMods = (methods.length > 0) ? (classMods | Modifier.ABSTRACT)
1642: : (classMods & ~Modifier.ABSTRACT);
1643: }
1644: dout.writeInt(classMods);
1645:
1646: if (!cl.isArray()) {
1647: /*
1648: * compensate for change in 1.2FCS in which
1649: * Class.getInterfaces() was modified to return Cloneable and
1650: * Serializable for array classes.
1651: */
1652: Class[] interfaces = cl.getInterfaces();
1653: String[] ifaceNames = new String[interfaces.length];
1654: for (int i = 0; i < interfaces.length; i++) {
1655: ifaceNames[i] = interfaces[i].getName();
1656: }
1657: Arrays.sort(ifaceNames);
1658: for (int i = 0; i < ifaceNames.length; i++) {
1659: dout.writeUTF(ifaceNames[i]);
1660: }
1661: }
1662:
1663: Field[] fields = cl.getDeclaredFields();
1664: MemberSignature[] fieldSigs = new MemberSignature[fields.length];
1665: for (int i = 0; i < fields.length; i++) {
1666: fieldSigs[i] = new MemberSignature(fields[i]);
1667: }
1668: Arrays.sort(fieldSigs, new Comparator() {
1669: public int compare(Object o1, Object o2) {
1670: String name1 = ((MemberSignature) o1).name;
1671: String name2 = ((MemberSignature) o2).name;
1672: return name1.compareTo(name2);
1673: }
1674: });
1675: for (int i = 0; i < fieldSigs.length; i++) {
1676: MemberSignature sig = fieldSigs[i];
1677: int mods = sig.member.getModifiers()
1678: & (Modifier.PUBLIC | Modifier.PRIVATE
1679: | Modifier.PROTECTED | Modifier.STATIC
1680: | Modifier.FINAL | Modifier.VOLATILE | Modifier.TRANSIENT);
1681: if (((mods & Modifier.PRIVATE) == 0)
1682: || ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) {
1683: dout.writeUTF(sig.name);
1684: dout.writeInt(mods);
1685: dout.writeUTF(sig.signature);
1686: }
1687: }
1688:
1689: if (hasStaticInitializer(cl)) {
1690: dout.writeUTF("<clinit>");
1691: dout.writeInt(Modifier.STATIC);
1692: dout.writeUTF("()V");
1693: }
1694:
1695: Constructor[] cons = cl.getDeclaredConstructors();
1696: MemberSignature[] consSigs = new MemberSignature[cons.length];
1697: for (int i = 0; i < cons.length; i++) {
1698: consSigs[i] = new MemberSignature(cons[i]);
1699: }
1700: Arrays.sort(consSigs, new Comparator() {
1701: public int compare(Object o1, Object o2) {
1702: String sig1 = ((MemberSignature) o1).signature;
1703: String sig2 = ((MemberSignature) o2).signature;
1704: return sig1.compareTo(sig2);
1705: }
1706: });
1707: for (int i = 0; i < consSigs.length; i++) {
1708: MemberSignature sig = consSigs[i];
1709: int mods = sig.member.getModifiers()
1710: & (Modifier.PUBLIC | Modifier.PRIVATE
1711: | Modifier.PROTECTED | Modifier.STATIC
1712: | Modifier.FINAL
1713: | Modifier.SYNCHRONIZED
1714: | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT);
1715: if ((mods & Modifier.PRIVATE) == 0) {
1716: dout.writeUTF("<init>");
1717: dout.writeInt(mods);
1718: dout.writeUTF(sig.signature.replace('/', '.'));
1719: }
1720: }
1721:
1722: MemberSignature[] methSigs = new MemberSignature[methods.length];
1723: for (int i = 0; i < methods.length; i++) {
1724: methSigs[i] = new MemberSignature(methods[i]);
1725: }
1726: Arrays.sort(methSigs, new Comparator() {
1727: public int compare(Object o1, Object o2) {
1728: MemberSignature ms1 = (MemberSignature) o1;
1729: MemberSignature ms2 = (MemberSignature) o2;
1730: int comp = ms1.name.compareTo(ms2.name);
1731: if (comp == 0) {
1732: comp = ms1.signature.compareTo(ms2.signature);
1733: }
1734: return comp;
1735: }
1736: });
1737: for (int i = 0; i < methSigs.length; i++) {
1738: MemberSignature sig = methSigs[i];
1739: int mods = sig.member.getModifiers()
1740: & (Modifier.PUBLIC | Modifier.PRIVATE
1741: | Modifier.PROTECTED | Modifier.STATIC
1742: | Modifier.FINAL
1743: | Modifier.SYNCHRONIZED
1744: | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT);
1745: if ((mods & Modifier.PRIVATE) == 0) {
1746: dout.writeUTF(sig.name);
1747: dout.writeInt(mods);
1748: dout.writeUTF(sig.signature.replace('/', '.'));
1749: }
1750: }
1751:
1752: dout.flush();
1753:
1754: MessageDigest md = MessageDigest.getInstance("SHA");
1755: byte[] hashBytes = md.digest(bout.toByteArray());
1756: long hash = 0;
1757: for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
1758: hash = (hash << 8) | (hashBytes[i] & 0xFF);
1759: }
1760: return hash;
1761: } catch (IOException ex) {
1762: throw new InternalError();
1763: } catch (NoSuchAlgorithmException ex) {
1764: throw new SecurityException(ex.getMessage());
1765: }
1766: }
1767:
1768: /**
1769: * Returns true if the given class defines a static initializer method,
1770: * false otherwise.
1771: */
1772: private native static boolean hasStaticInitializer(Class cl);
1773:
1774: /**
1775: * Class for computing and caching field/constructor/method signatures
1776: * during serialVersionUID calculation.
1777: */
1778: private static class MemberSignature {
1779:
1780: public final Member member;
1781: public final String name;
1782: public final String signature;
1783:
1784: public MemberSignature(Field field) {
1785: member = field;
1786: name = field.getName();
1787: signature = getClassSignature(field.getType());
1788: }
1789:
1790: public MemberSignature(Constructor cons) {
1791: member = cons;
1792: name = cons.getName();
1793: signature = getMethodSignature(cons.getParameterTypes(),
1794: Void.TYPE);
1795: }
1796:
1797: public MemberSignature(Method meth) {
1798: member = meth;
1799: name = meth.getName();
1800: signature = getMethodSignature(meth.getParameterTypes(),
1801: meth.getReturnType());
1802: }
1803: }
1804:
1805: /**
1806: * Class for setting and retrieving serializable field values in batch.
1807: */
1808: // REMIND: dynamically generate these?
1809: private static class FieldReflector {
1810:
1811: /** handle for performing unsafe operations */
1812: private static final Unsafe unsafe = Unsafe.getUnsafe();
1813:
1814: /** fields to operate on */
1815: private final ObjectStreamField[] fields;
1816: /** number of primitive fields */
1817: private final int numPrimFields;
1818: /** unsafe field keys */
1819: private final long[] keys;
1820: /** field data offsets */
1821: private final int[] offsets;
1822: /** field type codes */
1823: private final char[] typeCodes;
1824: /** field types */
1825: private final Class[] types;
1826:
1827: /**
1828: * Constructs FieldReflector capable of setting/getting values from the
1829: * subset of fields whose ObjectStreamFields contain non-null
1830: * reflective Field objects. ObjectStreamFields with null Fields are
1831: * treated as filler, for which get operations return default values
1832: * and set operations discard given values.
1833: */
1834: FieldReflector(ObjectStreamField[] fields) {
1835: this .fields = fields;
1836: int nfields = fields.length;
1837: keys = new long[nfields];
1838: offsets = new int[nfields];
1839: typeCodes = new char[nfields];
1840: ArrayList typeList = new ArrayList();
1841:
1842: for (int i = 0; i < nfields; i++) {
1843: ObjectStreamField f = fields[i];
1844: Field rf = f.getField();
1845: keys[i] = (rf != null) ? unsafe.objectFieldOffset(rf)
1846: : Unsafe.INVALID_FIELD_OFFSET;
1847: offsets[i] = f.getOffset();
1848: typeCodes[i] = f.getTypeCode();
1849: if (!f.isPrimitive()) {
1850: typeList.add((rf != null) ? rf.getType() : null);
1851: }
1852: }
1853:
1854: types = (Class[]) typeList.toArray(new Class[typeList
1855: .size()]);
1856: numPrimFields = nfields - types.length;
1857: }
1858:
1859: /**
1860: * Returns list of ObjectStreamFields representing fields operated on
1861: * by this reflector. The shared/unshared values and Field objects
1862: * contained by ObjectStreamFields in the list reflect their bindings
1863: * to locally defined serializable fields.
1864: */
1865: ObjectStreamField[] getFields() {
1866: return fields;
1867: }
1868:
1869: /**
1870: * Fetches the serializable primitive field values of object obj and
1871: * marshals them into byte array buf starting at offset 0. The caller
1872: * is responsible for ensuring that obj is of the proper type.
1873: */
1874: void getPrimFieldValues(Object obj, byte[] buf) {
1875: if (obj == null) {
1876: throw new NullPointerException();
1877: }
1878: /* assuming checkDefaultSerialize() has been called on the class
1879: * descriptor this FieldReflector was obtained from, no field keys
1880: * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
1881: */
1882: for (int i = 0; i < numPrimFields; i++) {
1883: long key = keys[i];
1884: int off = offsets[i];
1885: switch (typeCodes[i]) {
1886: case 'Z':
1887: Bits.putBoolean(buf, off, unsafe.getBoolean(obj,
1888: key));
1889: break;
1890:
1891: case 'B':
1892: buf[off] = unsafe.getByte(obj, key);
1893: break;
1894:
1895: case 'C':
1896: Bits.putChar(buf, off, unsafe.getChar(obj, key));
1897: break;
1898:
1899: case 'S':
1900: Bits.putShort(buf, off, unsafe.getShort(obj, key));
1901: break;
1902:
1903: case 'I':
1904: Bits.putInt(buf, off, unsafe.getInt(obj, key));
1905: break;
1906:
1907: case 'F':
1908: Bits.putFloat(buf, off, unsafe.getFloat(obj, key));
1909: break;
1910:
1911: case 'J':
1912: Bits.putLong(buf, off, unsafe.getLong(obj, key));
1913: break;
1914:
1915: case 'D':
1916: Bits
1917: .putDouble(buf, off, unsafe.getDouble(obj,
1918: key));
1919: break;
1920:
1921: default:
1922: throw new InternalError();
1923: }
1924: }
1925: }
1926:
1927: /**
1928: * Sets the serializable primitive fields of object obj using values
1929: * unmarshalled from byte array buf starting at offset 0. The caller
1930: * is responsible for ensuring that obj is of the proper type.
1931: */
1932: void setPrimFieldValues(Object obj, byte[] buf) {
1933: if (obj == null) {
1934: throw new NullPointerException();
1935: }
1936: for (int i = 0; i < numPrimFields; i++) {
1937: long key = keys[i];
1938: if (key == Unsafe.INVALID_FIELD_OFFSET) {
1939: continue; // discard value
1940: }
1941: int off = offsets[i];
1942: switch (typeCodes[i]) {
1943: case 'Z':
1944: unsafe.putBoolean(obj, key, Bits.getBoolean(buf,
1945: off));
1946: break;
1947:
1948: case 'B':
1949: unsafe.putByte(obj, key, buf[off]);
1950: break;
1951:
1952: case 'C':
1953: unsafe.putChar(obj, key, Bits.getChar(buf, off));
1954: break;
1955:
1956: case 'S':
1957: unsafe.putShort(obj, key, Bits.getShort(buf, off));
1958: break;
1959:
1960: case 'I':
1961: unsafe.putInt(obj, key, Bits.getInt(buf, off));
1962: break;
1963:
1964: case 'F':
1965: unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
1966: break;
1967:
1968: case 'J':
1969: unsafe.putLong(obj, key, Bits.getLong(buf, off));
1970: break;
1971:
1972: case 'D':
1973: unsafe
1974: .putDouble(obj, key, Bits.getDouble(buf,
1975: off));
1976: break;
1977:
1978: default:
1979: throw new InternalError();
1980: }
1981: }
1982: }
1983:
1984: /**
1985: * Fetches the serializable object field values of object obj and
1986: * stores them in array vals starting at offset 0. The caller is
1987: * responsible for ensuring that obj is of the proper type.
1988: */
1989: void getObjFieldValues(Object obj, Object[] vals) {
1990: if (obj == null) {
1991: throw new NullPointerException();
1992: }
1993: /* assuming checkDefaultSerialize() has been called on the class
1994: * descriptor this FieldReflector was obtained from, no field keys
1995: * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
1996: */
1997: for (int i = numPrimFields; i < fields.length; i++) {
1998: switch (typeCodes[i]) {
1999: case 'L':
2000: case '[':
2001: vals[offsets[i]] = unsafe.getObject(obj, keys[i]);
2002: break;
2003:
2004: default:
2005: throw new InternalError();
2006: }
2007: }
2008: }
2009:
2010: /**
2011: * Sets the serializable object fields of object obj using values from
2012: * array vals starting at offset 0. The caller is responsible for
2013: * ensuring that obj is of the proper type; however, attempts to set a
2014: * field with a value of the wrong type will trigger an appropriate
2015: * ClassCastException.
2016: */
2017: void setObjFieldValues(Object obj, Object[] vals) {
2018: if (obj == null) {
2019: throw new NullPointerException();
2020: }
2021: for (int i = numPrimFields; i < fields.length; i++) {
2022: long key = keys[i];
2023: if (key == Unsafe.INVALID_FIELD_OFFSET) {
2024: continue; // discard value
2025: }
2026: switch (typeCodes[i]) {
2027: case 'L':
2028: case '[':
2029: Object val = vals[offsets[i]];
2030: if (val != null
2031: && !types[i - numPrimFields]
2032: .isInstance(val)) {
2033: Field f = fields[i].getField();
2034: throw new ClassCastException(
2035: "cannot assign instance of "
2036: + val.getClass().getName()
2037: + " to field "
2038: + f.getDeclaringClass()
2039: .getName() + "."
2040: + f.getName() + " of type "
2041: + f.getType().getName()
2042: + " in instance of "
2043: + obj.getClass().getName());
2044: }
2045: unsafe.putObject(obj, key, val);
2046: break;
2047:
2048: default:
2049: throw new InternalError();
2050: }
2051: }
2052: }
2053: }
2054:
2055: /**
2056: * Matches given set of serializable fields with serializable fields
2057: * described by the given local class descriptor, and returns a
2058: * FieldReflector instance capable of setting/getting values from the
2059: * subset of fields that match (non-matching fields are treated as filler,
2060: * for which get operations return default values and set operations
2061: * discard given values). Throws InvalidClassException if unresolvable
2062: * type conflicts exist between the two sets of fields.
2063: */
2064: private static FieldReflector getReflector(
2065: ObjectStreamField[] fields, ObjectStreamClass localDesc)
2066: throws InvalidClassException {
2067: // class irrelevant if no fields
2068: Class cl = (localDesc != null && fields.length > 0) ? localDesc.cl
2069: : null;
2070: processQueue(Caches.reflectorsQueue, Caches.reflectors);
2071: FieldReflectorKey key = new FieldReflectorKey(cl, fields,
2072: Caches.reflectorsQueue);
2073: Reference<?> ref = Caches.reflectors.get(key);
2074: Object entry = null;
2075: if (ref != null) {
2076: entry = ref.get();
2077: }
2078: EntryFuture future = null;
2079: if (entry == null) {
2080: EntryFuture newEntry = new EntryFuture();
2081: Reference<?> newRef = new SoftReference<EntryFuture>(
2082: newEntry);
2083: do {
2084: if (ref != null) {
2085: Caches.reflectors.remove(key, ref);
2086: }
2087: ref = Caches.reflectors.putIfAbsent(key, newRef);
2088: if (ref != null) {
2089: entry = ref.get();
2090: }
2091: } while (ref != null && entry == null);
2092: if (entry == null) {
2093: future = newEntry;
2094: }
2095: }
2096:
2097: if (entry instanceof FieldReflector) { // check common case first
2098: return (FieldReflector) entry;
2099: } else if (entry instanceof EntryFuture) {
2100: entry = ((EntryFuture) entry).get();
2101: } else if (entry == null) {
2102: try {
2103: entry = new FieldReflector(matchFields(fields,
2104: localDesc));
2105: } catch (Throwable th) {
2106: entry = th;
2107: }
2108: future.set(entry);
2109: Caches.reflectors
2110: .put(key, new SoftReference<Object>(entry));
2111: }
2112:
2113: if (entry instanceof FieldReflector) {
2114: return (FieldReflector) entry;
2115: } else if (entry instanceof InvalidClassException) {
2116: throw (InvalidClassException) entry;
2117: } else if (entry instanceof RuntimeException) {
2118: throw (RuntimeException) entry;
2119: } else if (entry instanceof Error) {
2120: throw (Error) entry;
2121: } else {
2122: throw new InternalError("unexpected entry: " + entry);
2123: }
2124: }
2125:
2126: /**
2127: * FieldReflector cache lookup key. Keys are considered equal if they
2128: * refer to the same class and equivalent field formats.
2129: */
2130: private static class FieldReflectorKey extends
2131: WeakReference<Class<?>> {
2132:
2133: private final String sigs;
2134: private final int hash;
2135: private final boolean nullClass;
2136:
2137: FieldReflectorKey(Class cl, ObjectStreamField[] fields,
2138: ReferenceQueue<Class<?>> queue) {
2139: super (cl, queue);
2140: nullClass = (cl == null);
2141: StringBuilder sbuf = new StringBuilder();
2142: for (int i = 0; i < fields.length; i++) {
2143: ObjectStreamField f = fields[i];
2144: sbuf.append(f.getName()).append(f.getSignature());
2145: }
2146: sigs = sbuf.toString();
2147: hash = System.identityHashCode(cl) + sigs.hashCode();
2148: }
2149:
2150: public int hashCode() {
2151: return hash;
2152: }
2153:
2154: public boolean equals(Object obj) {
2155: if (obj == this ) {
2156: return true;
2157: }
2158:
2159: if (obj instanceof FieldReflectorKey) {
2160: FieldReflectorKey other = (FieldReflectorKey) obj;
2161: Class<?> referent;
2162: return (nullClass ? other.nullClass
2163: : ((referent = get()) != null)
2164: && (referent == other.get()))
2165: && sigs.equals(other.sigs);
2166: } else {
2167: return false;
2168: }
2169: }
2170: }
2171:
2172: /**
2173: * Matches given set of serializable fields with serializable fields
2174: * obtained from the given local class descriptor (which contain bindings
2175: * to reflective Field objects). Returns list of ObjectStreamFields in
2176: * which each ObjectStreamField whose signature matches that of a local
2177: * field contains a Field object for that field; unmatched
2178: * ObjectStreamFields contain null Field objects. Shared/unshared settings
2179: * of the returned ObjectStreamFields also reflect those of matched local
2180: * ObjectStreamFields. Throws InvalidClassException if unresolvable type
2181: * conflicts exist between the two sets of fields.
2182: */
2183: private static ObjectStreamField[] matchFields(
2184: ObjectStreamField[] fields, ObjectStreamClass localDesc)
2185: throws InvalidClassException {
2186: ObjectStreamField[] localFields = (localDesc != null) ? localDesc.fields
2187: : NO_FIELDS;
2188:
2189: /*
2190: * Even if fields == localFields, we cannot simply return localFields
2191: * here. In previous implementations of serialization,
2192: * ObjectStreamField.getType() returned Object.class if the
2193: * ObjectStreamField represented a non-primitive field and belonged to
2194: * a non-local class descriptor. To preserve this (questionable)
2195: * behavior, the ObjectStreamField instances returned by matchFields
2196: * cannot report non-primitive types other than Object.class; hence
2197: * localFields cannot be returned directly.
2198: */
2199:
2200: ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2201: for (int i = 0; i < fields.length; i++) {
2202: ObjectStreamField f = fields[i], m = null;
2203: for (int j = 0; j < localFields.length; j++) {
2204: ObjectStreamField lf = localFields[j];
2205: if (f.getName().equals(lf.getName())) {
2206: if ((f.isPrimitive() || lf.isPrimitive())
2207: && f.getTypeCode() != lf.getTypeCode()) {
2208: throw new InvalidClassException(localDesc.name,
2209: "incompatible types for field "
2210: + f.getName());
2211: }
2212: if (lf.getField() != null) {
2213: m = new ObjectStreamField(lf.getField(), lf
2214: .isUnshared(), false);
2215: } else {
2216: m = new ObjectStreamField(lf.getName(), lf
2217: .getSignature(), lf.isUnshared());
2218: }
2219: }
2220: }
2221: if (m == null) {
2222: m = new ObjectStreamField(f.getName(),
2223: f.getSignature(), false);
2224: }
2225: m.setOffset(f.getOffset());
2226: matches[i] = m;
2227: }
2228: return matches;
2229: }
2230:
2231: /**
2232: * Removes from the specified map any keys that have been enqueued
2233: * on the specified reference queue.
2234: */
2235: static void processQueue(ReferenceQueue<Class<?>> queue,
2236: ConcurrentMap<? extends WeakReference<Class<?>>, ?> map) {
2237: Reference<? extends Class<?>> ref;
2238: while ((ref = queue.poll()) != null) {
2239: map.remove(ref);
2240: }
2241: }
2242:
2243: /**
2244: * Weak key for Class objects.
2245: *
2246: **/
2247: static class WeakClassKey extends WeakReference<Class<?>> {
2248: /**
2249: * saved value of the referent's identity hash code, to maintain
2250: * a consistent hash code after the referent has been cleared
2251: */
2252: private final int hash;
2253:
2254: /**
2255: * Create a new WeakClassKey to the given object, registered
2256: * with a queue.
2257: */
2258: WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
2259: super (cl, refQueue);
2260: hash = System.identityHashCode(cl);
2261: }
2262:
2263: /**
2264: * Returns the identity hash code of the original referent.
2265: */
2266: public int hashCode() {
2267: return hash;
2268: }
2269:
2270: /**
2271: * Returns true if the given object is this identical
2272: * WeakClassKey instance, or, if this object's referent has not
2273: * been cleared, if the given object is another WeakClassKey
2274: * instance with the identical non-null referent as this one.
2275: */
2276: public boolean equals(Object obj) {
2277: if (obj == this ) {
2278: return true;
2279: }
2280:
2281: if (obj instanceof WeakClassKey) {
2282: Object referent = get();
2283: return (referent != null)
2284: && (referent == ((WeakClassKey) obj).get());
2285: } else {
2286: return false;
2287: }
2288: }
2289: }
2290: }
|