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