View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.event;
18  
19  
20  import org.apache.ldap.server.DirectoryServiceConfiguration;
21  import org.apache.ldap.server.invocation.Invocation;
22  import org.apache.ldap.server.invocation.InvocationStack;
23  import org.apache.ldap.server.interceptor.BaseInterceptor;
24  import org.apache.ldap.server.interceptor.NextInterceptor;
25  import org.apache.ldap.server.configuration.InterceptorConfiguration;
26  import org.apache.ldap.server.schema.AttributeTypeRegistry;
27  import org.apache.ldap.server.schema.OidRegistry;
28  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
29  import org.apache.ldap.server.partition.DirectoryPartitionNexusProxy;
30  import org.apache.ldap.common.filter.ExprNode;
31  import org.apache.ldap.common.filter.ScopeNode;
32  import org.apache.ldap.common.filter.BranchNode;
33  import org.apache.ldap.common.message.DerefAliasesEnum;
34  
35  import javax.naming.Name;
36  import javax.naming.NamingException;
37  import javax.naming.Binding;
38  import javax.naming.NamingEnumeration;
39  import javax.naming.event.*;
40  import javax.naming.directory.Attributes;
41  import javax.naming.directory.ModificationItem;
42  import javax.naming.directory.SearchControls;
43  import javax.naming.directory.Attribute;
44  import java.util.*;
45  
46  
47  /***
48   * An interceptor based serivice for notifying NamingListeners of EventContext
49   * and EventDirContext changes.
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   * @version $Rev$
53   */
54  public class EventService extends BaseInterceptor
55  {
56      private DirectoryPartitionNexus nexus;
57      private Map sources = new HashMap();
58      private Evaluator evaluator = null;
59  
60  
61      public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
62      {
63          super.init( factoryCfg, cfg );
64  
65          OidRegistry oidRegistry = factoryCfg.getGlobalRegistries().getOidRegistry();
66          AttributeTypeRegistry attrRegistry = factoryCfg.getGlobalRegistries().getAttributeTypeRegistry();
67          evaluator = new ExpressionEvaluator( oidRegistry, attrRegistry );
68          nexus = factoryCfg.getPartitionNexus();
69      }
70  
71  
72      /***
73       * Registers a NamingListener with this service for notification of change.
74       *
75       * @param ctx the context used to register on (the source)
76       * @param name the name of the base/target
77       * @param filter the filter to use for evaluating event triggering
78       * @param searchControls the search controls to use when evaluating triggering
79       * @param namingListener the naming listener to register
80       */
81      public void addNamingListener( EventContext ctx, Name name, ExprNode filter, SearchControls searchControls,
82                                     NamingListener namingListener )
83      {
84          ScopeNode scope = new ScopeNode( DerefAliasesEnum.NEVERDEREFALIASES, name.toString(),
85                  searchControls.getSearchScope() );
86          BranchNode and = new BranchNode( BranchNode.AND );
87          and.addNode( scope );
88          and.addNode( filter );
89          EventSourceRecord rec = new EventSourceRecord( name, and, ctx, searchControls, namingListener );
90          Object obj = sources.get( namingListener );
91  
92          if ( obj == null )
93          {
94              sources.put( namingListener, rec );
95          }
96          else if ( obj instanceof EventSourceRecord )
97          {
98              List list = new ArrayList();
99              list.add( obj );
100             list.add( rec );
101         }
102         else if ( obj instanceof List )
103         {
104             List list = ( List ) obj;
105             list.add( rec );
106         }
107     }
108 
109 
110     public void removeNamingListener( EventContext ctx, NamingListener namingListener )
111     {
112         Object obj = sources.get( namingListener );
113 
114         if ( obj == null )
115         {
116             return;
117         }
118 
119         if ( obj instanceof EventSourceRecord )
120         {
121             sources.remove( namingListener );
122         }
123         else if ( obj instanceof List )
124         {
125             List list = ( List ) obj;
126 
127             for ( int ii = 0; ii < list.size(); ii++ )
128             {
129                 EventSourceRecord rec = ( EventSourceRecord ) list.get( ii );
130                 if ( rec.getEventContext() == ctx )
131                 {
132                     list.remove( ii );
133                 }
134             }
135 
136             if ( list.isEmpty() )
137             {
138                 sources.remove( namingListener );
139             }
140         }
141 
142     }
143 
144 
145     public void add( NextInterceptor next, String upName, Name normName, Attributes entry ) throws NamingException
146     {
147         super.add( next, upName, normName, entry );
148         Set selecting = getSelectingSources( normName, entry );
149         if ( selecting.isEmpty() )
150         {
151             return;
152         }
153 
154         Iterator list = selecting.iterator();
155         while ( list.hasNext() )
156         {
157             EventSourceRecord rec = ( EventSourceRecord ) list.next();
158             NamingListener listener = rec.getNamingListener();
159 
160             if ( listener instanceof NamespaceChangeListener )
161             {
162                 NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
163                 Binding binding = new Binding( upName, entry, false );
164                 nclistener.objectAdded( new NamingEvent( rec.getEventContext(),
165                         NamingEvent.OBJECT_ADDED, null, binding, entry ) );
166             }
167         }
168     }
169 
170 
171     public void delete( NextInterceptor next, Name name ) throws NamingException
172     {
173         super.delete( next, name );
174         Attributes entry = nexus.lookup( name );
175         Set selecting = getSelectingSources( name, entry );
176         if ( selecting.isEmpty() )
177         {
178             return;
179         }
180 
181         Iterator list = selecting.iterator();
182         while ( list.hasNext() )
183         {
184             EventSourceRecord rec = ( EventSourceRecord ) list.next();
185             NamingListener listener = rec.getNamingListener();
186 
187             if ( listener instanceof NamespaceChangeListener )
188             {
189                 NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
190                 Binding binding = new Binding( name.toString(), entry, false );
191                 nclistener.objectRemoved( new NamingEvent( rec.getEventContext(),
192                         NamingEvent.OBJECT_REMOVED, binding, null, entry ) );
193             }
194         }
195     }
196 
197 
198     private void notifyOnModify( Name name, ModificationItem[] mods, Attributes oriEntry ) throws NamingException
199     {
200         Attributes entry = nexus.lookup( name );
201         Set selecting = getSelectingSources( name, entry );
202         if ( selecting.isEmpty() )
203         {
204             return;
205         }
206 
207         Iterator list = selecting.iterator();
208         while ( list.hasNext() )
209         {
210             EventSourceRecord rec = ( EventSourceRecord ) list.next();
211             NamingListener listener = rec.getNamingListener();
212 
213             if ( listener instanceof ObjectChangeListener )
214             {
215                 ObjectChangeListener oclistener = ( ObjectChangeListener ) listener;
216                 Binding before = new Binding( name.toString(), oriEntry, false );
217                 Binding after = new Binding( name.toString(), entry, false );
218                 oclistener.objectChanged( new NamingEvent( rec.getEventContext(),
219                         NamingEvent.OBJECT_CHANGED, after, before, mods ) );
220             }
221         }
222     }
223 
224     public void modify( NextInterceptor next, Name name, int modOp, Attributes mods ) throws NamingException
225     {
226         Invocation invocation = InvocationStack.getInstance().peek();
227         DirectoryPartitionNexusProxy proxy = invocation.getProxy();
228         Attributes oriEntry = proxy.lookup( name, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
229         super.modify( next, name, modOp, mods );
230 
231         // package modifications in ModItem format for event delivery
232         ModificationItem[] modItems = new ModificationItem[mods.size()];
233         NamingEnumeration list = mods.getAll();
234         for ( int ii = 0; ii < modItems.length; ii++ )
235         {
236             modItems[ii] = new ModificationItem( modOp, ( Attribute ) list.next() );
237         }
238         notifyOnModify( name, modItems, oriEntry );
239     }
240 
241 
242     public void modify( NextInterceptor next, Name name, ModificationItem[] mods ) throws NamingException
243     {
244         Invocation invocation = InvocationStack.getInstance().peek();
245         DirectoryPartitionNexusProxy proxy = invocation.getProxy();
246         Attributes oriEntry = proxy.lookup( name, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
247         super.modify( next, name, mods );
248         notifyOnModify( name, mods, oriEntry );
249     }
250 
251 
252     private void notifyOnNameChange( Name oldName, Name newName ) throws NamingException
253     {
254         Attributes entry = nexus.lookup( newName );
255         Set selecting = getSelectingSources( oldName, entry );
256         if ( selecting.isEmpty() )
257         {
258             return;
259         }
260 
261         Iterator list = selecting.iterator();
262         while ( list.hasNext() )
263         {
264             EventSourceRecord rec = ( EventSourceRecord ) list.next();
265             NamingListener listener = rec.getNamingListener();
266 
267             if ( listener instanceof NamespaceChangeListener )
268             {
269                 NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
270                 Binding oldBinding = new Binding( oldName.toString(), entry, false );
271                 Binding newBinding = new Binding( oldName.toString(), entry, false );
272                 nclistener.objectRenamed( new NamingEvent( rec.getEventContext(),
273                         NamingEvent.OBJECT_RENAMED, oldBinding, newBinding, entry ) );
274             }
275         }
276     }
277 
278 
279     public void modifyRn( NextInterceptor next, Name name, String newRn, boolean deleteOldRn ) throws NamingException
280     {
281         super.modifyRn( next, name, newRn, deleteOldRn );
282         Name newName = ( Name ) name.clone();
283         newName.remove( newName.size() - 1 );
284         newName.add( newRn );
285         notifyOnNameChange( name, newName );
286     }
287 
288 
289     public void move( NextInterceptor next, Name oriChildName, Name newParentName, String newRn, boolean deleteOldRn )
290             throws NamingException
291     {
292         super.move( next, oriChildName, newParentName, newRn, deleteOldRn );
293         Name newName = ( Name ) newParentName.clone();
294         newName.add( newRn );
295         notifyOnNameChange( oriChildName, newName );
296     }
297 
298 
299     public void move( NextInterceptor next, Name oriChildName, Name newParentName ) throws NamingException
300     {
301         super.move( next, oriChildName, newParentName );
302         Name newName = ( Name ) newParentName.clone();
303         newName.add( oriChildName.get( oriChildName.size() - 1 ) );
304         notifyOnNameChange( oriChildName, newName );
305     }
306 
307 
308     Set getSelectingSources( Name name, Attributes entry ) throws NamingException
309     {
310         if ( sources.isEmpty() )
311         {
312             return Collections.EMPTY_SET;
313         }
314 
315         Set selecting = new HashSet();
316         Iterator list = sources.values().iterator();
317         while ( list.hasNext() )
318         {
319             Object obj = list.next();
320             if ( obj instanceof EventSourceRecord )
321             {
322                 EventSourceRecord rec = ( EventSourceRecord ) obj;
323                 if ( evaluator.evaluate( rec.getFilter(), name.toString(), entry ) )
324                 {
325                     selecting.add( obj );
326                 }
327             }
328             else if ( obj instanceof List )
329             {
330                 List records = ( List ) obj;
331                 for ( int ii = 0; ii < records.size(); ii++ )
332                 {
333                     EventSourceRecord rec = ( EventSourceRecord ) records.get( ii );
334                     if ( evaluator.evaluate( rec.getFilter(), name.toString(), entry ) )
335                     {
336                         selecting.add( obj );
337                     }
338                 }
339             }
340             else
341             {
342                 throw new IllegalStateException( "Unexpected class type of " + obj.getClass() );
343             }
344         }
345 
346         return selecting;
347     }
348 
349 
350     class EventSourceRecord
351     {
352         private Name base;
353         private SearchControls controls;
354         private ExprNode filter;
355         private EventContext context;
356         private NamingListener listener;
357 
358         public EventSourceRecord( Name base, ExprNode filter, EventContext context, SearchControls controls, NamingListener listener )
359         {
360             this.filter = filter;
361             this.context = context;
362             this.base = base;
363             this.controls = controls;
364             this.listener = listener;
365         }
366 
367         public NamingListener getNamingListener()
368         {
369             return listener;
370         }
371 
372         public ExprNode getFilter()
373         {
374             return filter;
375         }
376 
377         public EventContext getEventContext()
378         {
379             return context;
380         }
381 
382         public Name getBase()
383         {
384             return base;
385         }
386 
387         public SearchControls getSearchControls()
388         {
389             return controls;
390         }
391     }
392 }