1 /* 2 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import java.util.Hashtable; 29 import java.util.Vector; 30 import java.util.EventObject; 31 32 import javax.naming.*; 33 import javax.naming.event.*; 34 import javax.naming.directory.SearchControls; 35 import javax.naming.ldap.UnsolicitedNotificationListener; 36 import javax.naming.ldap.UnsolicitedNotificationEvent; 37 import javax.naming.ldap.UnsolicitedNotification; 38 39 /** 40 * This is a utility class that can be used by a context that supports 41 * event notification. You can use an instance of this class as a member field 42 * of your context and delegate various work to it. 43 * It is currently structured so that each context should have its own 44 * EventSupport (instead of static version shared by all contexts 45 * of a service provider). 46 *<p> 47 * This class supports two types of listeners: those that register for 48 * NamingEvents, and those for UnsolicitedNotificationEvents (they can be mixed 49 * into the same listener). 50 * For NamingEvent listeners, it maintains a hashtable that maps 51 * registration requests--the key--to 52 * <em>notifiers</em>--the value. Each registration request consists of: 53 *<ul> 54 *<li>The name argument of the registration. 55 *<li>The filter (default is "(objectclass=*)"). 56 *<li>The search controls (default is null SearchControls). 57 *<li>The events that the listener is interested in. This is determined by 58 * finding out which {@code NamingListener} interface the listener supports. 59 *</ul> 60 *<p> 61 *A notifier ({@code NamingEventNotifier}) is a worker thread that is responsible 62 *for gathering information for generating events requested by its listeners. 63 *Each notifier maintains its own list of listeners; these listeners have 64 *all made the same registration request (at different times) and implements 65 *the same {@code NamingListener} interfaces. 66 *<p> 67 *For unsolicited listeners, this class maintains a vector, unsolicited. 68 *When an unsolicited listener is registered, this class adds itself 69 *to the context's LdapClient. When LdapClient receives an unsolicited 70 *notification, it notifies this EventSupport to fire an event to the 71 *the listeners. Special handling in LdapClient is done for the DISCONNECT 72 *notification. [It results in the EventSupport firing also a 73 *NamingExceptionEvent to the unsolicited listeners.] 74 *<p> 75 * 76 *When a context no longer needs this EventSupport, it should invoke 77 *cleanup() on it. 78 *<p> 79 *<h4>Registration</h4> 80 *When a registration request is made, this class attempts to find an 81 *existing notifier that's already working on the request. If one is 82 *found, the listener is added to the notifier's list. If one is not found, 83 *a new notifier is created for the listener. 84 * 85 *<h4>Deregistration</h4> 86 *When a deregistration request is made, this class attempts to find its 87 *corresponding notifier. If the notifier is found, the listener is removed 88 *from the notifier's list. If the listener is the last listener on the list, 89 *the notifier's thread is terminated and removed from this class's hashtable. 90 *Nothing happens if the notifier is not found. 91 * 92 *<h4>Event Dispatching</h4> 93 *The notifiers are responsible for gather information for generating events 94 *requested by their respective listeners. When a notifier gets sufficient 95 *information to generate an event, it creates invokes the 96 *appropriate {@code fireXXXEvent} on this class with the information and list of 97 *listeners. This causes an event and the list of listeners to be added 98 *to the <em>event queue</em>. 99 *This class maintains an event queue and a dispatching thread that dequeues 100 *events from the queue and dispatches them to the listeners. 101 * 102 *<h4>Synchronization</h4> 103 *This class is used by the main thread (LdapCtx) to add/remove listeners. 104 *It is also used asynchronously by NamingEventNotifiers threads and 105 *the context's Connection thread. It is used by the notifier threads to 106 *queue events and to update the notifiers list when the notifiers exit. 107 *It is used by the Connection thread to fire unsolicited notifications. 108 *Methods that access/update the 'unsolicited' and 'notifiers' lists are 109 *thread-safe. 110 * 111 * @author Rosanna Lee 112 */ 113 final class EventSupport { 114 final static private boolean debug = false; 115 116 private LdapCtx ctx; 117 118 /** 119 * NamingEventNotifiers; hashed by search arguments; 120 */ 121 private Hashtable<NotifierArgs, NamingEventNotifier> notifiers = 122 new Hashtable<>(11); 123 124 /** 125 * List of unsolicited notification listeners. 126 */ 127 private Vector<UnsolicitedNotificationListener> unsolicited = null; 128 129 /** 130 * Constructs EventSupport for ctx. 131 * <em>Do we need to record the name of the target context? 132 * Or can we assume that EventSupport is called on a resolved 133 * context? Do we need other add/remove-NamingListener methods? 134 * package private; 135 */ 136 EventSupport(LdapCtx ctx) { 137 this.ctx = ctx; 138 } 139 140 /** 141 * Adds {@code l} to list of listeners interested in {@code nm}. 142 */ 143 /* 144 * Make the add/removeNamingListeners synchronized to: 145 * 1. protect usage of 'unsolicited', which may be read by 146 * the Connection thread when dispatching unsolicited notification. 147 * 2. ensure that NamingEventNotifier thread's access to 'notifiers' 148 * is safe 149 */ 150 synchronized void addNamingListener(String nm, int scope, 151 NamingListener l) throws NamingException { 152 153 if (l instanceof ObjectChangeListener || 154 l instanceof NamespaceChangeListener) { 155 NotifierArgs args = new NotifierArgs(nm, scope, l); 156 157 NamingEventNotifier notifier = notifiers.get(args); 158 if (notifier == null) { 159 notifier = new NamingEventNotifier(this, ctx, args, l); 160 notifiers.put(args, notifier); 161 } else { 162 notifier.addNamingListener(l); 163 } 164 } 165 if (l instanceof UnsolicitedNotificationListener) { 166 // Add listener to this's list of unsolicited notifiers 167 if (unsolicited == null) { 168 unsolicited = new Vector<>(3); 169 } 170 171 unsolicited.addElement((UnsolicitedNotificationListener)l); 172 } 173 } 174 175 /** 176 * Adds {@code l} to list of listeners interested in {@code nm} 177 * and filter. 178 */ 179 synchronized void addNamingListener(String nm, String filter, 180 SearchControls ctls, NamingListener l) throws NamingException { 181 182 if (l instanceof ObjectChangeListener || 183 l instanceof NamespaceChangeListener) { 184 NotifierArgs args = new NotifierArgs(nm, filter, ctls, l); 185 186 NamingEventNotifier notifier = notifiers.get(args); 187 if (notifier == null) { 188 notifier = new NamingEventNotifier(this, ctx, args, l); 189 notifiers.put(args, notifier); 190 } else { 191 notifier.addNamingListener(l); 192 } 193 } 194 if (l instanceof UnsolicitedNotificationListener) { 195 // Add listener to this's list of unsolicited notifiers 196 if (unsolicited == null) { 197 unsolicited = new Vector<>(3); 198 } 199 unsolicited.addElement((UnsolicitedNotificationListener)l); 200 } 201 } 202 203 /** 204 * Removes {@code l} from all notifiers in this context. 205 */ 206 synchronized void removeNamingListener(NamingListener l) { 207 if (debug) System.err.println("EventSupport removing listener"); 208 209 // Go through list of notifiers, remove 'l' from each. 210 // If 'l' is notifier's only listener, remove notifier too. 211 for (NamingEventNotifier notifier : notifiers.values()) { 212 if (notifier != null) { 213 if (debug) 214 System.err.println("EventSupport removing listener from notifier"); 215 notifier.removeNamingListener(l); 216 if (!notifier.hasNamingListeners()) { 217 if (debug) 218 System.err.println("EventSupport stopping notifier"); 219 notifier.stop(); 220 notifiers.remove(notifier.info); 221 } 222 } 223 } 224 225 // Remove from list of unsolicited notifier 226 if (debug) System.err.println("EventSupport removing unsolicited: " + 227 unsolicited); 228 if (unsolicited != null) { 229 unsolicited.removeElement(l); 230 } 231 232 } 233 234 synchronized boolean hasUnsolicited() { 235 return (unsolicited != null && unsolicited.size() > 0); 236 } 237 238 /** 239 * package private; 240 * Called by NamingEventNotifier to remove itself when it encounters 241 * a NamingException. 242 */ 243 synchronized void removeDeadNotifier(NotifierArgs info) { 244 if (debug) { 245 System.err.println("EventSupport.removeDeadNotifier: " + info.name); 246 } 247 notifiers.remove(info); 248 } 249 250 /** 251 * Fire an event to unsolicited listeners. 252 * package private; 253 * Called by LdapCtx when its clnt receives an unsolicited notification. 254 */ 255 synchronized void fireUnsolicited(Object obj) { 256 if (debug) { 257 System.err.println("EventSupport.fireUnsolicited: " + obj + " " 258 + unsolicited); 259 } 260 if (unsolicited == null || unsolicited.size() == 0) { 261 // This shouldn't really happen, but might in case 262 // there is a timing problem that removes a listener 263 // before a fired event reaches here. 264 return; 265 } 266 267 if (obj instanceof UnsolicitedNotification) { 268 269 // Fire UnsolicitedNotification to unsolicited listeners 270 271 UnsolicitedNotificationEvent evt = 272 new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj); 273 queueEvent(evt, unsolicited); 274 275 } else if (obj instanceof NamingException) { 276 277 // Fire NamingExceptionEvent to unsolicited listeners. 278 279 NamingExceptionEvent evt = 280 new NamingExceptionEvent(ctx, (NamingException)obj); 281 queueEvent(evt, unsolicited); 282 283 // When an exception occurs, the unsolicited listeners 284 // are automatically deregistered. 285 // When LdapClient.processUnsolicited() fires a NamingException, 286 // it will update its listener list so we don't have to. 287 // Likewise for LdapCtx. 288 289 unsolicited = null; 290 } 291 } 292 293 /** 294 * Stops notifier threads that are collecting event data and 295 * stops the event queue from dispatching events. 296 * Package private; used by LdapCtx. 297 */ 298 synchronized void cleanup() { 299 if (debug) System.err.println("EventSupport clean up"); 300 if (notifiers != null) { 301 for (NamingEventNotifier notifier : notifiers.values()) { 302 notifier.stop(); 303 } 304 notifiers = null; 305 } 306 if (eventQueue != null) { 307 eventQueue.stop(); 308 eventQueue = null; 309 } 310 // %%% Should we fire NamingExceptionEvents to unsolicited listeners? 311 } 312 313 /* 314 * The queue of events to be delivered. 315 */ 316 private EventQueue eventQueue; 317 318 /** 319 * Add the event and vector of listeners to the queue to be delivered. 320 * An event dispatcher thread dequeues events from the queue and dispatches 321 * them to the registered listeners. 322 * Package private; used by NamingEventNotifier to fire events 323 */ 324 synchronized void queueEvent(EventObject event, 325 Vector<? extends NamingListener> vector) { 326 if (eventQueue == null) 327 eventQueue = new EventQueue(); 328 329 /* 330 * Copy the vector in order to freeze the state of the set 331 * of EventListeners the event should be delivered to prior 332 * to delivery. This ensures that any changes made to the 333 * Vector from a target listener's method during the delivery 334 * of this event will not take effect until after the event is 335 * delivered. 336 */ 337 @SuppressWarnings("unchecked") // clone() 338 Vector<NamingListener> v = 339 (Vector<NamingListener>)vector.clone(); 340 eventQueue.enqueue(event, v); 341 } 342 343 // No finalize() needed because EventSupport is always owned by 344 // an LdapCtx. LdapCtx's finalize() and close() always call cleanup() so 345 // there is no need for EventSupport to have a finalize(). 346 }