This may be a somewhat strange question, but I'm not sure how to do this another way. Basically, I am sharing connection code between two maven projects. One is a Maven Web application and the other is a regular Maven project.
To set up the database connection context, I'm using META_INF/context.xml in the Maven web application.
However, the regular Maven project doesn't use this file, so when the code to get the data source is run:
Context envCtx = (Context) new InitialContext().lookup("java:comp/env");
dbPool = (DataSource) envCtx.lookup("jdbc/MyAppUCP");
of course a NamingException is thrown. How can I reuse this method of getting the database connection with the regular Maven java application? Copying the META_INF/ directory into the project doesn't work.
The InitialContext with default constructor works only in managed environment (j2EE container). If you need to lookup for JNDI from a Java SE process, you need to pass in a property object as parameter to the overloaded constructor.
Each app/webserver has it's own set of properties that needs to be set in the property object. Refer to app server's documentation (http://docs.oracle.com/javase/jndi/tutorial/beyond/env/source.html).
Again, there would be security constraint on looking up for JNDI entries from external process.
Related
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
In java EE, the way you get an EJB from a remote server is by looking it up in JNDI. The specification defines the JNDI name for a given bean type.
However, this seems to be only if you want to get a bean off your local computer. I want to get the bean off a remote server, like most users would. How do I specify the server URL? Do I pass a map to the InitialContext constructor?
Note: There is another question that is pretty much the same, but that has become out of date since the definition of portable JNDI names by the specification.
I want to get the bean off a remote server
Yes, you need specify the IP/port where the remote server (JNDI service) is running/listening.
How do I specify the server URL?
You have to set the propertie: java.naming.provider.url and make it available to the InitialConetxt.
This can be done in different ways:
to use a jndi.properties file
to use system properties
passing the value in a Hashtable when you create a new instance of
InitialContext object.
The concrete value of this and others necessary properties to instantiate the InitialConetct are vendor dependen.
An example for JBoss could be:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://yourServer:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
Keep in mind that there is no way that you can get the EJB's stub from a remote server if you donĀ“t indicate the url.
By "Remote" I mean the client and the server are running in different JVM.
You do JNDI lookups of remote EJBs using exactly the same code as you would use when running server-side:
Context context = new InitialContext(); // No properties needed
MyEJB myEjbInstance = (MyEJB) context.lookup("ejb/MyEJB");
Or, of course, you can inject it:
#EJB
private MyEJB myEjbInstance;
To make the naming context work, you must run your application as a Java EE application client. An application client is exactly like a regular standalone Java program, with a standard main method; the only difference is that it needs to be run in a different manner. That manner is not specified in the Java EE Spec, so each application server has its own way of doing it.
GlassFish, for example, requires an application client to include some special jars in the classpath, and set a couple system properties. Specifically, you must include lib/gf-installer.jar and all the jars referenced by its manifest in your classpath, and you must set the org.omg.CORBA.ORBInitialHost and org.omg.CORBA.ORBInitialPort system properties.
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.
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?
I have a servlet that I would like to run within ColdFusion MX 7. I would like to make use of an existing ColdFusion DSN as a javax.sql.DataSource, if possible.
I thought something like
coldfusion.server.ServiceFactory.getDataSourceService().getDatasource(dsname);
would work, but unfortunately the servlet returns
java.lang.NoClassDefFoundError: coldfusion/server/ServiceFactory
That code will work fine, you just don't have ServiceFactory in your classpath. Ie, Java can't load that class. Try including a dependency on cfusion.jar from C:\CFusionMX7\lib.
It seems the simplest way to do this is to add an additional JNDI datasource into jrun-resources.xml. This can then be accessed in the conventional way:
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("mydatasource");
It does mean duplicating database connection configuration, but I would rather do this than work with the largely undocumented coldfusion.server.* classes.