Thursday, February 12, 2009

OpenJPA Enhancement

What is Enhancement Anyway?

I have cruised around the OpenJPA forums enough to notice that numerous new developers have posted questions regarding enhancement. Since I'm new to OpenJPA myself, I figured I'd dive in to enhancement and see what all the fuss is about. This post is directed mainly at developers that are taking a first look at OpenJPA and having problems getting started. No worries for those of you deploying an application to run in a Java EE 5 compliant application server such as WebSphere Application Server because your Entities will be automatically enhanced at runtime.

As I was exploring how to enhance, I started to wonder why do I need to enhance at all? As it turns out the JPA spec requires some type of monitoring of Entity objects, but the spec does not define how to implement this monitoring. Some JPA providers auto-generate new subclasses or proxy objects that front the user's Entity objects, while others like OpenJPA use byte-code weaving technologies to enhance the actual Entity class objects. Now that we've got that out of the way, lets get back to how to do the enhancement. I'm going to give runtime examples first, as I feel those are the easiest from a developers point of view.

Runtime enhancement-When running in a JSE environment or in a non EE-5 compliant container, OpenJPA defaults to using subclassing enhancement. The subclassing enhancement support was added originally as a convenience to new developers to reduce the amount of work to get a 'HelloWorld-ish' OpenJPA application working out of the box. It was never meant to run in production. So you're probably thinking that this sounds great! OpenJPA handles enhancement automatically for me and I can stop reading this post. Wrong! Subclassing has two major drawbacks. First off, it isn't nearly as fast as byte-code enhancement and the second drawback is that there are some documented functional problems when using the subclassing support. The moral of the story is, don't use this method of enhancement. I'm not the only one making this recommendation as I've come across form posts about removing subclassing enhancement as the default OpenJPA behavior. Additional information regarding the subclassing enhancement can be found here.

The second and recommended way get runtime enhancement is to provide a javaagent when launching the JVM that OpenJPA is going run in. This is the method that I use in most of my test environments because it is very painless. All that is required to get runtime enhancement in Eclipse is to specify the -javaagent:[open_jpa_jar_location] on the Run Configuration page. That is it.

Another simple way to get runtime enhancement is to specify the the -javaagent when launching an application from ant. Below is a snippet from my build.xml that shows how to pass the -javaagent when launching a JSE application that uses OpenJPA. 
    <path id="jpa.enhancement.classpath">
        <pathelement location="bin"/>
        <!-- lib contains all of the jars that came with the OpenJPA binary download -->
        <fileset dir="lib">
            <include name="**/*.jar"/>
        </fileset>
    </path>
...
<target name="drive" depends="clean, build">
        <echo message="Running test with run time enhancement."/>
        <java classname="main.Driver" failonerror="true" fork="yes">
            <jvmarg value="-javaagent:${openJPA-jar}"/>
            <classpath refid="jpa.enhancement.classpath"/>
        </java>
    </target>

Buildtime enhancement -
In this section I'm going to cover how to invoke the build time enhancer ant task that is packaged with OpenJPA through the use of ant and maven. Yes there are many different ways to buildtime enhancement bliss, but I'm only going to talk about using the ant task. Note that this entire section assumes you're running the ant/maven scripts from a command line. I can't say for certian how this works inside Eclipse. Warning: I'm not a maven wizard, so please be nice if I've commited some cardinal maven sin. :-)

I'll start first by showing how to define a OpenJPA enhancer task and how to invoke the task in ant. I had to fumble a little to get it working, but overall it was pretty straight forward. First you'll need to compile the Entites. (Note: as a prereq to running the enhance task, I copied my persistence.xml file to my /build directory. You might not need to do this, but the persistence.xml has to be in the classpath.) Next you'll need to configure the enhancer task and a classpath where the task can be found.(Adding the classpath is the part that the OpenJPA manual missed.) The final step is to call the enhance task. A snippet is provided below.
    <path id="jpa.enhancement.classpath">
        <pathelement location="bin"/>

        <!-- lib contains all of the jars that came with the OpenJPA binary download -->
        <fileset dir="lib">
            <include name="**/*.jar"/>
        </fileset>
    </path>


    <target name="enhance" depends="build">
    <!-- This is a bit of a hack, but I needed to copy the persistence.xml file from my src dir
        to the build dir when we run enhancement -->
    <copy includeemptydirs="false" todir="bin">
        <fileset dir="src" excludes="**/*.launch, **/*.java"/>
    </copy>


    <!-- define the openjpac task -->
    <taskdef name="openjpac" classname="org.apache.openjpa.ant.PCEnhancerTask">
        <classpath refid="jpa.enhancement.classpath"/>
    </taskdef>
      
    <!-- invoke enhancer the enhancer -->
    <openjpac>
        <classpath refid="jpa.enhancement.classpath"/>
    </openjpac>
    <echo message="Enhancing complete."/>
</target>
The second(different) path to build time enhancement is to use the maven antrun plug-in to launch the OpenJPA enhancer task. Since I'm new to maven this road was pretty clunky, but the steps were nearly identical to running directly in ant. I'll include the parts from my pom.xml that took the most time to figure out. Again, I'm not sure if you will need to move the persistence.xml file to the build directory, but I did(I assume I'm not doing something right).
  <build>
  <!-- Copy the persistence.xml file to the build dir -->
  <!-- You can skip this step if you put the persistence.xml in src/main/resources/META-INF instead of src/main/java/META-INF -->
  <resources>
      <resource>
        <directory> src/main/java </directory>
        <includes>
          <include> **/*.xml </include>
          </includes>
      </resource>
    </resources>
    <plugins>
.....           
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.2</version>
        <executions>
          <execution>
              <phase>process-classes</phase>
            <configuration>
              <tasks>
                  <taskdef name="openjpac" classname="org.apache.openjpa.ant.PCEnhancerTask" classpathref="maven.compile.classpath"/>
                  <openjpac>
                      <classpath refid="maven.compile.classpath"/>
                  </openjpac>
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
....
  </build>
Hopefully this helps someone out there! Feel free to contact me if you have any questions/comments/suggestions.

-Rick

33 comments:

则昊 said...

Hi~
I deploy my project with eclipse WTP (Run with server), is there any enhancement methods for that ?
(Maybe I should change the way of eclipse compiler...)

Kevin Sutter said...

Hi. If your server platform is either WAS v7 or WAS v6.1 with the EJB3 Feature Pack, then OpenJPA is already tied into the container's classloading mechanism and the enhancement processing is done dynamically. Nothing extra is required on your part.

则昊 said...

My server platform is Tomcat 6.0, is that mean I need enhance Java class manually? And, when I deploy my project with eclipse, I have not idea for enhancing with Ant.

Rick Curtis said...

Since Tomcat 6.0 isn't EE5 compliant, your entities won't be enhanced by the application server. So yes, you will need to perform an extra step to get your entities enhanced. Since you're using Eclipse to deploy your application, I think the easiest road to enhancement would be to look at the following plugin [1].

[1] http://people.apache.org/~ppoddar/eclipse/index.html

则昊 said...

Thank for helping~
And the plugin said:'The build step will enhance any Java class annotated with JPA annotations @Entity, @MappedSuperclass, @Embeddable', but my application using *.orm.xml file to mapping the entities, is there any other methods?

Rick Curtis said...

Sorry about taking so long to reply, please take a look at my latest blog posting [1]. I think it should work for what you are doing.

[1] http://webspherepersistence.blogspot.com/2009/04/openjpa-enhancement-eclipse-builder.html

jacks said...
This comment has been removed by a blog administrator.
Michael Vorburger said...

Kevin, the way you do the Maven thing via embedded Ant is not ideal... the maven-antrun-plugin has lot's of known problem (trying running that as part of a larger build in a Maven Reactor, with different maven-antrun-plugin invocations, and see weired stuff starting to get mixed up).

A better alternative is the http://mojo.codehaus.org/openjpa-maven-plugin/, also linked from http://openjpa.apache.org/enhancingwithmaven.html.

PS: I'm working on an OpenJPA Enhancer Eclipse Builder.. will post to the list when ready.

mikedd said...

@Michael V.

Pinaki Poddar wrote an eclipse plugin.

Not sure if you're doing essentially the same thing, but touching base with Pianki might save you a little time.

Silence said...

So you covered the stanadalone issues ... Could you add the actual WebSphere implications to this article? As has been stated, WebSphere 6.1 with the EJB3.0 feature pack is supposed to enhance at runtime automatically, but it clearly does not under some circumstances, in my case with code that has been working for six months. I reinstalled RAD 7.0 and WAS and EJB3 and FP 6.1.0.25 on a new machine and get the dreaded message:

Could not locate metadata for the class using alias "Person". This could mean that the OpenJPA enhancer or load-time weaver was not run on the type whose alias is "Person". Registered alias mappings: "{Person=null}"

and further on:

This configuration disallows runtime optimization, but the following listed types were not enhanced at build time or at class load time with a javaagent:
...

If this last statement is true, how do I "allow" runtime optimization?

Kevin Sutter said...

Silence...

The "runtime optimization" that is referenced in the message refers to the subclassing support earlier in Rick's posting. You really don't want this processing. There are several bugs associated with this subclassing enhancement. That's why we turn it off in the WebSphere environment.

If you are hitting this error message while executing container-managed persistence within WebSphere, you should probably open a PMR. As Entity classes are being loaded by the Container, they should be passed off to OpenJPA (or whatever JPA provider is plugged in) and be allowed to be enhanced. If that's not happening, it sounds like a bug.

Kevin

Silence said...

Sorry I come through as Silence, the "choose an identity" ui isn't all that clear. What the heck is OpneID?

I have opened a PMR, and have sent in my SystemOut and SystemErr logs. I am unable to run the application at all, so I am waiting for IBM to come back with an answer.

This is too weird because if I switch back to my old machine, it works correctly. However choosing between a Pentium4/2GB that works and a Core i7/4GB that doesn't is a problem.

Christopher R. Gardner said...

During my last spike, I found that WAS 7 did NOT automatically enhance entities. I had to enhance at build time.

Kevin Sutter said...

Hi Christopher,
Your experience should not be the normal processing when using WAS v7. You may experience better startup performance by doing the build-time enhancement, but it should not be necessary. The dynamic enhancement should be performed automatically when the Entities are loaded by the Container.

As my previous remark indicated, I would suggest opening a PMR if this is not the case.

Thanks,
Kevin

dawid said...

In Websphere 6.1 with EJB3, starting from version 6.1.0.25, there is a bug in dynamic enhancement i.e. won't work if you try to use entity for the first time outside of transaction

Rick Curtis said...

@dawid

I'd suggest opening a PMR.

Thanks,
Rick

Akram Chotu said...

I am using OpenJPA in an War/Ear deployed on RAD 7.x + WAS v6.0. For some reason immediately we cannot apply the fix pack http://www-01.ibm.com/software/webservers/appserv/was/featurepacks/osgi/ or migrate to RAD 8.x + WAS V8.x due to some environment issues and application dependencies and so only way for us is to go with build time enhancement using the approach told in http://webspherepersistence.blogspot.com/2009/04/openjpa-enhancement-eclipse-builder.html

I have a basic question regarding OpenJPA entity enhancement. After we do OpenJPA build time enhacement, how do we make sure that we are *really* seeing the benefits of OpenJPA entity enhancements? How do we test it? how to measure the performance before doing build time enhancements and after doing build time enhancement? Could you please clarify and point me to any links that can answer my question?

Rick Curtis said...

@Akram -

> After we do OpenJPA build time enhacement, how do we make sure that we are *really* seeing the benefits of OpenJPA entity enhancements?

To ensure that your Entities have been enhanced you can decompile them and make sure that they implement org.apache.openjpa.enhance.PersistenceCapable.

> How to measure the performance before doing build time enhancements and after doing build time enhancement?

That isn't a valid question. Since I wrote this blog post, OpenJPA has taken a step away from running with unehanced classes. It is now a requirement to enhance your Entities via one of the detailed methods.

Thanks,
Rick

Susana said...

Hi,

We are testing this but we got this error when executing mvn install:

[INFO] [antrun:run {execution: default}]
[INFO] Executing tasks
[ERROR] BUILD ERROR
[INFO] An Ant BuildException has occured: org.apache.openjpa.util.MetaDataException: MetaDataFactory no se pudo configurar (conf.newMetaDataFactoryInstance() ha devuelto nulo). Esto podría significar que no se han encontrado propiedades de configuración. Asegúrese de que tiene un archivo META-INF/persistence.xml, que está disponible en su classpath, o de que el archivo de propiedades que está utilizando para la configuración está disponible. Si está utilizando Ant, compruebe los atributos o del elemento anidado de la tarea. Esto se puede producir si sus archivos jar de distribución de OpenJPA están dañados, o si la política de seguridad es demasiado estricta.
...

Do you know what are we doing wrong?

Thanks.

Regards,
Susana

Kevin Sutter said...

Hi Susana,
When you say that you have tried "this", what aspect of enhancement are you referring to? The error message you posted is indicating that OpenJPA can not find any metadata for the configuration. The most common problem is that the persistence.xml can not be found during the enhancement processing. Depending on your environment (maven, ant, etc), this may also mean that the configuration is just not known to the running process. I would suggest turning on debug from your build environment and see if you can figure out what is missing.

Good luck,
Kevin

Susana said...

Hi,

Sorry for not being more clear. We are using EJB 3.0, JPA 2.0 and WAS 7. We are trying to use maven antrun plug-in to launch the OpenJPA enhancer task, and is when executing maven install with the antrun that we get this error.

If we don't include the antrun plugin to enhance the classes, we get this error at runtime:

Susana said...

javax.ejb.Exception: Injection failure; nested exception is: java.lang.IllegalStateException: EntityManagerFactory has not been created for PU: PuId=EAR_NTAG@POJO_NTAG_IMPL.jar#NtagPersistenceUnit

We have persistence.xml file in src/main/resources/META-INF directory.

Thanks for your help.

Kevin Sutter said...

Hi Susana,
The last exception you posted shows an issue with injection, which is separate from the enhancement processing. Since you are a WebSphere customer, I would suggest opening a PMR and following through with support. You'll get more complete support -- more than just me monitoring this blog posting.

Good luck,
Kevin

Shivreet Sekhon said...

Hi,

Could someone tell me if there is a way to ensure that WAS8 has actually enhanced my entities at deploytime. I do lookinside the installedApps but the entities dont seem to have changed.

Kevin Sutter said...

Hi Shivreet,
When running on WAS v8.0, the configuration properties are set to require enhancement of JPA entities. So, if your entities have not been enhanced (either at build time or dynamically at runtime), the condition will be detected and reported as an error.

Most of the time, that type of detection and reporting will be sufficient. But, if you are the curious type, you have a few other choices to determine whether your entities have been enhanced.

At runtime, you could turn on some Trace logging. The WebSphere Trace Specification could either turn on all Tracing (openjpa=all) or just for the Enhancement processing (openjpa.Enhance=all). The Trace output will tell you whether the Entity was previously enhanced, or if enhancement was required dynamically at runtime.

If you are enhancing at build time, you could also verify the enhancement process by running the "javap" command on your Entity classes. This will show two things. One is that your Entity class has now implemented the org.apache.openjpa.enhance.PersistenceCapable interface. And, you will notice several new methods inserted that begin with "pc".

Hope this helps,
Kevin

Shivreet Sekhon said...

Hi Kevin,

Thanks a lot for that. If you could just answer one more query for me around JPA. Does this msg mean anything to you.

The Entity "MYEntityName" was enhanced at level "2", but the current level of enhancement is "1,055,128".

What is the enhancement level.?

Michael Vorburger said...

Shivreet, I suspect your was enhanced at level vs. current level of enhancement error could mean that you enhanced your Entities with a different version of OpenJPA than the one you are running against.

Kevin Sutter said...

Michael is correct. The level of OpenJPA used to enhance your entities is at a different level of the runtime. Normally, this is not a big deal and the message is only "informational". But, if you are hitting other issues, then this "eye-catcher" may help diagnose the problem.

FYI, the formatting is not very good, but the latter version number you posted "1,055,128" is actually the SVN revision number 1055128. And, the version "2" is just the original initialized version number which doesn't correspond to anything...

Kevin

venkatesh muthukrishnan said...

All your links are dead because of my lateness, can you please link it to different url, and thanks for a wonderful and helpful article

Kevin Sutter said...

I've pinged the author to see if they could update the links. Thanks for letting us know.

In the mean time, much of the information in this posting can also be found here:
http://openjpa.apache.org/entity-enhancement.html

Leo said...

"All that is required to get runtime enhancement in Eclipse is to specify the -javaagent:[open_jpa_jar_location] on the Run Configuration page. That is it." - just a warning that this won't work on TomEE 1.5.2 because javaagent was broken until 1.6.0 (sept 2013). Thanks for the tutorial.

Rick Curtis said...

Leo --

Sorry, but I don't think your comment is true. This blog post talks exclusively about running with OpenJPA and no where is tomEE mentioned. The tomEE agent might have been broke for a period of time, but the OpenJPA agent has always worked.

Leo said...

Hi Rick, I am not blaming OpenJPA. The problem seems to be TomEE's.