Recently, my team and I started a new project and in an effort to stay on top of things, we decided to upgrade the underlying persistence layer from using OpenJPA 1.2 to OpenJPA 2.0.1. While I was familiar with the JPA specification and had used other ORM libraries in the past, OpenJPA was new to me, so we decided to leverage the existing configurations and extend them first before we attempted a wholesale upgrade. That way we could inspect our existing setup and test some of the limits of OpenJPA 1, and hopefully would make the transition to OpenJPA 2 a little smoother.
While setting up the application using an in memory HSQL database for our testing, the JPA code was simply not cooperating. First we loaded up a test case and saw the SQL CREATE TABLE and ALTER TABLE statemements, then we got the following error
org.apache.openjpa.persistence.ArgumentException: Table”MY_SCHEMA.MY_TABLE” given for “com.package.classes.Class” does not exist.
It took me the better part of two days to realize OpenJPA’s support for Schema’s in generated tables is somewhere between awful and non-existent. Then I got hung up on runtime class enhancement. Unenhanced classes are functional but much slower than their enhanced counterparts. Adding a javaagent to the JVM enabled dynamic enhancement and it worked.
Now on to the upgrade
Once we swapped out the underlying libraries, the only things breaking seemed to be some smaller issues such as annotations but nothing major. After some re-working of the META-INF/persistence.xml file and the spring declarations for the Entity Manager Factory and related classes, things looked okay. And once I updated the path from
our test cases ran perfectly inside of IntelliJ.
Since I don’t get paid to just build running test cases, I had to get this up and running in Tomcat.
Enhancing OpenJPA 2 Entities in Tomcat
This proved to be a lot more difficult. In order to get the javaagent working, I added the open openjpa-2.0.1.jar to the /lib directory of the tomcat installation and updated our catalina.bat (yes this was on Windows) file to include the line
set JAVA_OPTS=%JAVA_OPTS% -javaagent:"%CATALINA_HOME%\lib\openjpa-2.0.1.jar"
This enables Runtime Enhancement for the OpenJPA2 Entities in our persistence.xml file. On startup I got a host of NoClassDefinedErrors and using maven, IntelliJ and Google realized I needed to add the following files into my tomcat /lib folder
And update the catalina.bat file again with:
The app started up, however the JPA entities weren’t being enhanced at load time. OpenJPA essentially doesn’t support unenhanced entites, so any attempt to access them failed.
Enabling Load Time Weaving
After a lot of digging without results, I decided to track down an error I saw in startup that hadn’t prevented OpenJPA 1 from working:
Caused by: java.lang.IllegalStateException: Cannot apply class transformer without LoadTimeWeaver specified
I initially dismissed this error as it is caught and ignored by OpenJPA and doesn’t prevent my test cases from running properly. Unfortunately, OpenJPA2 requires load time weaving to be enabled for it to work in an application server. I still haven’t found where this is documented outside of Spring’s ORM documentation but using that as a guide I went into my tomcat /conf/context.xml and added
and moved the spring-instrument-tomcat-3.0.5.RELEASE.jar file into my tomcat /lib directory. This step adds a second ClassLoader to your Tomcat installation since the default Tomcat ClassLoader doesn’t support runtime proxying the way Spring and OpenJPA require.
With that, it all seemed to work… until we started our automated build process using Maven 3 and Junit 4.7 test cases.
Running OpenJPA 2 JUnit Tests in Maven 3
Tests were failing with the Runtime Enhancement errors we saw above. I edited the pom.xml file and updated the maven-surefire-plugin configuration to include
I re-ran our build and it failed, and with the LoadTimeWeaver issue. The solution to that is to use the Spring Agent jar as a javaagent. Thankfully, you can chain as many javaagents as you want onto the command line so I updated the argLine tag to
and it ran fine locally.
Moving it to the Build Server
When I finally committed all of these changes, our build server kicked off a build… and it failed. Since the spring-agent-2.5.6.jar and openjpa-2.0.1.jar aren’t used in the application, they weren’t included in the pom.xml file and subsequently didn’t exist on the build server. To fix this I added
to my pom.xml and everything worked perfectly. Now all I have to do is actually write the application that uses it.