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 look it up using 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. Whether a context supports either of these interfaces is optional. A context that supports neither 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 the EventDirContext for registering the listener
EventDirContext ctx = (EventDirContext)
    (new InitialDirContext(env).lookup("ou=People"));
To get an event source for the initial context itself, use the empty string as the name argument to lookup(). Here is an example.
// Get the EventDirContext for registering the listener
EventDirContext ctx = (EventDirContext)
    (new InitialDirContext(env).lookup(""));

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

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

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

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

// Register the listener for namespace change events
ctx.addNamingListener("ou=Objects,cn=Rosanna Lee", 
    EventContext.ONELEVEL_SCOPE, listener);
This example, when run, 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. The scope identifies whether the listener is to receive notifications on one of the following: Here is an example that adds listeners by using the same target but three different scopes.
// Get the EventContext for registering the listener
EventContext ctx = (EventContext)
    (new InitialContext(env).lookup("ou=People"));

String target = ...;

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

// Register the listeners by using different scopes
ctx.addNamingListener(target, EventContext.ONELEVEL_SCOPE, oneListener);
ctx.addNamingListener(target, EventContext.OBJECT_SCOPE, objListener);
ctx.addNamingListener(target, EventContext.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 will receive notifications for the changes applied to the children. The listener registered for subtree scope will get notifications for all of the changes.

Registration Errors

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 this case, 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 previous example, the entry named by target might not need to exist at the time that addNamingListener() is called. To check whether this feature is supported, you use targetMustExist()(in the API reference documentation). Here is an example.

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

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

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

// Register the listener
ctx.addNamingListener(target, EventContext.ONELEVEL_SCOPE, listener);
This example does not want to register an ObjectChangeListener for a nonexistent object, so it first checks whether the context requires the object to exist. If the context does not, then 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 deregisters 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, then you can use a search filter. The EventDirContext interface contains addNamingListener() overloads 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 the EventDirContext for registering the listener
EventDirContext ctx = (EventDirContext)
    (new InitialDirContext(env).lookup("ou=People"));

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

// Set up the search constraints
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
	    
// Register the 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 object class "javaobject".
ctx.addNamingListener("cn=Rosanna Lee", "(objectclass=javaobject)", 
    constraints, listener);
The filter applies to existing objects and to those that come into existence after the registration.

Deregistration

A registered listener becomes unregistered in any of three ways. An example of explicit deregistration is shown in the Nonexistent Targets section.


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