001: /*
002: * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap;
027:
028: import javax.naming.*;
029: import javax.naming.directory.*;
030: import javax.naming.event.*;
031: import javax.naming.ldap.*;
032: import javax.naming.ldap.LdapName;
033:
034: import java.util.Vector;
035: import com.sun.jndi.toolkit.ctx.Continuation;
036:
037: /**
038: * Gathers information to generate events by using the Persistent Search
039: * control.
040: *<p>
041: * This class maintains a list of listeners all interested in the same
042: * "search" request. It creates a thread that does the persistent search
043: * and blocks, collecting the results of the search.
044: * For each result that it receives from the search, it fires the
045: * corresponding event to its listeners. If an exception is encountered,
046: * it fires a NamingExceptionEvent.
047: *
048: * @author Rosanna Lee
049: */
050: final class NamingEventNotifier implements Runnable {
051: private final static boolean debug = false;
052:
053: private Vector namingListeners;
054: private Thread worker;
055: private LdapCtx context;
056: private EventContext eventSrc;
057: private EventSupport support;
058: private NamingEnumeration results;
059:
060: // package private; used by EventSupport to remove it
061: NotifierArgs info;
062:
063: NamingEventNotifier(EventSupport support, LdapCtx ctx,
064: NotifierArgs info, NamingListener firstListener)
065: throws NamingException {
066: this .info = info;
067: this .support = support;
068:
069: Control psearch;
070: try {
071: psearch = new PersistentSearchControl(info.mask,
072: true /* no info about original entry(s) */,
073: true /* additional info about changes */,
074: Control.CRITICAL);
075: } catch (java.io.IOException e) {
076: NamingException ne = new NamingException(
077: "Problem creating persistent search control");
078: ne.setRootCause(e);
079: throw ne;
080: }
081:
082: // Add psearch control to existing list
083: context = (LdapCtx) ctx.newInstance(new Control[] { psearch });
084: eventSrc = ctx;
085:
086: namingListeners = new Vector();
087: namingListeners.addElement(firstListener);
088:
089: worker = Obj.helper.createThread(this );
090: worker.setDaemon(true); // not a user thread
091: worker.start();
092: }
093:
094: // package private; used by EventSupport; namingListener already synchronized
095: void addNamingListener(NamingListener l) {
096: namingListeners.addElement(l);
097: }
098:
099: // package private; used by EventSupport; namingListener already synchronized
100: void removeNamingListener(NamingListener l) {
101: namingListeners.removeElement(l);
102: }
103:
104: // package private; used by EventSupport; namingListener already synchronized
105: boolean hasNamingListeners() {
106: return namingListeners.size() > 0;
107: }
108:
109: /**
110: * Execute "persistent search".
111: * For each result, create the appropriate NamingEvent and
112: * queue to be dispatched to listeners.
113: */
114: public void run() {
115: try {
116: Continuation cont = new Continuation();
117: cont.setError(this , info.name);
118: Name nm = (info.name == null || info.name.equals("")) ? new CompositeName()
119: : new CompositeName().add(info.name);
120:
121: results = context.searchAux(nm, info.filter, info.controls,
122: true, false, cont);
123:
124: // Change root of search results so that it will generate
125: // names relative to the event context instead of that
126: // named by nm
127: ((LdapSearchEnumeration) results)
128: .setStartName(context.currentParsedDN);
129:
130: SearchResult si;
131: Control[] respctls;
132: EntryChangeResponseControl ec;
133: long changeNum;
134:
135: while (results.hasMore()) {
136: si = (SearchResult) results.next();
137: respctls = (si instanceof HasControls) ? ((HasControls) si)
138: .getControls()
139: : null;
140:
141: if (debug) {
142: System.err.println("notifier: " + si);
143: System.err.println("respCtls: " + respctls);
144: }
145:
146: // Just process ECs; ignore all the rest
147: if (respctls != null) {
148: for (int i = 0; i < respctls.length; i++) {
149: // %%% Should be checking OID instead of class
150: // %%% in case using someone else's EC ctl
151: if (respctls[i] instanceof EntryChangeResponseControl) {
152: ec = (EntryChangeResponseControl) respctls[i];
153: changeNum = ec.getChangeNumber();
154: switch (ec.getChangeType()) {
155: case EntryChangeResponseControl.ADD:
156: fireObjectAdded(si, changeNum);
157: break;
158: case EntryChangeResponseControl.DELETE:
159: fireObjectRemoved(si, changeNum);
160: break;
161: case EntryChangeResponseControl.MODIFY:
162: fireObjectChanged(si, changeNum);
163: break;
164: case EntryChangeResponseControl.RENAME:
165: fireObjectRenamed(si, ec
166: .getPreviousDN(), changeNum);
167: break;
168: }
169: }
170: break;
171: }
172: }
173: }
174: } catch (InterruptedNamingException e) {
175: if (debug)
176: System.err.println("NamingEventNotifier Interrupted");
177: } catch (NamingException e) {
178: // Fire event to notify NamingExceptionEvent listeners
179: fireNamingException(e);
180:
181: // This notifier is no longer valid
182: support.removeDeadNotifier(info);
183: } finally {
184: cleanup();
185: }
186: if (debug)
187: System.err.println("NamingEventNotifier finished");
188: }
189:
190: private void cleanup() {
191: if (debug)
192: System.err.println("NamingEventNotifier cleanup");
193:
194: try {
195: if (results != null) {
196: if (debug)
197: System.err
198: .println("NamingEventNotifier enum closing");
199: results.close(); // this will abandon the search
200: results = null;
201: }
202: if (context != null) {
203: if (debug)
204: System.err
205: .println("NamingEventNotifier ctx closing");
206: context.close();
207: context = null;
208: }
209: } catch (NamingException e) {
210: }
211: }
212:
213: /**
214: * Stop the dispatcher so we can be destroyed.
215: * package private; used by EventSupport
216: */
217: void stop() {
218: if (debug)
219: System.err.println("NamingEventNotifier being stopping");
220: if (worker != null) {
221: worker.interrupt(); // kill our thread
222: worker = null;
223: }
224: }
225:
226: /**
227: * Fire an "object added" event to registered NamingListeners.
228: */
229: private void fireObjectAdded(Binding newBd, long changeID) {
230: if (namingListeners == null || namingListeners.size() == 0)
231: return;
232:
233: NamingEvent e = new NamingEvent(eventSrc,
234: NamingEvent.OBJECT_ADDED, newBd, null, new Long(
235: changeID));
236: support.queueEvent(e, namingListeners);
237: }
238:
239: /**
240: * Fire an "object removed" event to registered NamingListeners.
241: */
242: private void fireObjectRemoved(Binding oldBd, long changeID) {
243: if (namingListeners == null || namingListeners.size() == 0)
244: return;
245:
246: NamingEvent e = new NamingEvent(eventSrc,
247: NamingEvent.OBJECT_REMOVED, null, oldBd, new Long(
248: changeID));
249: support.queueEvent(e, namingListeners);
250: }
251:
252: /**
253: * Fires an "object changed" event to registered NamingListeners.
254: */
255: private void fireObjectChanged(Binding newBd, long changeID) {
256: if (namingListeners == null || namingListeners.size() == 0)
257: return;
258:
259: // Name hasn't changed; construct old binding using name from new binding
260: Binding oldBd = new Binding(newBd.getName(), null, newBd
261: .isRelative());
262:
263: NamingEvent e = new NamingEvent(eventSrc,
264: NamingEvent.OBJECT_CHANGED, newBd, oldBd, new Long(
265: changeID));
266: support.queueEvent(e, namingListeners);
267: }
268:
269: /**
270: * Fires an "object renamed" to registered NamingListeners.
271: */
272: private void fireObjectRenamed(Binding newBd, String oldDN,
273: long changeID) {
274: if (namingListeners == null || namingListeners.size() == 0)
275: return;
276:
277: Binding oldBd = null;
278: try {
279: LdapName dn = new LdapName(oldDN);
280: if (dn.startsWith(context.currentParsedDN)) {
281: String relDN = dn.getSuffix(
282: context.currentParsedDN.size()).toString();
283: oldBd = new Binding(relDN, null);
284: }
285: } catch (NamingException e) {
286: }
287:
288: if (oldBd == null) {
289: oldBd = new Binding(oldDN, null, false /* not relative name */);
290: }
291:
292: NamingEvent e = new NamingEvent(eventSrc,
293: NamingEvent.OBJECT_RENAMED, newBd, oldBd, new Long(
294: changeID));
295: support.queueEvent(e, namingListeners);
296: }
297:
298: private void fireNamingException(NamingException e) {
299: if (namingListeners == null || namingListeners.size() == 0)
300: return;
301:
302: NamingExceptionEvent evt = new NamingExceptionEvent(eventSrc, e);
303: support.queueEvent(evt, namingListeners);
304: }
305: }
|