3

In a Jakarta EE 8 environment: Is it possible to define a ("portable JNDI") data source at EAR level [1] in application.xml and use this data source as JTA data source in the persistence.xml inside a library/JAR module?

Purpose: Creating a common JAR module that defines JPA entities together with corresponding "repositories", so that this JAR module can be used by multiple WAR modules (e. g. a RESTful API and a UI module) and package this modules as an EAR that is deployable to multiple application servers.

With the following attempt/method (for a full example I created a simple git repo [2]), the deployment of such an EAR fails (at least with Payara and WildFly).


Attempt/Method

Let's say there's an application consisting of 2 WAR modules and both WAR modules uses a shared JAR module, so that there is a application structure something like this:

ear/
├── shared-lib-jar
|   ├── ...
|   └── META-INF
|       ├── ...
|       └── persistence.xml
├── api-war/
|   └── ...
├── ui-war/
|   └── ...
├── application.xml
├── ...

In the application.xml of the EAR the data source is defined like this:

<application>
  <!-- ... -->
  <data-source>
    <name>java:app/appDS</name>
    <!-- ... -->
  </data-source>
</application>

In the persistence.xml the JNDI name defined in the application.xml is used as the JTA data-source:

<persistence>
  <persistence-unit name="..." transaction-type="JTA">
    <jta-data-source>java:app/appDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <!-- ... -->
  </persistence-unit>
</persistence>

unexpected/faulty behavior/situation with different (2) application servers

Setup: org.h2.jdbcx.JdbcDataSource as class-name and a "file-based" data base

Payara (5.2020.2)

The data base file will not be created and the server log says:

[2020-07-07T22:56:32.731+0200] [Payara 5.2020] [SEVERE] [AS-DEPLOYMENT-00026] [javax.enterprise.system.tools.deployment.dol] [tid: _ThreadID=168 _ThreadName=admin-thread-pool::admin-listener(11)] [timeMillis: 1594155392731] [levelValue: 1000] [[
  JNDI lookup failed for the resource: Name: foo-core, Lookup: java:app/appDS, Type: javax.sql.DataSource.]]

[2020-07-07T22:56:32.731+0200] [Payara 5.2020] [SEVERE] [] [javax.enterprise.system.core] [tid: _ThreadID=168 _ThreadName=admin-thread-pool::admin-listener(11)] [timeMillis: 1594155392731] [levelValue: 1000] [[
  JNDI lookup failed for the resource: Name: [foo-core], Lookup: [java:app/appDS], Type: [javax.sql.DataSource]]]

WildFly (1.4.11.Final)

The data base file is created but the server log says:

{"WFLYCTL0062: Composite operation failed and was rolled back. Steps that failed:" => {"Operation step-2" => {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"foo-ear-0.0.1-SNAPSHOT.ear\".WeldStartService" => "Failed to start service
    Caused by: java.lang.IllegalArgumentException: WFLYWELD0037: Error injecting persistence unit into CDI managed bean. Can't find a persistence unit named 'foo-core' in deployment foo-ear-0.0.1-SNAPSHOT.ear for injection point protected javax.persistence.EntityManager com.acme.BookRepository.entityManager"}}}}

1: https://jakarta.ee/specifications/platform/8/platform-spec-8.html#a1688

2: https://gitlab.com/hjoeren/application-level-ds-example

hjoeren
  • 549
  • 1
  • 5
  • 12

2 Answers2

3

Yes, you are almost done, at least in case of Payara or Glasshish. You do not need to expose your datasource at application.xml.

First declare you datasource at persistence.xml like you have done:

<?xml version="1.0" encoding="UTF-8"?>
<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="my.PU" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    **<jta-data-source>jdbc/mydatasource</jta-data-source>**
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="eclipselink.logging.level" value="INFO"/>
      <property name="eclipselink.logging.parameters" value="true"/>
      <property name="eclipselink.persistence-context.flush-mode" value="COMMIT"/>
    </properties>
  </persistence-unit>
</persistence>

Then just configure appropriate connection pool and JDBC resource at your Payara (or GF) application server using Admin console or asadmin command. Example of file glassfish-resources.xml

$cat glassfish-resources.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">    
    <resources>
        <jdbc-connection-pool connection-creation-retry-interval-in-seconds="30" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" wrap-jdbc-objects="false" res-type="javax.sql.DataSource" `name="mysql_myrootPool"` is-connection-validation-required="true" connection-creation-retry-attempts="10" validate-atmost-once-period-in-seconds="60">
            <property name="User" value="root"/>
            <property name="Password" value="secret"/>
            <property name="URL" value="jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=convertToNull"/>
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="zeroDateTimeBehavior" value="convertToNull"/>
            <property name="characterEncoding" value="utf-8"/>
            <property name="useSSL" value="false"/>
        </jdbc-connection-pool>
        <jdbc-resource enabled="true" `jndi-name="jdbc/mydatasource"` object-type="user" `pool-name="mysql_myrootPool"`/>
    </resources>
$asadmin add-resources glassfish-resources.xml

Please note correct names of pool, persistence unit and resource.

Now you can inject your connection whereever it is allowed (EJB, Servlets, Interceptors , Etc) in any EJB or WEB module included in your EAR like this

@PersistenceContext(unitName = "my.PU")
private EntityManager em;
S. Kadakov
  • 861
  • 1
  • 6
  • 15
  • Thanks @s-kadakov for your answer. I know, that with Payara/Glassfish I can declare JDBC resources via the `glassfish-resources.xml`. But as mentioned in the question I am looking for some _application-server independent_ method ("... deployable to multiple application servers") and therefore want to use the `application.xml` as it would be a standard way of defining a data-source don't worrying about the used application server – hjoeren Jul 20 '20 at 10:40
  • I guess it is possible to declare datasource in WildFly server as well (https://docs.jboss.org/author/display/WFLY10/DataSource%20configuration.html), so it is pure deployment problem. – S. Kadakov Jul 20 '20 at 12:09
  • Yes, you're right: It is possible to declare data sources in WildFly as well. But why to declare data sources in several vendor specific ways if there is a way to declare it in a vendor neutral format that has to be understood by every application server (see [5.18.3 of Jakarta EE 8 Spec](https://jakarta.ee/specifications/platform/8/platform-spec-8.html#a1688)) – hjoeren Jul 20 '20 at 17:20
  • Environment explotation is deployment engineers responsibility. Using hardcoded datasources you force customer to repack your application on every environment change (IP change, for example), what a programmer is needed for. In other hand, it is not problem, when you deploy in virtual environment and can tune it with environment variables. – S. Kadakov Jul 21 '20 at 07:10
0

Finally found the mistake: I put the application.xml in the wrong folder. Instead of putting the application.xml to the root folder, it has to be placed one level deeper in the META-INF folder so that the project structure looks like this:

ear/
├── shared-lib-jar
|   ├── ...
|   └── META-INF
|       ├── ...
|       └── persistence.xml
├── api-war/
|   └── ...
├── ui-war/
|   └── ...
├── META-INF/
|   ├── ...
|   └──application.xml
├── ...

To see the full example, I fixed the mis-configuration in the repo for this question.

hjoeren
  • 549
  • 1
  • 5
  • 12