1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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 }