Currently Being Moderated

Ever wondered why you need to enhance an OpenJPA persistent class, or what exactly does the 'enhancer' do? This post explains why you need to enhance Elastic Path's persistent classes and talks a bit about what OpenJPA enhancer does to our code.

 

An OpenJPA enhancer post-processes the bytecode of the persistent classes that are generated by the Java compiler. During post-processing, the enhancer inserts necessary code into the generated bytecode so that it can include persistence features in classes.

 

For example, after the enhancer runs, persistent classes will implement the org.apache.renamed.openjpa.enhance.PersistenceCapable and the java.io.Externalizable interfaces. Some of the fields and methods generated include the ones that are required for implementing the PersistenceCapable interface, as well as the ones from Externalizable interface, which helps with the process of reading and writing data to disk. Other features that gets implemented into an enhanced class are the ability to lazy load collection proxies and a method of doing dirty-field checking, which is used to determine what needs to be persisted.

Important: Always use the accessor methods to access the fields of persistent objects. Accessing fields directly will bypass the dirty-field checking and lazy-loading features, which will cause persisted fields to be out of date.

 

Sample methods inserted by enhancer:
public void readExternal(ObjectInput objectinput)
    throws IOException, ClassNotFoundException {
    ...
}
 
public void writeExternal(ObjectOutput objectoutput)
    throws IOException {
    ...
}
 
public boolean pcIsDirty() {
    ...
}
 
public boolean pcIsPersistent() {
    ...
}
 
public boolean pcIsTransactional() {
    ...
}

 

In addition to adding methods from the PersistenceCapable and Externalizable interfaces, the enhancer adds a method for each setter and getter method in the pre-enhanced bytecode. For example, if there was a getUid() method for a persistent field uid, then there would be a method generated by the enhancer called pcgetUid(), and the original getUid() method would call the generated pcgetUid(). Inside each of the original setter and getter methods, there is a call to a global StateManager (also created by the enhancer), which is responsible for fetching and storing the persistent data.

 

The following code samples show the differences between the original Java code, the pre-enhanced bytecode, and the post-enhanced bytecode (decompiled). Notice how all enhancer-generated fields and methods that are not part of PersistenceCapable or Externalizable interface have the letters pc prepended to their names. The pc prefix stands for PersistenceCapable.

 

 

Original .java file:
@Basic
@Column(name = "IMAGE_URL")
public String getImageUrl() {
    return imageUrl;
}
 
public void setImageUrl(final String imageUrl) {
    this.imageUrl = imageUrl;
}

 

Pre-enhanced .class file:
public String getImageUrl() {
    return imageUrl;
}
 
public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;
}

 

Post-enhanced .class file:
public String getImageUrl() {
    if(pcStateManager == null) {
        return pcgetImageUrl();
    } else {
        int i = pcInheritedFieldCount + 2;
        pcStateManager.accessingField(i);
        return pcgetImageUrl();
    }
}
 
public void setImageUrl(String s) {
    if(pcStateManager == null) {
        pcsetImageUrl(s);
        return;
    } else {
        pcStateManager.settingStringField(this, pcInheritedFieldCount + 2, pcgetImageUrl(), s, 0);
        return;
    }
}
 
protected String pcgetImageUrl() {
    return imageUrl;
}
 
protected void pcsetImageUrl(String s) {
    this.imageUrl = s;
}

 

The bytecode modification is done in such a way that it allows the modified class to keep its compatibility with Java debuggers. This means that even with extra lines of code inserted within various areas of a class, the modifcations can still keep the line numbers the same in stack traces.

 

Now whenever you enhance a class, you'll know why you even bother doing it...but don't just take my word for it, wrap your de-compiler around an enhanced class and see for yourself!