I am developed a service that use the DataSource from a local JNDI (Server where to live the service and the JNDI).
In Spring boot this information is declared like this:
spring: datasource:
jndi-name: java:/comp/env/jdbc/MyLocalDB
I want to know if is possible to do something like this:
spring: datasource:
jndi-name: java:192.168.0.1:8080/comp/env/jdbc/MyLocalDB
Thanks
JNDI is an API to access directories. Application servers include directories to maintain information of the applications, the datasources and the EJB beans. JNDI can be used to access other types of directories. For instance, it can be used to access user information on Active Directoy and LDAP directory servers.
You can create client programs that access these JNDI directories and obtain data such as configuration data and RMI remote references to invoke EJB beans.
Accessing remote JNDI directories
If you are creating a client program, you can define an InitialContext describing the JNDI directory your program will use. You must define a Hashtable with the configuration, defining a Context.PROVIDER_URL and create the InitialContext.
For instance, you can create a program that access information in an LDAP directory.
// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
// ... do something useful with ctx
Depending on the library you use, you can define a configuration file describing the JNDI remote host. For instance, for the JBoss EJB Client library, you can define a jboss-ejb-client.propertiesfile. Glassfish uses a jndi.properties file.
Accessing remote resources
A resource is data (e.g an image, audio, or text) that a program needs to access independently of the location of the program code. You can use the JNDI API or the #Resource annotation to access the information.
For instance, you can use the API to ask for data of users in an LDAP directory.
try {
// Create the initial directory context
DirContext ctx = new InitialDirContext(env);
// Ask for all attributes of the object
Attributes attrs = ctx.getAttributes("cn=Ted Geisel, ou=People");
// Find the surname attribute ("sn") and print it
System.out.println("sn: " + attrs.get("sn").get());
} catch (NamingException e) {
System.err.println("Problem getting attribute:" + e);
}
Accessing remote EJBs
You can access EJBs with remote interfaces located in other VMs. On the one hand, you must create the EJBs using CORBA-style remote interfaces and register the EJBs in the java: namespace in the server. On the other hand, the applications can use a corbaname URL to access these beans.
In Websphere/Liberty, if you define an EJB bound to:
java:global/ExampleApp/ExampleModule/ExampleBean!com.ibm.example.ExampleRemoteInterface
java:app/ExampleModule/ExampleBean!com.ibm.example.ExampleRemoteInterface
java:module/ExampleBean!com.ibm.example.ExampleRemoteInterface
You can access remotely the EJB using RMI-IIOP (to invoke methods)
corbaname::test.ibm.com:2809#ejb/global/ExampleApp/ExampleModule/ExampleHomeBean!com.ibm.example.ExampleEJBHome
corbaname:rir:#ejb/global/ExampleApp/ExampleModule/ExampleHomeBean!com.ibm.example.ExampleEJBHome
Detailed configuration of these remote EJBs may vary from one application server to another. If you use Glassfish, you can check the documentation. There is an example in the Oracle blogs.
If you configure a cluster, there are many other options to access the remote resources using the names of the nodes in the cluster.
Accessing a remote Datasources
Datasources defined in a server cannot be accessed remotely. If you want to keep the configuration of the datasources in the JNDI directory, you can solutions define a custom class that implements the DataSource interface. A client can obtain the datasource configuration from the server and create a local database connection using that connection.
Related
While trying to port our application from WAS 8.5.5 to tomcat 8.0.33, we have hit a roadblock and we are unable to proceed. Hence requesting for your inputs.
Following is the scenario: we have DataSourceManager class which looks for datasource in the context as shown below.
Properties prop = new Properties();
prop.put("java.naming.factory.initial", "org.apache.naming.java.javaURLContextFactory");
prop.put("java.naming.provider.url", “rmi://localhost:1099”);
InitialContext context = new InitialContext(prop);
dataSource = (DataSource) context.lookup(“apl_datasource”);
The preceding code perfectly works well when code is run within the Tomcat container. But we are unable to access the context remotely, i.e. from outside the Tomcat container.
But this works fine in case of WAS when com.ibm.websphere.naming.WsnInitialContextFactory class is used.
While running a standalone client from a shell script, when inside DataSourceManager class we do a context lookup for a data source and the lookup fails with following exception.
javax.naming.NameNotFoundException: Name
[java:comp/env/jdbc/apl_datasource] is not bound in this Context.
Unable to find [java:comp].
It seems tomcat does not support remote access to its JNDI tree, and context initialized is empty. Tomcat does have the data source in the context but it is only accessible to the process running inside the containers and not accessible to processes running outside the container. PFB the following link for your reference.
Tomcat: what is the init context params to use for making an external client connection to Tomcat 5.5 JNDI tree?
Kindly provide your inputs on 3 points:
This link is for tomcat 5.5 and we are porting to tomcat 8. Has scenario changed in tomcat8
Is there any other way to access the JNDI tree remotely by standalone application.
Is the rmi protocol mentioned in provider url is supported by tomcat, or we should change it to some another protocol.
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?
http://localhost:8080/rtsclient/loginform.faces
Url jnp://localhost:1099
Application Server Type jboss40
Datasource jdbc/ilogDataSource
User rtsAdmin
Password rtsAdmin
The above is for jboss.
Now i have deployed RTS onto Sun Application Server. And i want to configure the jndi such that.
My RTS client can actually access it.
How do i go about this?
I asked this question here
http://forums.ilog.com/brms/index.php?topic=803.0
i know it is quite specific.
But how to do it generally in sun application server?
I think creating a jndi.properties file in your project root with the following should be enough.
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=1099
java.naming.security.principal=rtsAdmin
java.naming.security.credentials=rtsAdmin
There are also a few other things configurable if you need to
java.naming.provider.url=...
java.naming.factory.initial=...
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
A less flexible method is also available. On startup provide the needed values to the InitialContext()-constructor as a Hashmap
Properties prop = new Properties();
prop.put(Context. ...., "...");
e.g.
prop.put(Context.SECURITY_PRINCIPAL, "rtsAdmin");
prop.put(Context.SECURITY_CREDENTIALS, "rtsAdmin");
InitialContext context = new InitialContext(prop);
Check here what you can set via the constructor