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.interceptor;
18  
19  
20  import org.apache.ldap.server.authn.AuthenticationService;
21  import org.apache.ldap.server.invocation.Invocation;
22  import org.apache.ldap.server.authz.AuthorizationService;
23  import org.apache.ldap.server.schema.SchemaService;
24  import org.apache.ldap.server.operational.OperationalAttributeService;
25  import org.apache.ldap.server.exception.ExceptionService;
26  import org.apache.ldap.server.normalization.NormalizationService;
27  
28  import javax.naming.NamingException;
29  import java.util.*;
30  
31  
32  /***
33   * Manages the chain of {@link Interceptor}s.  <tt>InterceptorChain</tt>
34   * is also an {@link Interceptor}, and thus you can create hiararchical
35   * interceptor structure to break down complex interceptors.
36   * <p/>
37   * {@link org.apache.ldap.server.jndi.JndiProvider#invoke(Invocation)}
38   * redirects {@link Invocation}s to {@link #process(NextInterceptor, Invocation)}
39   * and the chain starts.
40   *
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42   * @version $Rev: 159467 $, $Date: 2005-03-30 02:38:30 -0500 (Wed, 30 Mar 2005) $
43   */
44  public class InterceptorChain implements Interceptor
45  {
46      /***
47       * The name of default interceptor that passes its control to the
48       * next interceptor in parent chain.
49       */
50      public static final String NEXT_INTERCEPTOR = "nextInterceptor";
51  
52  
53      /***
54       * Returns a new chain of default interceptors required to run core.
55       */
56      public static InterceptorChain newDefaultChain()
57      {
58          InterceptorChain chain = new InterceptorChain();
59  
60          chain.addFirst( "normalizationService", new NormalizationService() );
61  
62          chain.addBefore( NEXT_INTERCEPTOR, "authenticationService", new AuthenticationService() );
63  
64          chain.addBefore( NEXT_INTERCEPTOR, "authorizationService", new AuthorizationService() );
65  
66          chain.addBefore( NEXT_INTERCEPTOR, "exceptionService", new ExceptionService() );
67  
68          chain.addBefore( NEXT_INTERCEPTOR, "schemaService", new SchemaService() );
69  
70          chain.addBefore( NEXT_INTERCEPTOR, "operationalAttributeService", new OperationalAttributeService() );
71  
72          return chain;
73      }
74  
75  
76      private final Interceptor NEXT_INTERCEPTOR0 = new Interceptor()
77      {
78          public void init( InterceptorContext context )
79          {
80          }
81  
82  
83          public void destroy()
84          {
85          }
86  
87  
88          public void process( NextInterceptor nextInterceptor, Invocation invocation ) throws NamingException
89          {
90              if( parent != null )
91              {
92                  Entry e = ( Entry ) parent.interceptor2entry.get( InterceptorChain.this );
93  
94                  e.nextInterceptor.process( invocation );
95              }
96  
97              nextInterceptor.process( invocation );
98          }
99      };
100 
101 
102     private final Interceptor FINAL_INTERCEPTOR = new Interceptor()
103     {
104         private InterceptorContext ctx;
105 
106 
107         public void init( InterceptorContext context )
108         {
109             ctx = context;
110         }
111 
112 
113         public void destroy()
114         {
115             // unused
116         }
117 
118 
119         public void process( NextInterceptor nextInterceptor, Invocation call ) throws NamingException
120         {
121             if ( parent == null )
122             {
123                 // execute the actual backend operation only when this chain is root.
124 
125                 call.execute( ctx.getRootNexus() );
126             }
127         }
128     };
129 
130     private InterceptorChain parent;
131 
132     private final Map name2entry = new HashMap();
133 
134     private final Map interceptor2entry = new IdentityHashMap();
135 
136     private Entry head = new Entry( null, null, NEXT_INTERCEPTOR, NEXT_INTERCEPTOR0 );
137 
138     private final Entry tail = new Entry( null, null, "end", FINAL_INTERCEPTOR );
139 
140 
141     /***
142      * Create a new interceptor chain.
143      */
144     public InterceptorChain()
145     {
146         head.nextEntry = tail;
147 
148         tail.prevEntry = head;
149 
150         register( NEXT_INTERCEPTOR, head );
151     }
152 
153 
154     /***
155      * Initializes all interceptors this chain contains.
156      */
157     public synchronized void init( InterceptorContext ctx ) throws NamingException
158     {
159         ListIterator it = getAll().listIterator();
160 
161         Interceptor interceptor = null;
162 
163         try
164         {
165             while ( it.hasNext() )
166             {
167                 interceptor = ( Interceptor ) it.next();
168 
169                 String name = getName( interceptor );
170 
171                 Map config = InterceptorConfigBuilder.build( ctx.getConfig(), ( name == null ) ? "" : name );
172 
173                 InterceptorContext newCtx = new InterceptorContext( ctx.getEnvironment(),
174                         ctx.getSystemPartition(), ctx.getGlobalRegistries(), ctx.getRootNexus(), config );
175 
176                 interceptor.init( newCtx );
177             }
178         }
179         catch ( Throwable t )
180         {
181             while ( it.hasPrevious() )
182             {
183                 Interceptor i = ( Interceptor ) it.previous();
184 
185                 try
186                 {
187                     i.destroy();
188                 }
189                 catch ( Throwable t2 )
190                 {
191                     t2.printStackTrace();
192                 }
193             }
194 
195             if ( t instanceof NamingException )
196             {
197                 throw ( NamingException ) t;
198             }
199             else
200             {
201                 throw new InterceptorException( interceptor, null, "Failed to initialize interceptor chain.", t );
202             }
203         }
204     }
205 
206 
207     /***
208      * Deinitializes all interceptors this chain contains.
209      */
210     public synchronized void destroy()
211     {
212         ListIterator it = getAllReversed().listIterator();
213 
214         while ( it.hasNext() )
215         {
216             Interceptor interceptor = ( Interceptor ) it.next();
217 
218             try
219             {
220                 interceptor.destroy();
221             }
222             catch ( Throwable t )
223             {
224                 t.printStackTrace();
225             }
226         }
227     }
228 
229 
230     /***
231      * Returns the interceptor with the specified <code>name</code>.
232      *
233      * @return <code>null</code> if there is no interceptor with the specified <code>name</code>.
234      */
235     public Interceptor get( String name )
236     {
237         Entry e = ( Entry ) name2entry.get( name );
238 
239         if ( e == null )
240         {
241             return null;
242         }
243 
244         return e.interceptor;
245     }
246 
247 
248     private String getName( Interceptor interceptor )
249     {
250         Entry e = ( Entry ) interceptor2entry.get( interceptor );
251 
252         if ( e == null )
253         {
254             return null;
255         }
256 
257         return e.name;
258     }
259 
260 
261     /***
262      * Adds the specified interceptor with the specified name at the beginning of this chain.
263      */
264     public synchronized void addFirst( String name,
265                                        Interceptor interceptor )
266     {
267         checkAddable( name, interceptor );
268 
269         Entry newEntry = new Entry( null, head, name, interceptor );
270 
271         head.prevEntry = newEntry;
272 
273         head = newEntry;
274 
275         register( name, newEntry );
276     }
277 
278 
279     /***
280      * Adds the specified interceptor with the specified name at the end of this chain.
281      */
282     public synchronized void addLast( String name,
283                                       Interceptor interceptor )
284     {
285         checkAddable( name, interceptor );
286 
287         Entry newEntry = new Entry( tail.prevEntry, tail, name, interceptor );
288 
289         if ( tail.prevEntry != null )
290         {
291             tail.prevEntry.nextEntry = newEntry;
292         }
293         else
294         {
295             head = newEntry;
296         }
297 
298         tail.prevEntry = newEntry;
299 
300         register( name, newEntry );
301     }
302 
303 
304     /***
305      * Adds the specified interceptor with the specified name just before the interceptor whose name is
306      * <code>baseName</code> in this chain.
307      */
308     public synchronized void addBefore( String baseName, String name, Interceptor interceptor )
309     {
310         Entry baseEntry = checkOldName( baseName );
311 
312         checkAddable( name, interceptor );
313 
314         Entry prevEntry = baseEntry.prevEntry;
315 
316         Entry newEntry = new Entry( prevEntry, baseEntry, name, interceptor );
317 
318         if ( prevEntry == null )
319         {
320             baseEntry.prevEntry = newEntry;
321 
322             head = newEntry;
323             
324         }
325         else
326         {
327             baseEntry.prevEntry = newEntry;
328 
329             prevEntry.nextEntry = newEntry;
330         }
331 
332         register( name, newEntry );
333     }
334 
335 
336     /***
337      * Adds the specified interceptor with the specified name just after the interceptor whose name is
338      * <code>baseName</code> in this chain.
339      */
340     public synchronized void addAfter( String baseName, String name, Interceptor interceptor )
341     {
342         Entry baseEntry = checkOldName( baseName );
343 
344         checkAddable( name, interceptor );
345 
346         Entry nextEntry = baseEntry.nextEntry;
347 
348         Entry newEntry = new Entry( baseEntry, nextEntry, name, interceptor );
349 
350         if ( nextEntry == null )
351         {
352             throw new IllegalStateException();
353         }
354 
355         nextEntry.prevEntry.nextEntry = newEntry;
356 
357         nextEntry.prevEntry = newEntry;
358 
359         register( name, newEntry );
360     }
361 
362 
363     /***
364      * Removes the interceptor with the specified name from this chain.
365      */
366     public synchronized void remove( String name )
367     {
368         Entry entry = checkOldName( name );
369 
370         Entry prevEntry = entry.prevEntry;
371 
372         Entry nextEntry = entry.nextEntry;
373 
374         if ( prevEntry == null )
375         {
376             nextEntry.prevEntry = null;
377 
378             head = entry;
379         }
380         else
381         {
382             prevEntry.nextEntry = nextEntry;
383 
384             nextEntry.prevEntry = prevEntry;
385         }
386 
387         name2entry.remove( name );
388 
389         Interceptor interceptor = entry.interceptor;
390 
391         interceptor2entry.remove( interceptor );
392 
393         if ( interceptor instanceof InterceptorChain )
394         {
395             ( ( InterceptorChain ) interceptor ).parent = null;
396         }
397     }
398 
399 
400     /***
401      * Removes all interceptors added to this chain.
402      */
403     public synchronized void clear()
404     {
405         Iterator it = new ArrayList( name2entry.keySet() ).iterator();
406 
407         while ( it.hasNext() )
408         {
409             this.remove( ( String ) it.next() );
410         }
411     }
412 
413 
414     private void register( String name, Entry newEntry )
415     {
416         Interceptor interceptor = newEntry.interceptor;
417 
418         name2entry.put( name, newEntry );
419 
420         interceptor2entry.put( newEntry.interceptor, newEntry );
421 
422         if ( interceptor instanceof InterceptorChain )
423         {
424             ( ( InterceptorChain ) interceptor ).parent = this;
425         }
426     }
427 
428 
429     /***
430      * Throws an exception when the specified interceptor name is not registered in this chain.
431      *
432      * @return An interceptor entry with the specified name.
433      */
434     private Entry checkOldName( String baseName )
435     {
436         Entry e = ( Entry ) name2entry.get( baseName );
437 
438         if ( e == null )
439         {
440             throw new IllegalArgumentException( "Unknown interceptor name:" + baseName );
441         }
442 
443         return e;
444     }
445 
446 
447     /***
448      * Checks the specified interceptor name is already taken and throws an exception if already taken.
449      */
450     private void checkAddable( String name, Interceptor interceptor )
451     {
452         if ( name2entry.containsKey( name ) )
453         {
454             throw new IllegalArgumentException( "Other interceptor is using name '" + name + "'" );
455         }
456 
457         if ( interceptor instanceof InterceptorChain )
458         {
459             if ( ( ( InterceptorChain ) interceptor ).parent != null )
460             {
461                 throw new IllegalArgumentException( "This interceptor chain has its parent already." );
462             }
463         }
464     }
465 
466 
467     /***
468      * Start invocation chain with the specified invocation.
469      *
470      * @throws NamingException if invocation failed
471      */
472     public void process( NextInterceptor nextInterceptor, Invocation invocation ) throws NamingException
473     {
474         Entry head = this.head;
475 
476         try
477         {
478             head.interceptor.process( head.nextInterceptor, invocation );
479         }
480         catch ( NamingException ne )
481         {
482             throw ne;
483         }
484         catch ( Throwable e )
485         {
486             throw new InterceptorException( head.interceptor, invocation, "Unexpected exception.", e );
487         }
488     }
489 
490 
491     /***
492      * Returns the list of interceptors this chain in the order of evaluation.
493      */
494     public List getAll()
495     {
496         List list = new ArrayList();
497 
498         Entry e = head;
499 
500         do
501         {
502             list.add( e.interceptor );
503 
504             e = e.nextEntry;
505         }
506         while ( e != null );
507 
508         return list;
509     }
510 
511 
512     /***
513      * Returns the list of interceptors this chain in the reversed order of evaluation.
514      */
515     public List getAllReversed()
516     {
517         List list = new ArrayList();
518 
519         Entry e = tail;
520 
521         do
522         {
523             list.add( e.interceptor );
524 
525             e = e.prevEntry;
526         }
527 
528         while ( e != null );
529 
530         return list;
531     }
532 
533 
534     /***
535      * Represents an internal entry of this chain.
536      */
537     private class Entry
538     {
539         private Entry prevEntry;
540 
541         private Entry nextEntry;
542 
543         private final String name;
544 
545         private final Interceptor interceptor;
546 
547         private final NextInterceptor nextInterceptor;
548 
549 
550         private Entry( Entry prevEntry, Entry nextEntry,
551                        String name, Interceptor interceptor )
552         {
553             if ( interceptor == null )
554             {
555                 throw new NullPointerException( "interceptor" );
556             }
557             if ( name == null )
558             {
559                 throw new NullPointerException( "name" );
560             }
561 
562             this.prevEntry = prevEntry;
563 
564             this.nextEntry = nextEntry;
565 
566             this.name = name;
567 
568             this.interceptor = interceptor;
569 
570             this.nextInterceptor = new NextInterceptor()
571             {
572                 public void process( Invocation call ) throws NamingException
573                 {
574                     Interceptor interceptor = Entry.this.nextEntry.interceptor;
575 
576                     try
577                     {
578                         interceptor.process( Entry.this.nextEntry.nextInterceptor, call );
579                     }
580                     catch ( NamingException ne )
581                     {
582                         throw ne;
583                     }
584                     catch ( Throwable e )
585                     {
586                         throw new InterceptorException( interceptor, call, "Unexpected exception.", e );
587                     }
588                 }
589             };
590         }
591     }
592 }