Using a JNDI datasource created by another application with Tomcat - java

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?

Related

Tomee Server and Application Resources and how to reference in JNDI or using #Resource

Tomee has several locations where Resources can be defined:
Inherited from Tomcat/Catalina
Server
${catalina_home}/conf/server.xml
${catalina_home}/conf/context.xml
Application
${catalina_home}/conf/Catalina/localhost/appcontext.xml
${webapp}/META-INF/context.xml
Introduced by Tomee
Server
${catalina_home}/conf/tomee.xml
${catalina_home}/system.properties
Application
${webapp}/WEB-INF/resources.xml
Not sure, but I assume also some per application properties.
This is all confusing to me. I simply want to install a connection pool at java:comp/env/jdbc/mydb.
There are additional several notes I want to take:
I might want to consider defining the resource server side - so passwords are not part of the deployment.
I might want to consider the choice of one connection pool instantiated per webapp deployed, or one globally connection pool shared for all webapps.
I might want to restrict a connection pool (with given username/password) for just one webapp, although not deployed with the webapp (hence the choice of conf/Catalina/localhost/appcontext.xml).
I am aware of following documentation:
Tomee Directory Structure
Tomee Application Resources
Tomee Admin/Configuration/Resources
Tomee Datasources
I did test out setting a simple resource in WEB-INF/resources.xml, and it was reachable as simply jdbc/mydb used for example in a WEB-INF/classes/META-INF/persistence.xml. I did not have the same success when defining the resource in conf/Catalina/localhost/appcontext.xml.
It also seems to me that Tomee has a special way for choosing root contexts where services and our resources are deployed. During startup I see attempts to remap some of the resources. Example output:
[...].createRecipe Creating Resource(id=webapp/jdbc/mydb)
[...].AutoConfig.processResourceRef Auto-linking resource-ref 'openejb/Resource/webapp/jdbc/mydb' in bean MyService to Resource(id=webapp/jdbc/mydb)
And from Tomee Documentation:
<Resource id="Derby Database" type="DataSource">
. . . . .
</Resource>
The global jndi name would be java:openejb/Resource/Derby Database
So how do I refer to that in a portable way?
It seems I can solve that by simply putting this in the WEB-INF/web.xml:
<resource-ref>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
This allows me to access this in JNDI as java:comp/env/jdbc/mydb.
My apologies for inserting parts of answer here - but I am just adding info to this as I find it. Later, I will either move this into answers, or leave as is if someone finds a better satisfying answer.
I hope to understand:
What the mechanisms are for instantiating resources (when)
How they are reachable (directly through JNDI, persistence.xml, and or a #Resource tag).
Any standard practices around those resources

How to connect java application to database using JNDI?

I am implementing JNDI concept to get a connection to Database. I googled to get the starting point, however didn't get it.
The things that i want to do is have a simple java standalone application which used JNDI concept for getting connected to a database.
Sample source that i have is:
DataSource dataSource = null;
Context context = null;
try {
context = new InitialContext();
dataSource = (DataSource) context.lookup("database_connection");
}
catch (NamingException e) {
System.out.println("Got naming exception, details are -> " + e.getMessage());
}
Now, where we define database_connection? Is that defined in an xml file, and if so where do we specify that and what format of that is?
If any pointers can be provided, that would be great.
Thanks
The real difference between your question and the examples is that you're using a standalone Java application. Pretty-much every example assumes that you're running within a Java EE application container. In that case you define the database connection to the container, use JNDI to get the DataSource, and get the connection from that.
When you do a JNDI lookup to get your DataSource, the JNDI framework looks for an initial context factory (a class that implements InitialContextFactory). In your Java EE application, the container provides that. In your standalone Java application, the initial context is null, and no further lookups resolve correctly.
One of the ways you can resolve this is to create your own initial context factory:
public class MyContextFactory implements InitialContextFactory
and inject that into JNDI at startup:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "mypackag.MyContextFactory");
and then return a new ObjectFactory from the getInitialContext call, and then implement the ObjectFactory to return a DataSource (also probably custom) from the getConnection() call.
That would all work, but it's overkill. It would be easier to just use the normal JDBC connection string approach to get a connection directly in your application rather than trying to use a DataSource. Or use a framework like Spring to inject it for you or to separate the database connect information from the code (in which case you're using Spring configuration files rather than JNDI directly).
The one reason I'd advocate creating a custom context factory and data source approach is if you have common JPA code that you want to run both within a Java EE app and within a standalone app (it's otherwise non-trivial to configure the same code to do both). That doesn't sound like your case.
So, since you're standalone and not in a Java EE container, I think your real answer is that your use case is not appropriate for a DataSource (unless you move to a framework like Spring that would provide one).
There's a nice tutorial for that here and here
The database should be defined somewhere (LDAP)? There's a series of old articles using the directory service with JNDI (ultimately in your case to get the server information) here.
A nice introduction to naming services here

JNDI configuration in Tomcat

I am trying to set up JNDI lookup in web application to be deployed in Tomcat 7. I have specified following properties in jndi.properties file:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory,
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces,
java.naming.provider.url=localhost:1199
But when i get initial context and inspecting its attributes it reveals that java.naming.factory.initial changes to org.apache.naming.java.JavaURLContextFactory and all other properties remains same as in the jndi.properties file.I dont understand why this single property gets changes ? How can i prevent this and force tomcat to use property i have specified ?
I believe the JNDI properties should be vendor specific, What you trying to put is for Jboss JNDI properties, and may have been reverting to for Tomcat Specific Proerties. Although you can try to overwrite them programatically, it might not work properly.
You should put all this stuff into a Resource element in your META-INF/context.xml file, and look up that resource via a java:comp/env URL. That's what Tomcat is trying to set up here: an object factory for that namespace.
I think you should append your properties to your catalina.properties file,it's under your $tomcat_home/conf.

Simple JNDI ContextFactory?

My WAR application is deployed to Amazon Elastic Beanstalk. They don't support JNDI, but I need it for JPA and unit tests. What JNDI context factory I can use as a workaround? I need a simple open source solution, that would allow me to configure entire factory through jndi.properties. I tried GuiceyFruit, but looks like it doesn't allow data source configuration inside one file. Any suggestions?
ps. OpenEJB will work, but it's an overkill for such a simple task
Try Simple-JNDI. It gives you an in-memory implementation of a JNDI Service and allows you to populate the JNDI environment with objects defined in property files. There is also support for loading datasources or connection pools configured in a file.
To get a connection pool you have to create a file like this:
type=javax.sql.DataSource
driver=com.sybase.jdbc3.jdbc.SybDriver
pool=myDataSource
url=jdbc:sybase:Tds:servername:5000
user=user
password=password
In your application you can access the pool via
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("path/to/your/connectionPool");
You can find more about it at https://github.com/h-thurow/Simple-JNDI.

Tomcat JNDI: managed stack of objects

I have a class
class ObjFactory {
MyObject getObject() {...}
void returnObject() {...}
}
How can we configure Tomcat so that it would provide this factory through JNDI and manages
this factory?
Say, one web app should get access to this factory and it must exist only 1 factory in Tomcat, every copy of a web app references the same object.
What are specific rules to design this factory class? Maybe it should implement some interface to work with Tomcat, JNDI in the way I described?
How to glue this class, tomcat and webapp (what should be written in what file, web.xml, context.xml, etc)?
If you are using Tomcat 5.5, this is a good place to start: JNDI Resources HOW-TO.
Scroll down to the 'Adding Custom Resource Factories' section for a step-by-step explanation.

Categories

Resources