RFR [9] 8071472: Add field access to support setting final fields in readObject

Chris Hegarty chris.hegarty at oracle.com
Thu Mar 5 13:46:27 UTC 2015


Hi,

Peter Levart and I have been working on an API to support setting of final fields during custom deserialization. Dealing with final fields during deserialization is a pain point only partially addressed in JSR 133 ( in Java 5 ). What we are proposing is a simple API solution to resolve this.

Current limitations ( primary motivation for the new API ): 

 * defaultReadObject can set final fields, but it is somewhat limiting.
   The fields must have a value in the deserialized stream. This is
   not flexible when evolving a classes representation, or when
   dealing with transient final fields. 

 * Setting final fields in readObject using reflection requires
   java.lang.reflect.ReflectPermission("suppressAccessChecks"). 
   Granting this permission, when only required to set final fields
   during custom deserialization, is dangerous. Information
   (possibly confidential) and methods normally unavailable may
   be accessible to malicious code. 

 * It is not possible to grant 'suppressAccessChecks' for particular
   classes or objects, or restrict the ability to set final fields through
   reflection for the period of the readObject callback, and just for
   the object being reconstructed. 
  
 * Using reflection to set final fields can have a performance cost,
   as the reflective implementation may issue a barrier for each
   field set.

Outline of the new API:

 * New interface ObjectInputStream.FieldAccess with overloaded
   set(name, value) methods. Similar in some respects to Get/PutField,
   but with j.l.r.Field semantics.
 
 * The FieldAccess instance is retrieved from ObjectInputStream.fields(),
    which can only be called during a readObject callback. Similar to
    readFields() (returns GetField).

 * FieldAccess, once retrieved, ensures that all final fields are set,
   and set only once. 

 * No behaviour changes to existing code. Unless fields() is called
   existing code is unaffected.

 * Can be used with either GetFields, or defaultReadObject.

Specification:
   http://cr.openjdk.java.net/~chegar/8071472/00/specdiff/

Example:
    class Stamp implements Serializable {
        private transient final long millis;
        Stamp() { millis = System.currentTimeMillis(); }
        public long stamp() { return millis; }

        private void readObject(java.io.ObjectInputStream in) throws Exception {
            in.fields().set("millis", System.currentTimeMillis());
        }
    }

We have a fully working implementation, tests, etc, in the sandbox:
    hg clone http://hg.openjdk.java.net/jdk9/sandbox sandbox
    cd sandbox
    sh get_source.sh
    sh common/bin/hgforest.sh update -r serial-exp-branch
    
  Webrev for convenience:
    http://cr.openjdk.java.net/~chegar/8071472/00/webrev/

Feedback welcome.

-Chris.


More information about the core-libs-dev mailing list