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 */
020package org.apache.directory.server.core;
021
022
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.concurrent.locks.ReadWriteLock;
027import java.util.concurrent.locks.ReentrantReadWriteLock;
028
029import org.apache.directory.api.ldap.model.constants.Loggers;
030import org.apache.directory.api.ldap.model.constants.SchemaConstants;
031import org.apache.directory.api.ldap.model.entry.Attribute;
032import org.apache.directory.api.ldap.model.entry.Entry;
033import org.apache.directory.api.ldap.model.entry.Value;
034import org.apache.directory.api.ldap.model.exception.LdapAffectMultipleDsaException;
035import org.apache.directory.api.ldap.model.exception.LdapException;
036import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
037import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
038import org.apache.directory.api.ldap.model.exception.LdapOtherException;
039import org.apache.directory.api.ldap.model.exception.LdapPartialResultException;
040import org.apache.directory.api.ldap.model.exception.LdapReferralException;
041import org.apache.directory.api.ldap.model.exception.LdapServiceUnavailableException;
042import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
043import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
044import org.apache.directory.api.ldap.model.message.SearchScope;
045import org.apache.directory.api.ldap.model.name.Dn;
046import org.apache.directory.api.ldap.model.name.Rdn;
047import org.apache.directory.api.ldap.model.url.LdapUrl;
048import org.apache.directory.server.core.api.CoreSession;
049import org.apache.directory.server.core.api.DirectoryService;
050import org.apache.directory.server.core.api.OperationManager;
051import org.apache.directory.server.core.api.ReferralManager;
052import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
053import org.apache.directory.server.core.api.interceptor.Interceptor;
054import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
055import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
056import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
057import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
058import org.apache.directory.server.core.api.interceptor.context.GetRootDseOperationContext;
059import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
060import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
061import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
062import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
063import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
064import org.apache.directory.server.core.api.interceptor.context.OperationContext;
065import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
066import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
067import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
068import org.apache.directory.server.core.api.partition.Partition;
069import org.apache.directory.server.core.api.partition.PartitionTxn;
070import org.apache.directory.server.i18n.I18n;
071import org.slf4j.Logger;
072import org.slf4j.LoggerFactory;
073
074
075/**
076 * The default implementation of an OperationManager.
077 *
078 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
079 */
080public class DefaultOperationManager implements OperationManager
081{
082    /** A logger specifically for operations */
083    private static final Logger OPERATION_LOG = LoggerFactory.getLogger( Loggers.OPERATION_LOG.getName() );
084
085    /** A logger specifically for operations time */
086    private static final Logger OPERATION_TIME = LoggerFactory.getLogger( Loggers.OPERATION_TIME.getName() );
087
088    /** A logger specifically for operations statistics */
089    private static final Logger OPERATION_STAT = LoggerFactory.getLogger( Loggers.OPERATION_STAT.getName() );
090
091    /** Speedup for logs */
092    private static final boolean IS_DEBUG = OPERATION_LOG.isDebugEnabled();
093    private static final boolean IS_TIME = OPERATION_TIME.isDebugEnabled();
094    private static final boolean IS_STAT = OPERATION_STAT.isDebugEnabled();
095
096    /** The directory service instance */
097    private final DirectoryService directoryService;
098
099    /** A lock used to protect against concurrent operations */
100    private ReadWriteLock rwLock = new ReentrantReadWriteLock( true );
101
102    public DefaultOperationManager( DirectoryService directoryService )
103    {
104        this.directoryService = directoryService;
105    }
106
107
108    /**
109     * {@inheritDoc}
110     */
111    public ReadWriteLock getRWLock()
112    {
113        return rwLock;
114    }
115
116
117    /**
118     * Acquires a ReadLock
119     */
120    public void lockRead()
121    {
122        rwLock.readLock().lock();
123    }
124
125
126    /**
127     * Acquires a WriteLock
128     */
129    public void lockWrite()
130    {
131        rwLock.writeLock().lock();
132    }
133
134
135    /**
136     * Releases a WriteLock
137     */
138    public void unlockWrite()
139    {
140        rwLock.writeLock().unlock();
141    }
142
143
144    /**
145     * Releases a ReadLock
146     */
147    public void unlockRead()
148    {
149        rwLock.readLock().unlock();
150    }
151
152
153    /**
154     * Eagerly populates fields of operation contexts so multiple Interceptors
155     * in the processing pathway can reuse this value without performing a
156     * redundant lookup operation.
157     *
158     * @param opContext the operation context to populate with cached fields
159     */
160    private void eagerlyPopulateFields( OperationContext opContext ) throws LdapException
161    {
162        // If the entry field is not set for ops other than add for example
163        // then we set the entry but don't freak if we fail to do so since it
164        // may not exist in the first place
165
166        if ( opContext.getEntry() == null )
167        {
168            // We have to use the admin session here, otherwise we may have
169            // trouble reading the entry due to insufficient access rights
170            CoreSession adminSession = opContext.getSession().getDirectoryService().getAdminSession();
171
172            LookupOperationContext lookupContext = new LookupOperationContext( adminSession, opContext.getDn(),
173                SchemaConstants.ALL_ATTRIBUTES_ARRAY );
174            lookupContext.setPartition( opContext.getPartition() );
175            lookupContext.setTransaction( opContext.getTransaction() );
176            Entry foundEntry = opContext.getSession().getDirectoryService().getPartitionNexus().lookup( lookupContext );
177
178            if ( foundEntry != null )
179            {
180                opContext.setEntry( foundEntry );
181            }
182            else
183            {
184                // This is an error : we *must* have an entry if we want to be able to rename.
185                throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_256_NO_SUCH_OBJECT, opContext.getDn() ) );
186            }
187        }
188    }
189
190
191    private Entry getOriginalEntry( OperationContext opContext ) throws LdapException
192    {
193        // We have to use the admin session here, otherwise we may have
194        // trouble reading the entry due to insufficient access rights
195        CoreSession adminSession = opContext.getSession().getDirectoryService().getAdminSession();
196
197        Entry foundEntry = adminSession.lookup( opContext.getDn(), SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES,
198            SchemaConstants.ALL_USER_ATTRIBUTES );
199
200        if ( foundEntry != null )
201        {
202            return foundEntry;
203        }
204        else
205        {
206            // This is an error : we *must* have an entry if we want to be able to rename.
207            throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_256_NO_SUCH_OBJECT,
208                opContext.getDn() ) );
209        }
210    }
211
212
213    private LdapReferralException buildReferralException( Entry parentEntry, Dn childDn ) throws LdapException
214    {
215        // Get the Ref attributeType
216        Attribute refs = parentEntry.get( SchemaConstants.REF_AT );
217
218        List<String> urls = new ArrayList<>();
219
220        try
221        {
222            // manage each Referral, building the correct URL for each of them
223            for ( Value url : refs )
224            {
225                // we have to replace the parent by the referral
226                LdapUrl ldapUrl = new LdapUrl( url.getString() );
227
228                // We have a problem with the Dn : we can't use the UpName,
229                // as we may have some spaces around the ',' and '+'.
230                // So we have to take the Rdn one by one, and create a
231                // new Dn with the type and value UP form
232
233                Dn urlDn = ldapUrl.getDn().add( childDn );
234
235                ldapUrl.setDn( urlDn );
236                urls.add( ldapUrl.toString() );
237            }
238        }
239        catch ( LdapURLEncodingException luee )
240        {
241            throw new LdapOperationErrorException( luee.getMessage(), luee );
242        }
243
244        // Return with an exception
245        LdapReferralException lre = new LdapReferralException( urls );
246        lre.setRemainingDn( childDn );
247        lre.setResolvedDn( parentEntry.getDn() );
248        lre.setResolvedObject( parentEntry );
249
250        return lre;
251    }
252
253
254    private LdapReferralException buildReferralExceptionForSearch( Entry parentEntry, Dn childDn, SearchScope scope )
255        throws LdapException
256    {
257        // Get the Ref attributeType
258        Attribute refs = parentEntry.get( SchemaConstants.REF_AT );
259
260        List<String> urls = new ArrayList<>();
261
262        // manage each Referral, building the correct URL for each of them
263        for ( Value url : refs )
264        {
265            // we have to replace the parent by the referral
266            try
267            {
268                LdapUrl ldapUrl = new LdapUrl( url.getString() );
269
270                StringBuilder urlString = new StringBuilder();
271
272                if ( ( ldapUrl.getDn() == null ) || ( ldapUrl.getDn() == Dn.ROOT_DSE ) )
273                {
274                    ldapUrl.setDn( parentEntry.getDn() );
275                }
276                else
277                {
278                    // We have a problem with the Dn : we can't use the UpName,
279                    // as we may have some spaces around the ',' and '+'.
280                    // So we have to take the Rdn one by one, and create a
281                    // new Dn with the type and value UP form
282
283                    Dn urlDn = ldapUrl.getDn().add( childDn );
284
285                    ldapUrl.setDn( urlDn );
286                }
287
288                urlString.append( ldapUrl.toString() ).append( "??" );
289
290                switch ( scope )
291                {
292                    case OBJECT:
293                        urlString.append( "base" );
294                        break;
295
296                    case SUBTREE:
297                        urlString.append( "sub" );
298                        break;
299
300                    case ONELEVEL:
301                        urlString.append( "one" );
302                        break;
303
304                    default:
305                        throw new IllegalArgumentException( "Unexpected scope " + scope );
306                }
307
308                urls.add( urlString.toString() );
309            }
310            catch ( LdapURLEncodingException luee )
311            {
312                // The URL is not correct, returns it as is
313                urls.add( url.getString() );
314            }
315        }
316
317        // Return with an exception
318        LdapReferralException lre = new LdapReferralException( urls );
319        lre.setRemainingDn( childDn );
320        lre.setResolvedDn( parentEntry.getDn() );
321        lre.setResolvedObject( parentEntry );
322
323        return lre;
324    }
325
326
327    private LdapPartialResultException buildLdapPartialResultException( Dn childDn )
328    {
329        LdapPartialResultException lpre = new LdapPartialResultException( I18n.err( I18n.ERR_315 ) );
330
331        lpre.setRemainingDn( childDn );
332        lpre.setResolvedDn( Dn.EMPTY_DN );
333
334        return lpre;
335    }
336
337
338    /**
339     * {@inheritDoc}
340     */
341    public void add( AddOperationContext addContext ) throws LdapException
342    {
343        if ( IS_DEBUG )
344        {
345            OPERATION_LOG.debug( ">> AddOperation : {}", addContext );
346        }
347
348        long addStart = 0L;
349
350        if ( IS_TIME )
351        {
352            addStart = System.nanoTime();
353        }
354
355        ensureStarted();
356
357        // Normalize the addContext Dn
358        Dn dn = addContext.getDn();
359        
360        if ( !dn.isSchemaAware() )
361        {
362            dn = new Dn( directoryService.getSchemaManager(), dn );
363            addContext.setDn( dn );
364        }
365        
366        // Find the working partition
367        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
368        addContext.setPartition( partition );
369        
370        // We have to deal with the referral first
371        directoryService.getReferralManager().lockRead();
372
373        try
374        {
375            if ( directoryService.getReferralManager().hasParentReferral( dn ) )
376            {
377                Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
378                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
379
380                // Depending on the Context.REFERRAL property value, we will throw
381                // a different exception.
382                if ( addContext.isReferralIgnored() )
383                {
384                    throw buildLdapPartialResultException( childDn );
385                }
386                else
387                {
388                    throw buildReferralException( parentEntry, childDn );
389                }
390            }
391        }
392        finally
393        {
394            // Unlock the referral manager
395            directoryService.getReferralManager().unlock();
396        }
397
398        // Call the Add method
399        Interceptor head = directoryService.getInterceptor( addContext.getNextInterceptor() );
400
401        lockWrite();
402
403        // Start a Write transaction right away
404        PartitionTxn transaction = addContext.getSession().getTransaction( partition ); 
405        
406        try
407        {
408            if ( transaction == null )
409            {
410                transaction = partition.beginWriteTransaction();
411                
412                if ( addContext.getSession().hasSessionTransaction() )
413                {
414                    addContext.getSession().addTransaction( partition, transaction );
415                }
416            }
417            
418            addContext.setTransaction( transaction );
419
420            head.add( addContext );
421            
422            if ( !addContext.getSession().hasSessionTransaction() )
423            {
424                transaction.commit();
425            }
426        }
427        catch ( LdapException le )
428        {
429            try
430            {
431                if ( transaction != null )
432                {
433                    transaction.abort();
434                }
435                
436                throw le;
437            }
438            catch ( IOException ioe )
439            {
440                throw new LdapOtherException( ioe.getMessage(), ioe );
441            }
442        }
443        catch ( IOException ioe )
444        {
445            try
446            {
447                transaction.abort();
448                
449                throw new LdapOtherException( ioe.getMessage(), ioe );
450            }
451            catch ( IOException ioe2 )
452            {
453                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
454            }
455        }
456        finally
457        {
458            unlockWrite();
459        }
460
461        if ( IS_DEBUG )
462        {
463            OPERATION_LOG.debug( "<< AddOperation successful" );
464        }
465
466        if ( IS_TIME )
467        {
468            OPERATION_TIME.debug( "Add operation took {} ns", ( System.nanoTime() - addStart ) );
469        }
470    }
471
472
473    /**
474     * {@inheritDoc}
475     */
476    public void bind( BindOperationContext bindContext ) throws LdapException
477    {
478        if ( IS_DEBUG )
479        {
480            OPERATION_LOG.debug( ">> BindOperation : {}", bindContext );
481        }
482
483        long opStart = 0L;
484
485        if ( IS_TIME )
486        {
487            opStart = System.nanoTime();
488        }
489
490        ensureStarted();
491
492        // Call the Delete method
493        Interceptor head = directoryService.getInterceptor( bindContext.getNextInterceptor() );
494
495        // Normalize the addContext Dn
496        Dn dn = bindContext.getDn();
497        
498        if ( ( dn != null ) && !dn.isSchemaAware() )
499        {
500            dn = new Dn( directoryService.getSchemaManager(), dn );
501            bindContext.setDn( dn );
502        }
503
504        lockRead();
505
506        try
507        {
508            Partition partition = directoryService.getPartitionNexus().getPartition( dn );
509            
510            try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
511            {
512                bindContext.setPartition( partition );
513                bindContext.setTransaction( partitionTxn );
514                
515                head.bind( bindContext );
516            }
517            catch ( IOException ioe )
518            {
519                throw new LdapOtherException( ioe.getMessage(), ioe );
520            }
521        }
522        finally
523        {
524            unlockRead();
525        }
526
527        if ( IS_DEBUG )
528        {
529            OPERATION_LOG.debug( "<< BindOperation successful" );
530        }
531
532        if ( IS_TIME )
533        {
534            OPERATION_TIME.debug( "Bind operation took {} ns", ( System.nanoTime() - opStart )  );
535        }
536    }
537
538
539    /**
540     * {@inheritDoc}
541     */
542    public boolean compare( CompareOperationContext compareContext ) throws LdapException
543    {
544        if ( IS_DEBUG )
545        {
546            OPERATION_LOG.debug( ">> CompareOperation : {}", compareContext );
547        }
548
549        long opStart = 0L;
550
551        if ( IS_TIME )
552        {
553            opStart = System.nanoTime();
554        }
555
556        ensureStarted();
557        
558        // Normalize the compareContext Dn
559        Dn dn = compareContext.getDn();
560
561        if ( !dn.isSchemaAware() )
562        {
563            dn = new Dn( directoryService.getSchemaManager(), dn );
564            compareContext.setDn( dn );
565        }
566
567        // We have to deal with the referral first
568        directoryService.getReferralManager().lockRead();
569
570        try
571        {
572            // Check if we have an ancestor for this Dn
573            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
574
575            if ( parentEntry != null )
576            {
577                // We have found a parent referral for the current Dn
578                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
579
580                if ( directoryService.getReferralManager().isReferral( dn ) )
581                {
582                    // This is a referral. We can delete it if the ManageDsaIt flag is true
583                    // Otherwise, we just throw a LdapReferralException
584                    if ( !compareContext.isReferralIgnored() )
585                    {
586                        // Throw a Referral Exception
587                        throw buildReferralException( parentEntry, childDn );
588                    }
589                }
590                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
591                {
592                    // Depending on the Context.REFERRAL property value, we will throw
593                    // a different exception.
594                    if ( compareContext.isReferralIgnored() )
595                    {
596                        throw buildLdapPartialResultException( childDn );
597                    }
598                    else
599                    {
600                        throw buildReferralException( parentEntry, childDn );
601                    }
602                }
603            }
604        }
605        finally
606        {
607            // Unlock the ReferralManager
608            directoryService.getReferralManager().unlock();
609        }
610
611        // populate the context with the old entry
612        compareContext.setOriginalEntry( getOriginalEntry( compareContext ) );
613
614        // Call the Compare method
615        Interceptor head = directoryService.getInterceptor( compareContext.getNextInterceptor() );
616
617        boolean result = false;
618
619        lockRead();
620
621        try
622        {
623            Partition partition = directoryService.getPartitionNexus().getPartition( dn );
624            
625            try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
626            {
627                compareContext.setPartition( partition );
628                compareContext.setTransaction( partitionTxn );
629                
630                result = head.compare( compareContext );
631            }
632            catch ( IOException ioe )
633            {
634                throw new LdapOtherException( ioe.getMessage(), ioe );
635            }
636        }
637        finally
638        {
639            unlockRead();
640        }
641
642        if ( IS_DEBUG )
643        {
644            OPERATION_LOG.debug( "<< CompareOperation successful" );
645        }
646
647        if ( IS_TIME )
648        {
649            OPERATION_TIME.debug( "Compare operation took {} ns", ( System.nanoTime() - opStart ) );
650        }
651
652        return result;
653    }
654
655
656    /**
657     * {@inheritDoc}
658     */
659    public void delete( DeleteOperationContext deleteContext ) throws LdapException
660    {
661        if ( IS_DEBUG )
662        {
663            OPERATION_LOG.debug( ">> DeleteOperation : {}", deleteContext );
664        }
665
666        long opStart = 0L;
667
668        if ( IS_TIME )
669        {
670            opStart = System.nanoTime();
671        }
672
673        ensureStarted();
674
675        // Normalize the deleteContext Dn
676        Dn dn = deleteContext.getDn();
677        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
678        deleteContext.setPartition( partition );
679
680        if ( !dn.isSchemaAware() )
681        {
682            dn = new Dn( directoryService.getSchemaManager(), dn );
683            deleteContext.setDn( dn );
684        }
685
686        // We have to deal with the referral first
687        directoryService.getReferralManager().lockRead();
688
689        try
690        {
691            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
692
693            if ( parentEntry != null )
694            {
695                // We have found a parent referral for the current Dn
696                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
697
698                if ( directoryService.getReferralManager().isReferral( dn ) )
699                {
700                    // This is a referral. We can delete it if the ManageDsaIt flag is true
701                    // Otherwise, we just throw a LdapReferralException
702                    if ( !deleteContext.isReferralIgnored() )
703                    {
704                        // Throw a Referral Exception
705                        throw buildReferralException( parentEntry, childDn );
706                    }
707                }
708                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
709                {
710                    // We can't delete an entry which has an ancestor referral
711
712                    // Depending on the Context.REFERRAL property value, we will throw
713                    // a different exception.
714                    if ( deleteContext.isReferralIgnored() )
715                    {
716                        throw buildLdapPartialResultException( childDn );
717                    }
718                    else
719                    {
720                        throw buildReferralException( parentEntry, childDn );
721                    }
722                }
723            }
724        }
725        finally
726        {
727            // Unlock the ReferralManager
728            directoryService.getReferralManager().unlock();
729        }
730
731        // populate the context with the old entry
732        lockWrite();
733
734        // Start a Write transaction right away
735        PartitionTxn transaction = deleteContext.getSession().getTransaction( partition ); 
736        
737        try
738        {
739            if ( transaction == null )
740            {
741                transaction = partition.beginWriteTransaction();
742                
743                if ( deleteContext.getSession().hasSessionTransaction() )
744                {
745                    deleteContext.getSession().addTransaction( partition, transaction );
746                }
747            }
748            
749            deleteContext.setTransaction( transaction );
750
751            eagerlyPopulateFields( deleteContext );
752
753            // Call the Delete method
754            Interceptor head = directoryService.getInterceptor( deleteContext.getNextInterceptor() );
755
756            head.delete( deleteContext );
757
758            if ( !deleteContext.getSession().hasSessionTransaction() )
759            {
760                transaction.commit();
761            }
762        }
763        catch ( LdapException le )
764        {
765            try
766            {
767                if ( transaction != null )
768                {
769                    transaction.abort();
770                }
771                
772                throw le;
773            }
774            catch ( IOException ioe )
775            {
776                throw new LdapOtherException( ioe.getMessage(), ioe );
777            }
778        }
779        catch ( IOException ioe )
780        {
781            try
782            {
783                transaction.abort();
784                
785                throw new LdapOtherException( ioe.getMessage(), ioe );
786            }
787            catch ( IOException ioe2 )
788            {
789                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
790            }
791        }
792        finally
793        {
794            unlockWrite();
795        }
796
797        if ( IS_DEBUG )
798        {
799            OPERATION_LOG.debug( "<< DeleteOperation successful" );
800        }
801
802        if ( IS_TIME )
803        {
804            OPERATION_TIME.debug( "Delete operation took {} ns", ( System.nanoTime() - opStart ) );
805        }
806    }
807
808
809    /**
810     * {@inheritDoc}
811     */
812    public Entry getRootDse( GetRootDseOperationContext getRootDseContext ) throws LdapException
813    {
814        if ( IS_DEBUG )
815        {
816            OPERATION_LOG.debug( ">> GetRootDseOperation : {}", getRootDseContext );
817        }
818
819        long opStart = 0L;
820
821        if ( IS_TIME )
822        {
823            opStart = System.nanoTime();
824        }
825
826        ensureStarted();
827
828        Interceptor head = directoryService.getInterceptor( getRootDseContext.getNextInterceptor() );
829        Entry root;
830
831        try
832        {
833            lockRead();
834            
835            Partition partition = directoryService.getPartitionNexus().getPartition( Dn.ROOT_DSE );
836            
837            try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
838            {
839                getRootDseContext.setPartition( partition );
840                getRootDseContext.setTransaction( partitionTxn );
841                
842                root = head.getRootDse( getRootDseContext );
843            }
844            catch ( IOException ioe )
845            {
846                throw new LdapOtherException( ioe.getMessage(), ioe );
847            }
848        }
849        finally
850        {
851            unlockRead();
852        }
853
854        if ( IS_DEBUG )
855        {
856            OPERATION_LOG.debug( "<< getRootDseOperation successful" );
857        }
858
859        if ( IS_TIME )
860        {
861            OPERATION_TIME.debug( "GetRootDSE operation took {} ns", ( System.nanoTime() - opStart ) );
862        }
863
864        return root;
865    }
866
867
868    /**
869     * {@inheritDoc}
870     */
871    public boolean hasEntry( HasEntryOperationContext hasEntryContext ) throws LdapException
872    {
873        if ( IS_DEBUG )
874        {
875            OPERATION_LOG.debug( ">> hasEntryOperation : {}", hasEntryContext );
876        }
877
878        long opStart = 0L;
879
880        if ( IS_TIME )
881        {
882            opStart = System.nanoTime();
883        }
884
885        ensureStarted();
886
887        Interceptor head = directoryService.getInterceptor( hasEntryContext.getNextInterceptor() );
888
889        boolean result = false;
890
891        lockRead();
892
893        // Normalize the addContext Dn
894        Dn dn = hasEntryContext.getDn();
895        
896        if ( !dn.isSchemaAware() )
897        {
898            dn = new Dn( directoryService.getSchemaManager(), dn );
899            hasEntryContext.setDn( dn );
900        }
901
902        try
903        {
904            Partition partition = directoryService.getPartitionNexus().getPartition( dn );
905
906            try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
907            {
908                hasEntryContext.setPartition( partition );
909                hasEntryContext.setTransaction( partitionTxn );
910
911                result = head.hasEntry( hasEntryContext );
912            }
913            catch ( IOException ioe )
914            {
915                throw new LdapOtherException( ioe.getMessage(), ioe );
916            }
917        }
918        finally
919        {
920            unlockRead();
921        }
922
923        if ( IS_DEBUG )
924        {
925            OPERATION_LOG.debug( "<< HasEntryOperation successful" );
926        }
927
928        if ( IS_TIME )
929        {
930            OPERATION_TIME.debug( "HasEntry operation took {} ns", ( System.nanoTime() - opStart ) );
931        }
932
933        return result;
934    }
935
936
937    /**
938     * {@inheritDoc}
939     */
940    public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
941    {
942        if ( IS_DEBUG )
943        {
944            OPERATION_LOG.debug( ">> LookupOperation : {}", lookupContext );
945        }
946
947        long opStart = 0L;
948
949        if ( IS_TIME )
950        {
951            opStart = System.nanoTime();
952        }
953
954        ensureStarted();
955
956        Interceptor head = directoryService.getInterceptor( lookupContext.getNextInterceptor() );
957
958        Entry entry = null;
959
960        // Normalize the modifyContext Dn
961        Dn dn = lookupContext.getDn();
962
963        if ( !dn.isSchemaAware() )
964        {
965            dn = new Dn( directoryService.getSchemaManager(), dn );
966            lookupContext.setDn( dn );
967        }
968        
969        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
970        lookupContext.setPartition( partition );
971        
972        // Start a read transaction right away
973        try ( PartitionTxn transaction = partition.beginReadTransaction() )
974        {
975            lookupContext.setTransaction( transaction );
976
977            lockRead();
978    
979            try
980            {
981                entry = head.lookup( lookupContext );
982            }
983            finally
984            {
985                unlockRead();
986            }
987        }
988        catch ( IOException ioe )
989        {
990            throw new LdapOtherException( ioe.getMessage(), ioe );
991        }
992
993        if ( IS_DEBUG )
994        {
995            OPERATION_LOG.debug( "<< LookupOperation successful" );
996        }
997
998        if ( IS_TIME )
999        {
1000            OPERATION_TIME.debug( "Lookup operation took {} ns", ( System.nanoTime() - opStart ) );
1001        }
1002
1003        return entry;
1004    }
1005
1006
1007    /**
1008     * {@inheritDoc}
1009     */
1010    public void modify( ModifyOperationContext modifyContext ) throws LdapException
1011    {
1012        if ( IS_DEBUG )
1013        {
1014            OPERATION_LOG.debug( ">> ModifyOperation : {}", modifyContext );
1015        }
1016
1017        long opStart = 0L;
1018
1019        if ( IS_TIME )
1020        {
1021            opStart = System.nanoTime();
1022        }
1023
1024        ensureStarted();
1025
1026        // Normalize the modifyContext Dn
1027        Dn dn = modifyContext.getDn();
1028
1029        if ( !dn.isSchemaAware() )
1030        {
1031            dn = new Dn( directoryService.getSchemaManager(), dn );
1032            modifyContext.setDn( dn );
1033        }
1034
1035        ReferralManager referralManager = directoryService.getReferralManager();
1036
1037        // We have to deal with the referral first
1038        referralManager.lockRead();
1039
1040        try
1041        {
1042            // Check if we have an ancestor for this Dn
1043            Entry parentEntry = referralManager.getParentReferral( dn );
1044
1045            if ( parentEntry != null )
1046            {
1047                if ( referralManager.isReferral( dn ) )
1048                {
1049                    // This is a referral. We can delete it if the ManageDsaIt flag is true
1050                    // Otherwise, we just throw a LdapReferralException
1051                    if ( !modifyContext.isReferralIgnored() )
1052                    {
1053                        // Throw a Referral Exception
1054                        // We have found a parent referral for the current Dn
1055                        Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1056
1057                        throw buildReferralException( parentEntry, childDn );
1058                    }
1059                }
1060                else if ( referralManager.hasParentReferral( dn ) )
1061                {
1062                    // We can't delete an entry which has an ancestor referral
1063
1064                    // Depending on the Context.REFERRAL property value, we will throw
1065                    // a different exception.
1066                    if ( modifyContext.isReferralIgnored() )
1067                    {
1068                        // We have found a parent referral for the current Dn
1069                        Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1070
1071                        throw buildLdapPartialResultException( childDn );
1072                    }
1073                    else
1074                    {
1075                        // We have found a parent referral for the current Dn
1076                        Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1077
1078                        throw buildReferralException( parentEntry, childDn );
1079                    }
1080                }
1081            }
1082        }
1083        finally
1084        {
1085            // Unlock the ReferralManager
1086            referralManager.unlock();
1087        }
1088        
1089        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
1090        modifyContext.setPartition( partition );
1091        
1092        lockWrite();
1093        
1094        // Start a Write transaction right away
1095        PartitionTxn transaction = modifyContext.getSession().getTransaction( partition ); 
1096
1097        try
1098        {
1099            if ( transaction == null )
1100            {
1101                transaction = partition.beginWriteTransaction();
1102                
1103                if ( modifyContext.getSession().hasSessionTransaction() )
1104                {
1105                    modifyContext.getSession().addTransaction( partition, transaction );
1106                }
1107            }
1108
1109            modifyContext.setTransaction( transaction );
1110
1111            // populate the context with the old entry
1112            eagerlyPopulateFields( modifyContext );
1113
1114            // Call the Modify method
1115            Interceptor head = directoryService.getInterceptor( modifyContext.getNextInterceptor() );
1116
1117            head.modify( modifyContext );
1118            
1119            if ( !modifyContext.getSession().hasSessionTransaction() )
1120            {
1121                transaction.commit();
1122            }
1123        }
1124        catch ( LdapException le )
1125        {
1126            try 
1127            {
1128                if ( transaction != null )
1129                {
1130                    transaction.abort();
1131                }
1132                
1133                throw le;
1134            }
1135            catch ( IOException ioe )
1136            {
1137                throw new LdapOtherException( ioe.getMessage(), ioe );
1138            }
1139        }
1140        catch ( IOException ioe )
1141        {
1142            try 
1143            {
1144                transaction.abort();
1145                
1146                throw new LdapOtherException( ioe.getMessage(), ioe );
1147            }
1148            catch ( IOException ioe2 )
1149            {
1150                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1151            }
1152        }
1153        finally
1154        {
1155            unlockWrite();
1156        }
1157
1158        if ( IS_DEBUG )
1159        {
1160            OPERATION_LOG.debug( "<< ModifyOperation successful" );
1161        }
1162
1163        if ( IS_TIME )
1164        {
1165            OPERATION_TIME.debug( "Modify operation took {} ns", ( System.nanoTime() - opStart ) );
1166        }
1167    }
1168
1169
1170    /**
1171     * {@inheritDoc}
1172     */
1173    public void move( MoveOperationContext moveContext ) throws LdapException
1174    {
1175        if ( IS_DEBUG )
1176        {
1177            OPERATION_LOG.debug( ">> MoveOperation : {}", moveContext );
1178        }
1179
1180        long opStart = 0L;
1181
1182        if ( IS_TIME )
1183        {
1184            opStart = System.nanoTime();
1185        }
1186
1187        ensureStarted();
1188
1189        // Normalize the moveContext Dn
1190        Dn dn = moveContext.getDn();
1191
1192        if ( !dn.isSchemaAware() )
1193        {
1194            dn = new Dn( directoryService.getSchemaManager(), dn );
1195            moveContext.setDn( dn );
1196        }
1197
1198        // Normalize the moveContext superior Dn
1199        Dn newSuperiorDn = moveContext.getNewSuperior();
1200
1201        if ( !newSuperiorDn.isSchemaAware() )
1202        {
1203            newSuperiorDn = new Dn( directoryService.getSchemaManager(), newSuperiorDn );
1204            moveContext.setNewSuperior( newSuperiorDn );
1205        }
1206
1207        // We have to deal with the referral first
1208        directoryService.getReferralManager().lockRead();
1209
1210        try
1211        {
1212            // Check if we have an ancestor for this Dn
1213            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
1214
1215            if ( parentEntry != null )
1216            {
1217                // We have found a parent referral for the current Dn
1218                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1219
1220                if ( directoryService.getReferralManager().isReferral( dn ) )
1221                {
1222                    // This is a referral. We can delete it if the ManageDsaIt flag is true
1223                    // Otherwise, we just throw a LdapReferralException
1224                    if ( !moveContext.isReferralIgnored() )
1225                    {
1226                        // Throw a Referral Exception
1227                        throw buildReferralException( parentEntry, childDn );
1228                    }
1229                }
1230                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
1231                {
1232                    // We can't delete an entry which has an ancestor referral
1233
1234                    // Depending on the Context.REFERRAL property value, we will throw
1235                    // a different exception.
1236                    if ( moveContext.isReferralIgnored() )
1237                    {
1238                        throw buildLdapPartialResultException( childDn );
1239                    }
1240                    else
1241                    {
1242                        throw buildReferralException( parentEntry, childDn );
1243                    }
1244                }
1245            }
1246
1247            // Now, check the destination
1248            // If he parent Dn is a referral, or has a referral ancestor, we have to issue a AffectMultipleDsas result
1249            // as stated by RFC 3296 Section 5.6.2
1250            if ( directoryService.getReferralManager().isReferral( newSuperiorDn )
1251                || directoryService.getReferralManager().hasParentReferral( newSuperiorDn ) )
1252            {
1253                throw new LdapAffectMultipleDsaException();
1254            }
1255
1256        }
1257        finally
1258        {
1259            // Unlock the referral manager
1260            directoryService.getReferralManager().unlock();
1261        }
1262
1263        lockWrite();
1264        
1265        // Find the working partition
1266        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
1267        moveContext.setPartition( partition );
1268
1269        // Start a Write transaction right away
1270        PartitionTxn transaction = moveContext.getSession().getTransaction( partition ); 
1271        
1272        try
1273        {
1274            if ( transaction == null )
1275            {
1276                transaction = partition.beginWriteTransaction();
1277                
1278                if ( moveContext.getSession().hasSessionTransaction() )
1279                {
1280                    moveContext.getSession().addTransaction( partition, transaction );
1281                }
1282            }
1283        
1284            moveContext.setTransaction( transaction );
1285            Entry originalEntry = getOriginalEntry( moveContext );
1286
1287            moveContext.setOriginalEntry( originalEntry );
1288
1289            // Call the Move method
1290            Interceptor head = directoryService.getInterceptor( moveContext.getNextInterceptor() );
1291
1292            head.move( moveContext );
1293            
1294            if ( !moveContext.getSession().hasSessionTransaction() )
1295            {
1296                transaction.commit();
1297            }
1298        }
1299        catch ( LdapException le )
1300        {
1301            try
1302            {
1303                if ( transaction != null )
1304                {
1305                    transaction.abort();
1306                }
1307                
1308                throw le;
1309            }
1310            catch ( IOException ioe )
1311            {
1312                throw new LdapOtherException( ioe.getMessage(), ioe );
1313            }
1314        }
1315        catch ( IOException ioe )
1316        {
1317            try
1318            {
1319                if ( transaction != null )
1320                {
1321                    transaction.abort();
1322                }
1323                
1324                throw new LdapOtherException( ioe.getMessage(), ioe );
1325            }
1326            catch ( IOException ioe2 )
1327            {
1328                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1329            }
1330        }
1331        finally
1332        {
1333            unlockWrite();
1334        }
1335
1336        if ( IS_DEBUG )
1337        {
1338            OPERATION_LOG.debug( "<< MoveOperation successful" );
1339        }
1340
1341        if ( IS_TIME )
1342        {
1343            OPERATION_TIME.debug( "Move operation took {} ns", ( System.nanoTime() - opStart ) );
1344        }
1345    }
1346
1347
1348    /**
1349     * {@inheritDoc}
1350     */
1351    public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
1352    {
1353        if ( IS_DEBUG )
1354        {
1355            OPERATION_LOG.debug( ">> MoveAndRenameOperation : {}", moveAndRenameContext );
1356        }
1357
1358        long opStart = 0L;
1359
1360        if ( IS_TIME )
1361        {
1362            opStart = System.nanoTime();
1363        }
1364
1365        ensureStarted();
1366
1367        // Normalize the moveAndRenameContext Dn
1368        Dn dn = moveAndRenameContext.getDn();
1369
1370        if ( !dn.isSchemaAware() )
1371        {
1372            dn = new Dn( directoryService.getSchemaManager(), dn );
1373            moveAndRenameContext.setDn( dn );
1374        }
1375
1376        // We have to deal with the referral first
1377        directoryService.getReferralManager().lockRead();
1378
1379        try
1380        {
1381            // Check if we have an ancestor for this Dn
1382            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
1383
1384            if ( parentEntry != null )
1385            {
1386                // We have found a parent referral for the current Dn
1387                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1388
1389                if ( directoryService.getReferralManager().isReferral( dn ) )
1390                {
1391                    // This is a referral. We can delete it if the ManageDsaIt flag is true
1392                    // Otherwise, we just throw a LdapReferralException
1393                    if ( !moveAndRenameContext.isReferralIgnored() )
1394                    {
1395                        // Throw a Referral Exception
1396                        throw buildReferralException( parentEntry, childDn );
1397                    }
1398                }
1399                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
1400                {
1401                    // We can't delete an entry which has an ancestor referral
1402
1403                    // Depending on the Context.REFERRAL property value, we will throw
1404                    // a different exception.
1405                    if ( moveAndRenameContext.isReferralIgnored() )
1406                    {
1407                        throw buildLdapPartialResultException( childDn );
1408                    }
1409                    else
1410                    {
1411                        throw buildReferralException( parentEntry, childDn );
1412                    }
1413                }
1414            }
1415
1416            // Now, check the destination
1417            // Normalize the moveAndRenameContext Dn
1418            Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn();
1419
1420            if ( !newSuperiorDn.isSchemaAware() )
1421            {
1422                newSuperiorDn = new Dn( directoryService.getSchemaManager(), newSuperiorDn );
1423                moveAndRenameContext.setNewSuperiorDn( newSuperiorDn );
1424            }
1425
1426            // If he parent Dn is a referral, or has a referral ancestor, we have to issue a AffectMultipleDsas result
1427            // as stated by RFC 3296 Section 5.6.2
1428            if ( directoryService.getReferralManager().isReferral( newSuperiorDn )
1429                || directoryService.getReferralManager().hasParentReferral( newSuperiorDn ) )
1430            {
1431                // The parent Dn is a referral, we have to issue a AffectMultipleDsas result
1432                // as stated by RFC 3296 Section 5.6.2
1433                throw new LdapAffectMultipleDsaException();
1434            }
1435        }
1436        finally
1437        {
1438            // Unlock the ReferralManager
1439            directoryService.getReferralManager().unlock();
1440        }
1441
1442        // Find the working partition
1443        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
1444        moveAndRenameContext.setPartition( partition );
1445
1446        lockWrite();
1447        
1448        // Start a Write transaction right away
1449        PartitionTxn transaction = moveAndRenameContext.getSession().getTransaction( partition ); 
1450        
1451        try
1452        {
1453            if ( transaction == null )
1454            {
1455                transaction = partition.beginWriteTransaction();
1456                
1457                if ( moveAndRenameContext.getSession().hasSessionTransaction() )
1458                {
1459                    moveAndRenameContext.getSession().addTransaction( partition, transaction );
1460                }
1461            }
1462
1463            moveAndRenameContext.setOriginalEntry( getOriginalEntry( moveAndRenameContext ) );
1464            moveAndRenameContext.setModifiedEntry( moveAndRenameContext.getOriginalEntry().clone() );
1465            moveAndRenameContext.setTransaction( transaction );
1466
1467            // Call the MoveAndRename method
1468            Interceptor head = directoryService.getInterceptor( moveAndRenameContext.getNextInterceptor() );
1469
1470            head.moveAndRename( moveAndRenameContext );
1471
1472            if ( !moveAndRenameContext.getSession().hasSessionTransaction() )
1473            {
1474                transaction.commit();
1475            }
1476        }
1477        catch ( LdapException le )
1478        {
1479            try
1480            {
1481                if ( transaction != null )
1482                {
1483                    transaction.abort();
1484                }
1485                
1486                throw le;
1487            }
1488            catch ( IOException ioe )
1489            {
1490                throw new LdapOtherException( ioe.getMessage(), ioe );
1491            }
1492        }
1493        catch ( IOException ioe )
1494        {
1495            try
1496            {
1497                transaction.abort();
1498                
1499                throw new LdapOtherException( ioe.getMessage(), ioe );
1500            }
1501            catch ( IOException ioe2 )
1502            {
1503                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1504            }
1505        }
1506        finally
1507        {
1508            unlockWrite();
1509        }
1510
1511        if ( IS_DEBUG )
1512        {
1513            OPERATION_LOG.debug( "<< MoveAndRenameOperation successful" );
1514        }
1515
1516        if ( IS_TIME )
1517        {
1518            OPERATION_TIME.debug( "MoveAndRename operation took {} ns", ( System.nanoTime() - opStart ) );
1519        }
1520    }
1521
1522
1523    /**
1524     * {@inheritDoc}
1525     */
1526    public void rename( RenameOperationContext renameContext ) throws LdapException
1527    {
1528        if ( IS_DEBUG )
1529        {
1530            OPERATION_LOG.debug( ">> RenameOperation : {}", renameContext );
1531        }
1532
1533        long opStart = 0L;
1534
1535        if ( IS_TIME )
1536        {
1537            opStart = System.nanoTime();
1538        }
1539
1540        ensureStarted();
1541
1542        // Normalize the renameContext Dn
1543        Dn dn = renameContext.getDn();
1544
1545        if ( !dn.isSchemaAware() )
1546        {
1547            dn = new Dn( directoryService.getSchemaManager(), dn );
1548            renameContext.setDn( dn );
1549        }
1550
1551        // Inject the newDn into the operation context
1552        // Inject the new Dn into the context
1553        if ( !dn.isEmpty() )
1554        {
1555            Dn newDn = dn.getParent();
1556            Rdn newRdn = renameContext.getNewRdn();
1557            
1558            if ( !newRdn.isSchemaAware() )
1559            {
1560                newRdn = new Rdn( directoryService.getSchemaManager(), newRdn );
1561                renameContext.setNewRdn( newRdn );
1562            }
1563            
1564            newDn = newDn.add( renameContext.getNewRdn() );
1565            renameContext.setNewDn( newDn );
1566        }
1567
1568        // We have to deal with the referral first
1569        directoryService.getReferralManager().lockRead();
1570
1571        try
1572        {
1573            // Check if we have an ancestor for this Dn
1574            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
1575
1576            if ( parentEntry != null )
1577            {
1578                // We have found a parent referral for the current Dn
1579                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1580
1581                if ( directoryService.getReferralManager().isReferral( dn ) )
1582                {
1583                    // This is a referral. We can delete it if the ManageDsaIt flag is true
1584                    // Otherwise, we just throw a LdapReferralException
1585                    if ( !renameContext.isReferralIgnored() )
1586                    {
1587                        // Throw a Referral Exception
1588                        throw buildReferralException( parentEntry, childDn );
1589                    }
1590                }
1591                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
1592                {
1593                    // We can't delete an entry which has an ancestor referral
1594
1595                    // Depending on the Context.REFERRAL property value, we will throw
1596                    // a different exception.
1597                    if ( renameContext.isReferralIgnored() )
1598                    {
1599                        throw buildLdapPartialResultException( childDn );
1600                    }
1601                    else
1602                    {
1603                        throw buildReferralException( parentEntry, childDn );
1604                    }
1605                }
1606            }
1607        }
1608        finally
1609        {
1610            // Unlock the ReferralManager
1611            directoryService.getReferralManager().unlock();
1612        }
1613
1614        lockWrite();
1615
1616        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
1617
1618        // Start a Write transaction right away
1619        PartitionTxn transaction = renameContext.getSession().getTransaction( partition ); 
1620        
1621        // Call the rename method
1622        try
1623        {
1624            if ( transaction == null )
1625            {
1626                transaction = partition.beginWriteTransaction();
1627                
1628                if ( renameContext.getSession().hasSessionTransaction() )
1629                {
1630                    renameContext.getSession().addTransaction( partition, transaction );
1631                }
1632            }
1633
1634            renameContext.setPartition( partition );
1635
1636            // populate the context with the old entry
1637            PartitionTxn partitionTxn = null;
1638            
1639            try
1640            {
1641                partitionTxn = partition.beginReadTransaction();
1642                
1643                renameContext.setTransaction( partitionTxn );
1644                
1645                eagerlyPopulateFields( renameContext );
1646            }
1647            finally
1648            {
1649                try
1650                {
1651                    // Nothing to do
1652                    if ( partitionTxn != null )
1653                    {
1654                        partitionTxn.close();
1655                    }
1656                }
1657                catch ( IOException ioe )
1658                {
1659                    throw new LdapOtherException( ioe.getMessage(), ioe );
1660                }
1661            }
1662
1663            Entry originalEntry = getOriginalEntry( renameContext );
1664            renameContext.setOriginalEntry( originalEntry );
1665            renameContext.setModifiedEntry( originalEntry.clone() );
1666            Interceptor head = directoryService.getInterceptor( renameContext.getNextInterceptor() );
1667
1668            // Start a Write transaction right away
1669            transaction = renameContext.getSession().getTransaction( partition ); 
1670            
1671            // Call the Rename method
1672            try
1673            {
1674                if ( transaction == null )
1675                {
1676                    transaction = partition.beginWriteTransaction();
1677                    
1678                    if ( renameContext.getSession().hasSessionTransaction() )
1679                    {
1680                        renameContext.getSession().addTransaction( partition, transaction );
1681                    }
1682                }
1683
1684                renameContext.setTransaction( transaction );
1685
1686                head.rename( renameContext );
1687                
1688                if ( !renameContext.getSession().hasSessionTransaction() )
1689                {
1690                    transaction.commit();
1691                }
1692            }
1693            catch ( LdapException le )
1694            {
1695                try
1696                {
1697                    if ( transaction != null )
1698                    {
1699                        transaction.abort();
1700                    }
1701                    
1702                    throw le;
1703                }
1704                catch ( IOException ioe )
1705                {
1706                    throw new LdapOtherException( ioe.getMessage(), ioe );
1707                }
1708            }
1709            catch ( IOException ioe )
1710            {
1711                try
1712                {
1713                    if ( transaction != null )
1714                    {
1715                        transaction.abort();
1716                    }
1717                    
1718                    throw new LdapOtherException( ioe.getMessage(), ioe );
1719                }
1720                catch ( IOException ioe2 )
1721                {
1722                    throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1723                }
1724            }
1725        }
1726        finally
1727        {
1728            unlockWrite();
1729        }
1730
1731        if ( IS_DEBUG )
1732        {
1733            OPERATION_LOG.debug( "<< RenameOperation successful" );
1734        }
1735
1736        if ( IS_TIME )
1737        {
1738            OPERATION_TIME.debug( "Rename operation took {} ns", ( System.nanoTime() - opStart ) );
1739        }
1740    }
1741
1742
1743    /**
1744     * {@inheritDoc}
1745     */
1746    public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
1747    {
1748        if ( IS_DEBUG )
1749        {
1750            OPERATION_LOG.debug( ">> SearchOperation : {}", searchContext );
1751        }
1752
1753        long opStart = 0L;
1754
1755        if ( IS_TIME )
1756        {
1757            opStart = System.nanoTime();
1758        }
1759
1760        ensureStarted();
1761
1762        // Normalize the searchContext Dn
1763        Dn dn = searchContext.getDn();
1764
1765        if ( !dn.isSchemaAware() )
1766        {
1767            dn = new Dn( directoryService.getSchemaManager(), dn );
1768            searchContext.setDn( dn );
1769        }
1770
1771        // We have to deal with the referral first
1772        directoryService.getReferralManager().lockRead();
1773
1774        try
1775        {
1776            // Check if we have an ancestor for this Dn
1777            Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
1778
1779            if ( parentEntry != null )
1780            {
1781                // We have found a parent referral for the current Dn
1782                Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
1783
1784                if ( directoryService.getReferralManager().isReferral( dn ) )
1785                {
1786                    // This is a referral. We can return it if the ManageDsaIt flag is true
1787                    // Otherwise, we just throw a LdapReferralException
1788                    if ( !searchContext.isReferralIgnored() )
1789                    {
1790                        // Throw a Referral Exception
1791                        throw buildReferralExceptionForSearch( parentEntry, childDn, searchContext.getScope() );
1792                    }
1793                }
1794                else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
1795                {
1796                    // We can't search an entry which has an ancestor referral
1797
1798                    // Depending on the Context.REFERRAL property value, we will throw
1799                    // a different exception.
1800                    if ( searchContext.isReferralIgnored() )
1801                    {
1802                        throw buildLdapPartialResultException( childDn );
1803                    }
1804                    else
1805                    {
1806                        throw buildReferralExceptionForSearch( parentEntry, childDn, searchContext.getScope() );
1807                    }
1808                }
1809            }
1810        }
1811        finally
1812        {
1813            // Unlock the ReferralManager
1814            directoryService.getReferralManager().unlock();
1815        }
1816
1817        // Call the Search method
1818        Interceptor head = directoryService.getInterceptor( searchContext.getNextInterceptor() );
1819
1820        EntryFilteringCursor cursor = null;
1821        Partition partition = directoryService.getPartitionNexus().getPartition( dn );
1822        
1823        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1824        {
1825            searchContext.setPartition( partition );
1826            searchContext.setTransaction( partitionTxn );
1827            lockRead();
1828    
1829            try
1830            {
1831                cursor = head.search( searchContext );
1832            }
1833            finally
1834            {
1835                unlockRead();
1836            }
1837        }
1838        catch ( IOException ioe )
1839        {
1840            throw new LdapOtherException( ioe.getMessage(), ioe );
1841        }
1842
1843        if ( IS_DEBUG )
1844        {
1845            OPERATION_LOG.debug( "<< SearchOperation successful" );
1846        }
1847
1848        if ( IS_TIME )
1849        {
1850            OPERATION_TIME.debug( "Search operation took {} ns", ( System.nanoTime() - opStart ) );
1851        }
1852
1853        return cursor;
1854    }
1855
1856
1857    /**
1858     * {@inheritDoc}
1859     */
1860    public void unbind( UnbindOperationContext unbindContext ) throws LdapException
1861    {
1862        if ( IS_DEBUG )
1863        {
1864            OPERATION_LOG.debug( ">> UnbindOperation : {}", unbindContext );
1865        }
1866
1867        long opStart = 0L;
1868
1869        if ( IS_TIME )
1870        {
1871            opStart = System.nanoTime();
1872        }
1873
1874        ensureStarted();
1875
1876        // Call the Unbind method
1877        Interceptor head = directoryService.getInterceptor( unbindContext.getNextInterceptor() );
1878
1879        head.unbind( unbindContext );
1880
1881        if ( IS_DEBUG )
1882        {
1883            OPERATION_LOG.debug( "<< UnbindOperation successful" );
1884        }
1885
1886        if ( IS_TIME )
1887        {
1888            OPERATION_TIME.debug( "Unbind operation took {} ns", ( System.nanoTime() - opStart ) );
1889        }
1890    }
1891
1892
1893    private void ensureStarted() throws LdapServiceUnavailableException
1894    {
1895        if ( !directoryService.isStarted() )
1896        {
1897            throw new LdapServiceUnavailableException( ResultCodeEnum.UNAVAILABLE, I18n.err( I18n.ERR_316 ) );
1898        }
1899    }
1900}