Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource` - Mailing list pgsql-jdbc
From | Basil Bourque |
---|---|
Subject | Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource` |
Date | |
Msg-id | AD88A21D-F0A7-4966-BC22-080E4B9B42D0@gmail.com Whole thread Raw |
Responses |
Re: Configure Apache Tomcat’s JNDI to return a `DataSource` of class `PGSimpleDataSource`
(Dave Cramer <pg@fastcrypt.com>)
|
List | pgsql-jdbc |
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 theclass `org.postgresql.ds.PGSimpleDataSource`, rather than the `org.apache.tomcat.dbcp.dbcp2.BasicDataSource` class obtainedby default. I am using the JNDI approach to externalize the database connection info (username, password, etc.) as a config file forTomcat. 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 loadedin 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 thingto 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 tellTomcat 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` forthe name of the host, as I am running my Vaadin 14 web app from IntelliJ Ultimate edition 2019.3 externally in Tomcat9.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 abovein 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 notsee 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
pgsql-jdbc by date: