How do I tell Tomcat 9 to use a Postgres-specific object factory for producing DataSource object in response to JNDI query?
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.
(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:
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:
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:
—--—| 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:
Inside that file I write one line, the name of the implementing class:
—--—| Step 5 |———————
After cleaning and rebuilding my project, at runtime I execute this code:
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: