Re: Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource` - Mailing list pgsql-jdbc
From | Dave Cramer |
---|---|
Subject | Re: Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource` |
Date | |
Msg-id | CADK3HH+aMLaQ2MF-uGV4AmYNBLYDYEknRnANToyJPAHuzzTqzQ@mail.gmail.com Whole thread Raw |
In response to | Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource` (Basil Bourque <basil.bourque@gmail.com>) |
List | pgsql-jdbc |
On Tue, 15 Oct 2019 at 23:16, Basil Bourque <basil.bourque@gmail.com> wrote:
Question
How do I tell Tomcat 9 to use a Postgres-specific object factory for producing DataSource object in response to JNDI query?
Details
I have been wrestling Tomcat 9.0.26 to get its JNDI implementation to generate a `DataSource` whose implementation is the class `org.postgresql.ds.PGSimpleDataSource`, rather than the `org.apache.tomcat.dbcp.dbcp2.BasicDataSource` class obtained by default.
I am using the JNDI approach to externalize the database connection info (username, password, etc.) as a config file for Tomcat. That externalizing seems better for my needs than embedding those settings inside my web-app’s WAR file.
My steps:
(1) Get the `postgresql-42.2.8.jar` on the appropriate class loader.
(2) Write an XML file for `<Context>` with a `factory` attribute for an implementation of `javax.naming.spi.ObjectFactory`.
(3) Place that context definition file in correct location.
(4) Register the `PGSimpleDataSource` class with Java’s Service Provider Interface (SPI).
(5) In my Java code, use a JNDI context to access a `DataSource` object.
—--—| Step 1 |———————
In my designated “base” folder for Tomcat, in a `lib` folder, place the `postgresql-42.2.8.jar` file.
Given Tomcat’s default `catalina.properties` file’s settings, that *base*/lib location leads to the JDBC driver being loaded in Tomcat’s “Common” class loader.
—--—| Step 2 |———————
Write an XML file named the same as my context. In this case: `clepsydra.xml` for a context named `clepsydra`.
That file’s content:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Domain: DEV, TEST, ACPT, ED, PROD -->
<Environment name = "work.basil.example.deployment-mode"
description = "Signals whether to run this web-app with development, testing, or production settings."
value = "DEV"
type = "java.lang.String"
override = "false"
/>
<Resource
name="jdbc/postgres"
auth="Container"
type="javax.sql.DataSource"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://127.0.0.1:5432/mydb"
username="myuser"
password="mypasswd"
factory="org.postgresql.ds.common.PGObjectFactory"
/>
</Context>
Note how I included an attribute for `factory` in that `Resource` definition. I am only guessing that is the right thing to do, based on my reading of the “Resource Definitions” section of the Tomcat page “The Context Container” at:
https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Resource_Definitions
That page is confusing as it discusses only global resources, but I want this resource only for my own web-app, not globally.
My understanding is that `PGObjectFactory` implements `javax.naming.spi.ObjectFactory`. This means I should be able to tell Tomcat to use this object factory rather than Tomcat’s own object factory. The goal is to get at runtime a `org.postgresql.ds.PGSimpleDataSource` object rather than a `org.apache.tomcat.dbcp.dbcp2.BasicDataSource`.
—--—| Step 3 |———————
To place this XML file, I go to the `conf` folder I copied from Tomcat into my designated Tomcat “base” folder. In that `conf` folder, I create a `Catalina` folder for the name of the engine. Within that I create a folder named `localhost` for the name of the host, as I am running my Vaadin 14 web app from IntelliJ Ultimate edition 2019.3 externally in Tomcat 9.0.26 in macOS Mojave with Java 13.
I place my `clepsydra.xml` file in that *base*/conf/Catalina/localhost folder.
I know this context file is being loaded successfully because at runtime I am able to access the Environment entry seen above in the XML file:
ctxInitial = new InitialContext();
ctxEnv = ( Context ) ctxInitial.lookup( "java:comp/env" );
String deploymentModeString = ( String ) ctxEnv.lookup( "work.basil.example.deployment-mode" );
value: DEV
—--—| Step 4 |———————
I am guessing that given the `.spi.` in `javax.naming.spi.ObjectFactory`, I need to register my desired `PGObjectFactory` via Java Service Provider Interface (SPI) facility.
So in my Vaadin app’s `resources` folder I create a `META-INF` folder. In there I create a `services` folder. In that `resources/META-INF/services` folder I create a file named exactly the name of the interface:
javax.naming.spi.ObjectFactory
Inside that file I write one line, the name of the implementing class:
org.postgresql.ds.common.PGObjectFactory
—--—| Step 5 |———————
After cleaning and rebuilding my project, at runtime I execute this code:
ctxInitial = new InitialContext();
DataSource dataSource = ( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" );
System.out.println( "dataSource = " + dataSource );
value: null
After all that effort, I get a null `DataSource`, with no Exception thrown. Perhaps there are error messages, but I do not see any on the console within IntelliJ. Is there somewhere else to look for error messages?
Can anyone tell me what I have missed, or what I am doing wrong?
For another telling of this tale, see my Stack Overflow Question:
https://stackoverflow.com/q/58385528/642706
—Basil Bourque
Last time I looked at tomcat this: https://jdbc.postgresql.org/documentation/head/tomcat.html worked.
This may be wildly out of date now though?
Dave
pgsql-jdbc by date: