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 org.apache.directory.server.constants.ServerDNConstants;
24  import org.apache.directory.server.core.authn.AuthenticationInterceptor;
25  import org.apache.directory.server.core.authn.LdapPrincipal;
26  import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
27  import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
28  import org.apache.directory.server.core.changelog.ChangeLog;
29  import org.apache.directory.server.core.changelog.ChangeLogEvent;
30  import org.apache.directory.server.core.changelog.ChangeLogInterceptor;
31  import org.apache.directory.server.core.changelog.DefaultChangeLog;
32  import org.apache.directory.server.core.changelog.Tag;
33  import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
34  import org.apache.directory.server.core.cursor.Cursor;
35  import org.apache.directory.server.core.entry.DefaultServerEntry;
36  import org.apache.directory.server.core.entry.ServerEntry;
37  import org.apache.directory.server.core.event.EventInterceptor;
38  import org.apache.directory.server.core.event.EventService;
39  import org.apache.directory.server.core.exception.ExceptionInterceptor;
40  import org.apache.directory.server.core.interceptor.Interceptor;
41  import org.apache.directory.server.core.interceptor.InterceptorChain;
42  import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
43  import org.apache.directory.server.core.interceptor.context.AddOperationContext;
44  import org.apache.directory.server.core.interceptor.context.BindOperationContext;
45  import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
46  import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
47  import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
48  import org.apache.directory.server.core.normalization.NormalizationInterceptor;
49  import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
50  import org.apache.directory.server.core.partition.DefaultPartitionNexus;
51  import org.apache.directory.server.core.partition.Partition;
52  import org.apache.directory.server.core.partition.PartitionNexus;
53  import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
54  import org.apache.directory.server.xdbm.Index;
55  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
56  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
57  import org.apache.directory.server.core.schema.PartitionSchemaLoader;
58  import org.apache.directory.server.core.schema.SchemaInterceptor;
59  import org.apache.directory.server.core.schema.SchemaOperationControl;
60  import org.apache.directory.server.core.schema.SchemaPartitionDao;
61  import org.apache.directory.server.core.schema.SchemaService;
62  import org.apache.directory.server.core.security.TlsKeyGenerator;
63  import org.apache.directory.server.core.subtree.SubentryInterceptor;
64  import org.apache.directory.server.core.trigger.TriggerInterceptor;
65  import org.apache.directory.server.schema.SerializableComparator;
66  import org.apache.directory.server.schema.bootstrap.ApacheSchema;
67  import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
68  import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
69  import org.apache.directory.server.schema.bootstrap.CoreSchema;
70  import org.apache.directory.server.schema.bootstrap.Schema;
71  import org.apache.directory.server.schema.bootstrap.SystemSchema;
72  import org.apache.directory.server.schema.bootstrap.partition.DbFileListing;
73  import org.apache.directory.server.schema.bootstrap.partition.SchemaPartitionExtractor;
74  import org.apache.directory.server.schema.registries.DefaultOidRegistry;
75  import org.apache.directory.server.schema.registries.DefaultRegistries;
76  import org.apache.directory.server.schema.registries.OidRegistry;
77  import org.apache.directory.server.schema.registries.Registries;
78  import org.apache.directory.shared.ldap.NotImplementedException;
79  import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
80  import org.apache.directory.shared.ldap.constants.SchemaConstants;
81  import org.apache.directory.shared.ldap.entry.Entry;
82  import org.apache.directory.shared.ldap.entry.EntryAttribute;
83  import org.apache.directory.shared.ldap.entry.Modification;
84  import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
85  import org.apache.directory.shared.ldap.exception.LdapNamingException;
86  import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
87  import org.apache.directory.shared.ldap.ldif.ChangeType;
88  import org.apache.directory.shared.ldap.ldif.LdifEntry;
89  import org.apache.directory.shared.ldap.ldif.LdifReader;
90  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
91  import org.apache.directory.shared.ldap.name.LdapDN;
92  import org.apache.directory.shared.ldap.name.Rdn;
93  import org.apache.directory.shared.ldap.schema.OidNormalizer;
94  import org.apache.directory.shared.ldap.util.AttributeUtils;
95  import org.apache.directory.shared.ldap.util.DateUtils;
96  import org.apache.directory.shared.ldap.util.StringTools;
97  import org.slf4j.Logger;
98  import org.slf4j.LoggerFactory;
99  
100 import javax.naming.NamingException;
101 
102 import java.io.BufferedReader;
103 import java.io.File;
104 import java.io.IOException;
105 import java.io.StringReader;
106 import java.util.ArrayList;
107 import java.util.Arrays;
108 import java.util.HashSet;
109 import java.util.List;
110 import java.util.Map;
111 import java.util.Set;
112 
113 
114 /**
115  * Default implementation of {@link DirectoryService}.
116  * 
117  * @org.apache.xbean.XBean
118  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
119  */
120 public class DefaultDirectoryService implements DirectoryService
121 {
122     /** The logger */
123     private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class );
124 
125     private static final String ILLEGAL_STATE_MSG = "Something has got to be severely " +
126     "wrong with the core packaging\nor the build to have " +
127     "resulted in this exception.";
128     
129     private SchemaService schemaService;
130 
131     /** the registries for system schema objects */
132     private Registries registries;
133     
134     /** the root nexus */
135     private DefaultPartitionNexus partitionNexus;
136 
137     /** whether or not server is started for the first time */
138     private boolean firstStart;
139 
140     /** The interceptor (or interceptor chain) for this service */
141     private InterceptorChain interceptorChain;
142 
143     /** whether or not this instance has been shutdown */
144     private boolean started;
145 
146     /** the change log service */
147     private ChangeLog changeLog;
148     
149     /** 
150      * the interface used to perform various operations on this 
151      * DirectoryService
152      */
153     private OperationManager operationManager = new DefaultOperationManager( this );
154 
155     /** the distinguished name of the administrative user */
156     private LdapDN adminDn;
157     
158     /** session used as admin for internal operations */
159     private CoreSession adminSession;
160 
161     /** remove me after implementation is completed */
162     private static final String PARTIAL_IMPL_WARNING =
163             "WARNING: the changelog is only partially operational and will revert\n" +
164             "state without consideration of who made the original change.  All reverting " +
165             "changes are made by the admin user.\n Furthermore the used controls are not at " +
166             "all taken into account";
167 
168     // ------------------------------------------------------------------------
169     // Constructor
170     // ------------------------------------------------------------------------
171 
172 
173     /**
174      * Creates a new instance of the directory service.
175      */
176     public DefaultDirectoryService() 
177     {
178         setDefaultInterceptorConfigurations();
179         changeLog = new DefaultChangeLog();
180         
181         // --------------------------------------------------------------------
182         // Load the bootstrap schemas to start up the schema partition
183         // --------------------------------------------------------------------
184 
185         // setup temporary loader and temp registry 
186         BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
187         OidRegistry oidRegistry = new DefaultOidRegistry();
188         registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
189 
190         // load essential bootstrap schemas 
191         Set<Schema> bootstrapSchemas = new HashSet<Schema>();
192         bootstrapSchemas.add( new ApachemetaSchema() );
193         bootstrapSchemas.add( new ApacheSchema() );
194         bootstrapSchemas.add( new CoreSchema() );
195         bootstrapSchemas.add( new SystemSchema() );
196         
197         
198         try
199         {
200             loader.loadWithDependencies( bootstrapSchemas, registries );
201         }
202         catch ( Exception e )
203         {
204             throw new IllegalStateException( ILLEGAL_STATE_MSG, e );
205         }
206 
207         // run referential integrity tests
208         List<Throwable> errors = registries.checkRefInteg();
209         if ( !errors.isEmpty() )
210         {
211             NamingException e = new NamingException();
212             e.setRootCause( errors.get( 0 ) );
213             throw new IllegalStateException( ILLEGAL_STATE_MSG, e );
214         }
215         
216         SerializableComparator.setRegistry( registries.getComparatorRegistry() );
217     }
218 
219 
220     // ------------------------------------------------------------------------
221     // C O N F I G U R A T I O N   M E T H O D S
222     // ------------------------------------------------------------------------
223 
224 
225     public static final int MAX_SIZE_LIMIT_DEFAULT = 100;
226     public static final int MAX_TIME_LIMIT_DEFAULT = 10000;
227 
228     private String instanceId;
229     private File workingDirectory = new File( "server-work" );
230     private boolean exitVmOnShutdown = true; // allow by default
231     private boolean shutdownHookEnabled = true; // allow by default
232     private boolean allowAnonymousAccess = true; // allow by default
233     private boolean accessControlEnabled; // off by default
234     private boolean denormalizeOpAttrsEnabled; // off by default
235     private int maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT; // set to default value
236     private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT; // set to default value (milliseconds)
237     private List<Interceptor> interceptors;
238     private Partition systemPartition;
239     private Set<Partition> partitions = new HashSet<Partition>();
240     private List<? extends LdifEntry> testEntries = new ArrayList<LdifEntry>(); // List<Attributes>
241     private EventService eventService;
242 
243 
244 
245     public void setInstanceId( String instanceId )
246     {
247         this.instanceId = instanceId;
248     }
249 
250 
251     public String getInstanceId()
252     {
253         return instanceId;
254     }
255 
256 
257     /**
258      * Gets the {@link Partition}s used by this DirectoryService.
259      *
260      * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition"
261      * @return the set of partitions used
262      */
263     public Set<? extends Partition> getPartitions()
264     {
265         Set<Partition> cloned = new HashSet<Partition>();
266         cloned.addAll( partitions );
267         return cloned;
268     }
269 
270 
271     /**
272      * Sets {@link Partition}s used by this DirectoryService.
273      *
274      * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition"
275      * @param partitions the partitions to used
276      */
277     public void setPartitions( Set<? extends Partition> partitions )
278     {
279         Set<Partition> cloned = new HashSet<Partition>();
280         cloned.addAll( partitions );
281         Set<String> names = new HashSet<String>();
282         for ( Partition partition : cloned )
283         {
284             String id = partition.getId();
285             if ( names.contains( id ) )
286             {
287                 LOG.warn( "Encountered duplicate partition {} identifier.", id );
288             }
289             names.add( id );
290         }
291 
292         this.partitions = cloned;
293     }
294 
295 
296     /**
297      * Returns <tt>true</tt> if access control checks are enabled.
298      *
299      * @return true if access control checks are enabled, false otherwise
300      */
301     public boolean isAccessControlEnabled()
302     {
303         return accessControlEnabled;
304     }
305 
306 
307     /**
308      * Sets whether to enable basic access control checks or not.
309      *
310      * @param accessControlEnabled true to enable access control checks, false otherwise
311      */
312     public void setAccessControlEnabled( boolean accessControlEnabled )
313     {
314         this.accessControlEnabled = accessControlEnabled;
315     }
316 
317 
318     /**
319      * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE.
320      * If the access control subsystem is enabled then access to some entries may not be
321      * allowed even when full anonymous access is enabled.
322      *
323      * @return true if anonymous access is allowed on entries besides the RootDSE, false
324      * if anonymous access is allowed to all entries.
325      */
326     public boolean isAllowAnonymousAccess()
327     {
328         return allowAnonymousAccess;
329     }
330 
331 
332     /**
333      * Sets whether to allow anonymous access to entries other than the RootDSE.  If the
334      * access control subsystem is enabled then access to some entries may not be allowed
335      * even when full anonymous access is enabled.
336      *
337      * @param enableAnonymousAccess true to enable anonymous access, false to disable it
338      */
339     public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
340     {
341         this.allowAnonymousAccess = enableAnonymousAccess;
342     }
343 
344 
345     /**
346      * Returns interceptors in the server.
347      *
348      * @return the interceptors in the server.
349      */
350     public List<Interceptor> getInterceptors()
351     {
352         List<Interceptor> cloned = new ArrayList<Interceptor>();
353         cloned.addAll( interceptors );
354         return cloned;
355     }
356 
357 
358     /**
359      * Sets the interceptors in the server.
360      *
361      * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.interceptor.Interceptor"
362      * @param interceptors the interceptors to be used in the server.
363      */
364     public void setInterceptors( List<Interceptor> interceptors ) 
365     {
366         Set<String> names = new HashSet<String>();
367         for ( Interceptor interceptor : interceptors )
368         {
369             String name = interceptor.getName();
370             if ( names.contains( name ) )
371             {
372                 LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() );
373             }
374             names.add( name );
375         }
376 
377         this.interceptors = interceptors;
378     }
379 
380 
381     /**
382      * Returns test directory entries({@link LdifEntry}) to be loaded while
383      * bootstrapping.
384      *
385      * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry"
386      * @return test entries to load during bootstrapping
387      */
388     public List<LdifEntry> getTestEntries()
389     {
390         List<LdifEntry> cloned = new ArrayList<LdifEntry>();
391         cloned.addAll( testEntries );
392         return cloned;
393     }
394 
395 
396     /**
397      * Sets test directory entries({@link Attributes}) to be loaded while
398      * bootstrapping.
399      *
400      * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry"
401      * @param testEntries the test entries to load while bootstrapping
402      */
403     public void setTestEntries( List<? extends LdifEntry> testEntries )
404     {
405         //noinspection MismatchedQueryAndUpdateOfCollection
406         List<LdifEntry> cloned = new ArrayList<LdifEntry>();
407         cloned.addAll( testEntries );
408         this.testEntries = testEntries;
409     }
410 
411 
412     /**
413      * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are
414      * stored by default.
415      *
416      * @return the directory where partition's are stored.
417      */
418     public File getWorkingDirectory()
419     {
420         return workingDirectory;
421     }
422 
423 
424     /**
425      * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored
426      * by default.
427      *
428      * @param workingDirectory the directory where the server's partitions are stored by default.
429      */
430     public void setWorkingDirectory( File workingDirectory )
431     {
432         this.workingDirectory = workingDirectory;
433     }
434 
435 
436     public void setShutdownHookEnabled( boolean shutdownHookEnabled )
437     {
438         this.shutdownHookEnabled = shutdownHookEnabled;
439     }
440 
441 
442     public boolean isShutdownHookEnabled()
443     {
444         return shutdownHookEnabled;
445     }
446 
447 
448     public void setExitVmOnShutdown( boolean exitVmOnShutdown )
449     {
450         this.exitVmOnShutdown = exitVmOnShutdown;
451     }
452 
453 
454     public boolean isExitVmOnShutdown()
455     {
456         return exitVmOnShutdown;
457     }
458 
459 
460     public void setMaxSizeLimit( int maxSizeLimit )
461     {
462         this.maxSizeLimit = maxSizeLimit;
463     }
464 
465 
466     public int getMaxSizeLimit()
467     {
468         return maxSizeLimit;
469     }
470 
471 
472     public void setMaxTimeLimit( int maxTimeLimit )
473     {
474         this.maxTimeLimit = maxTimeLimit;
475     }
476 
477 
478     public int getMaxTimeLimit()
479     {
480         return maxTimeLimit;
481     }
482 
483     public void setSystemPartition( Partition systemPartition )
484     {
485         this.systemPartition = systemPartition;
486     }
487 
488 
489     public Partition getSystemPartition()
490     {
491         return systemPartition;
492     }
493 
494 
495     public boolean isDenormalizeOpAttrsEnabled()
496     {
497         return denormalizeOpAttrsEnabled;
498     }
499 
500 
501     public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
502     {
503         this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled;
504     }
505 
506 
507     public ChangeLog getChangeLog()
508     {
509         return changeLog;
510     }
511 
512 
513     public void setChangeLog( ChangeLog changeLog )
514     {
515         this.changeLog = changeLog;
516     }
517 
518 
519     public void addPartition( Partition parition ) throws Exception
520     {
521         partitions.add( parition );
522 
523         if ( ! started )
524         {
525             return;
526         }
527 
528         AddContextPartitionOperationContext addPartitionCtx = 
529             new AddContextPartitionOperationContext( adminSession, parition );
530         partitionNexus.addContextPartition( addPartitionCtx );
531     }
532 
533 
534     public void removePartition( Partition partition ) throws Exception
535     {
536         partitions.remove( partition );
537 
538         if ( ! started )
539         {
540             return;
541         }
542 
543         RemoveContextPartitionOperationContext removePartitionCtx =
544                 new RemoveContextPartitionOperationContext( adminSession, partition.getSuffixDn() );
545         partitionNexus.removeContextPartition( removePartitionCtx );
546     }
547 
548 
549     // ------------------------------------------------------------------------
550     // BackendSubsystem Interface Method Implementations
551     // ------------------------------------------------------------------------
552 
553 
554     private void setDefaultInterceptorConfigurations()
555     {
556         // Set default interceptor chains
557         List<Interceptor> list = new ArrayList<Interceptor>();
558 
559         list.add( new NormalizationInterceptor() );
560         list.add( new AuthenticationInterceptor() );
561         list.add( new AciAuthorizationInterceptor() );
562         list.add( new DefaultAuthorizationInterceptor() );
563         list.add( new ExceptionInterceptor() );
564         list.add( new ChangeLogInterceptor() );
565         list.add( new OperationalAttributeInterceptor() );
566         list.add( new SchemaInterceptor() );
567         list.add( new SubentryInterceptor() );
568         list.add( new CollectiveAttributeInterceptor() );
569         list.add( new EventInterceptor() );
570         list.add( new TriggerInterceptor() );
571 
572         setInterceptors( list );
573     }
574 
575     
576     public CoreSession getAdminSession()
577     {
578         return adminSession;
579     }
580     
581     
582     public CoreSession getSession() 
583     {
584         return new DefaultCoreSession( new LdapPrincipal(), this );
585     }
586     
587     
588     public CoreSession getSession( LdapPrincipal principal )
589     {
590         return new DefaultCoreSession( principal, this );
591     }
592     
593     
594     public CoreSession getSession( LdapDN principalDn, byte[] credentials ) 
595         throws Exception
596     {
597         if ( ! started )
598         {
599             throw new IllegalStateException( "Service has not started." );
600         }
601 
602         BindOperationContext bindContext = new BindOperationContext( null );
603         bindContext.setCredentials( credentials );
604         bindContext.setDn( principalDn );
605         operationManager.bind( bindContext );
606         
607         return bindContext.getSession();
608     }
609     
610     
611     public CoreSession getSession( LdapDN principalDn, byte[] credentials, String saslMechanism, String saslAuthId ) 
612         throws Exception
613     {
614         if ( ! started )
615         {
616             throw new IllegalStateException( "Service has not started." );
617         }
618 
619         BindOperationContext bindContext = new BindOperationContext( null );
620         bindContext.setCredentials( credentials );
621         bindContext.setDn( principalDn );
622         bindContext.setSaslMechanism( saslMechanism );
623         operationManager.bind( bindContext );
624         
625         return bindContext.getSession();
626     }
627 
628 
629     public long revert() throws Exception
630     {
631         if ( changeLog == null || ! changeLog.isEnabled() )
632         {
633             throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." );
634         }
635 
636         Tag latest = changeLog.getLatest();
637         
638         if ( null != latest )
639         {
640             if ( latest.getRevision() < changeLog.getCurrentRevision() )
641             {
642                 return revert( latest.getRevision() );
643             }
644             else
645             {
646                 LOG.info( "Ignoring request to revert without changes since the latest tag." );
647                 return changeLog.getCurrentRevision();
648             }
649         }
650 
651         throw new IllegalStateException( "There must be at least one tag to revert to the latest tag." );
652     }
653 
654 
655     /**
656      * We handle the ModDN/ModRDN operation for the revert here. 
657      */
658     private void moddn( LdapDN oldDn, LdapDN newDn, boolean delOldRdn ) throws Exception
659     {
660         if ( oldDn.size() == 0 )
661         {
662             throw new LdapNoPermissionException( "can't rename the rootDSE" );
663         }
664 
665         // calculate parents
666         LdapDN oldBase = ( LdapDN ) oldDn.clone();
667         oldBase.remove( oldDn.size() - 1 );
668         LdapDN newBase = ( LdapDN ) newDn.clone();
669         newBase.remove( newDn.size() - 1 );
670 
671         // Compute the RDN for each of the DN
672         Rdn newRdn = newDn.getRdn( newDn.size() - 1 );
673         Rdn oldRdn = oldDn.getRdn( oldDn.size() - 1 );
674 
675         /*
676          * We need to determine if this rename operation corresponds to a simple
677          * RDN name change or a move operation.  If the two names are the same
678          * except for the RDN then it is a simple modifyRdn operation.  If the
679          * names differ in size or have a different baseDN then the operation is
680          * a move operation.  Furthermore if the RDN in the move operation 
681          * changes it is both an RDN change and a move operation.
682          */
683         if ( ( oldDn.size() == newDn.size() ) && oldBase.equals( newBase ) )
684         {
685             adminSession.rename( oldDn, newRdn, delOldRdn );
686         }
687         else
688         {
689             LdapDN target = ( LdapDN ) newDn.clone();
690             target.remove( newDn.size() - 1 );
691 
692             if ( newRdn.equals( oldRdn ) )
693             {
694                 adminSession.move( oldDn, target );
695             }
696             else
697             {
698                 adminSession.moveAndRename( oldDn, target, new Rdn( newRdn ), delOldRdn );
699             }
700         }
701     }
702     
703     
704     public long revert( long revision ) throws Exception
705     {
706         if ( changeLog == null || ! changeLog.isEnabled() )
707         {
708             throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." );
709         }
710 
711         if ( revision < 0 )
712         {
713             throw new IllegalArgumentException( "revision must be greater than or equal to 0" );
714         }
715 
716         if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() )
717         {
718             throw new IllegalArgumentException( "revision must be less than the current revision" );
719         }
720 
721         Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision );
722 
723         /*
724          * BAD, BAD, BAD!!!
725          *
726          * No synchronization no nothing.  Just getting this to work for now
727          * so we can revert tests.  Any production grade use of this feature
728          * needs to synchronize on all changes while the revert is in progress.
729          *
730          * How about making this operation transactional?
731          *
732          * First of all just stop using JNDI and construct the operations to
733          * feed into the interceptor pipeline.
734          * 
735          * TODO review this code.
736          */
737 
738         try
739         {
740             LOG.warn( PARTIAL_IMPL_WARNING );
741             cursor.afterLast();
742             
743             while ( cursor.previous() ) // apply ldifs in reverse order
744             {
745                 ChangeLogEvent event = cursor.get();
746                 List<LdifEntry> reverses = event.getReverseLdifs();
747                 
748                 for ( LdifEntry reverse:reverses )
749                 {
750                     switch( reverse.getChangeType().getChangeType() )
751                     {
752                         case( ChangeType.ADD_ORDINAL ):
753                             adminSession.add( 
754                                 new DefaultServerEntry( registries, reverse.getEntry() ) ); 
755                             break;
756                             
757                         case( ChangeType.DELETE_ORDINAL ):
758                             adminSession.delete( reverse.getDn() );
759                             break;
760                             
761                         case( ChangeType.MODIFY_ORDINAL ):
762                             List<Modification> mods = reverse.getModificationItems();
763     
764                             adminSession.modify( reverse.getDn(), mods );
765                             break;
766                             
767                         case( ChangeType.MODDN_ORDINAL ):
768                             // NO BREAK - both ModDN and ModRDN handling is the same
769                         
770                         case( ChangeType.MODRDN_ORDINAL ):
771                             LdapDN forwardDn = event.getForwardLdif().getDn();
772                             LdapDN reverseDn = reverse.getDn();
773                             
774                             moddn( reverseDn, forwardDn, reverse.isDeleteOldRdn() );
775     
776                             break;
777                             
778                         default:
779                             LOG.error( "ChangeType unknown" );
780                             throw new NotImplementedException( "Reverts of change type " + reverse.getChangeType()
781                                     + " has not yet been implemented!");
782                     }
783                 }
784             }
785         }
786         catch ( IOException e )
787         {
788             String message = "Encountered a failure while trying to revert to a previous revision: "
789                 + revision;
790             LOG.error( message );
791             throw new NamingException( message );
792         }
793 
794         return changeLog.getCurrentRevision();
795     }
796 
797     
798     public OperationManager getOperationManager()
799     {
800         return operationManager;
801     }
802     
803 
804     /**
805      * @throws NamingException if the LDAP server cannot be started
806      */
807     public synchronized void startup() throws Exception
808     {
809         if ( started )
810         {
811             return;
812         }
813 
814         if ( shutdownHookEnabled )
815         {
816             Runtime.getRuntime().addShutdownHook( new Thread( new Runnable()
817             {
818                 public void run()
819                 {
820                     try
821                     {
822                         shutdown();
823                     }
824                     catch ( Exception e )
825                     {
826                         LOG.warn( "Failed to shut down the directory service: "
827                             + DefaultDirectoryService.this.instanceId, e );
828                     }
829                 }
830             }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) );
831 
832             LOG.info( "ApacheDS shutdown hook has been registered with the runtime." );
833         }
834         else if ( LOG.isWarnEnabled() )
835         {
836             LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime."
837                 + "  This default setting for standalone operation has been overriden." );
838         }
839 
840         initialize();
841         showSecurityWarnings();
842         started = true;
843 
844         if ( !testEntries.isEmpty() )
845         {
846             createTestEntries();
847         }
848     }
849 
850 
851     public synchronized void sync() throws Exception
852     {
853         if ( !started )
854         {
855             return;
856         }
857 
858         this.changeLog.sync();
859         this.partitionNexus.sync();
860     }
861 
862 
863     public synchronized void shutdown() throws Exception
864     {
865         if ( !started )
866         {
867             return;
868         }
869 
870         this.changeLog.sync();
871         this.changeLog.destroy();
872 
873         this.partitionNexus.sync();
874         this.partitionNexus.destroy();
875         this.interceptorChain.destroy();
876         this.started = false;
877         setDefaultInterceptorConfigurations();
878     }
879 
880 
881     public Registries getRegistries()
882     {
883         return registries;
884     }
885 
886 
887     public void setRegistries( Registries registries )
888     {
889         this.registries = registries;
890     }
891 
892 
893     public SchemaService getSchemaService()
894     {
895         return schemaService;
896     }
897 
898 
899     public void setSchemaService( SchemaService schemaService )
900     {
901         this.schemaService = schemaService;
902     }
903 
904 
905     public PartitionNexus getPartitionNexus()
906     {
907         return partitionNexus;
908     }
909 
910 
911     public InterceptorChain getInterceptorChain()
912     {
913         return interceptorChain;
914     }
915 
916 
917     public boolean isFirstStart()
918     {
919         return firstStart;
920     }
921 
922 
923     public boolean isStarted()
924     {
925         return started;
926     }
927 
928 
929     public ServerEntry newEntry( LdapDN dn ) 
930     {
931         return new DefaultServerEntry( registries, dn );
932     }
933     
934 
935     /**
936      * Returns true if we had to create the bootstrap entries on the first
937      * start of the server.  Otherwise if all entries exist, meaning none
938      * had to be created, then we are not starting for the first time.
939      *
940      * @return true if the bootstrap entries had to be created, false otherwise
941      * @throws javax.naming.NamingException if entries cannot be created
942      */
943     private boolean createBootstrapEntries() throws Exception
944     {
945         boolean firstStart = false;
946         
947         // -------------------------------------------------------------------
948         // create admin entry
949         // -------------------------------------------------------------------
950 
951         /*
952          * If the admin entry is there, then the database was already created
953          */
954         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, PartitionNexus.getAdminName() ) ) )
955         {
956             firstStart = true;
957 
958             ServerEntry serverEntry = new DefaultServerEntry( registries, PartitionNexus.getAdminName() );
959             
960             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
961                                 SchemaConstants.TOP_OC,
962                                 SchemaConstants.PERSON_OC,
963                                 SchemaConstants.ORGANIZATIONAL_PERSON_OC,
964                                 SchemaConstants.INET_ORG_PERSON_OC );
965 
966             serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID );
967             serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES );
968             serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
969             serverEntry.put( SchemaConstants.CN_AT, "system administrator" );
970             serverEntry.put( SchemaConstants.SN_AT, "administrator" );
971             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
972             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
973             serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
974 
975             TlsKeyGenerator.addKeyPair( serverEntry );
976             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
977         }
978 
979         // -------------------------------------------------------------------
980         // create system users area
981         // -------------------------------------------------------------------
982 
983         Map<String,OidNormalizer> oidsMap = registries.getAttributeTypeRegistry().getNormalizerMapping();
984         LdapDN userDn = new LdapDN( ServerDNConstants.USERS_SYSTEM_DN );
985         userDn.normalize( oidsMap );
986         
987         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, userDn ) ) )
988         {
989             firstStart = true;
990 
991             ServerEntry serverEntry = new DefaultServerEntry( registries, userDn );
992             
993             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
994                                 SchemaConstants.TOP_OC,
995                                 SchemaConstants.ORGANIZATIONAL_UNIT_OC );
996 
997             serverEntry.put( SchemaConstants.OU_AT, "users" );
998             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
999             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1000 
1001             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1002         }
1003 
1004         // -------------------------------------------------------------------
1005         // create system groups area
1006         // -------------------------------------------------------------------
1007 
1008         LdapDN groupDn = new LdapDN( ServerDNConstants.GROUPS_SYSTEM_DN );
1009         groupDn.normalize( oidsMap );
1010         
1011         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, groupDn ) ) )
1012         {
1013             firstStart = true;
1014 
1015             ServerEntry serverEntry = new DefaultServerEntry( registries, groupDn );
1016             
1017             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
1018                                 SchemaConstants.TOP_OC,
1019                                 SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1020 
1021             serverEntry.put( SchemaConstants.OU_AT, "groups" );
1022             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1023             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1024 
1025             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1026         }
1027 
1028         // -------------------------------------------------------------------
1029         // create administrator group
1030         // -------------------------------------------------------------------
1031 
1032         LdapDN name = new LdapDN( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
1033         name.normalize( oidsMap );
1034         
1035         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, name ) ) )
1036         {
1037             firstStart = true;
1038 
1039             ServerEntry serverEntry = new DefaultServerEntry( registries, name );
1040             
1041             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
1042                                 SchemaConstants.TOP_OC,
1043                                 SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC );
1044 
1045             serverEntry.put( SchemaConstants.CN_AT, "Administrators" );
1046             serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1047             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1048             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1049 
1050             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1051 
1052             // TODO - confirm if we need this at all since the 
1053             // group cache on initialization after this stage will
1054             // search the directory for all the groups anyway
1055             
1056 //            Interceptor authzInterceptor = interceptorChain.get( AciAuthorizationInterceptor.class.getName() );
1057 //            
1058 //            if ( authzInterceptor == null )
1059 //            {
1060 //                LOG.error( "The Authorization service is null : this is not allowed" );
1061 //                throw new NamingException( "The Authorization service is null" );
1062 //            }
1063 //            
1064 //            if ( !( authzInterceptor instanceof AciAuthorizationInterceptor ) )
1065 //            {
1066 //                LOG.error( "The Authorization service is not set correctly : '{}' is an incorect interceptor",
1067 //                    authzInterceptor.getClass().getName() );
1068 //                throw new NamingException( "The Authorization service is incorrectly set" );
1069 //                
1070 //            }
1071 //
1072 //            AciAuthorizationInterceptor authzSrvc = ( AciAuthorizationInterceptor ) authzInterceptor;
1073 //            authzSrvc.cacheNewGroup( name, serverEntry );
1074         }
1075 
1076         // -------------------------------------------------------------------
1077         // create system configuration area
1078         // -------------------------------------------------------------------
1079 
1080         LdapDN configurationDn = new LdapDN( "ou=configuration,ou=system" );
1081         configurationDn.normalize( oidsMap );
1082         
1083         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, configurationDn ) ) )
1084         {
1085             firstStart = true;
1086 
1087             ServerEntry serverEntry = new DefaultServerEntry( registries, configurationDn );
1088             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1089 
1090             serverEntry.put( SchemaConstants.OU_AT, "configuration" );
1091             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1092             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1093 
1094             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1095         }
1096 
1097         // -------------------------------------------------------------------
1098         // create system configuration area for partition information
1099         // -------------------------------------------------------------------
1100 
1101         LdapDN partitionsDn = new LdapDN( "ou=partitions,ou=configuration,ou=system" );
1102         partitionsDn.normalize( oidsMap );
1103         
1104         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, partitionsDn ) ) )
1105         {
1106             firstStart = true;
1107 
1108             ServerEntry serverEntry = new DefaultServerEntry( registries, partitionsDn );
1109             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1110 
1111             serverEntry.put( SchemaConstants.OU_AT, "partitions" );
1112             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1113             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1114 
1115             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1116         }
1117 
1118         // -------------------------------------------------------------------
1119         // create system configuration area for services
1120         // -------------------------------------------------------------------
1121 
1122         LdapDN servicesDn = new LdapDN( "ou=services,ou=configuration,ou=system" );
1123         servicesDn.normalize( oidsMap );
1124         
1125         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, servicesDn ) ) )
1126         {
1127             firstStart = true;
1128 
1129             ServerEntry serverEntry = new DefaultServerEntry( registries, servicesDn );
1130             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1131 
1132             serverEntry.put( SchemaConstants.OU_AT, "services" );
1133             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1134             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1135 
1136             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1137         }
1138 
1139         // -------------------------------------------------------------------
1140         // create system configuration area for interceptors
1141         // -------------------------------------------------------------------
1142 
1143         LdapDN interceptorsDn = new LdapDN( "ou=interceptors,ou=configuration,ou=system" );
1144         interceptorsDn.normalize( oidsMap );
1145         
1146         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, interceptorsDn ) ) )
1147         {
1148             firstStart = true;
1149 
1150             ServerEntry serverEntry = new DefaultServerEntry( registries, interceptorsDn );
1151             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1152 
1153             serverEntry.put( SchemaConstants.OU_AT, "interceptors" );
1154             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1155             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1156 
1157             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1158         }
1159 
1160         // -------------------------------------------------------------------
1161         // create system preferences area
1162         // -------------------------------------------------------------------
1163 
1164         LdapDN sysPrefRootDn = new LdapDN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
1165         sysPrefRootDn.normalize( oidsMap );
1166         
1167         if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, sysPrefRootDn ) ) )
1168         {
1169             firstStart = true;
1170 
1171             ServerEntry serverEntry = new DefaultServerEntry( registries, sysPrefRootDn );
1172             serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
1173                 SchemaConstants.TOP_OC, 
1174                 SchemaConstants.ORGANIZATIONAL_UNIT_OC,
1175                 SchemaConstants.EXTENSIBLE_OBJECT_OC );
1176 
1177             serverEntry.put( "prefNodeName", "sysPrefRoot" );
1178             serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1179             serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1180 
1181             partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1182         }
1183 
1184         return firstStart;
1185     }
1186 
1187 
1188     /**
1189      * Displays security warning messages if any possible secutiry issue is found.
1190      * @throws NamingException if there are failures parsing and accessing internal structures
1191      */
1192     private void showSecurityWarnings() throws Exception
1193     {
1194         // Warn if the default password is not changed.
1195         boolean needToChangeAdminPassword = false;
1196 
1197         LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );
1198         adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
1199         
1200         ServerEntry adminEntry = partitionNexus.lookup( new LookupOperationContext( adminSession, adminDn ) );
1201         Object userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get();
1202         
1203         if ( userPassword instanceof byte[] )
1204         {
1205             needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, ( byte[] ) userPassword );
1206         }
1207         else if ( userPassword.toString().equals( PartitionNexus.ADMIN_PASSWORD_STRING ) )
1208         {
1209             needToChangeAdminPassword = PartitionNexus.ADMIN_PASSWORD_STRING.equals( userPassword.toString() );
1210         }
1211 
1212         if ( needToChangeAdminPassword )
1213         {
1214             LOG.warn( "You didn't change the admin password of directory service " + "instance '" + instanceId + "'.  "
1215                 + "Please update the admin password as soon as possible " + "to prevent a possible security breach." );
1216         }
1217     }
1218 
1219 
1220     /**
1221      * Adds test entries into the core.
1222      *
1223      * @todo this may no longer be needed when JNDI is not used for bootstrapping
1224      * 
1225      * @throws NamingException if the creation of test entries fails.
1226      */
1227     private void createTestEntries() throws Exception
1228     {
1229         for ( LdifEntry testEntry : testEntries )
1230         {
1231             try
1232             {
1233                 LdifEntry ldifEntry = testEntry.clone();
1234                 Entry entry = ldifEntry.getEntry();
1235                 String dn = ldifEntry.getDn().getUpName();
1236 
1237                 try
1238                 {
1239                     getAdminSession().add( 
1240                         new DefaultServerEntry( registries, entry ) ); 
1241                 }
1242                 catch ( Exception e )
1243                 {
1244                     LOG.warn( dn + " test entry already exists.", e );
1245                 }
1246             }
1247             catch ( CloneNotSupportedException cnse )
1248             {
1249                 LOG.warn( "Cannot clone the entry ", cnse );
1250             }
1251         }
1252     }
1253 
1254 
1255     /**
1256      * Kicks off the initialization of the entire system.
1257      *
1258      * @throws javax.naming.NamingException if there are problems along the way
1259      */
1260     private void initialize() throws Exception
1261     {
1262         if ( LOG.isDebugEnabled() )
1263         {
1264             LOG.debug( "---> Initializing the DefaultDirectoryService " );
1265         }
1266 
1267         // --------------------------------------------------------------------
1268         // If not present extract schema partition from jar
1269         // --------------------------------------------------------------------
1270 
1271         File schemaDirectory = new File( workingDirectory, "schema" );
1272         SchemaPartitionExtractor extractor;
1273         if ( ! schemaDirectory.exists() )
1274         {
1275             try
1276             {
1277                 extractor = new SchemaPartitionExtractor( workingDirectory );
1278                 extractor.extract();
1279             }
1280             catch ( IOException e )
1281             {
1282                 NamingException ne = new NamingException( "Failed to extract pre-loaded schema partition." );
1283                 ne.setRootCause( e );
1284                 throw ne;
1285             }
1286         }
1287         
1288         // --------------------------------------------------------------------
1289         // Initialize schema partition
1290         // --------------------------------------------------------------------
1291 
1292         JdbmPartition schemaPartition = new JdbmPartition();
1293         schemaPartition.setId( "schema" );
1294         schemaPartition.setCacheSize( 1000 );
1295 
1296         DbFileListing listing;
1297         
1298         try 
1299         {
1300             listing = new DbFileListing();
1301         }
1302         catch( IOException e )
1303         {
1304             throw new LdapNamingException( "Got IOException while trying to read DBFileListing: " + e.getMessage(), 
1305                 ResultCodeEnum.OTHER );
1306         }
1307 
1308         Set<Index<?,ServerEntry>> indexedAttributes = new HashSet<Index<?,ServerEntry>>();
1309         
1310         for ( String attributeId : listing.getIndexedAttributes() )
1311         {
1312             indexedAttributes.add( new JdbmIndex<Object,ServerEntry>( attributeId ) );
1313         }
1314 
1315         schemaPartition.setIndexedAttributes( indexedAttributes );
1316         schemaPartition.setSuffix( ServerDNConstants.OU_SCHEMA_DN );
1317         schemaPartition.init( this );
1318 
1319         // --------------------------------------------------------------------
1320         // Enable schemas of all indices of partition configurations 
1321         // --------------------------------------------------------------------
1322 
1323         /*
1324          * We need to make sure that every attribute indexed by a partition is
1325          * loaded into the registries on the next step.  So here we must enable
1326          * the schemas of those attributes so they are loaded into the global
1327          * registries.
1328          */
1329         
1330         SchemaPartitionDao dao = new SchemaPartitionDao( schemaPartition, registries );
1331         Map<String,Schema> schemaMap = dao.getSchemas();
1332         Set<Partition> partitions = new HashSet<Partition>();
1333         partitions.add( systemPartition );
1334         partitions.addAll( this.partitions );
1335 
1336         for ( Partition partition : partitions )
1337         {
1338             if ( partition instanceof BTreePartition )
1339             {
1340                 JdbmPartition btpconf = ( JdbmPartition ) partition;
1341                 for ( Index<?,ServerEntry> index : btpconf.getIndexedAttributes() )
1342                 {
1343                     String schemaName = dao.findSchema( index.getAttributeId() );
1344                     if ( schemaName == null )
1345                     {
1346                         throw new NamingException( "Index on unidentified attribute: " + index.toString() );
1347                     }
1348 
1349                     Schema schema = schemaMap.get( schemaName );
1350                     if ( schema.isDisabled() )
1351                     {
1352                         dao.enableSchema( schemaName );
1353                     }
1354                 }
1355             }
1356         }
1357         
1358         // --------------------------------------------------------------------
1359         // Initialize schema subsystem and reset registries
1360         // --------------------------------------------------------------------
1361         
1362         PartitionSchemaLoader schemaLoader = new PartitionSchemaLoader( schemaPartition, registries );
1363         Registries globalRegistries = new DefaultRegistries( "global", schemaLoader, registries.getOidRegistry() );
1364         schemaLoader.loadEnabled( globalRegistries );
1365         registries = globalRegistries;
1366         SerializableComparator.setRegistry( globalRegistries.getComparatorRegistry() );
1367 
1368         SchemaOperationControl schemaControl = new SchemaOperationControl( registries, schemaLoader,
1369             new SchemaPartitionDao( schemaPartition, registries ) );
1370 
1371         schemaService = new SchemaService( registries, schemaPartition, schemaControl );
1372 
1373         adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1374         adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
1375         adminSession = new DefaultCoreSession( new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), this );
1376 
1377         partitionNexus = new DefaultPartitionNexus( new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN ) );
1378         partitionNexus.init( this );
1379         partitionNexus.addContextPartition( new AddContextPartitionOperationContext( adminSession, schemaPartition ) );
1380 
1381         // Create all the bootstrap entries before initializing chain
1382         firstStart = createBootstrapEntries();
1383 
1384         interceptorChain = new InterceptorChain();
1385         interceptorChain.init( this );
1386 
1387         if ( changeLog.isEnabled() )
1388         {
1389             changeLog.init( this );
1390         }
1391 
1392         if ( LOG.isDebugEnabled() )
1393         {
1394             LOG.debug( "<--- DefaultDirectoryService initialized" );
1395         }
1396     }
1397     
1398     
1399     /**
1400      * Read an entry (without DN)
1401      * 
1402      * @param text The ldif format file
1403      * @return An entry.
1404      */
1405     private Entry readEntry( String text )
1406     {
1407         StringReader strIn = new StringReader( text );
1408         BufferedReader in = new BufferedReader( strIn );
1409 
1410         String line = null;
1411         Entry entry = new DefaultClientEntry();
1412 
1413         try
1414         {
1415             while ( ( line = in.readLine() ) != null )
1416             {
1417                 if ( line.length() == 0 )
1418                 {
1419                     continue;
1420                 }
1421 
1422                 String addedLine = line.trim();
1423 
1424                 if ( StringTools.isEmpty( addedLine ) )
1425                 {
1426                     continue;
1427                 }
1428 
1429                 EntryAttribute attribute = AttributeUtils.toClientAttribute( 
1430                     LdifReader.parseAttributeValue( addedLine ) );
1431                 EntryAttribute oldAttribute = entry.get( attribute.getId() );
1432 
1433                 if ( oldAttribute != null )
1434                 {
1435                     try
1436                     {
1437                         oldAttribute.add( attribute.get() );
1438                         entry.put( oldAttribute );
1439                     }
1440                     catch ( NamingException ne )
1441                     {
1442                         // Do nothing
1443                     }
1444                 }
1445                 else
1446                 {
1447                     try
1448                     {
1449                         entry.put( attribute );
1450                     }
1451                     catch ( NamingException ne )
1452                     {
1453                         // TODO do nothing ...
1454                     }
1455                 }
1456             }
1457         }
1458         catch (IOException ioe)
1459         {
1460             // Do nothing : we can't reach this point !
1461         }
1462 
1463         return entry;
1464     }
1465 
1466     
1467     /**
1468      * Create a new ServerEntry
1469      * 
1470      * @param ldif The String representing the attributes, as a LDIF file
1471      * @param dn The DN for this new entry
1472      */
1473     public ServerEntry newEntry( String ldif, String dn )
1474     {
1475         try
1476         {
1477             Entry entry = readEntry( ldif );
1478             LdapDN ldapDn = new LdapDN( dn );
1479             
1480             entry.setDn( ldapDn );
1481             
1482             // TODO Let's get rid of this Attributes crap
1483             ServerEntry serverEntry = new DefaultServerEntry( registries, entry );
1484             return serverEntry;
1485         }
1486         catch ( Exception e )
1487         {
1488             LOG.error( "Cannot build an entry for '{}' and this DN :'{}'", ldif, dn );
1489             // do nothing
1490             return null;
1491         }
1492     }
1493 
1494 
1495     public EventService getEventService()
1496     {
1497         return eventService;
1498     }
1499 
1500 
1501     public void setEventService( EventService eventService )
1502     {
1503         this.eventService = eventService;
1504     }
1505 }