While deploying my application on weblogic 12 I am facing following exception
weblogic.application.ModuleException:
javax.naming.NameNotFoundException: While trying to look up
comp/env/UserTransaction in /app/webapp/Demo-UNCONTROLLED
ARTIFACT.war/895045661.; remaining name 'comp/env/UserTransaction'
My spring-jpa.xml is as follows
<jee:jndi-lookup id="userTransaction" jndi-name="java:comp/env/UserTransaction" resource-ref="true" cache="true"/>
context.xml is as follows
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/private/tool" sessionCookieName="JSESSIONID_TOOL" disableURLRewriting="true">
<Resource name="UserTransaction" auth="Container" type="javax.transaction.UserTransaction" factory="org.objectweb.jotm.UserTransactionFactory" jotm.timeout="60"/>
</Context>
How to configure UserTransaction in weblogic? Why my application is failing to lookup comp/env/UserTransaction ?
Related
I am trying to connect to my database using a Java DataSource. I am using Weblogic 12c and Hibernate to achieve this. My web.xml file contains the following resource definition:
<resource-ref>
<res-ref-name>jdbc/Microtek</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
In addition, my weblogic.xml contains the following:
<wls:resource-description>
<wls:res-ref-name>jdbc/Microtek</wls:res-ref-name>
<wls:jndi-name>microtek</wls:jndi-name>
</wls:resource-description>
In my Hibernate config file (hibernate.cfg.xml), I have set the following properties:
<property name="hibernate.connection.datasource">jdbc/Microtek</property>
<property name="hibernate.connection.pool_size">10</property>
<property name="show_sql">false</property>
<property name="dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<property name="javax.persistence.validation.mode">none</property>
Also, inside the META-INF directory, I created an XML file called context.xml which contains the following:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<ResourceLink global="jdbc/Microtek" name="jdbc/Microtek" type="javax.sql.DataSource"/>
</Context>
Finally, this is the exception I have been getting:
org.hibernate.HibernateException: Could not find datasource
at org.hibernate.connection.DatasourceConnectionProvider.configure(DatasourceConnectionProvider.java:79)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:143)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:84)
at org.hibernate.cfg.SettingsFactory.createConnectionProvider(SettingsFactory.java:459)
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:91)
at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2831)
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2827)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1838)
at com.microtekcomputers.models.test.main(test.java:15)
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:350)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at org.hibernate.connection.DatasourceConnectionProvider.configure(DatasourceConnectionProvider.java:75)
... 8 more
It is my first time using JNDI to connect to a data source so I might be missing something. All ideas & recommendations greatly appreciated.
UPDATE: After making the changes suggested by others, I am now getting the following exception:
Exception in thread "main" java.lang.NoClassDefFoundError: weblogic/kernel/KernelStatus
at weblogic.jndi.Environment.<clinit>(Environment.java:81)
at weblogic.jndi.WLInitialContextFactory.getInitialContext(WLInitialContextFactory.java:117)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.init(InitialContext.java:244)
at javax.naming.InitialContext.<init>(InitialContext.java:216)
at org.hibernate.util.NamingHelper.getInitialContext(NamingHelper.java:51)
at org.hibernate.connection.DatasourceConnectionProvider.configure(DatasourceConnectionProvider.java:75)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:143)
at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:84)
at org.hibernate.cfg.SettingsFactory.createConnectionProvider(SettingsFactory.java:459)
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:91)
at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2831)
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2827)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1838)
at com.microtekcomputers.models.Test.main(Test.java:12)
You do not have a connection factory defined for your data source, something like this in the code:
environment.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
Or this in the hibernate.cfg.xml:
<property name="jndi.class">weblogic.jndi.WLInitialContextFactory</property>
In weblogic you can configure connection factories to your need, WLInitialContextFactory is the default.
Also see the Oracle docs and client examples
I am getting the following exception while getting datasource through jndi:
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException:
Could not get JDBC Connection; nested exception is
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null
Tomcat's server.xml :
<Resource name="jdbc/testdb" auth="Container" type="javax.sql.DataSource"
username="test" password="test1234"
url="*********"
driverClass="oracle.jdbc.driver.OracleDriver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"/>
Tomcat's Context.xml :
<ResourceLink name="jdbc/testdb" global="jdbc/testdb"
type="javax.sql.DataSource" />
Spring's servlet xml :
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/testdb" resource-ref="true" />
and final Web.xml :
<resource-ref>
<description>Resource reference to database</description>
<res-ref-name>jdbc/testdb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Could somebody please help me?
Error Stack trace :
Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
Try driverClassName instead of driverClass:
driverClassName="oracle.jdbc.driver.OracleDriver"
I am using Eclipse STS 3.9.3, tomcat 8.5 and Spring Boot 2.0.0.RELEASE
I spent my entire morning about this problem.
So, I did a lot of things to solve:
Remove jdbc dependence from my pom.
<!-- dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency-->
Declaration resource TOMCAT_HOME/CONF/server.xml whit factory:
<GlobalNamingResources>
...
<Resource name="jdbc/postgres_jndi"
auth="Container"
type="javax.sql.DataSource"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://xxx.xxx.xx.xxx:5432/db"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
removeAbandonedOnBorrow="true"
removeAbandonedOnMaintenance="true"
timeBetweenEvictionRunsMillis="10000"
removeAbandonedTimeout="60"
logAbandoned="true"
username="xxx"
password="xxxxxx"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"/>
...
</GlobalNamingResources>
I realized that I was using Eclipse STS. The tomcat resource on server.xml only works after I put the resource declaration in server.xml inside of "Tomcat v8.5 Server at localhost-config" folder in my Server project. Did not work on TOMCAT_HOME/conf folder in server.xml file.
Change my application.yml to:
spring:
datasource:
platform: postgres
jndi-name: java:comp/env/jdbc/postgres_jndi
type: javax.sql.DataSource
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
database: POSTGRESQL
show-sql: true
#Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
properties:
hibernate:
temp:
use_jdbc_metadata_defaults: false
I think that you miss java:comp/env/ yours will be like this:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/testdb" resource-ref="true" />
A application.properties will be like:
spring.datasource.platform= postgres
spring.datasource.jndi-name= java:comp/env/jdbc/postgres_jndi
spring.datasource.type= javax.sql.DataSource
spring.datasource.driver-class-name= org.postgresql.Driver
spring.jpa.hibernate.ddl-auto= validate
spring.jpa.hibernatedatabase-platform= org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.database= POSTGRESQL
spring.jpa.show-sql= true
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
Define a context.xml file in src/main/webapp/META-INF/context.xml with definition:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/myapp">
<ResourceLink global="jdbc/postgres_jndi" name="jdbc/postgres_jni" type="javax.sql.DataSource"/>
</Context>
Put the file postgresql-42.2.2.jar in the TOMCAT_HOME/lib
I have one database JNDI connection pooling setup context.xml JNDI resources and web.xml env-ref and initial context. It is working good. But I need to build two more database for my application.
So do I have to configure two more JNDI resources and env-ref and initial context separately? Or same env-ref for three databases?
What is the efficient way to build connection pooling for more than one database? Please advise.
You are pretty much right about having to configure JNDI resources separately. The only thing that in web.xml there is NO <env-ref> element, but <resource-env-ref> and <resource-ref> elements.
In order to use more databases, you have to make the following additional configurations for each resource:
add a new <Resource> in context.xml file
add a new <resource-ref> or <resource-env-ref> in web.xml file
NOTE:
If you use #Resource annotation, the new resource no longer needs to be defined in the deployment descriptor (web.xml). This annotation is equivalent to declaring a resource-ref, message-destination-ref or env-ref, or resource-env-ref element in the deployment descriptor.
And accordingly you have to separately lookup each DataSource from the InitialContext (you can look up the required DataSource once and then just use the same instance).
Example
The below is an example of configuring two MySQL databases from one of my projects. One database is configured utilizing The Tomcat JDBC Connection Pool, another without connection pooling (you can do it however you like/need).
context.xml (located in /META-INF directory):
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Configuration for primary DB -->
<Resource name="jdbc/TestShopDB"
type="javax.sql.DataSource"
auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test_shop"
username="root"
password="mysql"
maxActive="100"
minIdle="10"
initialSize="10"
validatonQuery="SELECT 1"
validationInterval="30000"
removeAbandoned="true"
removeAbandonedTimeout="60"
abandonWhenPercentageFull="50"
closeMethod="close"/>
<!-- Configuration for administration DB -->
<Resource name="jdbc/TestShopAdminDB"
type="javax.sql.DataSource"
auth="Container"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test_shop_admin"
username="root"
password="mysql"/>
</Context>
web.xml (located in /WEB-INF directory; unrelated parts are omitted for the sake of brevity):
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
<!-- Configuration for primary DB -->
<resource-ref>
<res-ref-name>jdbc/TestShopDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<!-- Configuration for administration DB -->
<resource-ref>
<res-ref-name>jdbc/TestShopAdminDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
...
</web-app>
Additional reading
Apache Tomcat 7: JNDI Resources
HOW-TO
Apache Tomcat 7: JNDI Datasource
HOW-TO
Apache Tomcat 7: The Context
Container
Tomcat Expert: Configuring JDBC Pool for High
Concurency
JavaRanch Journal: JDBC Connection Pooling Best
Practices
I am trying to set up the database connection properties using JNDI for a Spring web application.
I am considering two approaches as below:
Approach 1:
In your Spring configuration you may have something like:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/facs"/>
Then in your webapp /META-INF/context.xml file you should have something similar too:
<?xml version='1.0' encoding='utf-8'?>
<!-- antiResourceLocking="true" -->
<Context path="/podd-apn"
reloadable="true"
cachingAllowed="false"
antiResourceLocking="true"
>
<Resource name="jdbc/facs"
type="javax.sql.DataSource" username="${database.username}" password="${database.password}"
driverClassName="org.postgresql.Driver"
url="${database.url}"
maxActive="8" maxIdle="4"
global="jdbc/facs"
/>
</Context>
And in your web.xml you should something like:
<!-- JNDI -->
<resource-ref>
<description>FACs Datasource</description>
<res-ref-name>jdbc/facs</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Approach 2:
Setup in the Spring context like this:
<jee:jndi-lookup id="dbDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
You can declare the JNDI resource in Tomcat's server.xml using something like this:
<GlobalNamingResources>
<Resource name="jdbc/DatabaseName" auth="Container" type="javax.sql.DataSource"
username="dbUsername" password="dbPasswd"
url="jdbc:postgresql://localhost/dbname"
driverClassName="org.postgresql.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"/>
</GlobalNamingResources/>
And reference the JNDI resource from Tomcat's web context.xml like this:
<ResourceLink name="jdbc/DatabaseName"
global="jdbc/DatabaseName"
type="javax.sql.DataSource"/>
My question is where is the best place to keep database properties? Should they be placed in server.xml or context.xml?
Also, if I have 2 databases, should I use two configs?
Also, is it best practice to directly place them in either server.xml or context.xml? Or do I need to configure through Tomcat Manager GUI console?
Thanks!
I prefer a third approach that takes the best from
Approach 1 and Approach 2 described by user1016403.
Approach 3
Save database properties on the server.xml
reference the server.xml database properties from the web application META-INF/context.xml
Approach 3 benefits
While the first point is useful for security reasons the second point is useful for referencing server properties value from the web application, even if server properties values will change.
Moreover decoupling resource definitions on the server from their use by the web application makes such configuration scalable across organizations with various complexity where different teams work on different tiers/layers: the server administrators team can work without conflicting with developers team if the administrator shares the same JNDI name with the developer for each resource.
Approach 3 implementation
Define the JNDI name jdbc/ApplicationContext_DatabaseName.
Declare the jdbc/ApplicationContext_DatabaseName's various properties and values in Tomcat's server.xml using something like this:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContext_DatabaseName" auth="Container" type="javax.sql.DataSource"
username="dbUsername" password="dbPasswd"
url="jdbc:postgresql://localhost/dbname"
driverClassName="org.postgresql.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"/>
</GlobalNamingResources/>
Link the jdbc/ApplicationContext_DatabaseName's properties from web application META-INF/context.xml by an application-private JNDI context java:comp/env/ specified in the name attribute:
<Context path="/ApplicationContext" ... >
<!--
"global" attribute links to GlobalNamingResources in the ${catalina.base}/conf/server.xml (server administrator team)
"name" attribute is relative to the application-private JNDI context java:comp/env/ and is looked up from the java web application (application developer team)
-->
<ResourceLink global="jdbc/ApplicationContext_DatabaseName" name="jdbc/DatabaseName" type="javax.sql.DataSource"/>
</Context>
Finally, in order to use the JNDI resource, specify the JNDI name jdbc/DatabaseName in web application's deployment descriptor:
<resource-ref>
<description>DatabaseName's Datasource</description>
<res-ref-name>jdbc/DatabaseName</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and in Spring context:
<jee:jndi-lookup id="DatabaseNameDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
Approach 3 drawbacks
If the JNDI name gets changed then both the server.xml and the META-INF/context.xml will have to be edited and a deploy would be necessary; nevertheless this scenario is rare.
Approach 3 variations
Many data sources used by one web application
Simply add configurations to Tomcat's server.xml:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContext_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContext_DatabaseName2" ... />
...
</GlobalNamingResources/>
Add link web application META-INF/context.xml by an application-private JNDI context java:comp/env/ specified in the name attribute:
<Context path="/ApplicationContext" ... >
<ResourceLink global="jdbc/ApplicationContext_DatabaseName1" name="jdbc/DatabaseName1" ... />
<ResourceLink global="jdbc/ApplicationContext_DatabaseName2" name="jdbc/DatabaseName2" ... />
...
</Context>
Finally add JNDI resources usage in web application's deployment descriptor:
<resource-ref>
<description>DatabaseName1's Datasource</description>
<res-ref-name>jdbc/DatabaseName1</res-ref-name> ...
</resource-ref>
<resource-ref>
<description>DatabaseName2's Datasource</description>
<res-ref-name>jdbc/DatabaseName2</res-ref-name> ...
</resource-ref>
...
and in Spring context:
<jee:jndi-lookup id="DatabaseName1DataSource"
jndi-name="jdbc/DatabaseName1" ... />
<jee:jndi-lookup id="DatabaseName2DataSource"
jndi-name="jdbc/DatabaseName2" ... />
...
Many data sources used by many web application on the same server
Simply add configuration to Tomcat's server.xml:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContextX_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContextX_DatabaseName2" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName2" ... />
...
</GlobalNamingResources/>
the others configuration should be deducible from previous variation case.
Many data sources to the same database used by many web application on the same server
In such case a Tomcat's server.xml configurations like:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContextX_DatabaseName" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName" ... />
ends up in two different web applications META-INF/context.xml like:
<Context path="/ApplicationContextX" ... >
<ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>
and like:
<Context path="/ApplicationContextY" ... >
<ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>
so someone might be worried about the fact that the same name="jdbc/DatabaseName" is looked up, and then used, by two different applications deployed on the same server: this is not a problem because the jdbc/DatabaseName is an application-private JNDI context java:comp/env/, so ApplicationContextX by using java:comp/env/ can't (by design) look up the resource linked to global="jdbc/ApplicationContextY_DatabaseName".
Of course if you felt more relaxed without this worry you might use a different naming strategy like:
<Context path="/ApplicationContextX" ... >
<ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/applicationXprivateDatabaseName" ... />
</Context>
and like:
<Context path="/ApplicationContextY" ... >
<ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/applicationYprivateDatabaseName" ... />
</Context>
YOUR_APP.xml file
I prefer Approach 2 (put everything (not only some attribute in the config), but instead of placing them in the global server.xml or global context.xml you should place it in the application-specific context.xml.default YOUR_APP.xml file in your Tomcat.
The YOUR_APP.xml file is located in $catalinaHome/conf/<engine>/<host> (for example conf/Catalina/localhost/YOUR_APP.xml).
The configuration in application specific YOUR_APP.xml is only available for the specific application.
See the guide published by MuleSoft. And see the official documentation, Tomcat Configuration Reference, page for The Context Container
Version 9
Version 8
To quote that documentation:
Individual Context elements may be explicitly defined:
• …
• In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The context path and version will be derived from the base name of the file (the file name less the .xml extension).
• …
Approach 4
Instead of using JNDI I work with .properties files and build complex object during program initialization instead on configuration time.
You already use Spring and it is easy construct DataSource by:
<context:property-placeholder location="classpath:app.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#${db.host}:${db.port}:${db.user}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.pass}"/>
</bean>
I completely agree with Ralph with using deployment descriptor in $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xmlbut instead JNDI I like plain key-value file!
With Spring injecting above properties into bean fields are easy:
#Value("${db.user}") String defaultSchema;
instead of JNDI:
#Inject ApplicationContext context;
Enviroment env = context.getEnvironment();
String defaultSchema = env.getProperty("db.user");
Note also that EL allow this (default values and deep recursive substitution):
#Value('${db.user:testdb}') private String dbUserName;
<property name='username' value='${db.user.${env}}'/>
To externalize .properties file I use modern Tomcat 7 that has org.apache.catalina.loader.VirtualWebappLoader:
<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
virtualClasspath="/srv/web/app/"/>
So your devops fill virtualClasspath with local external full paths which is separate per application and put local app.propertiesto that dir.
See also:
Adding a directory to tomcat classpath
Can I create a custom classpath on a per application basis in Tomcat
Externalizing Tomcat webapp config from .war file
How to read a properties file outside my webapp context in Tomcat
Configure Tomcat to use properties file to load DB connection information
Externalize Tomcat configuration
step 1: context.xml
<Context path="/projectname">
<Resource auth="Container"
driverClassName="com.mysql.jdbc.Driver"
logAbandoned="true"
maxActive="100" ``
maxIdle="30"
maxWait="10000"
name="refname"
removeAbandoned="true"
removeAbandonedTimeout="60"
type="javax.sql.DataSource"
url="jdbc:mysql://localhost:8080/dbname"
username="root"
password="root"/>
</Context>
Step 2 : web.xml
<resource-ref>
<description>DB Connection</description>
<res-ref-name>refname</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Step 3 : create a class to get connection
Connection connection = null;
Context context = (Context) new InitialContext().lookup("java:comp/env");
DataSource ds = (DataSource) context.lookup("refname");
connection = ds.getConnection();
Everything is set
You also can use JNDI URL support for different application configuration for test, integration test, production.
<Context>
...
<Resource auth="Container" factory="com.benasmussen.jndi.url.URLFactory"
name="url/MyUrl" type="java.net.URL" url="file:///your/path/to/file"/>
...
</Context>
<jee:jndi-lookup id="myUrl" jndi-name="java:comp/env/url/MyUrl" expected-type="java.net.URL" />
Check out the GitHub project Tomcat JNDI URL Support to enable JNDI URL support for Tomcat servers.
I'm using Hibernate 4.0 with a JPA persistence.xml file on Tomcat 7. No Struts, just straight Hibernate with some Jersey services. Here is the exception I'm running into:
Caused by: org.hibernate.service.jndi.JndiException: Unable to lookup JNDI name [jdbc/MyDB]
at org.hibernate.service.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:68)
at org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl.configure(DatasourceConnectionProviderImpl.java:116)
at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.buildJdbcConnectionAccess(JdbcServicesImpl.java:223)
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:89)
at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:71)
at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2273)
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2269)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1738)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:94)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
... 8 more
Caused by: javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
at org.apache.naming.NamingContext.lookup(NamingContext.java:820)
at org.apache.naming.NamingContext.lookup(NamingContext.java:154)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:135)
at javax.naming.InitialContext.lookup(InitialContext.java:396)
at org.hibernate.service.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:65)
... 23 more
I see the note about jbc is not bound in this context, but I'm confused as how this is happening. I'm deploying my context in an app-specific context.xml, below:
<?xml version='1.0' encoding='utf-8'?>
<Context>
<Resource name="jdbc/MyDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="..." password="..." driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb"/>
</Context>
And my persistence.xml file looks like:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="com.example.mysql" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>jdbc/MyDB</non-jta-data-source>
<class>...</class>
<properties>
<property name="hibernate.connection.datasource" value="jdbc/MyDB"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.id.new_generator_mappings" value ="true"/>
</properties>
</persistence-unit>
</persistence>
Finally, my web.xml file has the resource defined as so:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>My Web Application</display-name>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/MyDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
...
</web-app>
As for my layout, here is how my war file is structured:
app.war
+ META-INF
- context.xml
+ WEB-INF
+ classes
+ META-INF
- persistence.xml
+ lib
- web.xml
A few minor notes:
Using a global context versus an application-specific context makes no difference.
The code trying to instantiate an EntityManager instance is in a JAR file in the lib directory (part of a multi-project Maven build), but the persistence XML is in the main web app as outlined above.
I can see the JNDI datasource in Tomcat and I can query it using psi-probe, i.e. I can access the connection information and successfully execute SQL queries against the data source.
Since you use a portable resource with , you should call your JNDI using "java:comp/env/your_resource", like java:comp/env/jdbc/MyDB