Chapter 31

Introducing JNDI

Instead of having distributed Java programs instantiate lots of reusable objects over and over again, it’s better if these objects are pre-created and published at a known server, where they can be easily and quickly found. The role of the Java Naming and Directory Interface (JNDI) is to make it easier to find objects in distributed applications. It plays a role similar to that of a company telephone directory assistance service. Various software vendors offer specialized directory assistance software, and JNDI provides a standard API to read from and write to such directories.

In this lesson you’ll be introduced to the JNDI concepts and will see how to use JNDI for publishing (and looking up) administered JMS objects.

Java Naming and Directory Interface

A naming service enables you to add, change, or delete names of objects that exist in some naming hierarchy so other Java classes can look them up to find their location. One more analogy: In a library, you find the name of the physical location of the book in a directory, and then go to the shelf to pick up the book. A naming service provides a unique name for every entry that is registered (bound to) with the service. Every naming service will have one or more contexts — think of directories and subdirectories in a file system, where any directory tree with children is a context. The naming tree originates from a root node, which is also known as an initial context (like a root directory on the disk).

A directory service enables you to search the naming tree by object attributes rather than object names. One example is that of the domain name system, which is a distributed naming system that takes the domain name of a networked computer or service and returns the IP address and port number of the resource.

To allow clients to do lookups, there has to be a process that initially binds the objects to a naming tree. This can be handled by an independent program that (for example) binds employee names to a directory server of some organization. Java EE servers do a similar job (during start-up) by binding such objects as EJB, Servlets, JMS, and database connection pools to either their internal or external naming servers.

Administering JNDI Objects in GlassFish

Each Java EE application server offers a tool that allows administration of its service modules — we are interested in binding objects to their directory names. When you start GlassFish there is a message on the console: “Waiting for DAS to start…” DAS stands for Domain Administration Server, which authenticates the user with administrative privileges and responds to requests from a graphical web browser-based Admin Console. To use the console enter the following URL in your browser: http://localhost:4848/. You’ll be prompted for the user ID (admin) and password, which you selected when you installed GlassFish in Lesson 26.

After a successful log-on you’ll see the console, as in Figure 31-1, which enables you to administer various Java objects, but I’ll continue using the example of JMS-related ones. There are two JMS nodes in the directory tree on the left. One is called JMS Resources, and the other Java Message Service. The latter shows the physical queue named TestQueue that I created using the Open MQ administrative utility in Lesson 30.

The only reason GlassFish automatically knows about the destinations in this MOM is that this application server is integrated with Open MQ. To configure another MOM server you’d need to create a new JMS host by filling out the form shown in Figure 31-2.

Now you need to create a GlassFish JMS entry to map to the physical MOM queue. Add the new destination resource to JMS Resources (see Figure 31-3). I called this JMS resource MyJMSTestQueue and mapped it to TestQueue, created in the previous lesson.

Now create another administered resource, a connection factory. Because the creation and closing of connections and senders are slow operations, you may want to consider writing code for JMS connection pools. Java EE servers enable you to configure such pools. In Figure 31-4 you can see that the size of the connection pool is configurable. By default, eight JMS connections will be created and, as the number of users increases, up to 32 connections will be created by GlassFish.

To have JMS implementation as part of a Java EE server you need to download the full profile version (see Lesson 26).

Accessing the GlassFish Naming Service with JNDI

To start searching for an object in the JNDI tree a Java program needs to get a hold of its initial node. There are two use cases to consider here:

  • An external Java client needs to find the server that runs the naming service.
  • A Java client is running inside the Java EE server that runs the naming service.

In the first case you need to prepare an instance of the Java Hashtable or Properties object and give it as a parameter to the constructor of the javax.naming.InitialContext class. The values to put in such Properties objects vary — consult the documentation for your application server. The following code is an example:

Properties props = new Properties();
 
props.setProperty("java.naming.factory.initial",
                    "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs","com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state",
                "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
props.setProperty("org.omg.CORBA.ORBInitialPort", "8080");
 
InitialContext ic = new InitialContext(props);

In the second case the Java client can just create an instance of InitialContext with no argument constructor — the Java EE server knows where to find its naming service:

InitialContext ic = new InitialContext();

Now you can either perform a lookup starting from the initial context node, or ask the container to inject the reference to the required resource using the syntax of Java annotations.

Revisit the code in Listing 30-1. It creates a ConnectionFactory object connecting to a MOM server using classes specific to Open MQ. Now, because TestQueue is known to GlassFish, a Java client wouldn’t even need to know what MOM server is in use, but would just look up a standard JMS ConnectionFactory in the instance via the JNDI API.

The statement that a Java client is running inside the server may not sound right to you, but any program that’s trying to find another object can be considered a client. For example, imagine a web application in which the web browser invokes a Servlet that needs to place an order into a message queue or invoke an EJB. In Listing 31-1 you can see a Servlet that plays the role of a Java client operating inside a Java EE container.

download.eps

Listing 31-1: Servlet as Java client

import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet(description = "The servlet that Sends a message to a queue",
            urlPatterns = { "/quote" }, name="QuoteService")
public class QuoteService extends HttpServlet {
 
   protected void doGet(HttpServletRequest request, 
       HttpServletResponse response) throws ServletException, IOException {
 
       PrintWriter out = response.getWriter();
       out.println("<html><body bgcolor=yellow>");
      out.println("<h2>Hello from QuoteService</h2>");
      
      out.println("Sending a message to the TestQueue");
      
      MessageSender mySender=new MessageSender();
      mySender.sendMessage("IBM 200 Buy");
   }      
}

The Servlet receives a request from the client and instantiates the Java class MessageSender shown in Listing 31-2. Compare the code of this class with the sender from Listing 30-1. Now this code from Listing 31-1 doesn’t contain anything specific to the MOM provider.

If, say, you decide to change the MOM provider to the one that performs better, just re-create the administered JMS objects using the administration console of GlassFish and there is no need to change anything in the code of MessageSender. If you decide to switch from GlassFish to another server, just redeploy this Servlet and MessageSender in another Java EE application server.

download.eps

Listing 31-2: Looking up JNDI object and sending message

package com.practicaljava.lesson31;
 
import javax.jms.*;
import javax.naming.*;
 
public class MessageSender {
 
void sendMessage(String messageToSend){
 Session session=null;
 ConnectionFactory factory=null;
 Connection connection=null;
 
 try{  
    // Find the JNDI context
    Context jndiContext = new InitialContext();
   
   // Look up the factory and the queue
  factory = (ConnectionFactory) jndiContext.lookup("MyTestConnectionFactory");
  Queue ioQueue = (Queue) jndiContext.lookup("MyJMSTestQueue");
            
  connection = factory.createConnection();
      
  connection.start();
 
  session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  MessageProducer queueSender = session.createProducer(ioQueue);
            
  // Buy 200 shares of IBM in this example            
  TextMessage outMsg = session.createTextMessage(messageToSend);
           
  queueSender.send(outMsg);
  queueSender.close();
            
  System.out.println("Successfully placed an order to purchase 200 shares of 
    IBM");
 
 }catch (JMSException e){
                 System.out.println("Error: " + e.getMessage());
 }catch (NamingException e) {
                  
      e.printStackTrace();
 }finally{
   try{
    session.close();
    connection.close();
 } catch (Exception e) {
   System.out.println("Can't close JMS connection/session " + e.getMessage());
 }
 }      
}      
      
}

Run the quote Servlet in Eclipse or direct your web browser to http://localhost:8080/Lesson31/quote and the message to purchase 200 shares of IBM will be placed in the physical queue TestQueue in the Open MQ server. If you are not sure if this application is working the way you expect it to, see if there are error messages in the domain’s log file in the directory glassfish/domains/domain1/logs/server.log. If this program has executed properly, at the end of the log file you’ll see the following message: “Successfully placed an order to purchase 200 shares of IBM.”

Injection of JNDI Resources

Even though looking up the JNDI object doesn’t look too difficult, there is even a cleaner and simpler way of providing these resources to your Java EE components: using the annotation @Resource. The application server can inject the resource so you don’t need to do a JNDI lookup. For example, injection of ConnectionFactory and Queue can look like this:

import javax.annotation.Resource;
...
@Resource(name="MyTestConnectionFactory")   
private ConnectionFactory factory;
 
@Resource(name="MyJMSTestQueue")
private Queue ioQueue;

Resources can be injected into variables, as in the preceding example, or you can put the @Resource annotation above the method or a class definition. Depending on its location, the time of injection varies. If you put this annotation at the class level, the resource will be injected during run time when the application looks it up. If you put this annotation above the field or setter method declaration, the resource will be injected in the component when the application is initialized.

If you need to override resources specified in annotations, you can do it in XML configuration files. You’ll see more examples of using resource injection with EJB in Lesson 32.

DataSource and JNDI

In Lesson 22, while learning JDBC, you were creating database connections before executing any database queries. Imagine that multiple clients send requests to your application server, which has to execute database queries. Creating and closing connections are slow operations, and you want to do them as infrequently as possible.

Typically the administrator of the Java EE server pre-creates the pools of database connections, and configures the minimum and maximum number of connections and some other parameters. Figure 31-5 shows a snapshot of the GlassFish administration console, where the JNDI entry named DerbyPool represents a pool of JDBC connections. The object javax.sql.DataSource is a factory of database connections. The administrator configures this factory as a JNDI resource, specifying what JDBC driver to use, how many connections to create initially, and the maximum number of connections allowed.

The user’s request will communicate with a Java class deployed on the server, which will find the connection pool by its JNDI name (DerbyPool in this case) and make a getConnection() call on this resource. If there are available free connections, the Java class will immediately get an instance of the PooledConnection object (which works the same way as Connection). If many users are making concurrent requests, all connections may be taken, and there will be a slight delay until one of the busy connections is returned to the pool. Connections returned to the pool are not destroyed; they are preserved by the application server for future requests.

You can do either a JNDI lookup of the DataSource object or inject it to your Java code. If the name of the configured DataSource object is DerbyPool, the sample code to obtain a pooled database connection may look as follows:

InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("DerbyPool"); 
 
Connection myConnection = ds.getConnection();
//The rest of the JDBC processing goes here as in Lesson 22
 
// Closing the connection just returnes it back to the pool
// and makes it available for other Java clients
myConnection.close();

Injecting a DataSource using the @Resource syntax will look as follows:

@Resource(name="java:global/DerbyPool")
DataSource ds;

Lightweight Directory Access Protocol

LDAP servers are specialized software products that store directory entries in hierarchical trees and are highly optimized for reading. This makes them a good choice for such directory services as employee lists or phone directories in an organization. Directories are mainly read, not changed, and this is where LDAP servers shine.

From the enterprise Java perspective you should keep LDAP solutions in mind when a really fast lookup of some Java objects is needed, such as with JMS connection factories, queues, or topics. Java developers use the JNDI API to look up and update the entries in LDAP. To use the same analogy as in the previous lesson, JNDI is to LDAP servers as JDBC is to DBMSes.

These are some popular LDAP servers:

  • Oracle Directory Server (Oracle)
  • Microsoft Active Directory (Microsoft)
  • OpenLDAP (open-source, community developed)
  • ApacheDS (open-source, Apache)
  • OpenDS (open-source, Oracle)

The LDAP directory tree has a root entry, which consists of one or more distinguished names (unique identifiers). Typically the top of the hierarchy is an object with the prefix o for organization. One level below has the prefix ou for organizational unit, cn stands for common name, and so on. Unlike with other naming services, the search string starts from the very lowest hierarchical entry and the root entry has to be specified last. Here’s an example of a distinguished name that can be used in a search:

cn=jsmith, ou=accounting, o=oracle.com

This example corresponds to the following hierarchy in an LDAP tree:

o = oracle.com
  ou = accounting
     cn = jsmith

The following code snippet prepares the JNDI properties, connects to the LDAP server, and finds the object called CustomerHome.

Hashtable env = new Hashtable();        
env.put(Context.INITIAL_CONTEXT_FACTORY, 
                       "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389");
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
env.put(Context.SECURITY_CREDENTIALS,"myPassword");
    
DirContext ctx = new InitialDirContext(env);
 
CustomerHome custHome =(CustomerHome) ctx.lookup("cn=CusomerHome,
ou=RealBigProject, o=trump.com");

To study distributed applications you can run all the examples from this book (clients and servers) on a single computer, but real-world distributed applications can be constructed in various ways, for example:

  • Computer #1 runs the LDAP server.
  • Computer #2 runs an application server that has registered (published) some objects with the LDAP server on Computer #1.
  • Computer #3 has a client program that finds object references on Computer #1 and invokes their methods on Computer #2.
  • Computer #4 has a database management system that is being used by the application server running on Computer #2.
  • Computer #5 publishes financial market data, and Computer #2 subscribes to this service.

…and so on, and so on.

In this lesson you’ve seen how to use JNDI, and some example of why you may want to use it. Comparing the code in Listing 30-1 with the one in Listing 31-1 is just one example that shows that naming and binding specific physical objects under a generic JNDI name enables you to remove these physical locations from your program code. You’ll see more examples of using JNDI in Lesson 32.

Try It

Create a class called MessageReceiver to consume messages. It has to perform a JNDI lookup and start listening to TestQueue. When the message arrives MessageReceiver should display it. Making a request to the Servlet from Listing 31-1 has to send a message to the queue, and MessageReceiver receives it and prints its content.

Lesson Requirements

You should have Java and GlassFish v3 installed.

note.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson31 folder in the download.

Step-by-Step

1. Create a new class called MessageReceiver that implements MessageListener with the code similar to the one shown in Listing 30-2, but this time performing a JNDI lookup of MyTestConnectionFactory and MyJMSTestQueue.

2. Find out how to configure the GlassFish start-up class, and implement it to have MessageReceiver instantiated whenever the server starts.

3. Test a send/receive cycle by using the Servlet from Listing 31-1.

cd.ai

Please select Lesson 31 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.109.205