Re: Configure Apache Tomcat’s JNDI to return a `Da taSource` of class `PGSimpleDataSource`

Dave Cramer <[email protected]> Wed, 16 Oct 2019 07:38:04 +0200
Newsgroups gmane.comp.db.postgresql.jdbc
Message-ID <CADK3HH+aMLaQ2MF-uGV4AmYNBLYDYEknRnANToyJPAHuzzTqzQ@mail.gmail.com>
--000000000000eb355405950081da
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

On Tue, 15 Oct 2019 at 23:16, Basil Bourque <[email protected]> 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 externalizin=
g
> seems better for my needs than embedding those settings inside my web-app=
=E2=80=99s
> 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=E2=80=99s Service P=
rovider
> Interface (SPI).
> (5) In my Java code, use a JNDI context to access a `DataSource` object.
>
> =E2=80=94--=E2=80=94|  Step 1  |=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94
>
> In my designated =E2=80=9Cbase=E2=80=9D folder for Tomcat, in a `lib` fol=
der, place the
> `postgresql-42.2.8.jar` file.
>
> Given Tomcat=E2=80=99s default `catalina.properties` file=E2=80=99s setti=
ngs, that
> *base*/lib location leads to the JDBC driver being loaded in Tomcat=E2=80=
=99s
> =E2=80=9CCommon=E2=80=9D class loader.
>
> =E2=80=94--=E2=80=94|  Step 2  |=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94
>
> Write an XML file named the same as my context. In this case:
> `clepsydra.xml` for a context named `clepsydra`.
>
> That file=E2=80=99s content:
>
>     <?xml version=3D"1.0" encoding=3D"UTF-8"?>
>     <Context>
>         <!-- Domain: DEV, TEST, ACPT, ED, PROD  -->
>         <Environment name =3D "work.basil.example.deployment-mode"
>                      description =3D "Signals whether to run this web-app
> with development, testing, or production settings."
>                      value =3D "DEV"
>                      type =3D "java.lang.String"
>                      override =3D "false"
>                      />
>
>         <Resource
>                     name=3D"jdbc/postgres"
>                     auth=3D"Container"
>                     type=3D"javax.sql.DataSource"
>                     driverClassName=3D"org.postgresql.Driver"
>                     url=3D"jdbc:postgresql://127.0.0.1:5432/mydb"
>                     username=3D"myuser"
>                     password=3D"mypasswd"
>                     factory=3D"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 =E2=80=9CResource Definitions=E2=80=9D section of the Tomc=
at page =E2=80=9CThe
> Context Container=E2=80=9D at:
>
>
> https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Resource_Def=
initions
>
> 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=E2=80=99s 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`.
>
> =E2=80=94--=E2=80=94|  Step 3  |=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94
>
> To place this XML file, I go to the `conf` folder I copied from Tomcat
> into my designated Tomcat =E2=80=9Cbase=E2=80=9D folder. In that `conf` f=
older, I create a
> `Catalina` folder for the name of the engine. Within that I create a fold=
er
> 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 =3D new InitialContext();
>     ctxEnv =3D ( Context ) ctxInitial.lookup( "java:comp/env" );
>     String deploymentModeString =3D ( String ) ctxEnv.lookup(
> "work.basil.example.deployment-mode" );
>
> value: DEV
>
> =E2=80=94--=E2=80=94|  Step 4  |=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94
>
> 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=E2=80=99s `resources` folder I create a `META-INF` fo=
lder. 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
>
> =E2=80=94--=E2=80=94|  Step 5  |=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94
>
> After cleaning and rebuilding my project, at runtime I execute this code:
>
>     ctxInitial =3D new InitialContext();
>     DataSource dataSource =3D ( DataSource ) ctxInitial.lookup(
> "java:comp/env/jdbc/postgres" );
>     System.out.println( "dataSource =3D " + 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 messag=
es?
>
> 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
>
> =E2=80=94Basil 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

--000000000000eb355405950081da
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr"><div><div dir=3D"ltr" class=3D"gmail_sign=
ature" data-smartmail=3D"gmail_signature"><div dir=3D"ltr"><br></div></div>=
</div></div><br><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_=
attr">On Tue, 15 Oct 2019 at 23:16, Basil Bourque &lt;<a href=3D"mailto:bas=
[email protected]">[email protected]</a>&gt; wrote:<br></div><bloc=
kquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:=
1px solid rgb(204,204,204);padding-left:1ex">Question<br>
<br>
How do I tell Tomcat 9 to use a Postgres-specific object factory for produc=
ing DataSource object in response to JNDI query?<br>
<br>
Details<br>
<br>
I have been wrestling Tomcat 9.0.26 to get its JNDI implementation to gener=
ate a `DataSource` whose implementation is the class `org.postgresql.ds.PGS=
impleDataSource`, rather than the=C2=A0 `org.apache.tomcat.dbcp.dbcp2.Basic=
DataSource` class obtained by default. <br>
<br>
I am using the JNDI approach to externalize the database connection info (u=
sername, password, etc.) as a config file for Tomcat. That externalizing se=
ems better for my needs than embedding those settings inside my web-app=E2=
=80=99s WAR file.<br>
<br>
My steps:<br>
<br>
(1) Get the `postgresql-42.2.8.jar` on the appropriate class loader.<br>
(2) Write an XML file for `&lt;Context&gt;` with a `factory` attribute for =
an implementation of `javax.naming.spi.ObjectFactory`.<br>
(3) Place that context definition file in correct location.<br>
(4) Register the `PGSimpleDataSource` class with Java=E2=80=99s Service Pro=
vider Interface (SPI).<br>
(5) In my Java code, use a JNDI context to access a `DataSource` object.<br=
>
<br>
=E2=80=94--=E2=80=94|=C2=A0 Step 1=C2=A0 |=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94=E2=80=94<br>
<br>
In my designated =E2=80=9Cbase=E2=80=9D folder for Tomcat, in a `lib` folde=
r, place the `postgresql-42.2.8.jar` file. <br>
<br>
Given Tomcat=E2=80=99s default `catalina.properties` file=E2=80=99s setting=
s, that *base*/lib location leads to the JDBC driver being loaded in Tomcat=
=E2=80=99s =E2=80=9CCommon=E2=80=9D class loader.<br>
<br>
=E2=80=94--=E2=80=94|=C2=A0 Step 2=C2=A0 |=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94=E2=80=94<br>
<br>
Write an XML file named the same as my context. In this case: `clepsydra.xm=
l` for a context named `clepsydra`. <br>
<br>
That file=E2=80=99s content:<br>
<br>
=C2=A0 =C2=A0 &lt;?xml version=3D&quot;1.0&quot; encoding=3D&quot;UTF-8&quo=
t;?&gt;<br>
=C2=A0 =C2=A0 &lt;Context&gt;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 &lt;!-- Domain: DEV, TEST, ACPT, ED, PROD=C2=A0=
 --&gt;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 &lt;Environment name =3D &quot;work.basil.examp=
le.deployment-mode&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0description =3D &quot;Signals whether to run this web-app with developme=
nt, testing, or production settings.&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0value =3D &quot;DEV&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0type =3D &quot;java.lang.String&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0override =3D &quot;false&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0/&gt;<br>
<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 &lt;Resource<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name=
=3D&quot;jdbc/postgres&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 auth=
=3D&quot;Container&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type=
=3D&quot;javax.sql.DataSource&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 drive=
rClassName=3D&quot;org.postgresql.Driver&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 url=
=3D&quot;jdbc:postgresql://<a href=3D"http://127.0.0.1:5432/mydb" rel=3D"no=
referrer" target=3D"_blank">127.0.0.1:5432/mydb</a>&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 usern=
ame=3D&quot;myuser&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 passw=
ord=3D&quot;mypasswd&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 facto=
ry=3D&quot;org.postgresql.ds.common.PGObjectFactory&quot;<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /&gt;=
<br>
=C2=A0 =C2=A0 &lt;/Context&gt;<br>
<br>
<br>
Note how I included an attribute for `factory` in that `Resource` definitio=
n. I am only guessing that is the right thing to do, based on my reading of=
 the =E2=80=9CResource Definitions=E2=80=9D section of the Tomcat page =E2=
=80=9CThe Context Container=E2=80=9D at:<br>
<br>
<a href=3D"https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Res=
ource_Definitions" rel=3D"noreferrer" target=3D"_blank">https://tomcat.apac=
he.org/tomcat-9.0-doc/config/context.html#Resource_Definitions</a><br>
<br>
That page is confusing as it discusses only global resources, but I want th=
is resource only for my own web-app, not globally.<br>
<br>
My understanding is that `PGObjectFactory` implements `javax.naming.spi.Obj=
ectFactory`. This means I should be able to tell Tomcat to use this object =
factory rather than Tomcat=E2=80=99s 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`.<br>
<br>
=E2=80=94--=E2=80=94|=C2=A0 Step 3=C2=A0 |=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94=E2=80=94<br>
<br>
To place this XML file, I go to the `conf` folder I copied from Tomcat into=
 my designated Tomcat =E2=80=9Cbase=E2=80=9D folder. In that `conf` folder,=
 I create a `Catalina` folder for the name of the engine. Within that I cre=
ate 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 Tomc=
at 9.0.26 in macOS Mojave with Java 13.<br>
<br>
I place my `clepsydra.xml` file in that *base*/conf/Catalina/localhost fold=
er.<br>
<br>
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:<br>
<br>
=C2=A0 =C2=A0 ctxInitial =3D new InitialContext();<br>
=C2=A0 =C2=A0 ctxEnv =3D ( Context ) ctxInitial.lookup( &quot;java:comp/env=
&quot; );<br>
=C2=A0 =C2=A0 String deploymentModeString =3D ( String ) ctxEnv.lookup( &qu=
ot;work.basil.example.deployment-mode&quot; );<br>
<br>
value: DEV<br>
<br>
=E2=80=94--=E2=80=94|=C2=A0 Step 4=C2=A0 |=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94=E2=80=94<br>
<br>
I am guessing that given the `.spi.` in `javax.naming.spi.ObjectFactory`, I=
 need to register my desired `PGObjectFactory` via Java Service Provider In=
terface (SPI) facility. <br>
<br>
So in my Vaadin app=E2=80=99s `resources` folder I create a `META-INF` fold=
er. In there I create a `services` folder. In that `resources/META-INF/serv=
ices` folder I create a file named exactly the name of the interface: <br>
javax.naming.spi.ObjectFactory<br>
<br>
Inside that file I write one line, the name of the implementing class: <br>
org.postgresql.ds.common.PGObjectFactory<br>
<br>
=E2=80=94--=E2=80=94|=C2=A0 Step 5=C2=A0 |=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94=E2=80=94<br>
<br>
After cleaning and rebuilding my project, at runtime I execute this code:<b=
r>
<br>
=C2=A0 =C2=A0 ctxInitial =3D new InitialContext();<br>
=C2=A0 =C2=A0 DataSource dataSource =3D ( DataSource ) ctxInitial.lookup( &=
quot;java:comp/env/jdbc/postgres&quot; );<br>
=C2=A0 =C2=A0 System.out.println( &quot;dataSource =3D &quot; + dataSource =
);<br>
<br>
value: null<br>
<br>
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 with=
in IntelliJ. Is there somewhere else to look for error messages?<br>
<br>
Can anyone tell me what I have missed, or what I am doing wrong?<br>
<br>
For another telling of this tale, see my Stack Overflow Question:<br>
<a href=3D"https://stackoverflow.com/q/58385528/642706" rel=3D"noreferrer" =
target=3D"_blank">https://stackoverflow.com/q/58385528/642706</a><br>
<br>
=E2=80=94Basil Bourque<br>
<br></blockquote><div><br></div><div>Last time I looked at tomcat this:=C2=
=A0<a href=3D"https://jdbc.postgresql.org/documentation/head/tomcat.html">h=
ttps://jdbc.postgresql.org/documentation/head/tomcat.html</a>=C2=A0worked.<=
/div><div><br></div><div>This may be wildly out of date now though?</div><d=
iv><br></div><div>Dave=C2=A0</div></div></div>

--000000000000eb355405950081da--