diff options
Diffstat (limited to 'libjava/classpath/java/io/ObjectStreamClass.java')
-rw-r--r-- | libjava/classpath/java/io/ObjectStreamClass.java | 463 |
1 files changed, 288 insertions, 175 deletions
diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java index abb26d839ad..52a1ad42873 100644 --- a/libjava/classpath/java/io/ObjectStreamClass.java +++ b/libjava/classpath/java/io/ObjectStreamClass.java @@ -1,6 +1,6 @@ /* ObjectStreamClass.java -- Class used to write class information about serialized objects. - Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -59,8 +59,14 @@ import java.security.Security; import java.util.Arrays; import java.util.Comparator; import java.util.Hashtable; -import java.util.Vector; +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ public class ObjectStreamClass implements Serializable { static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0]; @@ -80,7 +86,7 @@ public class ObjectStreamClass implements Serializable * * @see java.io.Serializable */ - public static ObjectStreamClass lookup(Class cl) + public static ObjectStreamClass lookup(Class<?> cl) { if (cl == null) return null; @@ -132,7 +138,7 @@ public class ObjectStreamClass implements Serializable * * @see java.io.ObjectInputStream */ - public Class forClass() + public Class<?> forClass() { return clazz; } @@ -235,37 +241,45 @@ public class ObjectStreamClass implements Serializable return superClass; } - - // returns an array of ObjectStreamClasses that represent the super - // classes of CLAZZ and CLAZZ itself in order from most super to - // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ - // that is serializable. - static ObjectStreamClass[] getObjectStreamClasses(Class clazz) + /** + * returns an array of ObjectStreamClasses that represent the super + * classes of the class represented by this and the class + * represented by this itself in order from most super to this. + * ObjectStreamClass[0] is the highest superclass of this that is + * serializable. + * + * The result of consecutive calls this hierarchy() will be the same + * array instance. + * + * @return an array of ObjectStreamClass representing the + * super-class hierarchy of serializable classes. + */ + ObjectStreamClass[] hierarchy() { - ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); - - if (osc == null) - return new ObjectStreamClass[0]; - else - { - Vector oscs = new Vector(); - - while (osc != null) - { - oscs.addElement (osc); - osc = osc.getSuper(); - } - - int count = oscs.size(); - ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; - - for (int i = count - 1; i >= 0; i--) - sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); - - return sorted_oscs; + ObjectStreamClass[] result = hierarchy; + if (result == null) + { + int d = 0; + + for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + d++; + + result = new ObjectStreamClass[d]; + + for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + { + result[--d] = osc; + } + + hierarchy = result; } + return result; } + /** + * Cache for hierarchy() result. + */ + private ObjectStreamClass[] hierarchy = null; // Returns an integer that consists of bit-flags that indicate // properties of the class represented by this ObjectStreamClass. @@ -298,7 +312,7 @@ public class ObjectStreamClass implements Serializable * already set UID is found. */ void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException - { + {hierarchy = null; this.clazz = cl; cacheMethods(); @@ -309,8 +323,8 @@ public class ObjectStreamClass implements Serializable else { // Check that the actual UID of the resolved class matches the UID from - // the stream. - if (uid != class_uid) + // the stream. Mismatches for array classes are ignored. + if (!cl.isArray() && uid != class_uid) { String msg = cl + ": Local class not compatible: stream serialVersionUID=" @@ -425,6 +439,7 @@ public class ObjectStreamClass implements Serializable void setSuperclass (ObjectStreamClass osc) { superClass = osc; + hierarchy = null; } void calculateOffsets() @@ -547,21 +562,62 @@ outer: return null; } - private void cacheMethods() + /** + * Helper routine to check if a class was loaded by boot or + * application class loader. Classes for which this is not the case + * should not be cached since caching prevent class file garbage + * collection. + * + * @param cl a class + * + * @return true if cl was loaded by boot or application class loader, + * false if cl was loaded by a user class loader. + */ + private static boolean loadedByBootOrApplicationClassLoader(Class cl) { - Method[] methods = forClass().getDeclaredMethods(); + ClassLoader l = cl.getClassLoader(); + return + ( l == null /* boot loader */ ) + || (l == ClassLoader.getSystemClassLoader() /* application loader */); + } - readObjectMethod = findMethod(methods, "readObject", - new Class[] { ObjectInputStream.class }, - Void.TYPE, true); - writeObjectMethod = findMethod(methods, "writeObject", - new Class[] { ObjectOutputStream.class }, - Void.TYPE, true); + static Hashtable methodCache = new Hashtable(); + + static final Class[] readObjectSignature = { ObjectInputStream.class }; + static final Class[] writeObjectSignature = { ObjectOutputStream.class }; - // readResolve and writeReplace can be in parent classes, as long as they - // are accessible from this class. - readResolveMethod = findAccessibleMethod("readResolve", forClass()); - writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); + private void cacheMethods() + { + Class cl = forClass(); + Method[] cached = (Method[]) methodCache.get(cl); + if (cached == null) + { + cached = new Method[4]; + Method[] methods = cl.getDeclaredMethods(); + + cached[0] = findMethod(methods, "readObject", + readObjectSignature, + Void.TYPE, true); + cached[1] = findMethod(methods, "writeObject", + writeObjectSignature, + Void.TYPE, true); + + // readResolve and writeReplace can be in parent classes, as long as they + // are accessible from this class. + cached[2] = findAccessibleMethod("readResolve", cl); + cached[3] = findAccessibleMethod("writeReplace", cl); + + /* put in cache if classes not loaded by user class loader. + * For a user class loader, the cache may otherwise grow + * without limit. + */ + if (loadedByBootOrApplicationClassLoader(cl)) + methodCache.put(cl,cached); + } + readObjectMethod = cached[0]; + writeObjectMethod = cached[1]; + readResolveMethod = cached[2]; + writeReplaceMethod = cached[3]; } private ObjectStreamClass(Class cl) @@ -713,152 +769,208 @@ outer: calculateOffsets(); } + static Hashtable uidCache = new Hashtable(); + // Returns the serial version UID defined by class, or if that // isn't present, calculates value of serial version UID. private long getClassUID(Class cl) { - try + long result = 0; + Long cache = (Long) uidCache.get(cl); + if (cache != null) + result = cache.longValue(); + else { - // Use getDeclaredField rather than getField, since serialVersionUID - // may not be public AND we only want the serialVersionUID of this - // class, not a superclass or interface. - final Field suid = cl.getDeclaredField("serialVersionUID"); - SetAccessibleAction setAccessible = new SetAccessibleAction(suid); - AccessController.doPrivileged(setAccessible); - int modifiers = suid.getModifiers(); - - if (Modifier.isStatic(modifiers) - && Modifier.isFinal(modifiers) - && suid.getType() == Long.TYPE) - return suid.getLong(null); + try + { + result = getClassUIDFromField(cl); + } + catch (NoSuchFieldException ignore) + { + try + { + result = calculateClassUID(cl); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException + ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName(), e); + } + catch (IOException ioe) + { + throw new RuntimeException(ioe); + } + } + + if (loadedByBootOrApplicationClassLoader(cl)) + uidCache.put(cl,new Long(result)); } - catch (NoSuchFieldException ignore) + return result; + } + + /** + * Search for a serialVersionUID field in the given class and read + * its value. + * + * @return the contents of the serialVersionUID field + * + * @throws NoSuchFieldException if such a field does not exist or is + * not static, not final, not of type Long or not accessible. + */ + long getClassUIDFromField(Class cl) + throws NoSuchFieldException + { + long result; + + try { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + final Field suid = cl.getDeclaredField("serialVersionUID"); + SetAccessibleAction setAccessible = new SetAccessibleAction(suid); + AccessController.doPrivileged(setAccessible); + int modifiers = suid.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && suid.getType() == Long.TYPE) + result = suid.getLong(null); + else + throw new NoSuchFieldException(); } catch (IllegalAccessException ignore) { + throw new NoSuchFieldException(); } - // cl didn't define serialVersionUID, so we have to compute it - try - { - MessageDigest md; - try - { - md = MessageDigest.getInstance("SHA"); - } - catch (NoSuchAlgorithmException e) - { - // If a provider already provides SHA, use it; otherwise, use this. - Gnu gnuProvider = new Gnu(); - Security.addProvider(gnuProvider); - md = MessageDigest.getInstance("SHA"); - } - - DigestOutputStream digest_out = - new DigestOutputStream(nullOutputStream, md); - DataOutputStream data_out = new DataOutputStream(digest_out); - - data_out.writeUTF(cl.getName()); - - int modifiers = cl.getModifiers(); - // just look at interesting bits - modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL - | Modifier.INTERFACE | Modifier.PUBLIC); - data_out.writeInt(modifiers); - - // Pretend that an array has no interfaces, because when array - // serialization was defined (JDK 1.1), arrays didn't have it. - if (! cl.isArray()) - { - Class[] interfaces = cl.getInterfaces(); - Arrays.sort(interfaces, interfaceComparator); - for (int i = 0; i < interfaces.length; i++) - data_out.writeUTF(interfaces[i].getName()); - } - - Field field; - Field[] fields = cl.getDeclaredFields(); - Arrays.sort(fields, memberComparator); - for (int i = 0; i < fields.length; i++) - { - field = fields[i]; - modifiers = field.getModifiers(); - if (Modifier.isPrivate(modifiers) - && (Modifier.isStatic(modifiers) - || Modifier.isTransient(modifiers))) - continue; - - data_out.writeUTF(field.getName()); - data_out.writeInt(modifiers); - data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); - } - - // write class initializer method if present - if (VMObjectStreamClass.hasClassInitializer(cl)) - { - data_out.writeUTF("<clinit>"); - data_out.writeInt(Modifier.STATIC); - data_out.writeUTF("()V"); - } - - Constructor constructor; - Constructor[] constructors = cl.getDeclaredConstructors(); - Arrays.sort (constructors, memberComparator); - for (int i = 0; i < constructors.length; i++) - { - constructor = constructors[i]; - modifiers = constructor.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF("<init>"); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); - } - - Method method; - Method[] methods = cl.getDeclaredMethods(); - Arrays.sort(methods, memberComparator); - for (int i = 0; i < methods.length; i++) - { - method = methods[i]; - modifiers = method.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF(method.getName()); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); - } - - data_out.close(); - byte[] sha = md.digest(); - long result = 0; - int len = sha.length < 8 ? sha.length : 8; - for (int i = 0; i < len; i++) - result += (long) (sha[i] & 0xFF) << (8 * i); + return result; + } - return result; + /** + * Calculate class serial version UID for a class that does not + * define serialVersionUID: + * + * @param cl a class + * + * @return the calculated serial varsion UID. + * + * @throws NoSuchAlgorithmException if SHA algorithm not found + * + * @throws IOException if writing to the DigestOutputStream causes + * an IOException. + */ + long calculateClassUID(Class cl) + throws NoSuchAlgorithmException, IOException + { + long result; + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException - ("The SHA algorithm was not found to use in computing the Serial Version UID for class " - + cl.getName(), e); + // If a provider already provides SHA, use it; otherwise, use this. + Gnu gnuProvider = new Gnu(); + Security.addProvider(gnuProvider); + md = MessageDigest.getInstance("SHA"); + } + + DigestOutputStream digest_out = + new DigestOutputStream(nullOutputStream, md); + DataOutputStream data_out = new DataOutputStream(digest_out); + + data_out.writeUTF(cl.getName()); + + int modifiers = cl.getModifiers(); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt(modifiers); + + // Pretend that an array has no interfaces, because when array + // serialization was defined (JDK 1.1), arrays didn't have it. + if (! cl.isArray()) + { + Class[] interfaces = cl.getInterfaces(); + Arrays.sort(interfaces, interfaceComparator); + for (int i = 0; i < interfaces.length; i++) + data_out.writeUTF(interfaces[i].getName()); + } + + Field field; + Field[] fields = cl.getDeclaredFields(); + Arrays.sort(fields, memberComparator); + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers(); + if (Modifier.isPrivate(modifiers) + && (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers))) + continue; + + data_out.writeUTF(field.getName()); + data_out.writeInt(modifiers); + data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); + } + + // write class initializer method if present + if (VMObjectStreamClass.hasClassInitializer(cl)) + { + data_out.writeUTF("<clinit>"); + data_out.writeInt(Modifier.STATIC); + data_out.writeUTF("()V"); } - catch (IOException ioe) + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors(); + Arrays.sort (constructors, memberComparator); + for (int i = 0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF("<init>"); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods(); + Arrays.sort(methods, memberComparator); + for (int i = 0; i < methods.length; i++) { - throw new RuntimeException(ioe); + method = methods[i]; + modifiers = method.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF(method.getName()); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); } + + data_out.close(); + byte[] sha = md.digest(); + result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i = 0; i < len; i++) + result += (long) (sha[i] & 0xFF) << (8 * i); + + return result; } /** @@ -948,7 +1060,8 @@ outer: public static final ObjectStreamField[] NO_FIELDS = {}; - private static Hashtable classLookupTable = new Hashtable(); + private static Hashtable<Class,ObjectStreamClass> classLookupTable + = new Hashtable<Class,ObjectStreamClass>(); private static final NullOutputStream nullOutputStream = new NullOutputStream(); private static final Comparator interfaceComparator = new InterfaceComparator(); private static final Comparator memberComparator = new MemberComparator(); @@ -956,7 +1069,7 @@ outer: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; private ObjectStreamClass superClass; - private Class clazz; + private Class<?> clazz; private String name; private long uid; private byte flags; |