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