1180 words

JPA / JDO Class Enhancer

ObjectDB Enhancer is a post compilation tool that improves performance by modifying the byte code of compiled classes after compilation. Enhancement is mainly for user-defined persistable classes (entity classes, embeddable classes and mapped superclasses), and is usually optional.

There is one case, however, where enhancement is required. Non persistable classes that access directly (not through methods) persistent fields of enhanced classes must also be enhanced. It is a good practice (and actually required by JPA but not enforced by ObjectDB) to avoid accessing persistent fields of other classes directly.  Rather, the accessor and mutator methods of the desired class should be used (e.g. by using the get and set methods). If you follow this practice only user defined persistable classes should need to be enhanced.

The enhancer silently ignores any specified class that does not need to be enhanced.

Enhancement improves efficiency in three ways:

  • Enhanced code enables efficient tracking of persistent field modifications, avoiding the need for snapshot comparison of entities (as explained in the chapter 3). This is done by adding special code to enhanced classes that automatically notifies ObjectDB whenever a persistent field is modified.
  • Enhanced code enables lazy loading of entity objects. With no enhancement, only persistent collection and map fields can be loaded lazily (by using proxy objects), but persistent fields that reference entity objects directly (one-to-one relationship) have to be loaded eagerly.
  • Special optimized methods are added to enhanced classes as a replacement for using reflection. These optimized methods are much faster than using reflection.

Command Line Enhancement

ObjectDB Enhancer is a Java console application. It is contained in the objectdb.jar file.

You can run it from the command line as follows:

> java -cp objectdb.jar com.objectdb.Enhancer

If objectdb.jar is not in the current directory a path to it has to be specified.

Alternatively, you can run the Enhancer by using a shell script (enhancer.bat on Windows and enhancer.sh on Unix/Linux) from the ObjectDB bin directory. To use that script you have to edit the paths to theobjectdb.jar file and to the JVM.

A usage message is displayed if no arguments are specified on the command line:

ObjectDB Enhancer [version 2.0]
Copyright (c) 2010, ObjectDB Software. All rights reserved.
 
Usage: java com.objectdb.Enhancer [ <options> | <class> | <filename> ] ...
 <class> - name of a class (without .class suffix) in the CLASSPATH
 <filename> - path to class or jar file(s), *? wildcards supported
 <options> include:
 -cp <dir>  :  path to input user classes
 -pu <name> :  persistence unit name
 -s         :  include sub directories in search
 -d <dir>   :  output path for enhanced classes

You can specify class files and jar files for enhancement explicitly or by using wildcards:

> java com.objectdb.Enhancer test/*.class Main.class pc.jar

If the -s option is specified, files in subdirectories are also searched and enhanced:

> java com.objectdb.Enhancer -s "*.class"

The "*.class" expression above is enclosed in quotes to prevent extraction by the shell.

The result output message lists the classes that have been enhanced:

[ObjectDB 2.0]
3 persistable types have been enhanced:
 test.MyEntity1
 test.MyEntity2
 test.MyEmbeddable
2 NON persistable types have been enhanced:
 Main
 test.Manager

You can also specify names of classes that can be located on the classpath using the syntax of import statements (e.g. test.X for a single class, test.pc.* for a package):

> java com.objectdb.Enhancer test.X test.pc.*

Use the -pu option with the name of a persistence unit to enhance all the managed classes that are defined in that persistence unit:

> java com.objectdb.Enhancer -pu my-pu

The -cp option can be used to specify an alternative classpath (the default is the classpath in which the Enhancer itself is running):

> java com.objectdb.Enhancer -cp src test.X test.pc.*

By default, classes are enhanced in place, overriding the original class and jar files. Use the -d option to redirect output to a different directory, thus keeping the original files unchanged:

> java com.objectdb.Enhancer -s "*.class" -d enhanced

Maven and ANT Enhancement

Enhancement can be integrated into the build process.

The following Maven build file defines a Java compiler plugin that includes enhancement:

<build>
  ...
  <plugins>
  ...
    <plugin>
      ...
      <dependencies>
        <dependency>
          <groupId>com.objectdb</groupId>
          <artifactId>objectdb</artifactId>
          <version>2.2.2</version>
          <scope>compile</scope>
        </dependency>   
      </dependencies>
 
      <executions>
        <execution>
          <phase>compile</phase>
          <goals>
            <goal>java</goal>
          </goals>
        </execution>
      </executions>
 
      <configuration>
        <mainClass>com.objectdb.Enhancer</mainClass>
        <!-- List of your packages -->
        <arguments>
          <argument>com.x.y.a.*</argument>
          <argument>com.x.y.b.*</argument>
        </arguments>
      </configuration>
   </plugin>
   ...
 </plugins>
 ...
</build>

Similarly, enhancement can be also be integrated into an ANT build script, as so:

<java classname="com.objectdb.Enhancer" fork="true"
    classpath="c:\objectdb\bin\objectdb.jar">
  <arg line="-s c:\my-project\classes\*"/>
</java>

Enhancement API

The ObjectDB Enhancer can also be invoked from Java code:

 com.objectdb.Enhancer.enhance("test.pc.*,test.X");

The same arguments that can be specified on the command line can also be passed to the enhance method as a single string delimited by commas or spaces. In addition, a class loader for loading classes for enhancement can be specified as a second argument:

 com.objectdb.Enhancer.enhance(
   "test.pc.*,test.X", text.X.class.getClassLoader());

The enhancement API and invocation of the Enhancer from Java code is useful, for instance, in implementing custom enhancement ANT tasks.

Load Time (Java Agent) Enhancement

Instead of enhancing classes during build, classes can be enhanced when they are loaded into the JVM by running the application with objectdb.jar as a Java agent. For example, if objectdb.jar is located at c:\objectdb\bin, the JVM can be started by:

> java -javaagent:c:\objectdb\bin\objectdb.jar MyApplication

If the JVM is run with ObjectDB Enhancer as a Java Agent, every loaded class is checked and automatically enhanced in memory (if applicable). Notice, however, that only classes which are marked as persistable by annotations (e.g. @Entityjavax.persistence.EntityJPA annotationSpecifies that the class is an entity.
See JavaDoc Reference Page...
, @Embeddablejavax.persistence.EmbeddableJPA annotationDefines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.
See JavaDoc Reference Page...
) are enhanced by the Java Enhancer Agent. Therefore, when using this technique persistent fields may only be accessed directly from annotated persistable user classes.

Enhancement by a Java agent is very easy to use and convenient during development. For release, however, it is recommended to integrate the enhancement in the build process.

To use load time enhancement in web applications the web server or application server has to be run with the Java agent JVM argument.

Setting a Java Agent Enhancer in the IDE

In Eclipse JVM arguments can be set globally at:

Window > Preferences > Java > Installed JREs > Edit > Default VM Arguments

or for a specific run configuration, at:

Run Configurations… > Arguments > VM arguments

In NetBeans JVM arguments can be set at the project properties:

Right clicking the project > Properties > Run > VM Options

Automatic Java Agent Enhancer in JDK 6

Unless configured otherwise, ObjectDB tries to load the Enhancer as a Java Agent and enhance classes on the fly during load, even if a Java Agent is not specified explicitly.

This enhancement technique is inferior to the other techniques that are described above. First, currently it only works on Sun JDK 6 (and not on JRE 6 for example). Second, classes that are loaded into the JVM before accessing ObjectDB cannot be enhanced, so a careful organization of the code is essential in order to make it work.

Therefore, specifying a Java Agent explicitly, as explained above should always be preferred.