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 <<a href=3D"mailto:bas= [email protected]">[email protected]</a>> 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 `<Context>` 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 <?xml version=3D"1.0" encoding=3D"UTF-8&quo= t;?><br> =C2=A0 =C2=A0 <Context><br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 <!-- Domain: DEV, TEST, ACPT, ED, PROD=C2=A0= --><br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 <Environment name =3D "work.basil.examp= le.deployment-mode"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0description =3D "Signals whether to run this web-app with developme= nt, testing, or production settings."<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0value =3D "DEV"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0type =3D "java.lang.String"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0override =3D "false"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0/><br> <br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 <Resource<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name= =3D"jdbc/postgres"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 auth= =3D"Container"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 type= =3D"javax.sql.DataSource"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 drive= rClassName=3D"org.postgresql.Driver"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 url= =3D"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>"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 usern= ame=3D"myuser"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 passw= ord=3D"mypasswd"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 facto= ry=3D"org.postgresql.ds.common.PGObjectFactory"<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 />= <br> =C2=A0 =C2=A0 </Context><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( "java:comp/env= " );<br> =C2=A0 =C2=A0 String deploymentModeString =3D ( String ) ctxEnv.lookup( &qu= ot;work.basil.example.deployment-mode" );<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" );<br> =C2=A0 =C2=A0 System.out.println( "dataSource =3D " + 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--