
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 […]
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.
Share:
We’re excited to announce that Payara Platform Community 7 Beta application server is now fully certified as Jakarta EE 11 […]
If your Java EE 8 applications run on Red Hat JBoss Enterprise Application Platform (EAP) 7, you can’t afford […]
Managing Payara Server Just Got Smarter Imagine managing your Jakarta EE applications not just with Maven goals, but by […]
Ah.. good one ! thanks
Great man ! it could help for some cases thanks
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
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.