I've always used Spring's dependency injection to get datasource objects and use them in my DAOs, but now, I have to write an app without that.
With Spring I can write something like this:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1/app?characterEncoding=UTF-8" />
<property name="username" value="u" />
<property name="password" value="p" />
</bean>
But how can I use datasource in my DAOs without Spring or anything? I'm using servlets and JSPs only. Performance is very important factor.
Believe it or not, people were writing applications before Spring and some are still not using it :) In your case, you could use Tomcat connection pool (and there is a complete configuration example for MySQL in the documentation). Let me summarize it:
First, put your driver in $CATALINA_HOME/lib.
Then, configure a JNDI DataSource in Tomcat by adding a declaration for your resource to your Context:
<Context path="/DBTest" docBase="DBTest"
debug="5" reloadable="true" crossContext="true">
<!-- maxActive: Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to -1 for no limit.
-->
<!-- maxIdle: Maximum number of idle dB connections to retain in pool.
Set to -1 for no limit. See also the DBCP documentation on this
and the minEvictableIdleTimeMillis configuration parameter.
-->
<!-- maxWait: Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<!-- username and password: MySQL dB username and password for dB connections -->
<!-- driverClassName: Class name for the old mm.mysql JDBC driver is
org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
-->
<!-- url: The JDBC connection url for connecting to your MySQL dB.
-->
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest"/>
</Context>
Declare this resource in your web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<description>MySQL Test App</description>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
And get the datasource with a JNDI lookup in your application:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/TestDB");
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();
Note that such lookup is usually coded in a ServiceLocator (when you can't have a a DI container or a framework inject it for you).
I used to get error with sybase, I had missing META-INF folder in WebContent folder. Putting context.xml in that fixed the error Cannot create JDBC driver of class '' for connect URL 'null'...
// www.abbulkmailer.com
My context.xml looks like
<Context path="/reports" docBase="reports" debug="5" reloadable="true" crossContext="true">
<Resource name='jdbc/ASCSybaseConnection'
auth='Container'
type='javax.sql.DataSource'
username='fdd'
password='555'
driverClassName='com.sybase.jdbc2.jdbc.SybDriver'
maxActive='100'
maxIdle='100'
minIdle='10'
removeAbandoned="true"
removeAbandonedTimeout="60"
testOnBorrow="true"
logAbandoned="true"
url='jdbc:sybase:Tds:1.3.4.5:654/DB'/>
</Context>
You can declare your data source as a JNDI object and retrieve a datasource via a JNDI lookup:
DataSource ds = (DataSource)
envCtx.lookup("jdbc/EmployeeDB");
as documented here and here.
That's as bare-bones as you can get, so from there on, performance is completely up to you.
Related
I have developed an application on Tomcat 7.0 that uses Datanucleus / JDO to access to a database. I currently have the JDO connection properties stored in the "datanucleus.properties" located in the application itself. The connection is working fine, but I would like to store the connection information as JNDI, to have it on the server and no longer in the war itself (I always have to replace the file in the war when deploying it remotely).
I tried the following:
Create a in the web.xml of the application (jdbc/ConnectionDB)
In "Server.xml", I tried to add the following the context of my application
<Resource name="jdbc/ConnectionDB" auth="Container" type="javax.jdo.PersistenceManagerFactory" /> <ResourceParams name="jdbc/ConnectionDB
<parameter>
<name>javax.jdo.PersistenceManagerFactoryClass</name>
<value>org.datanucleus.api.jdo.JDOPersistenceManagerFactory</value>
</parameter>
<parameter>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<parameter>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://localhost/TomcatTest</value>
</parameter>
...
I then try to create a new PMF with the following syntax:
Context context = null;
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("java:comp/env/jdbc/ConnectionDB",context);
When I run my application, I get a javax.jdo.JDOUserException: You have either specified for this PMF to use a "persistence-unit" of "datanucleus.properties" (yet this doesnt exist!)
I don't really understand what is wrong in my setup.
Regards,
Marcel
I finally found the solution I was looking for, I post it here, it might help somebody else:
Create a resource in "Context.xml" file of the server
<Resource name="jdbc/SyncTestDB"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="root"
password="mysql"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/SyncTestDB"/>
Create a reference to that resource in the "web.xml" file of your application
<resource-ref>
<description>MySQL Database Connection</description>
<res-ref-name>jdbc/SyncTestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
And finally get a Persistence Manager Factory using the JNDI connection:
PersistenceManagerFactory pmf;
Properties properties = new Properties();
properties.setProperty("datanucleus.ConnectionFactoryName","java:comp/env/jdbc/SyncTestDB");
Read the javadoc for JDOHelper.getPersistenceManagerFactory(String) and it is obviously not for passing in some JNDI data source string.
Read the docs for Tomcat and you will also see that specifying a datasource you do not provide JDO connection details.
You can equally specify a persistence.xml with that JNDI string for the "javax.jdo.option.ConnectionFactoryName" property. As per the JDO spec and DataNucleus/Tomcat docs then
It is said that in the Spring javadoc article about DriverManagerDataSource class, that this class is very simple and that it is recommended
to use a JNDI DataSource provided by the container. Such a DataSource can be exposed as a DataSource bean in a Spring ApplicationContext via JndiObjectFactoryBean
The question is: how do I accomplish this?
For example, if I wish to have DataSource bean to access my custom MySQL database, what would I require then? What should I write in the context configuration, etc?
If using Spring's XML schema based configuration, setup in the Spring context like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
...
<jee:jndi-lookup id="dbDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
Alternatively, setup using simple bean configuration like this:
<bean id="DatabaseName" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/DatabaseName"/>
</bean>
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="dbUser"
password="dbPassword"
url="jdbc:postgresql://localhost/dbname"
driverClassName="org.postgresql.Driver"
initialSize="20"
maxWaitMillis="15000"
maxTotal="75"
maxIdle="20"
maxAge="7200000"
testOnBorrow="true"
validationQuery="select 1"
/>
</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"/>
Reference documentation:
Tomcat 8 JNDI Datasource HOW-TO
Tomcat 8 Context Resource Links Reference
Spring 4 JEE JNDI Lookup XML Schema Reference
Spring 4 JndiObjectFactoryBean Javadoc
Edit: This answer has been updated for Tomcat 8 and Spring 4. There have been a few property name changes for Tomcat's default datasource resource pool setup.
With Spring's JavaConfig mechanism, you can do it like so:
#Configuration
public class MainConfig {
...
#Bean
DataSource dataSource() {
DataSource dataSource = null;
JndiTemplate jndi = new JndiTemplate();
try {
dataSource = jndi.lookup("java:comp/env/jdbc/yourname", DataSource.class);
} catch (NamingException e) {
logger.error("NamingException for java:comp/env/jdbc/yourname", e);
}
return dataSource;
}
}
Assuming you have a "sampleDS" datasource definition inside your tomcat configuration, you can add following lines to your applicationContext.xml to access the datasource using JNDI.
<jee:jndi-lookup expected-type="javax.sql.DataSource" id="springBeanIdForSampleDS" jndi-name="sampleDS"/>
You have to define the namespace and schema location for jee prefix using:
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
Documentation: C.2.3.1 <jee:jndi-lookup/> (simple)
Example:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>
You just need to find out what JNDI name your appserver has bound the datasource to. This is entirely server-specific, consult the docs on your server to find out how.
Remember to declare the jee namespace at the top of your beans file, as described in C.2.3 The jee schema.
Another feature:
instead of of server.xml, you can add "Resource" tag in
your_application/META-INF/Context.xml
(according to tomcat docs)
like this:
<Context>
<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"/>
</Context>
According to Apache Tomcat 7 JNDI Datasource HOW-TO page there must be a resource configuration in web.xml:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
That works for me
In your spring class, You can inject a bean annotated like as
#Autowired
#Qualifier("dbDataSource")
private DataSource dataSource;
and You add this in your context.xml
<beans:bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<beans:property name="jndiName" value="java:comp/env/jdbc/MyLocalDB"/>
</beans:bean>
You can declare the JNDI resource in tomcat's server.xml using
<Resource name="jdbc/TestDB"
global="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/TestDB"
username="pankaj"
password="pankaj123"
maxActive="100"
maxIdle="20"
minIdle="5"
maxWait="10000"/>
back to context.xml de spring add this
<ResourceLink name="jdbc/MyLocalDB"
global="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource" />
if, like this exmple you are injecting connection to database, make sure that MySQL jar is present in the tomcat lib directory, otherwise tomcat will not be able to create the MySQL database connection pool.
I found this solution very helpful in a clean way to remove xml configuration entirely.
Please check this db configuration using JNDI and spring framework.
http://www.unotions.com/design/how-to-create-oracleothersql-db-configuration-using-spring-and-maven/
By this article, it explain how easy to create a db confguration based on database jndi(db/test) configuration. once you are done with configuration then all the db repositories are loaded using this jndi. I did find useful. If #Pierre has issue with this then let me know. It's complete solution to write db configuration.
I have some Java-console-apps. which are using Hibernate to take care some database stuff (I use MySQL). Now, I want Hibernate to use datasource from Tomcat Context. Could anyone tell me how?
Tomcat wiki contains the needed configuration.
in context.xml:
<?xml version="1.0" encoding="UTF-8"?> <Context antiJARLocking="true" path="/DVDStore">
<Resource auth="Container"
driverClassName="com.mysql.jdbc.Driver" maxActive="30" maxIdle="10" maxWait="10000" name="jdbc/sakila" password="*****"
type="javax.sql.DataSource" url="jdbc:mysql://localhost/sakila" username="*****"/>
</Context>
In WEB-INF/web.xml .
<resource-ref>
<description>This is a MySQL database connection</description>
<res-ref-name>jdbc/sakila</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
In hibernate.cfg.xml:
<!-- using container-managed JNDI -->
<propertyname="hibernate.connection.datasource">
java:comp/env/jdbc/sakila
</property>
You can find more in The TomcatHibernate Wiki
Edit:
In case you want to access the context directly, this is the way to do it problematically
ServletContext sc = getServletContext();
String parameterValue = sc.getInitParameter("parameterName");
I have a problem setting up my configuration for JNDI with Spring. I checked the other posts but could not get my problem solved. I am using Tomcat 6 as my container. From my understanding I need to set up a resource on the server. So in my server.xml file I have this:
<GlobalNamingResources>
<Resource auth="Container" driverClassName="org.postgresql.Driver"
maxActive="100" maxIdle="5" maxWait="10000"
minEvictableIdleTimeMillis="60000" name="jdbc/myTomcatPool"
password="password" testOnBorrow="true" testWhileIdle="true"
timeBetweenEvictionRunsMillis="10000" type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/postgis" username="postgres"
validationQuery="SELECT 1"/>
</GlobalNamingResources>
I have the following in my spring-context.xml (which is on the classpath):
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool" />
<bean id="geoCodeService" class="com.sample.SampleImpl">
<property name="dataSource" ref="geoCodeData"/>
</bean>
I then have this in file META-INF/context.xml:
<Context path="/myApp" reloadable="true" cacheMaxSize="51200"
cacheObjectMaxSize="2560">
<ResourceLink global="jdbc/myTomcatPool" name="jdbc/myTomcatPool"
type="javax.sql.DataSource"/>
</Context>
My server starts up free of errors.
When I try to run the following test (that worked before I added the JNDI code):
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring-context.xml");
}
}
I get the following error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'geoCodeData': Invocation of init method failed;
nested exception is 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
Is my configuration wrong or is the way I am trying to run the test incorrect?
When you run your testcases, you will want to use JDBC instead of JNDI lookup. The simple reason is because you usually don't run your testcases from the application server. Thus, JNDI lookup will fail.
What I do on my end is to place data source in a separate file. I have one file for production that uses JNDI:-
project-datasource.xml
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool"></jee:jndi-lookup>
... another another file for unit test that uses JDBC:-
project-datasource-test.xml
// use the same bean name "geoCodeData"
<bean id="geoCodeData" class="...">
<property name="driverClassName" value="..." />
<property name="url" value="..." />
<property name="username" value="..." />
<property name="password" value="..." />
</bean>
The web app will use project-datasource.xml whereas the unit test will use project-datasource-test.xml.
After several hours of tearing out my (already sparse) hair I have managed to get my tomcat server to pool database connections to Oracle and use the Spring framework.
I though I would reply to this question even though there seems to be an answer, just in case it helped anybody else.
What I wanted:
A connection pool administered by Tomcat (rather than per servlet) and the config for the DB connection in the Tomcat server config (again, rather than the servlet config files).
We have several instances of Tomcat and each one connects to a specific Oracle DB, but developers are producing servlets that could be required to run on any one of them, hence I don't want the DB connection details in the WAR file they produce, but to let them look it up and be provided with that server's data source, via JNDI.
In the Tomcat server conf/context.xml I added the following code:
<Resource name="jdbc/banner"
auth="Container"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#DBan8DB1.example.ac.uk:1522:bde8"
username="dbsro"
password="verysecret"
initialSize="5"
maxActive="20"
maxIdle="10"
removeAbandoned="true"
global="jdbc/banner"
maxWait="-1"/>
Obviously I have the ojdbc, pool and dbcp JAR files present in Tomcat's lib directory on the server.
An important point to note here is that the type is of "javax.sql.DataSource" rather than "org.apache.commons.dbcp.BasicDataSource" which I originally thought it should be.
Now in the Web Application's WEB-INF/web.xml I added the following:
<resource-ref>
<description>Oracle Banner Datasource</description>
<res-ref-name>jdbc/banner</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and in my Web Application's servlet-context.xml file (where ever you keep yours might vary) I had this. This is not the whole file but the XML name space is important here, for the jee parts:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/banner" resource-ref="true" />
Again note the fully qualified reference jndi-name="java:comp/env/jdbc/banner" which seemed to be required. Why is isn't needed in the resource-ref section of the web.xml file, I have no idea.
If anyone has any thoughts on this I would be pleased to read them.
By the way, this URL helped: Tomcat6 JNDI data source how-to
After all that, the connection worked. So I mopped the blood, sweat and tears from my work station and enjoyed a delicious cup of fresh coffee.
I have apache tomcat 5.5.28 on my windows box and I am trying to deploy a web application (WAR) which works fine on other servers.
However I am having trouble creating a datasource. I am not sure of the format. The db is oracle.
Here is what I have in server.xml.
<GlobalNamingResources>
<Environment
name="simpleValue"
type="java.lang.Integer"
value="30"/>
<Resource
name="tomdb11"
type="oracle.jdbc.pool.OracleDataSource"
maxActive="4"
maxIdle="2"
username="tomdb11"
maxWait="5000"
driverClassName="oracle.jdbc.driver.OracleDriver"
validationQuery="select * from dual"
password="remotedb11"
url="jdbc:oracle:thin:#dbserver:1521:orcl"/>
<Resource
auth="Container"
description="User database that can be updated and saved"
name="UserDatabase"
type="org.apache.catalina.UserDatabase"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml"/>
</GlobalNamingResources>
How do I access this in the web.xml where usually what I have which works in other servers is
<context-param>
<param-name>databaseUser</param-name>
<param-value>tomdb11</param-value>
</context-param>
<context-param>
<param-name>databasePassword</param-name>
<param-value>tomdb11</param-value>
</context-param>
<context-param>
<param-name>databaseSchema</param-name>
<param-value>TOMDBADMINN11</param-value>
</context-param>
Also am I missing something?
Edit: I get the following exception:
javax.naming.NameNotFoundException: Name tomdb11 is not bound in this Context
at org.apache.naming.NamingContext.lookup(NamingContext.java:770)
at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:137)
at javax.naming.InitialContext.lookup(Unknown Source)
at com.taw.database.DatabaseService.<init>(DatabaseService.java:19)
at com.taw.database.DatabaseServices.init(DatabaseServices.java:40)
If exception tells that it cannot find jdbc in the JNDI context, then it roughly means that you tried to obtain the DataSource as follows
dataSource = new InitialContext().lookup("jdbc/tomdb11");
while your server.xml file tells the following:
<Resource
name="tomdb11"
>
Those names are not the same. In fact, you should have been used:
dataSource = new InitialContext().lookup("tomdb11");
In Tomcat, however, the InitialContext doesn't directly point to java:comp/env/, so you'll need to replace it by:
dataSource = new InitialContext().lookup("java:comp/env/tomdb11");
The normal practice, however, is that you specify JDBC datasources with the jdbc prefix. So I would rename the resource as
<Resource
name="jdbc/tomdb11"
>
and access it by
dataSource = new InitialContext().lookup("java:comp/env/jdbc/tomdb11");
In the webapp's web.xml you should however also have the following resource declaration:
<resource-env-ref>
<resource-env-ref-name>jdbc/tomdb11</resource-env-ref-name>
<resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
</resource-env-ref>
For more details about Tomcat JNDI check this HOWTO: http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html. Hope this helps.
First... Make sure you have an Oracle JDBC Jar in your $TOMCAT_HOME/common/lib.
Second... Make sure your web.xml also contains a block like this...
<resource-ref>
<description>Oracle Datasource</description>
<res-ref-name>tomdb11</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
As for your <context-param>, I'm not sure that is doing anything as you already have those things defined in your <Resource>.