I have been developing the Yellowfire project against Glassfish 3.1 ever since that application server made a beta release but of late wanted to get Yellowfire to deploy to JBoss 7.1 as well. The snag I kept hitting my head against was that of the JNDI naming of the data source used in the persistence.xml since Glass wanted the global name, i.e. yellowfire/ds whereas JBoss 7.1 was adamant that it should be either java:/yellowfire/ds or java:jboss/yellowfire/ds.

I then brushed up on the use of the resource-refs and tried to apply the same logic to the persistence.xml data source name, much to my frustration but found that I was not alone. The Beware of soapy frogs JEE pitfalls webpage.

The only option was then to make this a build process requirement and at first I made the maven-war-plugin filter the different persistence.xml files for each application server depending on the Maven profile. Needless to say, maintaining 3 persistence.xml files (Glassfish, JBoss and Weblogic) is not child’s play when actively developing a new piece of software and is much harder when there are more than 1 developer.

The next step was to make the Maven build generate the persistence.xml file from a template and that is the objective of this post.

First we start with a profile for each application server in which the dependencies for that server is required that are different from the others. Sometimes this dependency management is an issue all on it’s own.

<profiles>
  <profile>
    <id>glassfish-3.1</id>
    .....
    <properties>
      <application.server>glassfish</application.server>
    </properties>
  </profile>
  <profile>
    <id>jboss-7.1</id>
    .....
    <properties>
      <application.server>jboss</application.server>
    </properties>
  </profile>
</profiles>

Notice that each profile has the same property application.server which will be used in the maven-resources-plugin do perform a bit of filtering.
The next step is to get a list of the entity classes used by the application. Once again this can be crafted by hand or since entity classes have annotations this list can be generated. In Yellowfire, the entity class list is generated using the jpa-maven-plugin.

<plugin>
 <groupId>com.edugility</groupId>
 <artifactId>jpa-maven-plugin</artifactId>
 <version>1.0</version>
 <executions>
   <execution>
     <id>Generate persistence-classes.properties</id>
     <goals>
       <goal>list-entity-classnames</goal>
     </goals>
     <configuration>
       <defaultPropertyName>entity-class-names</defaultPropertyName>
       <firstItemPrefix>&lt;class&gt;</firstItemPrefix>
       <lastItemSuffix>&lt;/class&gt;</lastItemSuffix>
       <outputFile>${basedir}/src/main/filter/persistence-classes.properties</outputFile>
     </configuration>
   </execution>
 </executions>
</plugin>

And then provide properties files for the different application servers. First is the glassfish-persistence-xml.properties

jta-data-source=yellowfire/ds
eclipselink.target-server=

#Note: Use this with application-scoped data sources
#<property name="javax.persistence.jtaDataSource" value="java:/app/jdbc/yellowfire" /-->
#Enable or disable EclipseLink's generation of database platform-specific SQL (as opposed to generic SQL).
# true – enable EclipseLink's generation of database platform-specific SQL.
# false – disable generation of database platform-specific SQL by EclipseLink.
eclipselink.jdbc.native-sql=true

# Logging
# OFF This setting disables the generation of the log output. You may want to set logging to OFF during production to avoid the overhead of logging.
# SEVERE This level enables reporting of failure cases only. Usually, if the failure occurs, the application stops.
# WARNING This level enables logging of issues that have a potential to cause problems. For example, a setting that is picked by the application and not by the user.
# INFO This level enables the standard output. The contents of this output is very limited. It is the default logging level if a logging level is not set.
# CONFIG This level enables logging of such configuration details as your database login information and some metadata information. You may want to use the CONFIG log level at deployment time.
# FINE This level enables logging of the first level of the debugging information and SQL. You may want to use this log level during debugging and testing, but not at production.
# FINER This level enables logging of more debugging information than the FINE setting. For example, the transaction information is logged at this level. You may want to use this log level during debugging and testing, but not at production.
# FINEST This level enables logging of more debugging information than the FINER setting, such as a very detailed information about certain features (for example, sequencing). You may want to use this log level during debugging and testing, but not at production.
# ALL This level currently logs at the same level as FINEST.
eclipselink.logging.level=INFO
eclipselink.logging.timestamp=true
eclipselink.logging.session=true
eclipselink.logging.thread=true
eclipselink.logging.exceptions=true

# Control whether or not the query uses parameter binding
# Note: Default is true
# true – bind all parameters.
# false – do not bind parameters.
eclipselink.jdbc.bind-parameters=false

# Specify the use of batch writing to optimize transactions with multiple write operations
# JDBC – use JDBC batch writing.
# Buffered – do not use either JDBC batch writing nor native platform batch writing.
# Oracle-JDBC – use both JDBC batch writing and Oracle native platform batch writing.
# Use OracleJDBC in your property map.
# None – do not use batch writing (turn it off).
eclipselink.jdbc.batch-writing=JDBC

# Enable or disable EclipseLink internal statement caching
# Note: Default is false
# Note: we recommend enabling this functionality if you are using EclipseLink connection pooling.
# true – enable EclipseLink's internal statement caching.
# false – disable internal statement caching.
eclipselink.jdbc.brokerCache-statements=true

# The number of statements held when using internal statement caching.
# Note: Default is 50
eclipselink.jdbc.brokerCache-statements.size=50

# Specify when a write connection is acquired lazily. For more information, see Lazy Connection Acquisition
# Note: Default is true
# true - aquire the write connection lazily.
# false - do not aquire the write connection lazily.
eclipselink.jdbc.exclusive-connection.is-lazy=true

# Specify when EclipseLink should perform reads through a write connection. For more information, see Exclusive Write Connections.
# Note: Default is Transactional
#
# You can set this property while creating either an EntityManagerFactory (either in the map passed to the createEntityManagerFactory method, or in the persistence.xml file), or an EntityManager (in the map passed to the createEntityManager method). Note that the latter overrides the former.
#
# The following are the valid values for the use in a persistence.xml file and for the org.eclipse.persistence.config.ExclusiveConnectionMode:
#
# Transactional - Create an isolated client session (see Isolated Client Sessions) if some or all entities require isolated brokerCache 4 ; otherwise, create a client session.
# Note: EclipseLink keeps the connection exclusive for the duration of the transaction. Inside the transaction, EclipseLink performs all writes and reads through the exclusive connection. However, outside the Eclipelink transaction, a new connection is acquired from the connection pool for each read and released back immediately after the query is executed.
# Isolated - Create an exclusive isolated client session if reading an isolated Entity; otherwise, raise an error.
# Note: EclipseLink keeps the connection exclusive for the lifetime of the owning EntityManager. Inside the transaction, EclipseLink performs all writes and reads through the exclusive connection. However, outside the Eclipelink transaction only isolated entities are read through the exclusive connection; for nonisolated entities, a new connection is acquired from the connection pool for each read and released back immediately after the query is executed.
# Always - Create an exclusive isolated client session (see Isolated Client Sessions) if reading an isolated Entity; otherwise, create an exclusive client session.
# Note: EclipseLink keeps the connection exclusive for the lifetime of the owning EntityManager and performs all writes and reads through the exclusive connection.
# For more information, see Configuring Connection Policy.
eclipselink.jdbc.exclusive-connection.mode=Transactional

# Profiler
# PerformanceProfiler – Use EclipseLink performance profiler (org.eclipse.persistence.tools.profiler.PerformanceProfiler class). For more information, see Measuring EclipseLink Performance with the EclipseLink Profiler.
# QueryMonitor – Monitor query executions and brokerCache hits (org.eclipse.persistence.tools.profiler.QueryMonitor class).This option provides a simple low-overhead means for measuring performance of query executions and brokerCache hits. You may want to use this option for performance analysis in a complex system.
# NoProfiler – Do not use a performance profiler.
# Custom profiler – Use your own custom profiler class. Create it by implementing the org.eclipse.persistence.sessions.SessionProfiler interface and providing a no-argument constructor.
eclipselink.profiler=NoProfiler
eclipselink.orm.validate.schema=true
eclipselink.exclude-eclipselink-orm=true

Then it is the jboss-persistence-xml.properties

jta-data-source=java:/yellowfire/ds
eclipselink.target-server=JBoss

#Note: Use this with application-scoped data sources
#<property name="javax.persistence.jtaDataSource" value="java:/app/jdbc/yellowfire" /-->
#Enable or disable EclipseLink's generation of database platform-specific SQL (as opposed to generic SQL).
# true – enable EclipseLink's generation of database platform-specific SQL.
# false – disable generation of database platform-specific SQL by EclipseLink.
eclipselink.jdbc.native-sql=true

# Logging
# OFF This setting disables the generation of the log output. You may want to set logging to OFF during production to avoid the overhead of logging.
# SEVERE This level enables reporting of failure cases only. Usually, if the failure occurs, the application stops.
# WARNING This level enables logging of issues that have a potential to cause problems. For example, a setting that is picked by the application and not by the user.
# INFO This level enables the standard output. The contents of this output is very limited. It is the default logging level if a logging level is not set.
# CONFIG This level enables logging of such configuration details as your database login information and some metadata information. You may want to use the CONFIG log level at deployment time.
# FINE This level enables logging of the first level of the debugging information and SQL. You may want to use this log level during debugging and testing, but not at production.
# FINER This level enables logging of more debugging information than the FINE setting. For example, the transaction information is logged at this level. You may want to use this log level during debugging and testing, but not at production.
# FINEST This level enables logging of more debugging information than the FINER setting, such as a very detailed information about certain features (for example, sequencing). You may want to use this log level during debugging and testing, but not at production.
# ALL This level currently logs at the same level as FINEST.
eclipselink.logging.level=INFO
eclipselink.logging.timestamp=true
eclipselink.logging.session=true
eclipselink.logging.thread=true
eclipselink.logging.exceptions=true

# Control whether or not the query uses parameter binding
# Note: Default is true
# true – bind all parameters.
# false – do not bind parameters.
eclipselink.jdbc.bind-parameters=false

# Specify the use of batch writing to optimize transactions with multiple write operations
# JDBC – use JDBC batch writing.
# Buffered – do not use either JDBC batch writing nor native platform batch writing.
# Oracle-JDBC – use both JDBC batch writing and Oracle native platform batch writing.
# Use OracleJDBC in your property map.
# None – do not use batch writing (turn it off).
eclipselink.jdbc.batch-writing=JDBC

# Enable or disable EclipseLink internal statement caching
# Note: Default is false
# Note: we recommend enabling this functionality if you are using EclipseLink connection pooling.
# true – enable EclipseLink's internal statement caching.
# false – disable internal statement caching.
eclipselink.jdbc.brokerCache-statements=true

# The number of statements held when using internal statement caching.
# Note: Default is 50
eclipselink.jdbc.brokerCache-statements.size=50

# Specify when a write connection is acquired lazily. For more information, see Lazy Connection Acquisition
# Note: Default is true
# true - aquire the write connection lazily.
# false - do not aquire the write connection lazily.
eclipselink.jdbc.exclusive-connection.is-lazy=true

# Specify when EclipseLink should perform reads through a write connection. For more information, see Exclusive Write Connections.
# Note: Default is Transactional
#
# You can set this property while creating either an EntityManagerFactory (either in the map passed to the createEntityManagerFactory method, or in the persistence.xml file), or an EntityManager (in the map passed to the createEntityManager method). Note that the latter overrides the former.
#
# The following are the valid values for the use in a persistence.xml file and for the org.eclipse.persistence.config.ExclusiveConnectionMode:
#
# Transactional - Create an isolated client session (see Isolated Client Sessions) if some or all entities require isolated brokerCache 4 ; otherwise, create a client session.
# Note: EclipseLink keeps the connection exclusive for the duration of the transaction. Inside the transaction, EclipseLink performs all writes and reads through the exclusive connection. However, outside the Eclipelink transaction, a new connection is acquired from the connection pool for each read and released back immediately after the query is executed.
# Isolated - Create an exclusive isolated client session if reading an isolated Entity; otherwise, raise an error.
# Note: EclipseLink keeps the connection exclusive for the lifetime of the owning EntityManager. Inside the transaction, EclipseLink performs all writes and reads through the exclusive connection. However, outside the Eclipelink transaction only isolated entities are read through the exclusive connection; for nonisolated entities, a new connection is acquired from the connection pool for each read and released back immediately after the query is executed.
# Always - Create an exclusive isolated client session (see Isolated Client Sessions) if reading an isolated Entity; otherwise, create an exclusive client session.
# Note: EclipseLink keeps the connection exclusive for the lifetime of the owning EntityManager and performs all writes and reads through the exclusive connection.
# For more information, see Configuring Connection Policy.
eclipselink.jdbc.exclusive-connection.mode=Transactional

# Profiler
# PerformanceProfiler – Use EclipseLink performance profiler (org.eclipse.persistence.tools.profiler.PerformanceProfiler class). For more information, see Measuring EclipseLink Performance with the EclipseLink Profiler.
# QueryMonitor – Monitor query executions and brokerCache hits (org.eclipse.persistence.tools.profiler.QueryMonitor class).This option provides a simple low-overhead means for measuring performance of query executions and brokerCache hits. You may want to use this option for performance analysis in a complex system.
# NoProfiler – Do not use a performance profiler.
# Custom profiler – Use your own custom profiler class. Create it by implementing the org.eclipse.persistence.sessions.SessionProfiler interface and providing a no-argument constructor.
eclipselink.profiler=NoProfiler
eclipselink.orm.validate.schema=true
eclipselink.exclude-eclipselink-orm=true

The persistence.xml template that is in the src\main\resources\META-INF look as follows.

<?xml version="1.0" encoding="UTF-8"?></pre>
<persistence version="2.0"
 xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="yellowfire" transaction-type="JTA">

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
 <jta-data-source>${jta-data-source}</jta-data-source>

${entityClassnames}

<properties>
 <!--property name="eclipselink.session-name" value="yellowfire"/-->
 <property name="eclipselink.target-server" value="${eclipselink.target-server}"/>
 <property name="eclipselink.target-database" value="SQLServer"/>

<property name="eclipselink.jdbc.native-sql" value="${eclipselink.jdbc.native-sql}"/>

<property name="eclipselink.logging.level" value="${eclipselink.logging.level}"/>
 <property name="eclipselink.logging.timestamp" value="${eclipselink.logging.timestamp}"/>
 <property name="eclipselink.logging.session" value="${eclipselink.logging.session}"/>
 <property name="eclipselink.logging.thread" value="${eclipselink.logging.thread}"/>
 <property name="eclipselink.logging.exceptions" value="${eclipselink.logging.exceptions}"/>

<property name="eclipselink.jdbc.bind-parameters" value="${eclipselink.jdbc.bind-parameters}"/>

<property name="eclipselink.jdbc.batch-writing" value="${eclipselink.jdbc.batch-writing}"/>

<property name="eclipselink.jdbc.brokerCache-statements" value="${eclipselink.jdbc.brokerCache-statements}"/>

<property name="eclipselink.jdbc.brokerCache-statements.size" value="${eclipselink.jdbc.brokerCache-statements.size}"/>

<property name="eclipselink.jdbc.exclusive-connection.is-lazy" value="${eclipselink.jdbc.exclusive-connection.is-lazy}"/>

<property name="eclipselink.jdbc.exclusive-connection.mode" value="${eclipselink.jdbc.exclusive-connection.mode}"/>

<property name="eclipselink.profiler" value="${eclipselink.profiler}"/>
 <property name="eclipselink.orm.validate.schema" value="${eclipselink.orm.validate.schema}"/>
 <property name="eclipselink.exclude-eclipselink-orm" value="${eclipselink.exclude-eclipselink-orm}"/>
 </properties>
 </persistence-unit>
</persistence>

And it is the maven-resources-plugin that makes all this magic possible 😉

<plugin>
  <artifactId>maven-resources-plugin</artifactId>
  <version>2.5</version>
  <executions>
    <execution>
      <id>persistence</id>
      <phase>process-test-classes</phase>
      <goals>
        <goal>copy-resources</goal>
      </goals>
      <configuration>
        <filters>
          <filter>${basedir}/src/main/filter/persistence-classes.properties</filter>
          <filter>${basedir}/src/main/filter/${application.server}-persistence-xml.properties</filter>
        </filters>
        <outputDirectory>${project.build.outputDirectory}/META-INF</outputDirectory>
        <resources>
          <resource>
            <filtering>true</filtering>
            <directory>src/main/resources/META-INF</directory>
            <includes>
              <include>persistence.xml</include>
            </includes>
          </resource>
        </resources>
      </configuration>
    </execution>
  </executions>
</plugin>
Advertisements