How to use EclipseLink with AS7

Introduction

This guide is targeted at AS7 (and EAP 6, by extension). We are anticipating that WildFly 8 will have pre-packaged support for EclipseLink through the Jipijapa project. See WFLY-203 for more details.

 

Remaining issues

 

  • Due to EclipseLink bug 383199, applications that use the static metamodel should set:

        <property name="eclipselink.deploy-on-startup" value="True" />

    either in persistence.xml or via a system property . This issue doesn't actually appear to be AS7 specific, but you need to set the property to get spec-compliant behaviour that matches Hibernate's.

  • Dynamic weaving to add lazy loading proxies, etc doesn't work with EclipseLink in 7.1.1.Final because of AS7-4996. You will need to use static weaving (as explained below) in your build if you want to support 7.1.1.Final . This issue will be resolved in 7.2.x and 7.1.2 when released, and is fixed in the current nightly builds.

Alternative 1: Use a pre-packaged integration library

 

There is now a library available that can be used instead of most of the manual configuration given here. It produces a JBoss AS 7 module you can install to handle EclipseLink integration without needing as many changes to your project, and creates an EclipseLink module for you. Details on the library are in the README (1.1.1).

 

The modules are produced using maven, but can be used by any code running on JBoss AS 7, no matter what its build system. There is no need to add a dependency on the library or use Maven in your project.

 

First, download the library:

 

git clone git://github.com/ringerc/as7-eclipselink-integration.git
cd as7-eclipselink-integration
git checkout 1.1.1

 

If you don't want to use git, instead download https://github.com/ringerc/as7-eclipselink-integration/zipball/1.1.1, unzip it and cd into the directory produced.

 

Now build the library and install the produced modules:

 

mvn -DskipTests clean install
cp -r target/as7module/* $JBOSS_HOME/modules

 

Finally, set system properties to turn it on; launch jboss-cli:

 

  $JBOSS_HOME/bin/jboss-cli.sh --connect

and in the cli run:

 

  /system-property=eclipselink.target-server:add(value=JBoss)
  /system-property=eclipselink.archive.factory:add(value=id.au.ringerc.as7.eclipselinkintegration.JBossArchiveFactoryImpl)
  /system-property=eclipselink.logging.logger:add(value=id.au.ringerc.as7.eclipselinkintegration.JBossLogger)

 

Note that by default the build will produce a module for EclipseLink 2.4.0. You an change that by passing -Declipselink.version=2.3.3-M3 (for example) to the Maven build. You can find a list of EclipseLink milestone builds here and nightlies here. Maven naming for artifacts is described here. EclipseLink don't use a browseable directory tree for their repo, don't use Nexus (yet), and don't publish to Central so there isn't any index or artifact listing.

 

You still need to follow step 4 ("Set up static weaving") of the instructions below if you need to support 7.1.1.Final or older versions of AS7.

 

 

 

Alternative 2: Manual integration


Follow step-by-step instructions below to integrate EclipseLink into your project and into AS7. You'll need to add some classes to your app and set some system properties.

Step 1: Add the EclipseLink module

Download EclipseLink. You must be using at least version 2.3.3. If you are using the 2.4 series, version 2.4.1 or later is recommended due to a race condition in the new JPQL parser that can cause deployment to fail. Version 2.5.0 is the latest available at time of writing.

 

For AS 7.1.1.Final and earlier, the correct module directory is $JBOSS_HOME/modules/org/eclipse/persistence/main.

For AS 7.2.0.Final (EAP 6.1.0.Alpha1) and later, the correct module directory is $JBOSS_HOME/modules/system/layers/base/org/eclipse/persistence/main.


Create the appropriate directory and drop the EclipseLink JAR into it. Then create module.xml and copy the below into it, making sure that the resource-root path is the name of the EclipseLink JAR. (Note that the JAR you download is just called 'eclipselink.jar'; it is recommended to rename it and attach the version number to avoid ambiguity about which version you have.)

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.eclipse.persistence">
 <resources>
  <resource-root path="eclipselink-2.5.0.jar"/>
 </resources>
 <dependencies>
  <module name="asm.asm"/>
  <module name="javax.api"/>
  <module name="javax.persistence.api"/>
  <module name="javax.transaction.api"/>
  <module name="javax.validation.api"/>
  <module name="javax.xml.bind.api"/>
  <module name="org.antlr"/>
  <module name="org.apache.commons.collections"/>
  <module name="org.dom4j"/>
  <module name="org.javassist"/>
  <module name="org.jboss.logging"/>
 </dependencies>
</module>

 

 

Step 2: Set eclipselink.target-server

Remove any Hibernate-specific properties and add the following provider and property:

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<property name="eclipselink.target-server" value="JBoss"/>

 

 

Alternately, you can set this as a system property with:

 

jboss-cli.sh --connect '/system-property=eclipselink.target-server:add(value=JBoss)'

 

Step 3: Add VFS integration

If you want EclipseLink to automatically detect your entity classes, you need to add the JBossArchiveFactoryImpl and VFSArchive classes (source attached to this article) to your project. To compile them, you'll need the JBoss VFS JAR on your classpath. If you're using Maven, you can accomplish that by putting the below in your pom.xml:

 

<dependency>
  <groupId>org.jboss</groupId>
  <artifactId>jboss-vfs</artifactId>
  <version>3.1.0.Final</version>
  <scope>provided</scope>
</dependency>

 

 

You will also need to configure EclipseLink to use the custom ArchiveFactory by setting the "eclipselink.archive.factory" system property to the fully-qualified name of the JBossArchiveFactoryImpl class. If you can't or don't want to add the classes and system property, then you must explicitly list all of your entity classes in persistence.xml, otherwise EclipseLink will be unable to find them.

 

To set the system property using the CLI:

 

jboss-cli.sh --connect '/system-property=eclipselink.archive.factory:add(value=CLASS.NAME.OF.YOUR.COPY.OF.JBossArchiveFactoryImpl)'

 

You can also set the system property in standalone.xml:

 

<system-properties>
  <property name="eclipselink.archive.factory" value="CLASS.NAME.OF.YOUR.COPY.OF.JBossArchiveFactoryImpl"/>
</system-properties>

 

 

Step 4: Set up static weaving

In order to take full advantage of EclipseLink's capabilities, your entity classes must be bytecode-enhanced (EclipseLink refers to this as "weaving"). By default, EclipseLink will use built-in EE mechanisms to weave your entity classes at run-time. However, you may prefer to weave them at build-time for various reasons. Additionally, dynamic weaving fails if you have entity classes that extend a @MappedSuperclass. For some reason, the dynamic weaver does not weave the superclass, leading to exceptions that look like this:

java.lang.NoSuchMethodError: my.mapped.superclass.<init>(Lorg/eclipse/persistence/internal/descriptors/PersistenceObject;)V

 

Therefore, if you use @MappedSuperclass, you MUST set up static weaving. Alternatively, you may be able to convert the superclass to an entity with TABLE_PER_CLASS inheritance.

 

Exactly how you set up static weaving depends on your build system. If you're using Ant, EclipseLink provides an Ant task; the class name is org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask. If you're using Maven, a plugin to enable static weaving is available, although it is not part of the EclipseLink project:

<plugin>
 <groupId>au.com.alderaan</groupId>
 <artifactId>eclipselink-staticweave-maven-plugin</artifactId>
 <version>1.0.3</version>
 <executions>
  <execution>
   <phase>process-classes</phase>
   <goals>
    <goal>weave</goal>
   </goals>
   <configuration>
    <logLevel>ALL</logLevel>
   </configuration>
  </execution>
 </executions>
 <dependencies>
  <dependency>
   <groupId>org.eclipse.persistence</groupId>
   <artifactId>eclipselink</artifactId>
   <version>${version.eclipselink}</version>
  </dependency>
 </dependencies>
</plugin>

<repository>
  <id>EclipseLink</id>
  <url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
</repository>

<dependency>
  <groupId>org.eclipse.persistence</groupId>
  <artifactId>eclipselink</artifactId>
  <version>${version.eclipselink}</version>
  <scope>provided</scope>
</dependency>

 

Note that the <repository> is not necessary if you are using EclipseLink 2.5.0 or later, as those releases are available in Maven Central.

 

You will also need to add the following property to your persistence.xml:

<property name="eclipselink.weaving" value="static"/>

 

 

If you are using Eclipse and a recent version of m2eclipse, m2eclipse will complain about not having any metadata for eclipselink-staticweave-maven-plugin. Use the quick-fix option to tell m2eclipse to ignore the plugin, then change the pluginExecution action from <ignore></ignore> to <execute><runOnIncremental>true</runOnIncremental></execute>. Also, Eclipse will eventually complain about missing classes referenced by required .class files if your enhanced classes in one project are referenced by another project, and the EclipseLink dependency is not part of the other project. To fix this, either put the EclipseLink dependency in both projects or put it in a parent POM that both projects inherit from.

 

Step 5: Configure EclipseLink logging (optional)

EclipseLink has a built-in logging facade that it uses to log all its messages. The default logger prints to System.out.println, which is undesirable. There is also a logger that uses the default JDK logger, which JBoss captures, but EclipseLink adds the entire VFS path of your deployment in the middle of the log category, making logs harder to read and configure. Also, JBoss prints the JDK logger levels, which can look a little strange next to all the other logging that uses JBoss Logging or SLF4J levels. Finally, there is a server logger, but the default version simply prints to a stream and EclipseLink does not ship with a subclass designed for JBoss.

 

If you would prefer a logger more in line with the rest of JBoss, use the JBossLogger class (source attached to this article). You will need the JBoss Logging JAR on your classpath to compile it. If you are using Maven, this can be accomplished by putting the below in your pom.xml:

 <dependency>
  <groupId>org.jboss.logging</groupId>
  <artifactId>jboss-logging</artifactId>
  <version>3.1.0.GA</version>
  <scope>provided</scope>
</dependency>

 

 

To set the EclipseLink logger implementation, add the following property in persistence.xml:

<property name="eclipselink.logging.logger" value="JavaLogger"/>

 

where the value may be either the fully-qualified name of a class that implements the SessionLog interface, or one of the values defined in org.eclipse.persistence.config.LoggerType.

 

Logging integratoin may also be set as a system property using jboss-cli so you don't have to change persistence.xml.

 

Converting to EclipseLink from Hibernate

The following is a list of known issues you may encounter if you are converting an existing project from Hibernate to EclipseLink:

  • Contrary to the JPA specification, Hibernate will allow Date and Calendar fields in entity objects without defined temporal types (it assumes TemporalType.TIMESTAMP). EclipseLink requires the temporal type to be defined.
  • Hibernate automatically flushes when em.persist() and em.merge() are called. EclipseLink flushes when the transaction is committed.
  • Hibernate supports JPQL with "IN (:collection)" instead of requiring you to manually expand it to "IN (:element1, :element2, ...)". This is a non-standard extension that EclipseLink does not support.