View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.core;
21  
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.concurrent.locks.ReadWriteLock;
27  import java.util.concurrent.locks.ReentrantReadWriteLock;
28  
29  import org.apache.directory.api.ldap.model.constants.Loggers;
30  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
31  import org.apache.directory.api.ldap.model.entry.Attribute;
32  import org.apache.directory.api.ldap.model.entry.Entry;
33  import org.apache.directory.api.ldap.model.entry.Value;
34  import org.apache.directory.api.ldap.model.exception.LdapAffectMultipleDsaException;
35  import org.apache.directory.api.ldap.model.exception.LdapException;
36  import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
37  import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
38  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
39  import org.apache.directory.api.ldap.model.exception.LdapPartialResultException;
40  import org.apache.directory.api.ldap.model.exception.LdapReferralException;
41  import org.apache.directory.api.ldap.model.exception.LdapServiceUnavailableException;
42  import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
43  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
44  import org.apache.directory.api.ldap.model.message.SearchScope;
45  import org.apache.directory.api.ldap.model.name.Dn;
46  import org.apache.directory.api.ldap.model.name.Rdn;
47  import org.apache.directory.api.ldap.model.url.LdapUrl;
48  import org.apache.directory.server.core.api.CoreSession;
49  import org.apache.directory.server.core.api.DirectoryService;
50  import org.apache.directory.server.core.api.OperationManager;
51  import org.apache.directory.server.core.api.ReferralManager;
52  import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
53  import org.apache.directory.server.core.api.interceptor.Interceptor;
54  import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
55  import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
56  import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
57  import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
58  import org.apache.directory.server.core.api.interceptor.context.GetRootDseOperationContext;
59  import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
60  import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
61  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
62  import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
63  import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
64  import org.apache.directory.server.core.api.interceptor.context.OperationContext;
65  import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
66  import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
67  import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
68  import org.apache.directory.server.core.api.partition.Partition;
69  import org.apache.directory.server.core.api.partition.PartitionTxn;
70  import org.apache.directory.server.i18n.I18n;
71  import org.slf4j.Logger;
72  import org.slf4j.LoggerFactory;
73  
74  
75  /**
76   * The default implementation of an OperationManager.
77   *
78   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
79   */
80  public class DefaultOperationManager implements OperationManager
81  {
82      /** A logger specifically for operations */
83      private static final Logger OPERATION_LOG = LoggerFactory.getLogger( Loggers.OPERATION_LOG.getName() );
84  
85      /** A logger specifically for operations time */
86      private static final Logger OPERATION_TIME = LoggerFactory.getLogger( Loggers.OPERATION_TIME.getName() );
87  
88      /** A logger specifically for operations statistics */
89      private static final Logger OPERATION_STAT = LoggerFactory.getLogger( Loggers.OPERATION_STAT.getName() );
90  
91      /** Speedup for logs */
92      private static final boolean IS_DEBUG = OPERATION_LOG.isDebugEnabled();
93      private static final boolean IS_TIME = OPERATION_TIME.isDebugEnabled();
94      private static final boolean IS_STAT = OPERATION_STAT.isDebugEnabled();
95  
96      /** The directory service instance */
97      private final DirectoryService directoryService;
98  
99      /** 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/context/LookupOperationContext.html#LookupOperationContext">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 }