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

advertisement

AddThis Social Bookmark Button
Java Enterprise in a Nutshell, 2nd Edition

Java Enterprise Breakthroughs, Part 1

by Jim Farley, coauthor of Java Enterprise in a Nutshell, 2nd Edition
05/22/2002

The second edition of Java Enterprise in a Nutshell includes an enormous amount of new material, reflecting the significant milestones that enterprise Java has achieved since the first edition was published in 1999. These milestones include the release of the full J2EE 1.2 spec in December 1999, the release of the J2EE 1.3 spec in the summer of 2001, the recent wave of Java support for Web services, and numerous other achievements in between.

In this article, I'll highlight just a few of the powerful new features found in the Java enterprise APIs that are described in the second edition.

1. Directory Event Notification with JNDI

As of JDK 1.3, the Java Naming and Directory Interface (JNDI) was added as a standard part of the core Java environment. (Previously, it was available separately as a "standard extension" API.) In this release, the JNDI API was also extended to include support for event notification. Some naming and directory services, such as LDAP, are capable of issuing notifications of changes to registered listeners. The JNDI API provides support for this in the form of the javax.naming.event sub-package.

If your JNDI provider supports event notification, its Context or DirContext implementation will extend the javax.naming.event.EventContext and/or javax.naming.event.EventDirContext interfaces. If you're using the LDAP provider supplied with either JDK 1.3 or 1.4 to connect to an LDAP server, for example, you can cast a Context generated from this provider to an EventDirContext:

// Obtain Context from LDAP provider through a lookup
Context ctx = (Context)initCtx.lookup("ou=people");
EventDirContext evCtx = (EventDirContext)ctx;

The EventContext and EventDirContext interfaces provide the additional functionality needed to register event listeners with the underlying JNDI provider. All JNDI event listeners implement the javax.naming.event.NamingListener interface. JNDI provides two subclasses of this interface: NamespaceChangeListeners, to listen for changes in the namespace of the JNDI provider, and ObjectChangeListener, to listen for changes to the actual objects stored in the JNDI provider.

To receive notifications of changes in a naming/directory service, you simply implement a concrete subclass of one or both of these types of NamingListeners and register them with a Context obtained from the JNDI provider. If, for example, you wanted to log a message each time the namespace changed, you could implement a NamespaceChangeListener such as the following:

import javax.naming.*;
import javax.naming.event.*;
public class NSChangeLogger implements NamespaceChangeListener {
  // Default constructor
  public NSChangeLogger() {}
  // Callback for object addition events
  public void objectAdded(NamingEvent ev) {
    Binding b = ev.getNewBinding();
    System.out.println("--> ADD: Object of type " + b.getClassName() +
                       " added at binding \"" + b.toString() + "\"");
  }
     
  // Callback for object removal events
  public void objectRemoved(NamingEvent ev) {
    Binding b = ev.getOldBinding();
    System.out.println("--> REMOVE: Object of type " + b.getClassName() +
                       " removed from binding \"" + b.toString() + "\"");
  }
  // Callback for object rename events
  public void objectRenamed(NamingEvent ev) {
    Binding bNew = ev.getNewBinding();
    Binding bOld = ev.getOldBinding();
    System.out.println("--> RENAME: Object of type " + bNew.getClassName() +
                       " renamed from binding \"" + bOld.toString() +
                       "\" to binding \"" + bNew.toString() + "\"");
  }
  // Callback for errors in the naming service
  public void namingExceptionThrown(NamingExceptionEvent ev) {
    System.out.println("--> ERROR: An error occurred in the naming service:");
    ev.getException().printStackTrace();
  }
}

Each method on our NSChangeLogger is a callback that will be invoked by the JNDI provider whenever a corresponding event occurs in the naming/directory service. To register this listener with the JNDI provider, you invoke the addNamingListener() method on either the EventContext or EventDirContext interface. For example, to log namespace change events for any objects in the "people" branch of an LDAP server that belongs to the organization "O'Reilly and Associates", you might register one of our NSChangeLogger listeners like so:

EventDirContext evCtx = (EventDirContext)initCtx.lookup("ou=people");
NSChangeLogger nsLogger = new NSChangeLogger();
evCtx.addNamingListener(“o=O'Reilly and Associates”,
  EventContext.SUBTREE_SCOPE, nsLogger);

2. URLs for CORBA Objects: The CORBA Interoperable Naming Service

As of JDK 1.4, Java IDL (the standard implementation of the Java binding of the CORBA specifications) supports the Interoperable Naming Service (INS) extensions to the CORBA Naming Service. These extensions were added circa CORBA 2.3.1, and they provide new, simpler approaches for clients to locate CORBA objects registered in CORBA Naming Services. CORBA has always provided a scheme for obtaining a "stringified object reference" for a registered object, using the object_to_string() method on the ORB interface:

// Initialize the ORB
ORB myORB = ORB.init(...);
// Initialize a remote object and connect it to the ORB
org.omg.CORBA.Object myRmtObject = . . .;
// Get a stringified reference to the object and print it
String ior = myORB.object_to_string(myRmtObject);
System.out.println("The IOR for the object is:\n" + ior);

Running this example code (with the missing bits filled in, of course) will generate output like the following:

The IOR for the object is:
IOR:000000000000002349444c3a6f7265696c6c792f6a656e742f636f7262612f416
3636f756e743a312e300000000000010000000000000068000102000000000a313237
2e302e302e3100046600000021afabcb0000000020363f810b0000000100000000000
000000000000400000000030000000000000100000001000000200000000000010001
00000002050100010001002000101090000000100010100

A client can generate a remote reference to this CORBA object by passing this "IOR:" string into the string_to_object() method on its ORB:

Object rmtRef = clientORB.string_to_object("IOR:000...");

INS supports simpler, human-readable object URLs, similar to the URLs used to locate RMI objects stored in an RMI registry. If a CORBA object is registered in a CORBA Naming Service under the name "myObject" on a server named inshost.org, for example, a client can obtain a remote reference to this object by constructing a corresponding corbaname URL:

Object rmtRef2 = clientORB.string_to_object("corbaname:iiop:inshost.org#myObject");

Among other things, these human-readable object URLs can be much more easily assembled in a programmatic way, based on the component parts (protocol, host, object name). These simplified URLs are also easier to manage and distribute than the more complicated IORs. It also helps a great deal in eyeballing configuration parameters for distributed systems -- manually checking an IOR for correctness is akin to reading the machine code for an operating system kernel to find a bug.

Pages: 1, 2

Next Pagearrow