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:

Previous
From: Basil Bourque
Date:
Subject: Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource`
Next
From: Andrei Tsibets
Date:
Subject: Order of generated keys during inserts