29 April 2012

Metamodel classes for OpenJPA with Maven, Eclipse and m2e

Metamodel classes are a not-so-well known feature of the JPA 2.0 standard. They allow you to write typesafe criteria queries using metamodel attributes instead of plain old strings to reference entity fields. (See this tutorial for some more background.)

Metamodel classes are generated by an annotation processor during the build process, but this is not enabled by default. OpenJPA logs a runtime warning when you build a criteria query and the metamodel classes cannot be found. So even if you don't use the metamodel, you may want to generate it just to get rid of this warning.

This article explains how to generate the metamodel both in Maven batch builds and in the Eclipse workspace.


According to the OpenJPA Manual, the following POM snippet should be sufficient to generate the metamodel:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>2.3.2</version>
  <configuration>
    <source>1.6</source>
    <target>1.6</target>
    <compilerArgument>-Aopenjpa.metamodel=true -Aopenjpa.log=TRACE</compilerArgument>
  </configuration>
</plugin>

Unfortunately, this does not work due to a bug in the Maven Compiler Plugin, which does not handle multiple compiler arguments correctly - the alternative configuration element <compilerArguments> (note the plural) does not work either. So make sure to use only one compiler argument without any blank spaces (the log option is not required anyway):

<compilerArgument>-Aopenjpa.metamodel=true</compilerArgument>

After this change, the OpenJPA annotation processor should be triggered when running mvn compile.

You may now hit the next problem with the following exception:
java.lang.ClassCastException: com.sun.tools.javac.code.Symbol$PackageSymbol 
cannot be cast to javax.lang.model.element.TypeElement 

which is caused by the OpenJPA annotation processor choking on a package-info.java file. This is a bug fixed in OpenJPA 2.2.0, so if you see this exception, upgrade OpenJPA or delete any package-info.java.

If you don't work with Eclipse, this is all you need to know.

For Eclipse + m2e users, some more tweaking is required, as the Eclipse Java compiler does not pick up the annotation processor option automatically. The preferred solution, an m2e extension for annotation processing, is not yet available, and the best approach I've found so far is not really straightforward but nonetheless effective. It requires three steps:

  • Annotation processing is performed by the Maven Processor Plugin in the generate-sources phase.
  • The directory with the generated sources is made known to the compiler with the Maven Build Helper Plugin.
  • This directory will be picked up by the Eclipse compiler when the m2e buildhelper connector is installed.

So first of all, install the m2e buildhelper connector via Window | Preferences | Maven | Discovery | Open Catalog. Then remove the <compilerArgument> from the maven-compiler-plugin configuration and add the following to your POM:

<plugin>
  <groupId>org.bsc.maven</groupId>
  <artifactId>maven-processor-plugin</artifactId>
  <version>2.0.5</version>
  <executions>
    <execution>
      <id>process</id>
      <goals>
        <goal>process</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <processors>
          <processor>org.apache.openjpa.persistence.meta.AnnotationProcessor6</processor>
        </processors>
        <optionMap>
          <openjpa.metamodel>true</openjpa.metamodel>
        </optionMap>
      </configuration>
    </execution>
  </executions>
</plugin>
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <version>1.7</version>
  <executions>
    <execution>
      <id>add-source</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>add-source</goal>
      </goals>
      <configuration>
        <sources>
          <source>${project.build.directory}/generated-sources/apt</source>
        </sources>
      </configuration>
    </execution>
  </executions>
</plugin>

Then run Maven | Update Project Configuration from the context menu of your Eclipse project. The metamodel classes will now be generated whenever Eclipse rebuilds your project.



7 comments:

Unknown said...

Works great!! Thanks

httPants said...

Thanks for the info. What about using openjpa for class enhancement? I have issues getting this working with eclipse + m2e when autobuild is enabled. I have to disable the plugin execution in eclipse or else it goes in an endless loop because the resource refresh that is required to load the enhanced classes triggers another autobuild in eclipse.

Just by coincidence, today i found an openejpa m2e plugin for eclipse at https://github.com/hwellmann/m2eclipse-extras/raw/master/p2/ when i was looking at jaxb plugins. What does this do?

Harald Wellmann said...

@httPants: The openjpa m2e extension should help you to make class enhancement work in Eclipse.

See https://github.com/hwellmann/m2eclipse-extras/wiki for more details.

Craig Ringer said...

Thanks very much for that. I've been swearing at Eclipse whenever I work with it because of this. It "helpfully" removes target/generated-sources/annotations from the build path whenever a mvn clean is run and the dir disappears, so I can't just tell Eclipse to look for the files where the maven-compiler-plugin generates them.

NetBeans just does this automatically with no configuration; hopefully Eclipse+m2e will down the track ( though it's already been nearly 2 years since the metamodel stuff appeared). Alas, NetBeans still doesn't support JBoss AS 7, and the compile-deploy cycles were beginning to drive me insane, so I've moved to Eclipse for now.

Thanks for the tip on how to get eclipse to play. It's ugly, but at least it works.

httPants said...

Harald, i'm really keen to try out your m2e OpenJPA plugin for eclipse but haven't been able to install it successfully. I keep getting the following error...

Cannot complete the install because of a conflicting dependency.
Software being installed: m2e Connector for OpenJPA 0.1.0.201201021525 (org.omadac.m2e.openjpa.feature.feature.group 0.1.0.201201021525)
Software currently installed: m2e - Maven Integration for Eclipse 1.1.0.20120530-0009 (org.eclipse.m2e.feature.feature.group 1.1.0.20120530-0009)
Only one of the following can be installed at once:
Maven Integration for Eclipse 1.1.0.20111210-1509 (org.eclipse.m2e.core 1.1.0.20111210-1509)
Maven Integration for Eclipse 1.0.0.20110607-2117 (org.eclipse.m2e.core 1.0.0.20110607-2117)
Maven Integration for Eclipse 1.0.200.20111228-1245 (org.eclipse.m2e.core 1.0.200.20111228-1245)
Maven Integration for Eclipse 1.1.0.20120320-0058 (org.eclipse.m2e.core 1.1.0.20120320-0058)
Maven Integration for Eclipse 1.1.0.20120130-2016 (org.eclipse.m2e.core 1.1.0.20120130-2016)
Maven Integration for Eclipse 1.1.0.20111108-0328 (org.eclipse.m2e.core 1.1.0.20111108-0328)
Maven Integration for Eclipse 1.1.0.20120530-0009 (org.eclipse.m2e.core 1.1.0.20120530-0009)
Maven Integration for Eclipse 1.1.0.20120523-1730 (org.eclipse.m2e.core 1.1.0.20120523-1730)
Maven Integration for Eclipse 1.0.100.20110804-1717 (org.eclipse.m2e.core 1.0.100.20110804-1717)
Maven Integration for Eclipse 1.1.0.20120505-1126 (org.eclipse.m2e.core 1.1.0.20120505-1126)
Cannot satisfy dependency:
From: m2e - Maven Integration for Eclipse 1.1.0.20120530-0009 (org.eclipse.m2e.feature.feature.group 1.1.0.20120530-0009)
To: org.eclipse.m2e.core [1.1.0.20120530-0009]
Cannot satisfy dependency:
From: m2e connector for OpenJPA 0.1.0.201201021525 (org.omadac.m2e.openjpa 0.1.0.201201021525)
To: bundle org.eclipse.m2e.core [1.0.0,1.1.0)
Cannot satisfy dependency:
From: m2e Connector for OpenJPA 0.1.0.201201021525 (org.omadac.m2e.openjpa.feature.feature.group 0.1.0.201201021525)
To: org.omadac.m2e.openjpa [0.1.0.201201021525]

Harald Wellmann said...

@httPants: Are you on an Eclipse Juno prerelease?

The openjpa extension is built on top of the Indigo target platform which requires m2e [1.0, 1.1), and I'm not so sure where this setting comes from or how to change it.

I suppose I'll have to build a new update site once Eclipse 3.8 is out.

Unknown said...

Hey, thanks for the info. I just tried it out but saw there is now a m2e-apt plugin available. Installing this and enabling the annotation processing in the pref->maven menu will work for me with the compiler-plugin directive only.