6.4 Changes Made by the Enhancer

The remainder of this chapter describes in more detail some of the changes made to your class files by the enhancer. We do not cover all the methods added by an enhancer. Nor do we explain all of the functionality added to a class to enable transparent persistence. You do not need to understand all the details of class enhancement; your application should never directly use the fields and methods added by enhancement. But it is useful, though not necessary, to have a basic understanding of how your classes are modified by the process. We list all the fields that are added by class enhancement and some of the methods. To gain a thorough understanding of the enhancement contract, you should read the JDO specification. You do not need to understand the remaining material in this chapter to use JDO. If you are not interested in the details of enhancement, you can skip over the remainder of this chapter.

The enhancer adds an interface, fields, and methods to your persistent classes so that they can be stored in a datastore transparently. The enhancer adds the following line to the definition of a persistent class:

implements javax.jdo.spi.PersistenceCapable

The PersistenceCapable interface defines methods the JDO implementation uses to manage instances in a JDO runtime environment. The enhancer adds the implementation of these PersistenceCapable methods. It also adds metadata information to each class, which is used by the JDO runtime environment to manage the fields.

A getfield byte-code instruction performs all field-read accesses at the class-file level, and a putfield byte-code instruction performs all field modifications. There is a different getfield and putfield instruction for each type in Java. The JDO implementation mediates all accesses and updates to a managed field to ensure its value has been retrieved from the datastore before your application accesses it and all modifications have been captured. The enhancer replaces each getfield and putfield byte-code instruction for a managed field with a call to a method it generates to provide this mediation.

6.4.1 Metadata

The enhancer generates its own metadata, based on the class declaration and the metadata you have defined. This metadata is added during enhancement to each persistent class as static fields. The JDO runtime environment uses this information to manage the fields of the class. Access of this metadata information is much more efficient than using Java reflection.

6.4.1.1 Class metadata

The following static fields are added to represent class-level metadata:

private final static int       jdoInheritedFieldCount;
private final static Class     jdoPersistenceCapableSuperclass;
private final static long      serialVersionUID;
jdoInheritedFieldCount

Initialized to the number of managed fields inherited from superclasses.

jdoPersistenceCapableSuperclass

Initialized to the Class instance of the most immediate superclass that is persistent within the hierarchy. It is null if the class is the topmost persistent class in the hierarchy or if it is not in an inheritance hierarchy.

serialVersionUID

Added only if it does not already exist in the class. It is used with serialization and has the same value as the class in its non-enhanced form. This allows you to serialize a persistent instance and later deserialize it into an instance of the class in its unenhanced form.

6.4.1.2 Field metadata

The following fields provide information about each managed field in the class:

private final static String[]  jdoFieldNames;
private final static Class[]   jdoFieldTypes;
private final static byte[]    jdoFieldFlags;

Each managed field has an index value that is used to identify it uniquely. A field's index value is used to access its entries in these arrays.

jdoFieldNames

Contains the name of each field.

jdoFieldTypes

Contains the type of each field.

jdoFieldFlags

Contains some flags to indicate the form of access and mediation that should be performed for the fields. It also has a flag to indicate whether the field should be serialized.

6.4.1.3 Class registration

A static initializer is added to each persistent class. This static initialization code is executed after any other initialization you may have defined in the class. It registers the class with the JDO runtime environment by calling the static registerClass( ) method defined in the JDOImplHelper class. This class is defined in the javax.jdo.spi package, and it provides utility methods used by JDO implementations. If the persistent class is not abstract, a helper instance of the class is constructed and passed to registerClass( ).

The generated static metadata fields are passed as arguments to registerClass( ). The JDOImplHelper class provides methods that allow this information to be shared by all JDO implementations that manage instances of the class in the JVM.

6.4.2 Instance-Level Data

The reference enhancer adds the following two fields to the least-derived (topmost) persistent class in an inheritance hierarchy:

protected transient javax.jdo.spi.StateManager  jdoStateManager;
protected transient byte                        jdoFlags;

These are the only two fields added to a class that affect the size of an instance in memory.

jdoStateManager

This field contains a reference to the StateManager that manages the fields of persistent and transient transactional instances. This field is null for nontransactional transient instances.

jdoFlags

This field indicates the state of the fields in the instance.

The StateManager instance referenced by jdoStateManager manages the value of the jdoFlags field. Since these two fields are transient, they do not impact serialization.

6.4.3 Field Mediation

Access to a managed field is mediated by the JDO implementation to ensure its value has been retrieved from the datastore before it is accessed by the application and to capture all application modifications to the field. Nonmanaged fields are ignored by the enhancer. No enhancement is performed on access to nonmanaged fields, because they lie outside the domain of persistence and may be accessed like any normal Java field, obeying the accessibility rules dictated by the public, private, and protected modifiers and default package access.

6.4.3.1 Generated accessors and mutators

The enhancer generates a get and set method for each managed field in a persistent class. These methods have the following form:

final static mmm ttt   jdoGetField(theclass instance);
final static mmm void  jdoSetField(theclass instance, ttt newValue);

with the following elements:

Field

This is the name of the field in the class.

mmm

This is the same access modifier (public, private, or protected) as the corresponding field in the nonenhanced class. This ensures the security of instances by preserving the same field access restrictions that are declared in the class.

ttt

This is the type of the field in the nonenhanced class.

theclass

This is the class in which this static method is defined. This parameter is used to pass an instance of the class to the static method.

These generated methods examine the values in jdoFlags and jdoFieldFlags and perform the appropriate behavior to get or set the field's value. These methods provide access mediation of the managed fields.

The enhancer must enhance every class that has a getfield or putfield byte-code instruction for a managed field of a persistent class. Each getfield is replaced with a call to the corresponding jdoGetField( ), and each putfield is replaced with a call to the corresponding jdoSetField( ). The jdoSetField( ) methods enable the StateManager to track which fields in each instance are modified by the application. The PersistenceManager can then automatically propagate all instance modifications to the datastore at transaction commit.

As it turns out, the stack signature required for the getfield and putfield byte codes matches the stack signature needed for the call to jdoGetField( ) and jdoSetField( ). The enhancer needs to replace only a single byte-code instructiongetfield or putfieldwithout needing to add or alter any other byte-code instructions. So, replacing these byte codes does not increase the size of the byte code in your class.

The timing of managed field accesses, for both transient and persistent instances, will be different from the timing of field accesses in an unenhanced class, because the getfield and putfield byte-code instructions are replaced with calls to these generated static methods. But the methods are defined as static and final, which reduces their method-call overhead. Furthermore, since they are static and final methods, a HotSpot or other Just-In-Time (JIT) environment can optimize the byte code by removing the method call entirely.

6.4.3.2 Management of field values

The methods described in this section are used to mediate application access to managed fields. The StateManager instance referenced by the jdoStateManager field manages the state of the managed fields in a persistent instance by using the following two methods added by enhancement:

public void jdoReplaceField(int field);
public void jdoProvideField(int field);

The parameter passed to these methods is the index value that uniquely identifies a field.

Since jdoReplaceField( ) and jdoProvideField( ) are placed in the class, the StateManager can access and alter every managed field, regardless of the field's access modifier (e.g., default package-level, private, and protected). At the same time, it preserves the field-accessibility restrictions for all classes except the StateManager, which must be granted permission explicitly in Java runtime environments that enforce security. You must use the JDOPermission class, described in Chapter 2, to grant permission to the StateManager.

The StateManager uses jdoReplaceField( ) to store values from the datastore in the instance. jdoReplaceField( ) calls the StateManager method replacingXXXField( ) to get a value for the field. The XXX corresponds to one of the specific field types handled in JDO. The StateManager has a replacingXXXField( ) method for each field type. The jdoReplaceField( ) method assigns to the field the value that is returned by replacingXXXField( ).

The StateManager uses jdoProvideField( ) to retrieve a field value from an instance. jdoProvideField( ) calls the StateManager method providedXXXField( ) to access a field's value. There is a providedXXXField( ) method for each field type, denoted by XXX.