Here is the last patch on serialization. This patch fixes the behaviour of readFields() in ObjectInputStream: * if called multiple times read fields and build field decoder only once. * more documentation * GetField methods should take into account the flags concerning each serialized fields. If transient we have to make it return a default value, if not we try to get it in the pool of read fields. If the field doesn't exist we should throw an exception.
ChangeLog entry:
2003-12-07 Guilhem Lavaux <[EMAIL PROTECTED]>
* java/io/ObjectInputStream.java: (readFields): Changed implementation of GetField. (readClassDescriptor): Documented.
Cheers,
Guilhem.
--- java/io/ObjectInputStream.java.orig 2003-12-04 19:36:56.000000000 +0100 +++ java/io/ObjectInputStream.java 2003-12-04 19:57:15.000000000 +0100 @@ -417,6 +417,22 @@ return ret_val; } + /** + * This method reads a class descriptor from the real input stream + * and use these data to create a new instance of ObjectStreamClass. + * Fields are sorted and ordered for the real read which occurs for + * each instance of the described class. Be aware that if you call that + * method you must ensure that the stream is synchronized, in the other + * case it may be completely desynchronized. + * + * @return A new instance of ObjectStreamClass containing the freshly + * created descriptor. + * @throws ClassNotFoundException if the required class to build the + * descriptor has not been found in the system. + * @throws IOException An input/output error occured. + * @throws InvalidClassException If there was a compatibility problem + * between the class present in the system and the serialized class. + */ protected ObjectStreamClass readClassDescriptor () throws ClassNotFoundException, IOException { @@ -585,7 +601,15 @@ { return Class.forName (osc.getName(), true, currentLoader()); } - + + /** + * This method invokes the method currentClassLoader for the + * current security manager (or build an empty one if it is not + * present). + * + * @return The most recent non-system ClassLoader on the execution stack. + * @see java.lang.SecurityManager#currentClassLoader() + */ private ClassLoader currentLoader () { SecurityManager sm = System.getSecurityManager (); @@ -1014,14 +1038,31 @@ throws IOException, IllegalArgumentException; } + /** + * This method should be called by a method called 'readObject' in the + * deserializing class (if present). It cannot (and should not)be called + * outside of it. Its goal is to read all fields in the real input stream + * and keep them accessible through the [EMAIL PROTECTED] #GetField} class. Calling + * this method will not alterate the deserializing object. + * + * @return A valid freshly created 'GetField' instance to get access to + * the deserialized stream. + * @throws IOException An input/output exception occured. + * @throws ClassNotFoundException + * @throws NotActiveException + */ public GetField readFields () throws IOException, ClassNotFoundException, NotActiveException { if (this.currentObject == null || this.currentObjectStreamClass == null) throw new NotActiveException ("readFields called by non-active class and/or object"); + if (prereadFields != null) + return prereadFields; + if (fieldsAlreadyRead) - throw new NotActiveException ("readFields called but fields already read from stream (by defaultReadObject or readFields)"); + throw new NotActiveException ("readFields called but fields already read from" + + " stream (by defaultReadObject or readFields)"); final ObjectStreamClass clazz = this.currentObjectStreamClass; final byte[] prim_field_data = new byte[clazz.primFieldSize]; @@ -1036,7 +1077,7 @@ objs[i] = readObject (); setBlockDataMode (oldmode); - return new GetField () + prereadFields = new GetField () { public ObjectStreamClass getObjectStreamClass () { @@ -1046,7 +1087,31 @@ public boolean defaulted (String name) throws IOException, IllegalArgumentException { - return clazz.getField (name) == null; + ObjectStreamField f = clazz.getField (name); + + /* First if we have a serialized field use the descriptor */ + if (f != null) + { + /* It is in serialPersistentFields but setClass tells us + * it should not be set. This value is defaulted. + */ + if (f.isPersistent() && !f.isToSet()) + return true; + + return false; + } + + /* This is not a serialized field. There should be + * a default value only if the field really exists. + */ + try + { + return (clazz.forClass().getDeclaredField (name) != null); + } + catch (NoSuchFieldException e) + { + throw new IllegalArgumentException (e.getMessage()); + } } public boolean get (String name, boolean defvalue) @@ -1188,24 +1253,73 @@ throws IllegalArgumentException { ObjectStreamField field = clazz.getField (name); + boolean illegal = false; - if (field == null) - return null; - - Class field_type = field.getType (); + try + { + try + { + Class field_type = field.getType(); + + if (type == field_type || + (type == null && !field_type.isPrimitive())) + { + /* See defaulted */ + return field; + } + + illegal = true; + throw new IllegalArgumentException ("Field requested is of type " + + field_type.getName () + + ", but requested type was " + + (type == null ? + "Object" : type.getName ())); + } + catch (NullPointerException _) + { + /* Here we catch NullPointerException, because it may + only come from the call 'field.getType()'. If field + is null, we have to return null and classpath ethic + say we must try to avoid 'if (xxx == null)'. + */ + } + catch (IllegalArgumentException e) + { + throw e; + } + return null; + } + finally + { + /* If this is an unassigned field we should return + * the default value. + */ + if (!illegal && field != null && !field.isToSet() && field.isPersistent()) + return null; + + /* We do not want to modify transient fields. They should + * be left to 0. + */ + try + { + Field f = clazz.forClass().getDeclaredField (name); + if (Modifier.isTransient(f.getModifiers())) + throw new IllegalArgumentException("no such field (non transient) " + name); + if (field == null && f.getType() != type) + throw new IllegalArgumentException ("Invalid requested type for field " + name); + } + catch (NoSuchFieldException e) + { + if (field == null) + throw new IllegalArgumentException (e.getMessage()); + } + + } - if (type == field_type || - (type == null && ! field_type.isPrimitive ())) - return field; - - throw new IllegalArgumentException ("Field requested is of type " - + field_type.getName () - + ", but requested type was " - + (type == null ? - "Object" : type.getName ())); - } }; + fieldsAlreadyRead = true; + return prereadFields; } /** @@ -1977,6 +2091,7 @@ private boolean fieldsAlreadyRead; private Vector validators; private Hashtable classLookupTable; + private GetField prereadFields; private static boolean dump;
_______________________________________________ Classpath mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/classpath