ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Developing a Simple JMS Example
Pages: 1, 2, 3, 4

Obtaining a JNDI connection

The chat client starts by obtaining a JNDI connection to the JMS messaging server. JNDI is an implementation-independent API for directory and naming systems. A directory service provides JMS clients with access to ConnectionFactory and Destinations (topics and queues) objects. ConnectionFactory and Destination objects are the only things in JMS that cannot be obtained using the JMS API -- unlike connections, sessions, producers, consumers, and messages, which are manufactured using the JMS API. JNDI provides a convenient, location-transparent, configurable, and portable mechanism for obtaining ConnectionFactory and Destination objects, also called JMS administered objects because they are established and configured by a system administrator.



Using JNDI, a JMS client can obtain access to a JMS provider by first looking up a ConnectionFactory. The ConnectionFactory is used to create JMS connections, which can then be used for sending and receiving messages. Destination objects, which represent virtual channels (topics and queues) in JMS, are also obtained via JNDI and are used by the JMS client. The directory service can be configured by the system administrator to provide JMS administered objects so that the JMS clients don't need to use proprietary code to access a JMS provider.

JMS servers will either work with a separate directory service (e.g., LDAP) or provide their own directory service that supports the JNDI API. For more details on JNDI, see the sidebar below, "Understanding JNDI."

Understanding JNDI

JNDI is a standard Java extension that provides a uniform API for accessing a variety of directory and naming services. In this respect, it is somewhat similar to JDBC. JDBC lets you write code that can access different relational databases such as Oracle, SQLServer, or Sybase; JNDI lets you write code that can access different directory and naming services, such as LDAP, Novell Netware NDS, CORBA Naming Service, and proprietary naming services provided by JMS servers.

In JMS, JNDI is used mostly as a naming service to locate administered objects. Administered objects are JMS objects that are created and configured by the system administrator. Administered objects include JMS ConnectionFactory and Destination objects like topics and queues.

Administered objects are bound to a name in a naming service. A naming service associates names with distributed objects, files, and devices so that they can be located on the network using simple names instead of cryptic network addresses. An example of a naming service is the DNS, which converts an Internet hostname like www.oreilly.com into a network address that browsers use to connect to web servers. There are many other naming services, such as COSNaming in CORBA and the Java RMI registry. Naming services allow printers, distributed objects, and JMS administered objects to be bound to names and organized in a hierarchy similar to a filesystem. A directory service is a more sophisticated kind of naming service.

JNDI provides an abstraction that hides the specifics of the naming service, making client applications more portable. Using JNDI, JMS clients can browse a naming service and obtain references to administered objects without knowing the details of the naming service or how it is implemented. JMS servers are usually be used in combination with a standard JNDI driver (a.k.a. service provider) and directory service like LDAP, or provide a proprietary JNDI service provider and directory service.

JNDI is both virtual and dynamic. It is virtual because it allows one naming service to be linked to another. Using JNDI, you can drill down through directories to files, printers, JMS administered objects, and other resources following virtual links between naming services. The user doesn't know or care where the directories are actually located. As an administrator, you can create virtual directories that span a variety of different services over many different physical locations.

JNDI is dynamic because it allows the JNDI drivers for specific types of naming and directory services to be loaded dynamically at runtime. A driver maps a specific kind of naming or directory service into the standard JNDI class interfaces. Drivers have been created for LDAP, Novell NetWare NDS, Sun Solaris NIS+, CORBA COSNaming, and many other types of naming and directory services, including proprietary ones. Dynamically loading JNDI drivers (service providers) makes it possible for a client to navigate across arbitrary directory services without knowing in advance what kinds of services it is likely to find.

The constructor of the Chat class starts by obtaining a connection to the JNDI naming service used by the JMS server:

// Obtain a JNDI connection
Properties env = new Properties( );
// ... specify the JNDI properties specific to the vendor

InitialContext jndi = new InitialContext(env);
Creating a connection to a JNDI naming service requires that a javax.naming.InitialContext object be created. An InitialContext is the starting point for any JNDI lookup -- it's similar in concept to the root of a filesystem. The InitialContext provides a network connection to the directory service that acts as a root for accessing JMS administered objects. The properties used to create an InitialContext depend on which JMS directory service you are using. The code used to create a JNDI InitialContext in BEA's Weblogic naming service, for example, would look something like this:

Properties env = new Properties( );
env.put(Context.SECURITY_PRINCIPAL, "guest");  
env.put(Context.SECURITY_CREDENTIALS, "guest");
env.put(Context.INITIAL_CONTEXT_FACTORY, 
   "<kbd class=userinput>weblogic.jndi.WLInitialContextFactory</kbd>");
env.put(Context.PROVIDER_URL, 
   "<kbd class=userinput>t3://localhost:7001</kbd>");
 
InitialContext jndi = new InitialContext(env);

When SonicMQ is used in combination with a third party LDAP directory service, the connection properties would be very different. For example, the following shows how a SonicMQ JMS client would use JNDI to access JMS administered objects stored in a LDAP directory server:

Properties env = new Properties( );
env.put(Context.SECURITY_PRINCIPAL, "guest");  
env.put(Context.SECURITY_CREDENTIALS, "guest");
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "<kbd class=userinput>com.sun.jndi.ldap.LdapCtxFactory</kbd>");
env.put(Context.PROVIDER_URL,
     "<kbd class=userinput>ldap://localhost:389/o=acme.com</kbd>");
 
InitialContext jndi = new InitialContext(env);

NOTE: Alternatively, the InitialContext( ) can be created without properties (no-arg constructor). In this case JNDI will read the vendor-specific JNDI properties from a special file in the classpath named jndi.properties. This eliminates provider-specific code in JMS clients, making them more portable.

The TopicConnectionFactory

Once a JNDI InitialContext object is instantiated, it can be used to look up the TopicConnectionFactory in the messaging server's naming service:

TopicConnectionFactory conFactory = 
(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");

The javax.jms.TopicConnectionFactory is used to manufacture connections to a message server. A TopicConnectionFactory is a type of administered object, which means that its attributes and behavior are configured by the system administrator responsible for the messaging server. The TopicConnectionFactory is implemented differently by each vendor, so configuration options available to system administrators vary from product to product. A connection factory might, for example, be configured to manufacture connections that use a particular protocol, security scheme, clustering strategy, etc. A system administrator might choose to deploy several different TopicConnectionFactory objects, each configured with its own JNDI lookup name.

The TopicConnectionFactory provides two overloaded versions of the createTopicConnection( ) method:

package javax.jms;

public interface TopicConnectionFactory extends ConnectionFactory {
   public TopicConnection createTopicConnection( ) 
      throws JMSException, JMSSecurityException;
   public TopicConnection createTopicConnection(String username, 
      String password) throws JMSException, JMSSecurityException;
}

These methods are used to create TopicConnection objects. The behavior of the no-arg method depends on the JMS provider. Some JMS providers will assume that the JMS client is connecting under anonymous security context, while other providers may assume that the credentials can be obtained from JNDI or the current thread. The second method provides the client with a username-password authentication credential, which can be used to authenticate the connection. In our code, we choose to authenticate the connection explicitly with a username and password.

Pages: 1, 2, 3, 4

Next Pagearrow