Event Notification |
To receive event notifications, a listener registers with an event source. In the JNDI, the event sources implement either the EventContext or EventDirContext interface. To get an event source, you must look it up using the naming/directory service. That is, you perform a lookup() 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.
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("ou=People"));// 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().
Here is an example that registers a NamespaceChangeListener with a context.
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.// 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);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.
- Only the target ( OBJECT_SCOPE)
- The immediate children of the target (which must be a context) ( ONELEVEL_SCOPE)
- The target and all of its descendants ( SUBTREE_SCOPE)
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.// 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);Registration Errors
addNamingListener() can throw a NamingException 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() 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(). Here is an example.
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.// 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);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 interface do.Here is an example that registers interest only in objects that have the object class "javaobject".
The filter applies to existing objects and to those that come into existence after the registration.// 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);Deregistration
A registered listener becomes unregistered in any of three ways.An example of explicit deregistration is shown in the Nonexistent Targets section.
- When Context.close() is invoked on the event source, any registered listeners are automatically deregistered.
- When a listener receives an error notification via namingExceptionThrown(), it is automatically deregistered.
- When a listener is explicitly removed via a call to EventContext.removeNamingListener(), it is deregistered.
Event Notification |