Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java
41161 views
/*1* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import javax.naming.*;28import javax.naming.directory.*;29import javax.naming.event.*;30import javax.naming.ldap.*;31import javax.naming.ldap.LdapName;3233import java.util.Vector;34import com.sun.jndi.toolkit.ctx.Continuation;3536/**37* Gathers information to generate events by using the Persistent Search38* control.39*<p>40* This class maintains a list of listeners all interested in the same41* "search" request. It creates a thread that does the persistent search42* and blocks, collecting the results of the search.43* For each result that it receives from the search, it fires the44* corresponding event to its listeners. If an exception is encountered,45* it fires a NamingExceptionEvent.46*47* @author Rosanna Lee48*/49final class NamingEventNotifier implements Runnable {50private static final boolean debug = false;5152private Vector<NamingListener> namingListeners;53private Thread worker;54private LdapCtx context;55private EventContext eventSrc;56private EventSupport support;57private NamingEnumeration<SearchResult> results;5859// package private; used by EventSupport to remove it60NotifierArgs info;6162NamingEventNotifier(EventSupport support, LdapCtx ctx, NotifierArgs info,63NamingListener firstListener) throws NamingException {64this.info = info;65this.support = support;6667Control psearch;68try {69psearch = new PersistentSearchControl(70info.mask,71true /* no info about original entry(s) */,72true /* additional info about changes */,73Control.CRITICAL);74} catch (java.io.IOException e) {75NamingException ne = new NamingException(76"Problem creating persistent search control");77ne.setRootCause(e);78throw ne;79}8081// Add psearch control to existing list82context = (LdapCtx)ctx.newInstance(new Control[]{psearch});83eventSrc = ctx;8485namingListeners = new Vector<>();86namingListeners.addElement(firstListener);8788worker = Obj.helper.createThread(this);89worker.setDaemon(true); // not a user thread90worker.start();91}9293// package private; used by EventSupport; namingListener already synchronized94void addNamingListener(NamingListener l) {95namingListeners.addElement(l);96}9798// package private; used by EventSupport; namingListener already synchronized99void removeNamingListener(NamingListener l) {100namingListeners.removeElement(l);101}102103// package private; used by EventSupport; namingListener already synchronized104boolean hasNamingListeners() {105return namingListeners.size() > 0;106}107108/**109* Execute "persistent search".110* For each result, create the appropriate NamingEvent and111* queue to be dispatched to listeners.112*/113public void run() {114try {115Continuation cont = new Continuation();116cont.setError(this, info.name);117Name nm = (info.name == null || info.name.isEmpty()) ?118new CompositeName() : new CompositeName().add(info.name);119120results = context.searchAux(nm, info.filter, info.controls,121true, false, cont);122123// Change root of search results so that it will generate124// names relative to the event context instead of that125// named by nm126((LdapSearchEnumeration)(NamingEnumeration)results)127.setStartName(context.currentParsedDN);128129SearchResult si;130Control[] respctls;131EntryChangeResponseControl ec;132long changeNum;133134while (results.hasMore()) {135si = results.next();136respctls = (si instanceof HasControls) ?137((HasControls) si).getControls() : null;138139if (debug) {140System.err.println("notifier: " + si);141System.err.println("respCtls: " + respctls);142}143144// Just process ECs; ignore all the rest145if (respctls != null) {146for (int i = 0; i < respctls.length; i++) {147// %%% Should be checking OID instead of class148// %%% in case using someone else's EC ctl149if (respctls[i] instanceof EntryChangeResponseControl) {150ec = (EntryChangeResponseControl)respctls[i];151changeNum = ec.getChangeNumber();152switch (ec.getChangeType()) {153case EntryChangeResponseControl.ADD:154fireObjectAdded(si, changeNum);155break;156case EntryChangeResponseControl.DELETE:157fireObjectRemoved(si, changeNum);158break;159case EntryChangeResponseControl.MODIFY:160fireObjectChanged(si, changeNum);161break;162case EntryChangeResponseControl.RENAME:163fireObjectRenamed(si, ec.getPreviousDN(),164changeNum);165break;166}167}168break;169}170}171}172} catch (InterruptedNamingException e) {173if (debug) System.err.println("NamingEventNotifier Interrupted");174} catch (NamingException e) {175// Fire event to notify NamingExceptionEvent listeners176fireNamingException(e);177178// This notifier is no longer valid179support.removeDeadNotifier(info);180} finally {181cleanup();182}183if (debug) System.err.println("NamingEventNotifier finished");184}185186private void cleanup() {187if (debug) System.err.println("NamingEventNotifier cleanup");188189try {190if (results != null) {191if (debug) System.err.println("NamingEventNotifier enum closing");192results.close(); // this will abandon the search193results = null;194}195if (context != null) {196if (debug) System.err.println("NamingEventNotifier ctx closing");197context.close();198context = null;199}200} catch (NamingException e) {}201}202203/**204* Stop the dispatcher so we can be destroyed.205* package private; used by EventSupport206*/207void stop() {208if (debug) System.err.println("NamingEventNotifier being stopping");209if (worker != null) {210worker.interrupt(); // kill our thread211worker = null;212}213}214215/**216* Fire an "object added" event to registered NamingListeners.217*/218private void fireObjectAdded(Binding newBd, long changeID) {219if (namingListeners == null || namingListeners.size() == 0)220return;221222NamingEvent e = new NamingEvent(eventSrc, NamingEvent.OBJECT_ADDED,223newBd, null, changeID);224support.queueEvent(e, namingListeners);225}226227/**228* Fire an "object removed" event to registered NamingListeners.229*/230private void fireObjectRemoved(Binding oldBd, long changeID) {231if (namingListeners == null || namingListeners.size() == 0)232return;233234NamingEvent e = new NamingEvent(eventSrc, NamingEvent.OBJECT_REMOVED,235null, oldBd, changeID);236support.queueEvent(e, namingListeners);237}238239/**240* Fires an "object changed" event to registered NamingListeners.241*/242private void fireObjectChanged(Binding newBd, long changeID) {243if (namingListeners == null || namingListeners.size() == 0)244return;245246// Name hasn't changed; construct old binding using name from new binding247Binding oldBd = new Binding(newBd.getName(), null, newBd.isRelative());248249NamingEvent e = new NamingEvent(250eventSrc, NamingEvent.OBJECT_CHANGED, newBd, oldBd, changeID);251support.queueEvent(e, namingListeners);252}253254/**255* Fires an "object renamed" to registered NamingListeners.256*/257private void fireObjectRenamed(Binding newBd, String oldDN, long changeID) {258if (namingListeners == null || namingListeners.size() == 0)259return;260261Binding oldBd = null;262try {263LdapName dn = new LdapName(oldDN);264if (dn.startsWith(context.currentParsedDN)) {265String relDN = dn.getSuffix(context.currentParsedDN.size()).toString();266oldBd = new Binding(relDN, null);267}268} catch (NamingException e) {}269270if (oldBd == null) {271oldBd = new Binding(oldDN, null, false /* not relative name */);272}273274NamingEvent e = new NamingEvent(275eventSrc, NamingEvent.OBJECT_RENAMED, newBd, oldBd, changeID);276support.queueEvent(e, namingListeners);277}278279private void fireNamingException(NamingException e) {280if (namingListeners == null || namingListeners.size() == 0)281return;282283NamingExceptionEvent evt = new NamingExceptionEvent(eventSrc, e);284support.queueEvent(evt, namingListeners);285}286}287288289