001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.interceptor;
021    
022    
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import javax.naming.ConfigurationException;
030    
031    import org.apache.directory.server.core.CoreSession;
032    import org.apache.directory.server.core.DirectoryService;
033    import org.apache.directory.server.core.entry.ClonedServerEntry;
034    import org.apache.directory.server.core.filtering.EntryFilteringCursor;
035    import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
036    import org.apache.directory.server.core.interceptor.context.AddOperationContext;
037    import org.apache.directory.server.core.interceptor.context.BindOperationContext;
038    import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
039    import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
040    import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
041    import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
042    import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
043    import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
044    import org.apache.directory.server.core.interceptor.context.ListOperationContext;
045    import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
046    import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
047    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
048    import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
049    import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
050    import org.apache.directory.server.core.interceptor.context.OperationContext;
051    import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
052    import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
053    import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
054    import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
055    import org.apache.directory.server.core.invocation.InvocationStack;
056    import org.apache.directory.server.core.partition.ByPassConstants;
057    import org.apache.directory.server.core.partition.PartitionNexus;
058    import org.apache.directory.server.i18n.I18n;
059    import org.apache.directory.shared.ldap.constants.SchemaConstants;
060    import org.apache.directory.shared.ldap.name.DN;
061    import org.slf4j.Logger;
062    import org.slf4j.LoggerFactory;
063    
064    
065    /**
066     * Manages the chain of {@link Interceptor}s.
067     *
068     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
069     * @version $Rev: 918766 $, $Date: 2010-03-04 00:25:11 +0100 (Thu, 04 Mar 2010) $
070     */
071    public class InterceptorChain
072    {
073        private static final Logger LOG = LoggerFactory.getLogger( InterceptorChain.class );
074    
075        /** Speedup for logs */
076        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
077    
078        private final Interceptor FINAL_INTERCEPTOR = new Interceptor()
079        {
080            private PartitionNexus nexus;
081    
082    
083            public String getName()
084            {
085                return "FINAL";
086            }
087    
088            public void init( DirectoryService directoryService )
089            {
090                this.nexus = directoryService.getPartitionNexus();
091            }
092    
093    
094            public void destroy()
095            {
096                // unused
097            }
098    
099    
100            public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws Exception
101            {
102                return nexus.compare( opContext );
103            }
104    
105    
106            public ClonedServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext opContext ) throws Exception
107            {
108                return nexus.getRootDSE( opContext );
109            }
110    
111    
112            public DN getMatchedName( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws Exception
113            {
114                return ( DN ) nexus.getMatchedName( opContext ).clone();
115            }
116    
117    
118            public DN getSuffix( NextInterceptor next, GetSuffixOperationContext opContext ) throws Exception
119            {
120                return ( DN ) nexus.getSuffix( opContext ).clone();
121            }
122    
123    
124            public Set<String> listSuffixes( NextInterceptor next, ListSuffixOperationContext opContext ) throws Exception
125            {
126                return nexus.listSuffixes( opContext );
127            }
128    
129    
130            public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception
131            {
132                nexus.delete( opContext );
133            }
134    
135    
136            public void add( NextInterceptor next, AddOperationContext opContext ) throws Exception
137            {
138                nexus.add( opContext );
139            }
140    
141    
142            public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
143            {
144                nexus.modify( opContext );
145            }
146    
147    
148            public EntryFilteringCursor list( NextInterceptor next, ListOperationContext opContext ) throws Exception
149            {
150                return nexus.list( opContext );
151            }
152    
153    
154            public EntryFilteringCursor search( NextInterceptor next, SearchOperationContext opContext ) throws Exception
155            {
156                return nexus.search( opContext );
157            }
158    
159    
160            public ClonedServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws Exception
161            {
162                return nexus.lookup( opContext );
163            }
164    
165    
166            public boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws Exception
167            {
168                return nexus.hasEntry( opContext );
169            }
170    
171    
172            public void rename( NextInterceptor next, RenameOperationContext opContext )
173                throws Exception
174            {
175                nexus.rename( opContext );
176            }
177    
178    
179            public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception
180            {
181                nexus.move( opContext );
182            }
183    
184    
185            public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
186                throws Exception
187            {
188                nexus.moveAndRename( opContext );
189            }
190    
191    
192            public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext )
193                throws Exception
194            {
195                nexus.addContextPartition( opContext );
196            }
197    
198    
199            public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws Exception
200            {
201                nexus.removeContextPartition( opContext );
202            }
203    
204    
205            public void bind( NextInterceptor next, BindOperationContext opContext )  throws Exception
206            {
207                nexus.bind( opContext );
208            }
209    
210    
211            public void unbind( NextInterceptor next, UnbindOperationContext opContext ) throws Exception
212            {
213                nexus.unbind( opContext );
214            }
215        };
216    
217        private final Map<String, Entry> name2entry = new HashMap<String, Entry>();
218    
219        private final Entry tail;
220    
221        private Entry head;
222    
223        private DirectoryService directoryService;
224    
225    
226        /**
227         * Create a new interceptor chain.
228         */
229        public InterceptorChain()
230        {
231            tail = new Entry( "tail", null, null, FINAL_INTERCEPTOR );
232            head = tail;
233        }
234    
235    
236        /**
237         * Initializes and registers all interceptors according to the specified
238         * {@link DirectoryService}.
239         * @throws javax.naming.Exception if an interceptor cannot be initialized.
240         * @param directoryService the directory core
241         */
242        public synchronized void init( DirectoryService directoryService ) throws Exception
243        {
244            // Initialize tail first.
245            this.directoryService = directoryService;
246            FINAL_INTERCEPTOR.init( directoryService );
247    
248            // And register and initialize all interceptors
249            try
250            {
251                for ( Interceptor interceptor: directoryService.getInterceptors() )
252                {
253                    if ( IS_DEBUG )
254                    {
255                        LOG.debug( "Adding interceptor " + interceptor.getName() );
256                    }
257    
258                    register( interceptor );
259                }
260            }
261            catch ( Throwable t )
262            {
263                // destroy if failed to initialize all interceptors.
264                destroy();
265    
266                if ( t instanceof Exception )
267                {
268                    throw ( Exception ) t;
269                }
270                else
271                {
272                    throw new InterceptorException( null, I18n.err( I18n.ERR_329 ), t );
273                }
274            }
275        }
276    
277    
278        /**
279         * Deinitializes and deregisters all interceptors this chain contains.
280         */
281        public synchronized void destroy()
282        {
283            List<Entry> entries = new ArrayList<Entry>();
284            Entry e = tail;
285    
286            do
287            {
288                entries.add( e );
289                e = e.prevEntry;
290            }
291            while ( e != null );
292    
293            for ( Entry entry:entries )
294            {
295                if ( entry != tail )
296                {
297                    try
298                    {
299                        deregister( entry.getName() );
300                    }
301                    catch ( Throwable t )
302                    {
303                        LOG.warn( "Failed to deregister an interceptor: " + entry.getName(), t );
304                    }
305                }
306            }
307        }
308    
309    
310        /**
311         * Returns the registered interceptor with the specified name.
312         * @param interceptorName name of the interceptor to look for
313         * @return <tt>null</tt> if the specified name doesn't exist.
314         */
315        public Interceptor get( String interceptorName )
316        {
317            Entry e = name2entry.get( interceptorName );
318            if ( e == null )
319            {
320                return null;
321            }
322    
323            return e.interceptor;
324        }
325    
326    
327        /**
328         * Returns the list of all registered interceptors.
329         * @return a list of all the registered interceptors.
330         */
331        public synchronized List<Interceptor> getAll()
332        {
333            List<Interceptor> result = new ArrayList<Interceptor>();
334            Entry e = head;
335    
336            do
337            {
338                result.add( e.interceptor );
339                e = e.nextEntry;
340            }
341            while ( e != tail );
342    
343            return result;
344        }
345    
346    
347        public synchronized void addFirst( Interceptor interceptor ) throws Exception
348        {
349            register0( interceptor, head );
350        }
351    
352    
353        public synchronized void addLast( Interceptor interceptor ) throws Exception
354        {
355            register0( interceptor, tail );
356        }
357    
358    
359        public synchronized void addBefore( String nextInterceptorName, Interceptor interceptor )
360            throws Exception
361        {
362            Entry e = name2entry.get( nextInterceptorName );
363            if ( e == null )
364            {
365                throw new ConfigurationException( I18n.err( I18n.ERR_330, nextInterceptorName ) );
366            }
367            register0( interceptor, e );
368        }
369    
370    
371        public synchronized String remove( String interceptorName ) throws Exception
372        {
373            return deregister( interceptorName );
374        }
375    
376    
377        public synchronized void addAfter( String prevInterceptorName, Interceptor interceptor )
378            throws Exception
379        {
380            Entry e = name2entry.get( prevInterceptorName );
381            if ( e == null )
382            {
383                throw new ConfigurationException( I18n.err( I18n.ERR_330, prevInterceptorName ) );
384            }
385            register0( interceptor, e.nextEntry );
386        }
387    
388    
389        /**
390         * Adds and initializes an interceptor with the specified configuration.
391         * @param interceptor interceptor to add to end of chain
392         * @throws javax.naming.Exception if there is already an interceptor of this name or the interceptor
393         * cannot be initialized.
394         */
395        private void register( Interceptor interceptor ) throws Exception
396        {
397            checkAddable( interceptor );
398            register0( interceptor, tail );
399        }
400    
401    
402        /**
403         * Removes and deinitializes the interceptor with the specified name.
404         * @param name name of interceptor to remove
405         * @return name of interceptor removed, if any
406         * @throws javax.naming.ConfigurationException if no interceptor registered under that name
407         */
408        private String deregister( String name ) throws ConfigurationException
409        {
410            Entry entry = checkOldName( name );
411            Entry prevEntry = entry.prevEntry;
412            Entry nextEntry = entry.nextEntry;
413    
414            if ( nextEntry == null )
415            {
416                // Don't deregister tail
417                return null;
418            }
419    
420            if ( prevEntry == null )
421            {
422                nextEntry.prevEntry = null;
423                head = nextEntry;
424            }
425            else
426            {
427                prevEntry.nextEntry = nextEntry;
428                nextEntry.prevEntry = prevEntry;
429            }
430    
431            name2entry.remove( name );
432            entry.interceptor.destroy();
433    
434            return entry.getName();
435        }
436    
437        
438        private void register0( Interceptor interceptor, Entry nextEntry ) throws Exception
439        {
440            String name = interceptor.getName();
441    
442            interceptor.init( directoryService );
443            Entry newEntry;
444            if ( nextEntry == head )
445            {
446                newEntry = new Entry( interceptor.getName(), null, head, interceptor );
447                head.prevEntry = newEntry;
448                head = newEntry;
449            }
450            else if ( head == tail )
451            {
452                newEntry = new Entry( interceptor.getName(), null, tail, interceptor );
453                tail.prevEntry = newEntry;
454                head = newEntry;
455            }
456            else
457            {
458                newEntry = new Entry( interceptor.getName(), nextEntry.prevEntry, nextEntry, interceptor );
459                nextEntry.prevEntry.nextEntry = newEntry;
460                nextEntry.prevEntry = newEntry;
461            }
462    
463            name2entry.put( name, newEntry );
464        }
465    
466    
467        /**
468         * Throws an exception when the specified interceptor name is not registered in this chain.
469         *
470         * @param name name of interceptor to look for
471         * @return An interceptor entry with the specified name.
472         * @throws javax.naming.ConfigurationException if no interceptor has that name
473         */
474        private Entry checkOldName( String name ) throws ConfigurationException
475        {
476            Entry e = name2entry.get( name );
477    
478            if ( e == null )
479            {
480                throw new ConfigurationException( I18n.err( I18n.ERR_331, name ) );
481            }
482    
483            return e;
484        }
485    
486    
487        /**
488         * Checks the specified interceptor name is already taken and throws an exception if already taken.
489         * @param interceptor interceptor to check
490         * @throws javax.naming.ConfigurationException if interceptor name is already registered
491         */
492        private void checkAddable( Interceptor interceptor ) throws ConfigurationException
493        {
494            if ( name2entry.containsKey( interceptor.getName() ) )
495            {
496                throw new ConfigurationException( I18n.err( I18n.ERR_332, interceptor.getName() ) );
497            }
498        }
499    
500    
501        /**
502         * Gets the InterceptorEntry to use first with bypass information considered.
503         *
504         * @return the first entry to use.
505         */
506        private Entry getStartingEntry()
507        {
508            if ( InvocationStack.getInstance().isEmpty() )
509            {
510                return head;
511            }
512    
513            OperationContext opContext = InvocationStack.getInstance().peek();
514            
515            if ( !opContext.hasBypass() )
516            {
517                return head;
518            }
519    
520            if ( opContext.isBypassed( ByPassConstants.BYPASS_ALL ) )
521            {
522                return tail;
523            }
524    
525            Entry next = head;
526            
527            while ( next != tail )
528            {
529                if ( opContext.isBypassed( next.getName() ) )
530                {
531                    next = next.nextEntry;
532                }
533                else
534                {
535                    return next;
536                }
537            }
538    
539            return tail;
540        }
541    
542    
543        public ClonedServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws Exception
544        {
545            Entry entry = getStartingEntry();
546            Interceptor head = entry.interceptor;
547            NextInterceptor next = entry.nextInterceptor;
548    
549            try
550            {
551                return head.getRootDSE( next, opContext );
552            }
553            catch ( Exception ne )
554            {
555                throw ne;
556            }
557            catch ( Throwable e )
558            {
559                throwInterceptorException( head, e );
560                throw new InternalError(); // Should be unreachable
561            }
562        }
563    
564    
565        public DN getMatchedName( GetMatchedNameOperationContext opContext ) throws Exception
566        {
567            Entry entry = getStartingEntry();
568            Interceptor head = entry.interceptor;
569            NextInterceptor next = entry.nextInterceptor;
570    
571            try
572            {
573                return head.getMatchedName( next, opContext );
574            }
575            catch ( Exception ne )
576            {
577                throw ne;
578            }
579            catch ( Throwable e )
580            {
581                throwInterceptorException( head, e );
582                throw new InternalError(); // Should be unreachable
583            }
584        }
585    
586    
587        public DN getSuffix( GetSuffixOperationContext opContext ) throws Exception
588        {
589            Entry entry = getStartingEntry();
590            Interceptor head = entry.interceptor;
591            NextInterceptor next = entry.nextInterceptor;
592    
593            try
594            {
595                return head.getSuffix( next, opContext );
596            }
597            catch ( Exception ne )
598            {
599                throw ne;
600            }
601            catch ( Throwable e )
602            {
603                throwInterceptorException( head, e );
604                throw new InternalError(); // Should be unreachable
605            }
606        }
607    
608    
609        public boolean compare( CompareOperationContext opContext ) throws Exception
610        {
611            Entry entry = getStartingEntry();
612            Interceptor head = entry.interceptor;
613            NextInterceptor next = entry.nextInterceptor;
614    
615            try
616            {
617                return head.compare( next, opContext );
618            }
619            catch ( Exception ne )
620            {
621                throw ne;
622            }
623            catch ( Throwable e )
624            {
625                throwInterceptorException( head, e );
626                throw new InternalError(); // Should be unreachable
627            }
628        }
629    
630    
631        public Set<String> listSuffixes( ListSuffixOperationContext opContext ) throws Exception
632        {
633            Entry entry = getStartingEntry();
634            Interceptor head = entry.interceptor;
635            NextInterceptor next = entry.nextInterceptor;
636    
637            try
638            {
639                return head.listSuffixes( next, opContext );
640            }
641            catch ( Exception ne )
642            {
643                throw ne;
644            }
645            catch ( Throwable e )
646            {
647                throwInterceptorException( head, e );
648                throw new InternalError(); // Should be unreachable
649            }
650        }
651    
652    
653        public void addContextPartition( AddContextPartitionOperationContext opContext ) throws Exception
654        {
655            Entry entry = getStartingEntry();
656            Interceptor head = entry.interceptor;
657            NextInterceptor next = entry.nextInterceptor;
658    
659            try
660            {
661                head.addContextPartition( next, opContext );
662            }
663            catch ( Exception ne )
664            {
665                throw ne;
666            }
667            catch ( Throwable e )
668            {
669                throwInterceptorException( head, e );
670                throw new InternalError(); // Should be unreachable
671            }
672        }
673    
674    
675        public void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws Exception
676        {
677            Entry entry = getStartingEntry();
678            Interceptor head = entry.interceptor;
679            NextInterceptor next = entry.nextInterceptor;
680    
681            try
682            {
683                head.removeContextPartition( next, opContext );
684            }
685            catch ( Exception ne )
686            {
687                throw ne;
688            }
689            catch ( Throwable e )
690            {
691                throwInterceptorException( head, e );
692                throw new InternalError(); // Should be unreachable
693            }
694        }
695    
696        
697        /**
698         * Eagerly populates fields of operation contexts so multiple Interceptors 
699         * in the processing pathway can reuse this value without performing a 
700         * redundant lookup operation.
701         *
702         * @param opContext the operation context to populate with cached fields
703         */
704        private void eagerlyPopulateFields( OperationContext opContext )
705        {
706            // If the entry field is not set for ops other than add for example 
707            // then we set the entry but don't freak if we fail to do so since it
708            // may not exist in the first place
709            
710            if ( opContext.getEntry() == null )
711            {
712                try
713                {
714                    // We have to use the admin session here, otherwise we may have
715                    // trouble reading the entry due to insufficient access rights 
716                    CoreSession adminSession = opContext.getSession().getDirectoryService().getAdminSession();
717                    opContext.setEntry( adminSession.lookup( opContext.getDn(), SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES_ARRAY ) );
718                }
719                catch ( Exception e )
720                {
721                    // might not exist
722                }
723            }
724        }
725        
726    
727        public void delete( DeleteOperationContext opContext ) throws Exception
728        {
729            Entry entry = getStartingEntry();
730            Interceptor head = entry.interceptor;
731            NextInterceptor next = entry.nextInterceptor;
732            eagerlyPopulateFields( opContext );
733            
734            try
735            {
736                head.delete( next, opContext );
737            }
738            catch ( Exception ne )
739            {
740                throw ne;
741            }
742            catch ( Throwable e )
743            {
744                throwInterceptorException( head, e );
745            }
746        }
747    
748    
749        public void add( AddOperationContext opContext ) throws Exception
750        {
751            Entry node = getStartingEntry();
752            Interceptor head = node.interceptor;
753            NextInterceptor next = node.nextInterceptor;
754    
755            try
756            {
757                head.add( next, opContext );
758            }
759            catch ( Exception ne )
760            {
761                throw ne;
762            }
763            catch ( Throwable e )
764            {
765                throwInterceptorException( head, e );
766            }
767        }
768    
769    
770        public void bind( BindOperationContext opContext ) throws Exception
771        {
772            Entry node = getStartingEntry();
773            Interceptor head = node.interceptor;
774            NextInterceptor next = node.nextInterceptor;
775            eagerlyPopulateFields( opContext );
776    
777            try
778            {
779                head.bind( next, opContext );
780            }
781            catch ( Exception ne )
782            {
783                throw ne;
784            }
785            catch ( Throwable e )
786            {
787                throwInterceptorException( head, e );
788            }
789        }
790    
791    
792        public void unbind( UnbindOperationContext opContext ) throws Exception
793        {
794            Entry node = getStartingEntry();
795            Interceptor head = node.interceptor;
796            NextInterceptor next = node.nextInterceptor;
797    
798            try
799            {
800                head.unbind( next, opContext );
801            }
802            catch ( Exception ne )
803            {
804                throw ne;
805            }
806            catch ( Throwable e )
807            {
808                throwInterceptorException( head, e );
809            }
810        }
811    
812    
813        public void modify( ModifyOperationContext opContext ) throws Exception
814        {
815            Entry entry = getStartingEntry();
816            Interceptor head = entry.interceptor;
817            NextInterceptor next = entry.nextInterceptor;
818            eagerlyPopulateFields( opContext );
819    
820            try
821            {
822                head.modify( next, opContext );
823            }
824            catch ( Exception ne )
825            {
826                throw ne;
827            }
828            catch ( Throwable e )
829            {
830                throwInterceptorException( head, e );
831            }
832        }
833    
834    
835        public EntryFilteringCursor list( ListOperationContext opContext ) throws Exception
836        {
837            Entry entry = getStartingEntry();
838            Interceptor head = entry.interceptor;
839            NextInterceptor next = entry.nextInterceptor;
840            eagerlyPopulateFields( opContext );
841    
842            try
843            {
844                return head.list( next, opContext );
845            }
846            catch ( Exception ne )
847            {
848                throw ne;
849            }
850            catch ( Throwable e )
851            {
852                throwInterceptorException( head, e );
853                throw new InternalError(); // Should be unreachable
854            }
855        }
856    
857    
858        public EntryFilteringCursor search( SearchOperationContext opContext )
859            throws Exception
860        {
861            Entry entry = getStartingEntry();
862            Interceptor head = entry.interceptor;
863            NextInterceptor next = entry.nextInterceptor;
864            eagerlyPopulateFields( opContext );
865    
866            try
867            {
868                return head.search( next, opContext );
869            }
870            catch ( Exception ne )
871            {
872                throw ne;
873            }
874            catch ( Throwable e )
875            {
876                throwInterceptorException( head, e );
877                throw new InternalError(); // Should be unreachable
878            }
879        }
880    
881    
882        public ClonedServerEntry lookup( LookupOperationContext opContext ) throws Exception
883        {
884            Entry entry = getStartingEntry();
885            Interceptor head = entry.interceptor;
886            NextInterceptor next = entry.nextInterceptor;
887    
888            try
889            {
890                return head.lookup( next, opContext );
891            }
892            catch ( Exception ne )
893            {
894                throw ne;
895            }
896            catch ( Throwable e )
897            {
898                throwInterceptorException( head, e );
899                throw new InternalError(); // Should be unreachable
900            }
901        }
902    
903    
904        public boolean hasEntry( EntryOperationContext opContext ) throws Exception
905        {
906            Entry entry = getStartingEntry();
907            Interceptor head = entry.interceptor;
908            NextInterceptor next = entry.nextInterceptor;
909    
910            try
911            {
912                return head.hasEntry( next, opContext );
913            }
914            catch ( Exception ne )
915            {
916                throw ne;
917            }
918            catch ( Throwable e )
919            {
920                throwInterceptorException( head, e );
921                throw new InternalError(); // Should be unreachable
922            }
923        }
924    
925    
926        public void rename( RenameOperationContext opContext ) throws Exception
927        {
928            Entry entry = getStartingEntry();
929            Interceptor head = entry.interceptor;
930            NextInterceptor next = entry.nextInterceptor;
931            eagerlyPopulateFields( opContext );
932    
933            try
934            {
935                head.rename( next, opContext );
936            }
937            catch ( Exception ne )
938            {
939                throw ne;
940            }
941            catch ( Throwable e )
942            {
943                throwInterceptorException( head, e );
944            }
945        }
946    
947    
948        public void move( MoveOperationContext opContext ) throws Exception
949        {
950            Entry entry = getStartingEntry();
951            Interceptor head = entry.interceptor;
952            NextInterceptor next = entry.nextInterceptor;
953            eagerlyPopulateFields( opContext );
954    
955            try
956            {
957                head.move( next, opContext );
958            }
959            catch ( Exception ne )
960            {
961                throw ne;
962            }
963            catch ( Throwable e )
964            {
965                throwInterceptorException( head, e );
966            }
967        }
968    
969    
970        public void moveAndRename( MoveAndRenameOperationContext opContext ) throws Exception
971        {
972            Entry entry = getStartingEntry();
973            Interceptor head = entry.interceptor;
974            NextInterceptor next = entry.nextInterceptor;
975            eagerlyPopulateFields( opContext );
976    
977            try
978            {
979                head.moveAndRename( next, opContext );
980            }
981            catch ( Exception ne )
982            {
983                throw ne;
984            }
985            catch ( Throwable e )
986            {
987                throwInterceptorException( head, e );
988            }
989        }
990    
991        /**
992         * Represents an internal entry of this chain.
993         */
994        private class Entry
995        {
996            private volatile Entry prevEntry;
997    
998            private volatile Entry nextEntry;
999    
1000            private final String name;
1001    
1002            private final Interceptor interceptor;
1003    
1004            private final NextInterceptor nextInterceptor;
1005    
1006    
1007            private String getName()
1008            {
1009                return name;
1010            }
1011    
1012    
1013            private Entry( String name, Entry prevEntry, Entry nextEntry, Interceptor interceptor )
1014            {
1015                this.name = name;
1016    
1017                if ( interceptor == null )
1018                {
1019                    throw new NullPointerException( "interceptor" );
1020                }
1021    
1022                this.prevEntry = prevEntry;
1023                this.nextEntry = nextEntry;
1024                this.interceptor = interceptor;
1025                this.nextInterceptor = new NextInterceptor()
1026                {
1027                    private Entry getNextEntry()
1028                    {
1029                        if ( InvocationStack.getInstance().isEmpty() )
1030                        {
1031                            return Entry.this.nextEntry;
1032                        }
1033    
1034                        OperationContext opContext = InvocationStack.getInstance().peek();
1035                        if ( !opContext.hasBypass() )
1036                        {
1037                            return Entry.this.nextEntry;
1038                        }
1039    
1040                        //  I don't think we really need this since this check is performed by the chain when
1041                        //  getting the interceptor head to use.
1042                        //
1043                        //                    if ( invocation.isBypassed( DirectoryPartitionNexusProxy.BYPASS_ALL ) )
1044                        //                    {
1045                        //                        return tail;
1046                        //                    }
1047    
1048                        Entry next = Entry.this.nextEntry;
1049                        while ( next != tail )
1050                        {
1051                            if ( opContext.isBypassed( next.getName() ) )
1052                            {
1053                                next = next.nextEntry;
1054                            }
1055                            else
1056                            {
1057                                return next;
1058                            }
1059                        }
1060    
1061                        return next;
1062                    }
1063    
1064    
1065                    public boolean compare( CompareOperationContext opContext ) throws Exception
1066                    {
1067                        Entry next = getNextEntry();
1068                        Interceptor interceptor = next.interceptor;
1069    
1070                        try
1071                        {
1072                            return interceptor.compare( next.nextInterceptor, opContext );
1073                        }
1074                        catch ( Exception ne )
1075                        {
1076                            throw ne;
1077                        }
1078                        catch ( Throwable e )
1079                        {
1080                            throwInterceptorException( interceptor, e );
1081                            throw new InternalError(); // Should be unreachable
1082                        }
1083                    }
1084    
1085    
1086                    public ClonedServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws Exception
1087                    {
1088                        Entry next = getNextEntry();
1089                        Interceptor interceptor = next.interceptor;
1090    
1091                        try
1092                        {
1093                            return interceptor.getRootDSE( next.nextInterceptor, opContext );
1094                        }
1095                        catch ( Exception ne )
1096                        {
1097                            throw ne;
1098                        }
1099                        catch ( Throwable e )
1100                        {
1101                            throwInterceptorException( interceptor, e );
1102                            throw new InternalError(); // Should be unreachable
1103                        }
1104                    }
1105    
1106    
1107                    public DN getMatchedName( GetMatchedNameOperationContext opContext ) throws Exception
1108                    {
1109                        Entry next = getNextEntry();
1110                        Interceptor interceptor = next.interceptor;
1111    
1112                        try
1113                        {
1114                            return interceptor.getMatchedName( next.nextInterceptor, opContext );
1115                        }
1116                        catch ( Exception ne )
1117                        {
1118                            throw ne;
1119                        }
1120                        catch ( Throwable e )
1121                        {
1122                            throwInterceptorException( interceptor, e );
1123                            throw new InternalError(); // Should be unreachable
1124                        }
1125                    }
1126    
1127    
1128                    public DN getSuffix( GetSuffixOperationContext opContext ) throws Exception
1129                    {
1130                        Entry next = getNextEntry();
1131                        Interceptor interceptor = next.interceptor;
1132    
1133                        try
1134                        {
1135                            return interceptor.getSuffix( next.nextInterceptor, opContext );
1136                        }
1137                        catch ( Exception ne )
1138                        {
1139                            throw ne;
1140                        }
1141                        catch ( Throwable e )
1142                        {
1143                            throwInterceptorException( interceptor, e );
1144                            throw new InternalError(); // Should be unreachable
1145                        }
1146                    }
1147    
1148    
1149                    public Set<String> listSuffixes( ListSuffixOperationContext opContext ) throws Exception
1150                    {
1151                        Entry next = getNextEntry();
1152                        Interceptor interceptor = next.interceptor;
1153    
1154                        try
1155                        {
1156                            return interceptor.listSuffixes( next.nextInterceptor, opContext );
1157                        }
1158                        catch ( Exception ne )
1159                        {
1160                            throw ne;
1161                        }
1162                        catch ( Throwable e )
1163                        {
1164                            throwInterceptorException( interceptor, e );
1165                            throw new InternalError(); // Should be unreachable
1166                        }
1167                    }
1168    
1169    
1170                    public void delete( DeleteOperationContext opContext ) throws Exception
1171                    {
1172                        Entry next = getNextEntry();
1173                        Interceptor interceptor = next.interceptor;
1174    
1175                        try
1176                        {
1177                            interceptor.delete( next.nextInterceptor, opContext );
1178                        }
1179                        catch ( Exception ne )
1180                        {
1181                            throw ne;
1182                        }
1183                        catch ( Throwable e )
1184                        {
1185                            throwInterceptorException( interceptor, e );
1186                        }
1187                    }
1188    
1189    
1190                    public void add( AddOperationContext opContext ) throws Exception
1191                    {
1192                        Entry next = getNextEntry();
1193                        Interceptor interceptor = next.interceptor;
1194    
1195                        try
1196                        {
1197                            interceptor.add( next.nextInterceptor, opContext );
1198                        }
1199                        catch ( Exception ne )
1200                        {
1201                            throw ne;
1202                        }
1203                        catch ( Throwable e )
1204                        {
1205                            throwInterceptorException( interceptor, e );
1206                        }
1207                    }
1208    
1209    
1210                    public void modify( ModifyOperationContext opContext ) throws Exception
1211                    {
1212                        Entry next = getNextEntry();
1213                        Interceptor interceptor = next.interceptor;
1214    
1215                        try
1216                        {
1217                            interceptor.modify( next.nextInterceptor, opContext );
1218                        }
1219                        catch ( Exception ne )
1220                        {
1221                            throw ne;
1222                        }
1223                        catch ( Throwable e )
1224                        {
1225                            throwInterceptorException( interceptor, e );
1226                        }
1227                    }
1228    
1229                    
1230                    public EntryFilteringCursor list( ListOperationContext opContext ) throws Exception
1231                    {
1232                        Entry next = getNextEntry();
1233                        Interceptor interceptor = next.interceptor;
1234    
1235                        try
1236                        {
1237                            return interceptor.list( next.nextInterceptor, opContext );
1238                        }
1239                        catch ( Exception ne )
1240                        {
1241                            throw ne;
1242                        }
1243                        catch ( Throwable e )
1244                        {
1245                            throwInterceptorException( interceptor, e );
1246                            throw new InternalError(); // Should be unreachable
1247                        }
1248                    }
1249    
1250    
1251                    public EntryFilteringCursor search( SearchOperationContext opContext )
1252                        throws Exception
1253                    {
1254                        Entry next = getNextEntry();
1255                        Interceptor interceptor = next.interceptor;
1256    
1257                        try
1258                        {
1259                            return interceptor.search( next.nextInterceptor, opContext );
1260                        }
1261                        catch ( Exception ne )
1262                        {
1263                            throw ne;
1264                        }
1265                        catch ( Throwable e )
1266                        {
1267                            throwInterceptorException( interceptor, e );
1268                            throw new InternalError(); // Should be unreachable
1269                        }
1270                    }
1271    
1272    
1273                    public ClonedServerEntry lookup( LookupOperationContext opContext ) throws Exception
1274                    {
1275                        Entry next = getNextEntry();
1276                        Interceptor interceptor = next.interceptor;
1277    
1278                        try
1279                        {
1280                            return interceptor.lookup( next.nextInterceptor, opContext );
1281                        }
1282                        catch ( Exception ne )
1283                        {
1284                            throw ne;
1285                        }
1286                        catch ( Throwable e )
1287                        {
1288                            throwInterceptorException( interceptor, e );
1289                            throw new InternalError(); // Should be unreachable
1290                        }
1291                    }
1292    
1293    
1294                    public boolean hasEntry( EntryOperationContext opContext ) throws Exception
1295                    {
1296                        Entry next = getNextEntry();
1297                        Interceptor interceptor = next.interceptor;
1298    
1299                        try
1300                        {
1301                            return interceptor.hasEntry( next.nextInterceptor, opContext );
1302                        }
1303                        catch ( Exception ne )
1304                        {
1305                            throw ne;
1306                        }
1307                        catch ( Throwable e )
1308                        {
1309                            throwInterceptorException( interceptor, e );
1310                            throw new InternalError(); // Should be unreachable
1311                        }
1312                    }
1313    
1314    
1315                    public void rename( RenameOperationContext opContext ) throws Exception
1316                    {
1317                        Entry next = getNextEntry();
1318                        Interceptor interceptor = next.interceptor;
1319    
1320                        try
1321                        {
1322                            interceptor.rename( next.nextInterceptor, opContext );
1323                        }
1324                        catch ( Exception ne )
1325                        {
1326                            throw ne;
1327                        }
1328                        catch ( Throwable e )
1329                        {
1330                            throwInterceptorException( interceptor, e );
1331                        }
1332                    }
1333    
1334    
1335                    public void move( MoveOperationContext opContext ) throws Exception
1336                    {
1337                        Entry next = getNextEntry();
1338                        Interceptor interceptor = next.interceptor;
1339    
1340                        try
1341                        {
1342                            interceptor.move( next.nextInterceptor, opContext );
1343                        }
1344                        catch ( Exception ne )
1345                        {
1346                            throw ne;
1347                        }
1348                        catch ( Throwable e )
1349                        {
1350                            throwInterceptorException( interceptor, e );
1351                        }
1352                    }
1353    
1354    
1355                    public void moveAndRename( MoveAndRenameOperationContext opContext )
1356                        throws Exception
1357                    {
1358                        Entry next = getNextEntry();
1359                        Interceptor interceptor = next.interceptor;
1360    
1361                        try
1362                        {
1363                            interceptor.moveAndRename( next.nextInterceptor, opContext );
1364                        }
1365                        catch ( Exception ne )
1366                        {
1367                            throw ne;
1368                        }
1369                        catch ( Throwable e )
1370                        {
1371                            throwInterceptorException( interceptor, e );
1372                        }
1373                    }
1374    
1375    
1376                    public void bind( BindOperationContext opContext ) throws Exception
1377                    {
1378                        Entry next = getNextEntry();
1379                        Interceptor interceptor = next.interceptor;
1380        
1381                        try
1382                        {
1383                            interceptor.bind( next.nextInterceptor, opContext );
1384                        }
1385                        catch ( Exception ne )
1386                        {
1387                            throw ne;
1388                        }
1389                        catch ( Throwable e )
1390                        {
1391                            throwInterceptorException( interceptor, e );
1392                        }
1393                    }
1394    
1395    
1396                    public void unbind( UnbindOperationContext opContext ) throws Exception
1397                    {
1398                        Entry next = getNextEntry();
1399                        Interceptor interceptor = next.interceptor;
1400    
1401                        try
1402                        {
1403                            interceptor.unbind( next.nextInterceptor, opContext );
1404                        }
1405                        catch ( Exception ne )
1406                        {
1407                            throw ne;
1408                        }
1409                        catch ( Throwable e )
1410                        {
1411                            throwInterceptorException( interceptor, e );
1412                        }
1413                    }
1414    
1415    
1416                    public void addContextPartition( AddContextPartitionOperationContext opContext ) throws Exception
1417                    {
1418                        Entry next = getNextEntry();
1419                        Interceptor interceptor = next.interceptor;
1420    
1421                        try
1422                        {
1423                            interceptor.addContextPartition( next.nextInterceptor, opContext );
1424                        }
1425                        catch ( Exception ne )
1426                        {
1427                            throw ne;
1428                        }
1429                        catch ( Throwable e )
1430                        {
1431                            throwInterceptorException( interceptor, e );
1432                            throw new InternalError(); // Should be unreachable
1433                        }
1434                    }
1435    
1436    
1437                    public void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws Exception
1438                    {
1439                        Entry next = getNextEntry();
1440                        Interceptor interceptor = next.interceptor;
1441    
1442                        try
1443                        {
1444                            interceptor.removeContextPartition( next.nextInterceptor, opContext );
1445                        }
1446                        catch ( Exception ne )
1447                        {
1448                            throw ne;
1449                        }
1450                        catch ( Throwable e )
1451                        {
1452                            throwInterceptorException( interceptor, e );
1453                            throw new InternalError(); // Should be unreachable
1454                        }
1455                    }
1456                };
1457            }
1458        }
1459    
1460    
1461        private static void throwInterceptorException( Interceptor interceptor, Throwable e ) throws InterceptorException
1462        {
1463            throw new InterceptorException( interceptor, I18n.err( I18n.ERR_333 ), e );
1464        }
1465    }