Java Naming and Directory Interface (JNDI) is a Java application programming interface (API) providing naming and directory functionality for Java software clients to discover and look up data and objects via a name. WSO2 Application Server supports JNDI InitialContext implementation by inheriting the JNDI implementation of Tomcat (http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html). JNDI helps decouple object creation from the object look-up. When you have registered a datasource with JNDI, others can discover it through a JNDI look-up and use it.
The following sections describe how to register and look up JNDI resources, and how to expose datasources as JNDI resources.
Registering JNDI Resources
First, register JNDI resources for webapps context. Registering can be done in two ways - webapp-level or global level.
Webapp-Level Registering
To register a JNDI resource on a webapp-level, place the JNDI resource in the context.xml file of the webapp. Since each webapp’s context is isolated from each other, this ensures that the resources will be available to the webapp context only.
For example,
<Resource name="jdbc/TestDataSource" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" maxActive="100" maxIdle="30" maxWait="10000" url="jdbc:mysql://localhost:3306/test_db" username="root" password="root"/>
The example above defines a database resource and its properties. You can define any number of resources. Properties of a resource should go inside its <Resource> element.
Global Registering
Place resources under the <GlobalNamingResources> element in tomcat's server server configuration file (<PRODUCT_HOME>repository/conf/tomcat/catalina-server.xml in WSO2 products). Then, the resources can be referenced by linking them in the webapp's context.xml file. Global resources are visible to all webapps deployed in the server.
For example,
<ResourceLink name="jdbc/TestDataSource" global="jdbc/TestDataSource" type="javax.sql.DataSource"/>
Note
Finally, have a reference to those resources in the web.xml of the webapp. For example,
<resource-ref> <description>Your Description</description> <res-ref-name>jdbc/TestDataSource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
JNDI Look-up
Now that you are done registering JNDI resources, let's see how to look them up. In the example code segment below, SelectorContext is the Tomcat JNDI Context implementation. Similarly, build the Catalina selector context using the IntialContext environment, using which you can look up the JNDI resources.
Hashtable environment = new Hashtable(); environment.put("java.naming.factory.initial", "org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory"); environment.put("java.naming.webapp.bound", true); Context initCtx = new InitialContext(environment); Context initialContext = (Context) initCtx.lookup("java:comp/env");
Once done, you can look up for the resource defined earlier. For example,
DataSource ds = (DataSource) initialContext.lookup("jdbc/TestDataSource");
Accessing JNDI Resources within the Webapps
You can expose datasources as JNDI resources form the management console UI of the Application Server as explained in section Expose as a JNDI Datasource.
Next, let's take a look at how to access these JNDI resources within the webapps that are hosted in the same Application Server instance. This was not possible in previous versions due to the way JNDI context isolation is implemented for webapps at tomcat level. The implementation states that one webapp’s JNDI context cannot be accessed by another webapp, unless the JNDI resources are registered globally. Since webapps run isolated from each other, their contexts will also be isolated. The JNDI context of carbon is also usually isolated from webapps.
Shown below is how the naming context factory class (i.e. org.apache.naming.java.javaURLContextFactory), which is used as the default Initial Context Factory class in WSO2 Carbon products, is implemented:
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException { if (ContextBindings.isThreadBound() || (ContextBindings.isClassLoaderBound())) { // Redirect the request to the bound initial context return new SelectorContext( (Hashtable<String, Object>) environment, true); } // If the thread is not bound, return a shared writable context if (initialContext == null) { synchronized (javaURLContextFactory.class) { if (initialContext == null) { initialContext = new NamingContext( (Hashtable<String, Object>) environment, MAIN); } } } }
In order to access JNDI resources within webapps, WSO2 has written a InitialContextFactory, which extends the above class and overrides the above methods. This new implementation of javaUrlContextFactory in WSO2 Carbon products does not check whether the current context is bound to a thread or class loader. It returns the JNDI of the Carbon runtime.
The following segment is set as the default Initial Context Factory class in <PRODUCT_HOME>/repository/conf/carbon.xml file.
<DefaultInitialContextFactory>org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory</DefaultInitialContextFactory>
When you want to access the Carbon JNDI Context to lookup for Datasources, add the following JNDI Initial Context Factory Environment property:
java.naming.factory.initial : "org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory"
The following steps explain how this works.
1. Log on to the product's management console and select "Datasources."
2. Click on the "Add Datasource" link in the "Datasources" page.
3. Select RDBMS as the Datasource Type. Provide the rest of the details accordingly. You will find an option to expose this datasource as a JNDI Datasource at the end of the screen.
In this example, we are exposing the new Datasource as jdbc/MyCarbonDataSource.
- Name : Name of the JNDI datasource which will be visible to others in object look-up.
- Use Datasource Factory : If the datasource needs to be accessed from an external environment, the "Datasource Factory" option should be used. When it is selected, a reference object is created with defined datasource properties. When accessing the datasource from an external environment, the "Datasource Factory" creates a datasource instance based on the values of the reference object. In the configuration, this is set as useDataSourceFactory="true".
- JNDI Properties : Properties related to the jndi datasource (username, password etc). If the "Datasource Factory" is selected, following properties should be specified.
- java.naming.factory.initial - Used to select the registry service provider as the initial context.
- java.naming.provider.url - Specifies the location of the registry when the registry is being used as the initial context.
4. Click Save to complete. It will expose the Datasource.
5. The code segment below shows how to access this exposed Datasource within a webapp.
try { Hashtable environment = new Hashtable(); environment.put("java.naming.factory.initial", "org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory"); Context initContext = new InitialContext(environment); Object result = initContext.lookup("jdbc/MyCarbonDataSource"); if (result != null) { // Do your work here } else { System.out.println(“Cannot find MyCarbonDataSource”); } } catch (NamingException e) { e.printStackTrace(); }
In the above code segment, we create an IntialContext with the relevant environment properties. It gives the JNDI context of Carbon, using which you can look for any object that is bound to that context. Here you are looking jdbc/MyCarbonDataSource up.
Similarly, you can look up any Datasource, which is exposed as a JNDI resource, from webapps.