Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make JNDI names compatible with both GlassFish and WildFly

I am developing a Java EE 7 application and have a requirement for the application to be deployed onto application servers running either GlassFish 4.0 or WildFly 8.1.0. The issue I've got is GlassFish and WildFly use slightly different formats for JNDI names but I can't see how to make my application compatible with both.

In GlassFish my persistence.xml file references the data source jdbc/myDataSouce, but in WildFly the data source needs to be java:/jdbc/myDataSource.

The same is also true for classes that are annotated with @Resource. In GlassFish the annotation for a class using JavaMail would be @Resource(name = "mail/myMailSession"), but to deploy onto WildFly this would need to be @Resource(name = "java:mail/myMailSession").

I know that I could unpack the EAR and JAR files to manually edit files such as persistence.xml but I can't do that for classes that have been annotated with @Resource.

Is there a way I can allow my complied application to be deployed onto GlassFish and WildFly without maintaining two different versions of the code? I'm assuming the answer probably lies with application specific deployment descriptors but I can't find any examples that cover these two scenarios.

Can anyone point me in the right direction please?

like image 922
Paul H Avatar asked Oct 19 '14 13:10

Paul H


2 Answers

You can modify the Wildfly JNDi names and strip the undesired prefixes from the respective JNDI names to find the least common denominator in both app servers. The following works for me with Glassfish and JBoss AS 7.1. Since I expect Wildfly to be backwards-compatible to JBoss in this regard, I guess it'll work for Wildfly as well.

Persistence

Inject as:

@PersistenceContext(unitName="TestPU")
private EntityManager entityManager;

or via ejb-jar.xml:

<persistence-context-ref>
    <persistence-context-ref-name>entityManager</persistence-context-ref-name>
    <persistence-unit-name>TestPU</persistence-unit-name>
    <injection-target> ... </injection-target>
</persistence-context-ref>

The corresponding persistence.xml:

<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="TestPU" transaction-type="JTA">
    <jta-data-source>datasources/TestDS</jta-data-source>
    <class>org.jeeventstore.persistence.jpa.EventStoreEntry</class>
    <properties>
      <property name="hibernate.show_sql" value="false"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="hibernate.connection.charSet" value="UTF-8"/>
      <property name="eclipselink.logging.level" value="FINE"/>
      <property name="eclipselink.logging.level.sql" value="FINE"/>
      <property name="eclipselink.logging.parameters" value="true"/>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

(note the simple jta-data-source JNDI name)

Here's a glassfish-resources.xml file used to specify a Derby database on deployment, a similar setup can be used for MySQL or Postgres.

<resources>

    <jdbc-resource pool-name="ArquillianEmbeddedDerbyPool"
                   jndi-name="datasources/TestDS"/>

    <jdbc-connection-pool name="ArquillianEmbeddedDerbyPool"
                          res-type="javax.sql.DataSource"
                          datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
                          is-isolation-level-guaranteed="false">
        <property name="databaseName" value="target/databases/derby"/>
        <property name="createDatabase" value="create"/>
    </jdbc-connection-pool>

</resources>

And the settings from the JBoss standalone.xml:

<datasource jta="true" jndi-name="java:/datasources/TestDS" pool-name="TestDS" enabled="true" use-ccm="false">
    <connection-url>jdbc:postgresql://localhost/test_db</connection-url>
    ...
</datasource>

Resources

I have not injected a JavaMail component on Glassfish, but similar to the datasoruce settings, it might be worth a try to strip the "java:" part from the @Resource annotation as well.

@Resource(name = "mail/myMailSession")

and then configure Wildfly such that that the mail resource is available at the "java:mail/myMailSession" JNDI location.

Injection via ejb-jar.xml

Another option is to manually inject the fields via a ejb-jar.xml file, and then use a build tool such as maven to copy either of ejb-jar-glassfish.xml or ejb-jar-wildfly.xml to the desired ejb-jar.xml at assembly time.

In one of our projects we use a mixed approach to avoid the burden with the xml configuration: We configure a small number of "provider" beans via ejb-jar.xml to inject, e.g., the persistence context into a PersistenceContextProvider, and then use CDI to inject the PersistenceContextProvider into the EJBs via @EJB, which are found without further configuration since they reside in the same EAR.

like image 65
Alexander Langer Avatar answered Nov 14 '22 22:11

Alexander Langer


I haven't hit the mail-dilemma just yet. But I've ran into the same problem your having when it comes to data source definition and my solution has been to not setup the data sources using the server's console, but make them deployable together with your archive using the @DataSourceDefinition annotation. Turns out WildFly won't complain about java:app/blabla.. if the data source is setup during deployment!

Here is a real world example for you that works on both GlassFish and WildFly:

https://github.com/martinanderssondotcom/java-ee-concepts/../ArquillianDS.java

Note that the data source JNDI name declared is:

java:app/env/ArquillianDS

And here is the related persistence.xml file (don't mind the name of the file in this repository, the repository represents a test project that build archives during runtime and the app will change the name of the file in the archive to persistence.xml):

https://github.com/MartinanderssonDotcom/java-ee-concepts/../persistence-update.xml

Also note that the persistence unit need a data source located using this JNDI name:

java:app/env/ArquillianDS

This deployment works perfectly fine with both GlassFish and WildFly. I've noted that if we declare the data source during deployment, then we pay the price of not seeing the data source listed anywhere in the admin gui/console. For me, that is a small price to pay in order to have a truly portable application. As an added bonus, I don't have to write lengthy installation/setup instructions. For all my projects, the data source is an intrinsic part of the application and I don't mind having a class file in the archive that represents the data source.

The above data source is using a Java DB (or "Apache Derby" for old school people). As some comments in the ArquillianDS.java file describe: GlassFish has problems using a simple URL connection string combined with Java DB. Hence I resorted to specifying all attributes of the @DataSourceDefinition explicitly. Recently in another project of mine (alas not a public one), I used the same construct of deployment time data source definition but targeting MySQL. Here's that data source definition and it works on both servers:

@DataSourceDefinition(
        name = "java:app/env/maLivechatDS",
        url = "jdbc:mysql://localhost:3306/malivechat_db?createDatabaseIfNotExist=true&user=root&password",
        className = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
)
@ManagedBean
public class MySQLDataSource { }

Note that the driver is MysqlDataSource and not MysqlXADataSource. One point in my application uses a rather complex transaction scheme and GlassFish ran into problems if I used the XA-driver. However, the non-XA driver used by my application still work properly with JTA transactions so for me, it was just a cheap trick to get the boat floating. You should probably use the XA-driver.

like image 33
Martin Andersson Avatar answered Nov 14 '22 22:11

Martin Andersson