We have a personal java library which is deployed on a tomcat server (v5.5 & v6).
This library is shared with many web applications, so it was deployed on shared/lib in tomcat directory.
Exceptionaly, i have to create a new class into this library which lookup a jdbc datasource via JNDI.
For that, here my code :
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource dataSource = (DataSource) envCtx.lookup("jdbc/corp.xx.xx.xxxxDS");
All context are instantiated without problem. But when it lookup the jdbc datasource, i have an exception : Name jdbc not Found in Context
We are not in a webapp, is it a problem ?
Is it possible to declare a context.xml with a ResourceLink ? how ?
nb: the jndi is setted in server.xml (a resource in GlobalNamingResources)
<Context ...>
...
<ResourceLink name="linkToGlobalResource"
global="simpleValue"
type="java.lang.Integer"
...
</Context>
Where name is the new name (name relative to java:comp/env), global is the global jndi name configured in server.xml
So, from your code, look up linkToGlobalResource which will give you the linked global resource
Reference: http://tomcat.apache.org/tomcat-5.5-doc/config/context.html#Resource_Links
Answer Updated:
Your JNDI access will be from web-app and not shared jar because standalone jar does not have the concept of context.
Related
I'm trying to use a datasource configurated as a JNDI on a WebSphere Application Server Liberty however I'm getting the follwoing error:
javax.naming.NameNotFoundException: java:comp/env/jdbc/myapp/master
The configuration for the datasource in Websphere application server is:
<dataSource commitOrRollbackOnCleanup="commit" id="jdbc/myapp/master" jdbcDriverRef="ojdbc7" jndiName="jdbc/myapp/master">
<properties.oracle URL="jdbc:oracle:thin:#127.0.0.1:1521:xe" oracleRACXARecoveryDelay="0" password="xxxxxxxx" user="app_master">
</properties.oracle>
<connectionManager maxPoolSize="50"/>
</dataSource>
The connection to database is made via this code inside a servlet (jndi=jdbc/myapp/master):
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup(jndi);
setConnection(ds.getConnection());
System.out.println(getConnection().toString() );
What am I doing wrong?
The java:comp/env requires resource reference. You have following options to solve that:
1) Use resource injection - so instead of looking up it directly (via InitialContext) just add the following in your servlet class
#Resource(lookup = "jdbc/myapp/master", name="jdbc/myapp/master")
private DataSource dataSource;
2) Define resource reference in your web.xml like
<resource-ref>
<description>my datasource</description>
<res-ref-name>jdbc/myapp/master</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>CONTAINER</res-auth>
</resource-ref>
or you can create just reference also via annotation in the code.
3) Use direct JNDI, without reference (not a Java EE best practice)
DataSource ds = (DataSource) initCtx.lookup("jdbc/myapp/master");
In addition to what is already stated in the other answer, you should also check that you have enabled the jndi-1.0 feature. This is a common cause of lookups not working in Liberty.
For example, in server.xml,
<featureManager>
<feature>jdbc-4.2</feature>
<feature>jndi-1.0</feature>
... other features that you use
</featureManager>
And if this, too, is not enough to get it working, you should also check the configuration of resources upon which the dataSource depends, such as the jdbcDriver with id of ojdbc7 which is referenced in the config snippet that you provided.
there's some things I definitely don't understand while using JNDI.
This should simplify our Java developers' life, but this promise sounds false.
The simplest way tu use a database from within an application seems to build each time you need it (so, once per application) a datasource class giving you a connection or session you will use.
It's a tool class written in a few minutes where you have to declare the driver, url, login and password. And it's done.
The recommanded way is to rely on the server resources management, JNDI and all resources such as directory, etc.
But is it really usefull and simple ??
With Tomcat, you have to have to fully describe the datasource in server.xml, then declare it as a resource in context.xml.
server.xml
<GlobalNamingResources>
<Resource auth="Container" driverClassName="org.postgresql.Driver" maxIdle="30" maxTotal="100" maxWaitMillis="10000" name="jdbc/dsTriasEmployees" password="pwd" type="javax.sql.DataSource" url="jdbc:postgresql://localhost:5432/trias_employees" username="login"/>
</GlobalNamingResources>
context.xml
<ResourceLink name="jdbc/dsTriasEmployees" global="jdbc/dsTriasEmployees"
type="javax.sql.DataSource" />
Java side, you should be able to inject it thanks to a Context Lookup code ou a #Resource(mappedBy...) annotation.
DataSource datasource;
public Connection connectJNDI() {
try {
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
datasource = (DataSource) envContext.lookup("jdbc/trias_employees");
Connection con = datasource.getConnection();
genericLogger.info("JNDI LOOKUP -> " + con);
return con;
} catch (Exception ex) {
genericLogger.error("JNDI LOOKUP -> " + ex);
return null;
}
But after doing that, you still have to deal with error messages telling you that it's not enough just like this one :
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
I have read somewhere that I now should add a JNDI properties file and a declaration in the web.xml file like this :
web.xml
<resource-ref>
<description>
DB PostgreSQL
</description>
<res-ref-name>
jdbc/trias_employees
</res-ref-name>
<res-type>
javax.sql.DataSource
</res-type>
<res-auth>
Container
</res-auth>
</resource-ref>
But when I wanna test with jUnit, my web.xml is not usefull...
How many XML and properties files should I write just to respect the best practice that want me to delegate access to the datasource to the server ?
How many times will I have to duplicate all the informations related to url, pool connection, etc. ??
I've read 2 or 3 books on Java EE, 50 posts on StackOverFlow and dozens of forums and all the JNDI Resources HOW-TO on the Tomcat website.
But it still does not work...
Is there somewhere the full description of all I have to do to connect to my database using the JNDI process ?
Help me please, or I'll kill a kitten for each more minute I'm losing just to do my job in "the recommanded way".
Thanx by advance.
To test classes dependent on a DataSource or any other object provided by Tomcat via JNDI outside of Tomcat you could rely on TomcatJNDI. Point it to your server.xml and context.xml and it will initialize a JNDI environment with all the objects configured in the files.
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processServerXml(serverXmlFile)
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
Then lookup the datasource
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/jdbc/trias_employee")
You can find out more about TomcatJNDI here.
I want to use a JDBC Connetion in my webapp which is configured in WebSphere. (Like this one here: How to use JDBC in JavaEE?)
I had used this DataSource before via JPA but our customer wants to have native SQL ... don't ask.
I found a lot of examples and tutorial (e.g. http://www.wickcentral.com/java/dl/ds_resreferencesetts_Websphere.pdf, Websphere JNDI lookup fails) but nothing want work.
The DataSource in the WebSphere has the JNDI-Name "jdbc/myDS"
I added a resource-ref to my web.xml:
<resource-ref>
<res-ref-name>jdbc/myDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
And I tryed to get the DataSource in my Dao:
ds = (DataSource) new InitialContext()
.lookup("java:comp/env/jdbc/myDS");
But what I get is a
com.ibm.wsspi.injectionengine.InjectionException: CWNEN0044E: A resource reference binding could not be found for the following resource references [jdbc/myDS], defined for the MyAPP component.
I tryed a lot. Did anyone sees the fault?
Did you match your web-app defined datasource with a Websphere defined datasource during installation? Websphere usually asks you to bind resources when they are detected on the installation process (If I remember correctly, it is in the step named "Map reference resources to resources").
Other common issue is a Websphere datasource in a different context (Cell/Node/Server) than your app, so it cannot be found at runtime.
You need to add the binding in ibm-web-bnd.xml:
<resource-ref name="jdbc/myDS" binding-name="jdbc/myDS" />
I'm trying to figure out how to use datasources for my web service. I have the oracle-ds.xml deployed on my jboss 4.2.3 server, and the datasources are showing as bounded to JNDI names java:TestDS, java:WeatherDS, etc.
I try doing an initialcontext.lookup but I can't find it. I tried referencing the resource in the web.xml but I get "java:WeatherDS has no valid JNDI binding"... I've tried referencing "java:/WeatherDS", "WeatherDS", "java:WeatherDS", "jdbc/WeatherDS" and some others, but I think I need to somehow map the reference name to the jndi name.
I found a snippet of code that says:
...
<resource-ref>
<res-ref-name>jdbc/DefaultDS</res-ref-name>
<jndi-name>java:/DefaultDS</jndi-name>
</resource-ref>
...
But, it doesn't say where this file is and what else is needed. I don't know if I need a resource reference for sure or not. Can anyone point me in the direction of some more complete information of how to access the datasource from the code once it has been deployed? I am trying to make it so the connections are pooled for my web service.
In JBoss-4.2.3 you define a datasource in an XML-File in folder [JBOSS_HOME]/server/[MYSERVER]/deploy/
Create a file in this folder with name: mydatasource-ds.xml.
XML-File content:
<datasources>
<local-tx-datasource>
<jndi-name>mydatasource</jndi-name>
<!-- Properties for SQLServer -->
<connection-url>jdbc:jtds:sqlserver://hostname:1433/db-name;TDS=8.0;lastUpdateCount=true;sendStringParametersAsUnicode=false</connection-url>
<driver-class>net.sourceforge.jtds.jdbc.Driver</driver-class>
<!-- Properties for SQLServer end -->
<user-name>name</user-name>
<password>pwd</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>50</max-pool-size>
<idle-timeout-minutes>15</idle-timeout-minutes>
<blocking-timeout-millis>15000</blocking-timeout-millis>
</local-tx-datasource>
</datasources>
To can access these datasource from every application deployed in the same JBoss by normal JNDI lookup.
IMPORTANT: Use prefix java:/ in the jndi name in your code. Full JNDI-Name in your application for the datasource above is java:/mydatasource
I have a .properties file in my application which contains dataSource properties.
I set up a JNDI reference to this dataSource using the following code :
// first I create MyDataSource from the properties found in the .properties file
//then :
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind(jdbc/oracle/myDataSource, MyDataSource);
If I use a lookup in this application, the dataSource is found :
Context initContext = new InitialContext();
BasicDataSource dataSource =
(BasicDataSource) initContext.lookup("jdbc/oracle/myDataSource")
//everything works fine and I can use my dataSource to getConnection,
//requests, etc...
Now I would like to use this dataSource in another application. But if I do the same lookup than previously, I don't find myDataSource (whereas there is still the previous application in tomcat and the jndi binding is done on start-up with the help of a listener).
How can I get myDataSource in this second application, given that I can't use a Tomcat's resource in server.xml or a context.xml file (for different reasons I have to use this .properties file)?
Thanks
"local" JDNI directories are read-only in Tomcat. Nevertheless, you can bind "global" JNDI resources in a LifecycleListener, and then "link" them to your context(s)(*):
You need to implement org.apache.catalina.LifecycleListener http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/LifecycleListener.html
Then register it in your server.xml like this (along with the other listeners):
<Listener className="yourlistener.YourLifecycleListener"/>
Your listener should await for 2 events:
public void lifecycleEvent(final LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
// Create your datasource instance...
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind("jdbc/oracle/myDataSource", myDataSource);
} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
// unbind...
}
}
Then you'll have to propagate resource accesses by "linking" them from "global" JNDI directory to "local" JNDI directory using ResourceLink element in your META-INF/context.xml:
<ResourceLink name="jdbc/oracle/myDataSource" global="jdbc/oracle/myDataSource"
type="javax.sql.DataSource" />
That worked for me so far.
(*) Some notes:
There's an advantage on using lifecycle listeners. Since the order of context creation is not guaranteed. The advantage is that all of your contexts will see this object created.
If you need to create and configure datasource creation more dynamically that on lifecycle listener creation, note that you can bind a custom class implementing the Factory pattern.
To avoid classloading incompatibility problems, consider putting your listener, datasource, etc. classes in a jar file in the Tomcat lib directory, so they're included y the common classloader.
Regards.
What you are trying to do is not going to work. J2EE applications are not allowed to modify the JNDI environment provided by the application server (J2EE spec, section 5.2.2) and the Tomcat JNDI documentation also states, that each web applications gets each own read-only JNDI environment. I'm not sure why binding/rebinding your datasource is not failing immediately and why it's working within the same web application, but even such application-internal usage of the JNDI environment is undocumented behaviour, which I would not rely on.
A couple people have already commented on this, but I think the answer to your question is: Tomcat has a file called server.xml that you need to use. A good reference I have used before is below:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html
Resources defined here will be visible to all apps deployed (if set up correctly). If you set up a JNDI resource in your apps context or web xml file, it should only be available to your app.
JNDI context are private to each webapp. Context created in one app can't be accessed by others.
Try to create an entry in GlobalNamingResources and links in both webapps using <ResourceLink> to see if it works.
I used this setup before to read from both apps but never tried to write from one. So not sure if it will work.
Actually, it is possible to access others JNDI resources, if the servlet implements org.apache.catalina.ContainerServlet. This interface has a org.apache.catalina.Wrapper attribute, that is "populated" by the container itself.
through that, I created a simple application to monitor resources.
BUT, I would like to do that in a listener, so my resource monitor could start when the container starts. Anyone knows a way?