Deploying Data Source Configuration

Uncategorized

Since Java EE 6 it’s possible to define data sources in a portable way.

This does mean though that the data source is embedded in the application archive. For some use cases, this is exactly what’s needed, but for others it may not be ideal.

 

The alternatives are typically defining a data source on the Java EE server itself, using an admin console or CLI of some kind.

These alternatives do typically require a different syntax to be used, which can be problematic if the data source is often moved in and out of the application. They also require a vendor specific syntax, which in turn can be problematic if application servers from different vendors have to be supported.

 

A somewhat obscure feature of Java EE data source definitions allows for another variant; deploying an application archive (e.g. a .war) containing only a data source definition and its driver and making that data source available to a second application. This is made possible since the valid JNDI locations for registering the data source includes java:global, which is the special server wide namespace.

 

To demonstrate this with some example code, we first create a Maven project with only a single web.xml as follows:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1" >
     
     <data-source>
        <name>java:global/datasource</name>
        <class-name>org.h2.jdbcx.JdbcDataSource</class-name>
        <url>jdbc:h2:mem:test</url>
    </data-source>
 
</web-app>

 

And the following pom.xml:

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
     
    <groupId>fish.example</groupId>
    <artifactId>datasource</artifactId>
    <version>1</version>
     
    <packaging>war</packaging>
     
    <build>
       <finalName>datasource</finalName>
    </build>
     
    <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
     
    <dependencies>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.195</version>
        </dependency>
    </dependencies>
     
</project>

 

We then build and install this as usual using mvn clean install.

Next, we build a simple application that consumes this data source, which we call datasource-consumer. It only contains a single java file:

 

@Startup
@Singleton
public class StartupService {
     
    @Resource
    private DataSource dataSource;
 
    @PostConstruct
    public void init() {
        try (Connection connection = dataSource.getConnection()) {
            out.println(
                connection.getMetaData().getDatabaseProductName() + "-" +
                connection.getCatalog()
            );
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
     
}

 

 

And the following pom.xml:

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
     
    <groupId>fish.example</groupId>
    <artifactId>datasource-consumer</artifactId>
    <version>1</version>
     
    <packaging>war</packaging>
     
    <build>
       <finalName>datasource-consumer</finalName>
    </build>
     
    <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
     
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
         
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.195</version>
        </dependency>
    </dependencies>
     
</project>
 
 

We subsequently deploy both the “resource providing app” and the “resource consuming app” via Payara Micro as follows:

java -jar payara-micro.jar --deploy ~/.m2/repository/fish/example/datasource/1/datasource-1.war --deploy ~/.m2/repository/fish/example/datasource-consumer/1/datasource-consumer-1.war

And lo and behold, during startup we see “h2-test" being printed in the log.

Having a separately deployable data source means we can configure the data source outside the application, but still use only Java EE APIs and descriptors. If we do venture a bit back into the vendor specific realm, we can even add another layer to the configurability by using placeholders in the deployment descriptors using the somewhat common ${} syntax.

 

For example:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1" >
     
     <data-source>
        <name>java:global/datasource</name>
        <class-name>org.h2.jdbcx.JdbcDataSource</class-name>
        <url>jdbc:h2:mem:${db}</url>
    </data-source>
 
</web-app>
 
 

If we now start up Payara Micro as follows, we can control the DB (catalog) where we connect to from the commandline:

java -Ddb=foo -jar payara-micro.jar --deploy ~/.m2/repository/fish/example/datasource/1/datasource-1.war --deploy ~/.m2/repository/fish/example/datasource-consumer/1/datasource-consumer-1.war

If we look at the logs again, we’ll now see “h2-foo" being printed.

Defining a data source this way is not perfect yet, as unlike the deployment of a resource connector (.rar), the class loader for an application is often isolated. So though the resource is indeed globally defined, the classes the resource makes use of are not. Hopefully the technique demonstrated here can still be useful for some cases as-is, while better solutions may be presented in the future.

 
{{cta(‘4c7626f5-1e53-418e-90c8-add6e4af19c9’)}}
 

Comments (4)

Post a comment

Your email address will not be published. Required fields are marked *

Payara needs the contact information you provide to us to contact you about our products and services. You may unsubscribe from these communications at any time. For information on how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy, please review our Legal & Privacy Policy.

  1. Abhishek Gupta

    Ah.. good one ! thanks

  2. Raul Caso

    Great man ! it could help for some cases thanks

  3. Michael Remijan

    I’ve been trying to get this feature to work but at this point I’m stumped. I’m using “Payara Server 4.1.1.171.1 #badassfish (build 139)” Is there something else you need to do so that the web.xml data-source tag is evaluated when the WAR is deployed. No matter what I try, it seems as if Payara is completely ignoring the web.xml data-source tag.

    I am attempting to connect to an external Derby database I have running. My web.xml looks like this:

    Sample DataSource definition
    java:global/MyDataSource
    org.apache.derby.jdbc.ClientDataSource
    localhost
    1125
    Ch24
    sa
    sa
    0
    true
    TRANSACTION_READ_COMMITTED
    20
    30
    20
    300
    50

    index.html

    When I try to create a consumer, I try to inject like this:

    @Resource(name=”java:global/MyDataSource”)
    private DataSource dsc;

    But it seems like there is nothing ever to inject because I get this exception from the consumer:

    java.lang.IllegalStateException: Exception attempting to inject Res-Ref-Env-Property: java:global/MyDataSource@javax.sql.DataSource@ resolved as: jndi: java:global/MyDataSource@res principal: null@mail: null
    No Runtime properties

  4. Michael Remijan

    Just as a followup, I tried setting the web.xml class-name value to a bogus class that doesn’t exist:

    org.apache.derby.jdbc.ClientDataSourceXXXXYYYY

    And the WAR deployed fine with no errors. It’s like is being completely ignored.

Related Posts

4 minutes
Uncategorized

Leading the Way: Payara Platform Community 7 Beta Now Fully Jakarta EE 11 Certified

We’re excited to announce that Payara Platform Community 7 Beta application server is now fully certified as Jakarta EE 11 […]

JBoss ELS Decoded 5 minutes
Migration

JBoss ELS Decoded: What Extended Lifecycle Support Really Means for Your Java Applications​

If your Java EE 8 applications run on Red Hat JBoss Enterprise Application Platform (EAP) 7, you can’t afford […]

Understanding the Security Issues of Aging Middleware 6 minutes
Community

Boost Developer Productivity with Payara Server Maven Plugin + AI Agent

Managing Payara Server Just Got Smarter  Imagine managing your Jakarta EE applications not just with Maven goals, but by […]