Previous | Next | Trail Map | Beyond the Basics | Event Notification

Listener Registration

To receive event notifications, a listener registers with an event source. In the JNDI, the event sources implement either the EventContext(in the API reference documentation) or EventDirContext(in the API reference documentation) interface. To get an event source, you must lookup it up from the naming/directory service. That is, you perform a lookup()(in the API reference documentation) on an object and then cast the result to an EventContext or EventDirContext. It is optional whether a context supports either of these interfaces. If a context supports neither interfaces, it does not support event notification.

Here is an example that looks up a name from the initial context and casts it to an EventDirContext:

// Get event DirContext for registering listener
EventDirContext ctx = (EventDirContext)
    (new InitialDirContext(env).lookup("ou=People"));

EventContext is intended for applications that can name the object of interest. You register a listener to receive notifications by using the EventContext.addNamingListener()(in the API reference documentation) method.

Here is an example that registers a NamespaceChangeListener(in the API reference documentation)with a context:

// Get event context for registering listener
EventContext ctx = (EventContext)
    (new InitialContext(env).lookup("ou=People"));

// Create listener
NamingListener listener = new SampleNCListener("nc1");

// Register listener for namespace change events
ctx.addNamingListener("ou=Objects,cn=Rosanna Lee", 
    EventContext.ONELEVEL_SCOPE, listener);
When you run this example, it will wait for one minute so that the main program (the listener) can receive notifications about the changes that a worker thread has made.

Target and Scope

The object named by the name parameter to addNamingListener() is called the target. The second parameter specifies the scope, which identifies whether the listener is to receive notifications on: Here is an example that add listeners using the same target but three different scopes:
// Get event context for registering listener
EventContext ctx = (EventContext)
    (new InitialContext(env).lookup("ou=People"));

String target = ...;

// Create listeners
NamingListener oneListener = new SampleListener("ONELEVEL");
NamingListener objListener = new SampleListener("OBJECT");
NamingListener subListener = new SampleListener("SUBTREE");

// Register listeners using different scopes
ctx.addNamingListener(target, EventContext.ONELEVEL_SCOPE, oneListener);
ctx.addNamingListener(target, EventContext.OBJECT_SCOPE, objListener);
ctx.addNamingListener(target, entContext.SUBTREE_SCOPE, subListener);
After registering the listeners, this program creates a thread that makes namespace changes to the LDAP server. It makes changes to the target, the children of the target, and the grandchildren of the target. The listener registered for object scope will receive the two notifications for changes applied to the target. The listener registered for one-level scope won't receives notifications for these two changes, but instead, receives notifications for the changes applied to the children. The listener registered for subtree scope will get notifications for all of the changes.

Registration Errors

The addNamingListener() can throw a NamingException(in the API reference documentation) when it encounters an error while registering the listener. However, there is no guarantee that the data supplied will be verified immediately at registration time. For example, some verification might require possibly open-ended server interaction. When an error occurs in such a scenario, the service provider will invoke the listener's namingExceptionThrown()(in the API reference documentation) method to notify it of the problem. Therefore, the application must be prepared to handle the error regardless of whether it occurs at registration time or asynchronously in the listener's code.

Nonexistent Targets

Some service providers/services might allow registration for nonexistent targets. That is, in the above example, the entry named by target might not need to exist at the time addNamingListener() is called. To check whether this feature is supported, you use targetMustExist()(in the API reference documentation). Here is an example:

// Get event context for registering listener
EventContext ctx = (EventContext)new InitialContext(env).lookup("");

// Create listener
NamingListener listener = new MyListener();
String target = ...;

// Check whether object exists so that we don't wait
// forever for nonexistent object
if (!ctx.targetMustExist()) {
    // Check that object exists before continuing
    // If lookup fails, exception will be thrown and we'd skip registration
    ctx.lookup(target);
}

// Register listener
ctx.addNamingListener(target, EventContext.ONELEVEL_SCOPE, listener);
This example does not want to register an ObjectChangeListener for a nonexistent object. Therefore, it first checks whether the context requires the object to exists. If the context does not require the object to exist, the program performs a lookup()). The example also uses a listener that implements NamespaceChangeListener so that it can detect when the object has disappeared, at which point the listener notifies the user and unregisters itself.
public void objectRemoved(NamingEvent evt) {
    System.out.println(">>> removed: " + evt.getOldBinding().getName());
    deregisterSelf(evt.getEventContext());
}
private void deregisterSelf(EventContext ctx) {
    System.out.println("Deregistering listener...");
    synchronized (ctx) {
	try {
	    ctx.removeNamingListener(this);
	} catch (NamingException e) {
	    System.out.println("Listener removal problem: " + e);
	}
    }
}

Using Search Filters

If you want to be more selective about the objects for which you register interest, you can use a search filter. The EventDirContext interface contains addNamingListener() methods that accept a search filter, in the same way that some of the search methods in the DirContext(in the API reference documentation) interface do. Here is an example that registers interest only in objects that have the object class "javaobject".
// Get event DirContext for registering listener
EventDirContext ctx = (EventDirContext)
    (new InitialDirContext(env).lookup("ou=People"));

// Create listener
NamingListener listener = new SampleNCListener("nc1");

// Set up search constraints
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
	    
// Register listener for namespace change events for
// entries identified using a search filter.
// In this example, register interest in namespace changes to
// objects that have the objectclass "javaobject".
ctx.addNamingListener("cn=Rosanna Lee", "(objectclass=javaobject)", 
    constraints, listener);
The filter applies to existing objects and those that come into existence after the registration.

Deregistration

There are three ways in which a registered listener becomes unregistered: There is an example of explicit deregistration in the nonexistent target example shown earlier.


Previous | Next | Trail Map | Beyond the Basics | Event Notification