Setting up JDO3 Google App Engine project in JetBrains IDEA

In one of my personal projects I recently needed a simplistic webapp backend. Just like, you know, log in, fetch data, a couple of update methods, that’s it. I never worked with Google App Engine before and decided it’s a good chance to get to know it. I did work with JPA and JDO though, so while using low-level Datastore API was tempting, I decided to stick with a familiar approach to minimize entropy. Thus, my choice of persistence API was JDO3.

While it may seem that most of open-source guys and girls use Eclipse for their projects, I for a long time was a big fan of JetBrains IDEA. Reasons for that aside, I created a project, set it up with GAE and JDO3 and went on with the implementation just to find out my build attempts result in an enhancer exception, which goes like this: [learn_more caption=”Google AppEngine Enhancer: java.lang.RuntimeException: Unexpected exception”] at com.google.appengine.tools.enhancer.Enhancer.execute(Enhancer.java:76)
at com.google.appengine.tools.enhancer.Enhance.(Enhance.java:71)
at com.google.appengine.tools.enhancer.Enhance.main(Enhance.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.appengine.rt.EnhancerRunner.main(EnhancerRunner.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:121)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.google.appengine.tools.enhancer.Enhancer.execute(Enhancer.java:74)
… 12 more
Caused by: org.datanucleus.exceptions.NucleusException: Plugin (Bundle) “org.datanucleus” is already registered. Ensure you dont have multiple JAR versions of the same plugin in the classpath. The URL “file:/D:/dev/appengine-java-sdk-1.8.1.1/lib/user/orm/datanucleus-core-1.1.5.jar” is already registered, and you are trying to register an identical plugin located at URL “file:/D:/dev/appengine-java-sdk-1.8.1.1/lib/opt/tools/datanucleus/v2/datanucleus-core-3.1.3.jar.”
at org.datanucleus.plugin.NonManagedPluginRegistry.registerBundle(NonManagedPluginRegistry.java:541)
at org.datanucleus.plugin.NonManagedPluginRegistry.registerBundle(NonManagedPluginRegistry.java:395)
at org.datanucleus.plugin.NonManagedPluginRegistry.registerExtensions(NonManagedPluginRegistry.java:219)
at org.datanucleus.plugin.NonManagedPluginRegistry.registerExtensionPoints(NonManagedPluginRegistry.java:160)
at org.datanucleus.plugin.PluginManager.(PluginManager.java:65)
at org.datanucleus.plugin.PluginManager.createPluginManager(PluginManager.java:427)
at org.datanucleus.NucleusContext.(NucleusContext.java:224)
at org.datanucleus.NucleusContext.(NucleusContext.java:204)
at org.datanucleus.enhancer.DataNucleusEnhancer.(DataNucleusEnhancer.java:160)
at org.datanucleus.enhancer.DataNucleusEnhancer.main(DataNucleusEnhancer.java:1133)
… 17 more
[/learn_more] and it tells that while a proper version of the enhancer gets invoked, it fails to employ wrong version of ORM libraries. Long story short, the root of the problem was an incorrect set of App Engine libraries specified by IDEA when choosing JDO3 in project settings.

This brings us to the topic of this post.

 

HOW-TO fix JDO3 libraries in a GAE IDEA project

 
The following applies to IDEA 12 under Windows 7 & JDK 1.7
 

Longer story goes like this: While GAE’s JDO2 API uses DataNucleus 1.x, the JDO3 API employs DataNucleus 3.x. This is where IDEA stumbles. See, when a GAE project gets created, two predefined libraries are added to it – AppEngine API and AppEngine ORM. While the former points to some classes universal for JDO2 and JDO3, the latter specifically references JDO2 and DataNucleus 1.x jars even if JDO3 was chosen in the project options.

Here I lay out the steps needed to create a new IDEA project for GAE JDO3 development. They also required and suitable (from step 5) when changing existing GAE project setting from JDO2 to JDO3.

1. Create a new Java Module project, click Next
create-new-project

2. Mark Google App Engine module, in its settings choose path to an installed App Engine SDK and set JDO3 as persistence API, click Finish

setup-new-project

If you try to build the project now, it will succeed. This probably is because we haven’t used any JDO3 annotations so the enhancer has nothing to do. Let’s add a persistent class.

3. Add a new class, click OK

add-new-class

4. Set up the class so that it uses JDO annotations

new-class-code

Exact class persistence settings are irrelevant in our scope. What is relevant, if you try to build now, you are going to get the exception I mentioned before. Which makes this a good time to correct project libraries settings.

5. Open Project Settings dialog

project-settings-libraries

Here they are, our two predefined libraries, pointing to App Engine SDK parts. The AppEngine API library points to the GAE API definition jars and is correct. The AppEngine ORM is not. As you can see at the screenshot, it points to the DataNucleus 1.x and JDO2 API libraries of the SDK, despite the fact we chose JDO3 earlier. Let’s replace this library with a proper one.

6. Click at the green plus icon at the top and click Java to add a new Java library

create-new-library

7. A new dialog will pop up asking to choose library files. DataNucleus 3.x libraries are included in App Engine SDK and located in <GAE SDK>/lib/opt/user/datanucleus/v2 (v2 here stands for GAE DataNucleus plugin version). Choose this folder and click OK

select-api-files

8. Assign the new library a name, like AppEngine ORM JDO3, which suits my taste. As you may see at the status bar, IDEA points out that the library is not included to any artifact. Click red lamp icon to fix this and choose Add ‘your lib name’ to the artifact

add-library-to-artifact

9. We can now remove old JDO2 AppEngine ORM library. Select it, click on red minus icon at the top, and confirm deletion

delete-old-library

10. Click OK to apply new settings and close Project Structure dialog.

11. One little thing left is to update your JDO configuration. Open jdoconfig.xml and replace javax.jdo.PersistenceManagerFactoryClass’s value with org.datanucleus.api.jdo.JDOPersistenceManagerFactory

jdo-config-update

If you rebuild the project now, it should build without errors. You can now add any JDO3-backed implementation you like. Enjoy and make sure you also read GAE docs on the topic.

This makes the topic of this article. The issue is no doubt a piece of cake for a seasoned java developer; this might be why I wasn’t able to find any coverage on the net. But I remember my earlier days in Java and it was the project environment and build configuration parts done right that were the hardest for me, be it ant, maven, Eclipse or IDEA. While tools do get smarter, this explanation may still be of help to someone like me in those days.

Leave a Reply