| 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` 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`.

> >     // 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()`.

> Here is an example that registers a `NamespaceChangeListener` 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:
> - 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`)
> 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` 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.

> >     // 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` 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.
> - 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.
> An example of explicit deregistration is shown in the Nonexistent Targets section.

| Event Notification