001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.server.core;
021
022
023import java.io.BufferedReader;
024import java.io.File;
025import java.io.FileNotFoundException;
026import java.io.IOException;
027import java.io.RandomAccessFile;
028import java.io.StringReader;
029import java.lang.reflect.Method;
030import java.nio.channels.FileLock;
031import java.nio.channels.OverlappingFileLockException;
032import java.security.MessageDigest;
033import java.util.ArrayList;
034import java.util.HashSet;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038import java.util.UUID;
039import java.util.concurrent.ConcurrentHashMap;
040import java.util.concurrent.locks.Lock;
041import java.util.concurrent.locks.ReadWriteLock;
042import java.util.concurrent.locks.ReentrantReadWriteLock;
043
044import org.apache.directory.api.ldap.codec.api.LdapApiService;
045import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
046import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
047import org.apache.directory.api.ldap.model.constants.SchemaConstants;
048import org.apache.directory.api.ldap.model.csn.Csn;
049import org.apache.directory.api.ldap.model.csn.CsnFactory;
050import org.apache.directory.api.ldap.model.cursor.Cursor;
051import org.apache.directory.api.ldap.model.entry.Attribute;
052import org.apache.directory.api.ldap.model.entry.DefaultEntry;
053import org.apache.directory.api.ldap.model.entry.Entry;
054import org.apache.directory.api.ldap.model.entry.Modification;
055import org.apache.directory.api.ldap.model.entry.Value;
056import org.apache.directory.api.ldap.model.exception.LdapException;
057import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException;
058import org.apache.directory.api.ldap.model.exception.LdapOperationException;
059import org.apache.directory.api.ldap.model.exception.LdapOtherException;
060import org.apache.directory.api.ldap.model.ldif.ChangeType;
061import org.apache.directory.api.ldap.model.ldif.LdifEntry;
062import org.apache.directory.api.ldap.model.ldif.LdifReader;
063import org.apache.directory.api.ldap.model.name.Dn;
064import org.apache.directory.api.ldap.model.name.DnUtils;
065import org.apache.directory.api.ldap.model.name.Rdn;
066import org.apache.directory.api.ldap.model.schema.SchemaManager;
067import org.apache.directory.api.ldap.util.tree.DnNode;
068import org.apache.directory.api.util.TimeProvider;
069import org.apache.directory.api.util.DateUtils;
070import org.apache.directory.api.util.Strings;
071import org.apache.directory.api.util.exception.NotImplementedException;
072import org.apache.directory.server.constants.ApacheSchemaConstants;
073import org.apache.directory.server.constants.ServerDNConstants;
074import org.apache.directory.server.core.admin.AdministrativePointInterceptor;
075import org.apache.directory.server.core.api.AttributeTypeProvider;
076import org.apache.directory.server.core.api.CoreSession;
077import org.apache.directory.server.core.api.DirectoryService;
078import org.apache.directory.server.core.api.DnFactory;
079import org.apache.directory.server.core.api.InstanceLayout;
080import org.apache.directory.server.core.api.InterceptorEnum;
081import org.apache.directory.server.core.api.LdapPrincipal;
082import org.apache.directory.server.core.api.ObjectClassProvider;
083import org.apache.directory.server.core.api.OperationEnum;
084import org.apache.directory.server.core.api.OperationManager;
085import org.apache.directory.server.core.api.ReferralManager;
086import org.apache.directory.server.core.api.administrative.AccessControlAdministrativePoint;
087import org.apache.directory.server.core.api.administrative.CollectiveAttributeAdministrativePoint;
088import org.apache.directory.server.core.api.administrative.SubschemaAdministrativePoint;
089import org.apache.directory.server.core.api.administrative.TriggerExecutionAdministrativePoint;
090import org.apache.directory.server.core.api.changelog.ChangeLog;
091import org.apache.directory.server.core.api.changelog.ChangeLogEvent;
092import org.apache.directory.server.core.api.changelog.Tag;
093import org.apache.directory.server.core.api.changelog.TaggableSearchableChangeLogStore;
094import org.apache.directory.server.core.api.event.EventService;
095import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
096import org.apache.directory.server.core.api.interceptor.Interceptor;
097import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
098import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
099import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
100import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
101import org.apache.directory.server.core.api.interceptor.context.OperationContext;
102import org.apache.directory.server.core.api.journal.Journal;
103import org.apache.directory.server.core.api.partition.Partition;
104import org.apache.directory.server.core.api.partition.PartitionNexus;
105import org.apache.directory.server.core.api.partition.PartitionTxn;
106import org.apache.directory.server.core.api.schema.SchemaPartition;
107import org.apache.directory.server.core.api.subtree.SubentryCache;
108import org.apache.directory.server.core.api.subtree.SubtreeEvaluator;
109import org.apache.directory.server.core.authn.AuthenticationInterceptor;
110import org.apache.directory.server.core.authn.ppolicy.PpolicyConfigContainer;
111import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
112import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
113import org.apache.directory.server.core.changelog.ChangeLogInterceptor;
114import org.apache.directory.server.core.changelog.DefaultChangeLog;
115import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
116import org.apache.directory.server.core.event.EventInterceptor;
117import org.apache.directory.server.core.exception.ExceptionInterceptor;
118import org.apache.directory.server.core.journal.DefaultJournal;
119import org.apache.directory.server.core.journal.JournalInterceptor;
120import org.apache.directory.server.core.normalization.NormalizationInterceptor;
121import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
122import org.apache.directory.server.core.referral.ReferralInterceptor;
123import org.apache.directory.server.core.schema.SchemaInterceptor;
124import org.apache.directory.server.core.shared.DefaultCoreSession;
125import org.apache.directory.server.core.shared.DefaultDnFactory;
126import org.apache.directory.server.core.shared.partition.DefaultPartitionNexus;
127import org.apache.directory.server.core.subtree.SubentryInterceptor;
128import org.apache.directory.server.core.trigger.TriggerInterceptor;
129import org.apache.directory.server.i18n.I18n;
130import org.slf4j.Logger;
131import org.slf4j.LoggerFactory;
132
133
134/**
135 * Default implementation of {@link DirectoryService}.
136 *
137 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
138 */
139public class DefaultDirectoryService implements DirectoryService
140{
141    /** The logger */
142    private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class );
143
144    private SchemaPartition schemaPartition;
145
146    /** A reference on the SchemaManager */
147    private SchemaManager schemaManager;
148
149    /** The LDAP Codec Service */
150    private LdapApiService ldapCodecService = LdapApiServiceFactory.getSingleton();
151
152    /** the root nexus */
153    private DefaultPartitionNexus partitionNexus;
154
155    /** whether or not server is started for the first time */
156    private boolean firstStart;
157
158    /** whether or not this instance has been shutdown */
159    private boolean started;
160
161    /** the change log service */
162    private ChangeLog changeLog;
163
164    /** the journal service */
165    private Journal journal;
166
167    /**
168     * the interface used to perform various operations on this
169     * DirectoryService
170     */
171    private OperationManager operationManager = new DefaultOperationManager( this );
172
173    /** the distinguished name of the administrative user */
174    private Dn adminDn;
175
176    /** session used as admin for internal operations */
177    private CoreSession adminSession;
178
179    /** The referral manager */
180    private ReferralManager referralManager;
181
182    /** A flag to tell if the userPassword attribute's value must be hidden */
183    private boolean passwordHidden = false;
184
185    /** The service's CSN factory */
186    private CsnFactory csnFactory;
187
188    /** The directory instance replication ID */
189    private int replicaId;
190
191    /** remove me after implementation is completed */
192    private static final String PARTIAL_IMPL_WARNING =
193        "WARNING: the changelog is only partially operational and will revert\n"
194            + "state without consideration of who made the original change.  All reverting "
195            + "changes are made by the admin user.\n Furthermore the used controls are not at "
196            + "all taken into account";
197
198    /** The delay to wait between each sync on disk */
199    private long syncPeriodMillis;
200
201    /** The default delay to wait between sync on disk : 15 seconds */
202    private static final long DEFAULT_SYNC_PERIOD = 15000;
203
204    /** The default timeLimit : 100 entries */
205    public static final int MAX_SIZE_LIMIT_DEFAULT = 100;
206
207    /** The default timeLimit : 10 seconds */
208    public static final int MAX_TIME_LIMIT_DEFAULT = 10000;
209
210    /** The instance Id */
211    private String instanceId;
212
213    /** The server directory layout*/
214    private InstanceLayout instanceLayout;
215
216    /**
217     * A flag used to shutdown the VM when stopping the server. Useful
218     * when the server is standalone. If the server is embedded, we don't
219     * want to shutdown the VM
220     */
221    private boolean exitVmOnShutdown = true; // allow by default
222
223    /** A flag used to indicate that a shutdown hook has been installed */
224    private boolean shutdownHookEnabled = true; // allow by default
225
226    /** Manage anonymous access to entries other than the RootDSE */
227    private boolean allowAnonymousAccess = false; // forbid by default
228
229    /** Manage the basic access control checks */
230    private boolean accessControlEnabled; // off by default
231
232    /** Manage the operational attributes denormalization */
233    private boolean denormalizeOpAttrsEnabled; // off by default
234
235    /** The list of declared interceptors */
236    private List<Interceptor> interceptors;
237    private Map<String, Interceptor> interceptorNames;
238
239    /** A lock to protect the interceptors List */
240    private ReadWriteLock interceptorsLock = new ReentrantReadWriteLock();
241
242    /** The read and write locks */
243    private Lock readLock = interceptorsLock.readLock();
244    private Lock writeLock = interceptorsLock.writeLock();
245
246    /** A map associating a list of interceptor to each operation */
247    private Map<OperationEnum, List<String>> operationInterceptors;
248
249    /** The System partition */
250    private Partition systemPartition;
251
252    /** The set of all declared partitions */
253    private Set<Partition> partitions = new HashSet<>();
254
255    /** A list of LDIF entries to inject at startup */
256    private List<? extends LdifEntry> testEntries = new ArrayList<>(); // List<Attributes>
257
258    /** The event service */
259    private EventService eventService;
260
261    /** The maximum size for an incoming PDU */
262    private int maxPDUSize = Integer.MAX_VALUE;
263
264    /** lock file for directory service's working directory */
265    private RandomAccessFile lockFile = null;
266
267    private static final String LOCK_FILE_NAME = ".dirservice.lock";
268
269    /** The AccessControl AdministrativePoint cache */
270    private DnNode<AccessControlAdministrativePoint> accessControlAPCache;
271
272    /** The CollectiveAttribute AdministrativePoint cache */
273    private DnNode<CollectiveAttributeAdministrativePoint> collectiveAttributeAPCache;
274
275    /** The Subschema AdministrativePoint cache */
276    private DnNode<SubschemaAdministrativePoint> subschemaAPCache;
277
278    /** The TriggerExecution AdministrativePoint cache */
279    private DnNode<TriggerExecutionAdministrativePoint> triggerExecutionAPCache;
280
281    /** The Dn factory */
282    private DnFactory dnFactory;
283
284    /** The Subentry cache */
285    SubentryCache subentryCache = new SubentryCache();
286
287    /** The Subtree evaluator instance */
288    private SubtreeEvaluator evaluator;
289
290    /** The attribute type provider */
291    private AttributeTypeProvider atProvider;
292
293    /** The object class provider */
294    private ObjectClassProvider ocProvider;
295    
296    private TimeProvider timeProvider;
297
298
299    // ------------------------------------------------------------------------
300    // Constructor
301    // ------------------------------------------------------------------------
302
303    /**
304     * Creates a new instance of the directory service.
305     * 
306     * @throws LdapException If the instance cannot be created
307     */
308    public DefaultDirectoryService() throws LdapException
309    {
310        changeLog = new DefaultChangeLog();
311        journal = new DefaultJournal();
312        syncPeriodMillis = DEFAULT_SYNC_PERIOD;
313        csnFactory = new CsnFactory( replicaId );
314        evaluator = new SubtreeEvaluator( schemaManager );
315        setDefaultInterceptorConfigurations();
316        timeProvider = TimeProvider.DEFAULT;
317    }
318
319
320    // ------------------------------------------------------------------------
321    // C O N F I G U R A T I O N   M E T H O D S
322    // ------------------------------------------------------------------------
323
324    public void setInstanceId( String instanceId )
325    {
326        this.instanceId = instanceId;
327    }
328
329
330    public String getInstanceId()
331    {
332        return instanceId;
333    }
334
335
336    /**
337     * Gets the {@link Partition}s used by this DirectoryService.
338     *
339     * @return the set of partitions used
340     */
341    public Set<? extends Partition> getPartitions()
342    {
343        Set<Partition> cloned = new HashSet<>();
344        cloned.addAll( partitions );
345        return cloned;
346    }
347
348
349    /**
350     * Sets {@link Partition}s used by this DirectoryService.
351     *
352     * @param partitions the partitions to used
353     */
354    public void setPartitions( Set<? extends Partition> partitions )
355    {
356        Set<Partition> cloned = new HashSet<>();
357        cloned.addAll( partitions );
358        Set<String> names = new HashSet<>();
359
360        for ( Partition partition : cloned )
361        {
362            String id = partition.getId();
363
364            if ( names.contains( id ) )
365            {
366                LOG.warn( "Encountered duplicate partition {} identifier.", id );
367            }
368
369            names.add( id );
370        }
371
372        this.partitions = cloned;
373    }
374
375
376    /**
377     * Returns <tt>true</tt> if access control checks are enabled.
378     *
379     * @return true if access control checks are enabled, false otherwise
380     */
381    public boolean isAccessControlEnabled()
382    {
383        return accessControlEnabled;
384    }
385
386
387    /**
388     * Sets whether to enable basic access control checks or not.
389     *
390     * @param accessControlEnabled true to enable access control checks, false otherwise
391     */
392    public void setAccessControlEnabled( boolean accessControlEnabled )
393    {
394        this.accessControlEnabled = accessControlEnabled;
395    }
396
397
398    /**
399     * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE.
400     * If the access control subsystem is enabled then access to some entries may not be
401     * allowed even when full anonymous access is enabled.
402     *
403     * @return true if anonymous access is allowed on entries besides the RootDSE, false
404     * if anonymous access is allowed to all entries.
405     */
406    public boolean isAllowAnonymousAccess()
407    {
408        return allowAnonymousAccess;
409    }
410
411
412    /**
413     * Sets whether to allow anonymous access to entries other than the RootDSE.  If the
414     * access control subsystem is enabled then access to some entries may not be allowed
415     * even when full anonymous access is enabled.
416     *
417     * @param enableAnonymousAccess true to enable anonymous access, false to disable it
418     */
419    public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
420    {
421        this.allowAnonymousAccess = enableAnonymousAccess;
422    }
423
424
425    /**
426     * Returns interceptors in the server.
427     *
428     * @return the interceptors in the server.
429     */
430    public List<Interceptor> getInterceptors()
431    {
432        List<Interceptor> cloned = new ArrayList<>();
433
434        readLock.lock();
435
436        try
437        {
438            cloned.addAll( interceptors );
439
440            return cloned;
441        }
442        finally
443        {
444            readLock.unlock();
445        }
446    }
447
448
449    /**
450     * Returns interceptors in the server for a given operation.
451     *
452     * @return the interceptors in the server for the given operation.
453     */
454    public List<String> getInterceptors( OperationEnum operation )
455    {
456        List<String> cloned = new ArrayList<>();
457
458        readLock.lock();
459
460        try
461        {
462            cloned.addAll( operationInterceptors.get( operation ) );
463
464            return cloned;
465        }
466        finally
467        {
468            readLock.unlock();
469        }
470
471    }
472
473
474    /**
475     * Compute the list of  to call for each operation
476     */
477    private void initOperationsList()
478    {
479        writeLock.lock();
480
481        try
482        {
483            operationInterceptors = new ConcurrentHashMap<>();
484
485            for ( OperationEnum operation : OperationEnum.getOperations() )
486            {
487                List<String> operationList = new ArrayList<>();
488
489                for ( Interceptor interceptor : interceptors )
490                {
491                    gatherInterceptors( interceptor, interceptor.getClass(), operation, operationList );
492                }
493
494                operationInterceptors.put( operation, operationList );
495            }
496        }
497        finally
498        {
499            writeLock.unlock();
500        }
501    }
502
503
504    /**
505     * Recursively checks if the given interceptor can be added to the list of interceptors for a given
506     * operation and adds to the list of interceptors if it implements the respective operation
507     * 
508     * @param interceptor the instance of the interceptor
509     * @param interceptorClz the class of the interceptor
510     * @param operation type of operation
511     * @param selectedInterceptorList the list of selected interceptors
512     */
513    private void gatherInterceptors( Interceptor interceptor, Class<?> interceptorClz, OperationEnum operation,
514        List<String> selectedInterceptorList )
515    {
516        // We stop recursing when we reach the Base class
517        if ( ( interceptorClz == null ) || ( interceptorClz == BaseInterceptor.class ) )
518        {
519            return;
520        }
521
522        // We don't call getMethods() because it would get back the default methods
523        // from the BaseInterceptor, something we don't want.
524        Method[] methods = interceptorClz.getDeclaredMethods();
525
526        for ( Method method : methods )
527        {
528            Class<?>[] param = method.getParameterTypes();
529
530            // check for the correct signature
531            if ( ( param != null ) && ( param.length == 1 ) 
532                    && OperationContext.class.isAssignableFrom( param[0] ) && method.getName().equals( operation.getMethodName() ) )
533            {
534                if ( !selectedInterceptorList.contains( interceptor.getName() ) )
535                {
536                    selectedInterceptorList.add( interceptor.getName() );
537                }
538
539                break;
540            }
541        }
542
543        // Recurse on extended classes, as we have used getDeclaredMethods() instead of getmethods()
544        gatherInterceptors( interceptor, interceptorClz.getSuperclass(), operation, selectedInterceptorList );
545    }
546
547
548    /**
549     * Add an interceptor to the list of interceptors to call for each operation
550     * @throws LdapException
551     */
552    private void addInterceptor( Interceptor interceptor, int position ) throws LdapException
553    {
554        // First, init the interceptor
555        interceptor.init( this );
556
557        writeLock.lock();
558
559        try
560        {
561            for ( OperationEnum operation : OperationEnum.getOperations() )
562            {
563                List<String> operationList = operationInterceptors.get( operation );
564
565                Method[] methods = interceptor.getClass().getDeclaredMethods();
566
567                for ( Method method : methods )
568                {
569                    if ( method.getName().equals( operation.getMethodName() ) )
570                    {
571                        if ( position == -1 )
572                        {
573                            operationList.add( interceptor.getName() );
574                        }
575                        else
576                        {
577                            operationList.add( position, interceptor.getName() );
578                        }
579
580                        break;
581                    }
582                }
583            }
584
585            interceptorNames.put( interceptor.getName(), interceptor );
586
587            if ( position == -1 )
588            {
589                interceptors.add( interceptor );
590            }
591            else
592            {
593                interceptors.add( position, interceptor );
594            }
595        }
596        finally
597        {
598            writeLock.unlock();
599        }
600    }
601
602
603    /**
604     * Remove an interceptor to the list of interceptors to call for each operation
605     */
606    private void removeOperationsList( String interceptorName )
607    {
608        Interceptor interceptor = interceptorNames.get( interceptorName );
609
610        writeLock.lock();
611
612        try
613        {
614            for ( OperationEnum operation : OperationEnum.getOperations() )
615            {
616                List<String> operationList = operationInterceptors.get( operation );
617
618                Method[] methods = interceptor.getClass().getDeclaredMethods();
619
620                for ( Method method : methods )
621                {
622                    if ( method.getName().equals( operation.getMethodName() ) )
623                    {
624                        operationList.remove( interceptor.getName() );
625
626                        break;
627                    }
628                }
629            }
630
631            interceptorNames.remove( interceptorName );
632            interceptors.remove( interceptor );
633        }
634        finally
635        {
636            writeLock.unlock();
637        }
638    }
639
640
641    /**
642     * Sets the interceptors in the server.
643     *
644     * @param interceptors the interceptors to be used in the server.
645     */
646    public void setInterceptors( List<Interceptor> interceptors )
647    {
648        Map<String, Interceptor> interceptorNames = new ConcurrentHashMap<>();
649
650        // Check if we don't have duplicate names in the interceptors list
651        for ( Interceptor interceptor : interceptors )
652        {
653            if ( interceptorNames.containsKey( interceptor.getName() ) )
654            {
655                LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() );
656                continue;
657            }
658
659            interceptorNames.put( interceptor.getName(), interceptor );
660        }
661
662        this.interceptors = interceptors;
663        this.interceptorNames = interceptorNames;
664
665        // Now update the Map that connect each operation with the list of interceptors.
666        initOperationsList();
667    }
668
669
670    /**
671     * Initialize the interceptors
672     */
673    private void initInterceptors() throws LdapException
674    {
675        for ( Interceptor interceptor : interceptors )
676        {
677            interceptor.init( this );
678        }
679    }
680
681
682    /**
683     * Returns test directory entries({@link LdifEntry}) to be loaded while
684     * bootstrapping.
685     *
686     * @return test entries to load during bootstrapping
687     */
688    public List<LdifEntry> getTestEntries()
689    {
690        List<LdifEntry> cloned = new ArrayList<>();
691        cloned.addAll( testEntries );
692
693        return cloned;
694    }
695
696
697    /**
698     * Sets test directory entries to be loaded while bootstrapping.
699     *
700     * @param testEntries the test entries to load while bootstrapping
701     */
702    public void setTestEntries( List<? extends LdifEntry> testEntries )
703    {
704        //noinspection MismatchedQueryAndUpdateOfCollection
705        List<LdifEntry> cloned = new ArrayList<>();
706        cloned.addAll( testEntries );
707        this.testEntries = testEntries;
708    }
709
710
711    /**
712     * {@inheritDoc}
713     */
714    public InstanceLayout getInstanceLayout()
715    {
716        return instanceLayout;
717    }
718
719
720    /**
721     * {@inheritDoc}
722     */
723    public void setInstanceLayout( InstanceLayout instanceLayout ) throws IOException
724    {
725        this.instanceLayout = instanceLayout;
726
727        // Create the directories if they are missing
728        if ( !instanceLayout.getInstanceDirectory().exists() && !instanceLayout.getInstanceDirectory().mkdirs() )
729        {
730            throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECTORY,
731                instanceLayout.getInstanceDirectory() ) );
732        }
733
734        if ( !instanceLayout.getLogDirectory().exists() && !instanceLayout.getLogDirectory().mkdirs() )
735        {
736            throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECTORY,
737                instanceLayout.getLogDirectory() ) );
738        }
739
740        if ( !instanceLayout.getRunDirectory().exists() && !instanceLayout.getRunDirectory().mkdirs() )
741        {
742            throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECTORY,
743                instanceLayout.getRunDirectory() ) );
744        }
745
746        if ( !instanceLayout.getPartitionsDirectory().exists() && !instanceLayout.getPartitionsDirectory().mkdirs() )
747        {
748            throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECTORY,
749                instanceLayout.getPartitionsDirectory() ) );
750        }
751
752        if ( !instanceLayout.getConfDirectory().exists() && !instanceLayout.getConfDirectory().mkdirs() )
753        {
754            throw new IOException( I18n.err( I18n.ERR_112_COULD_NOT_CREATE_DIRECTORY,
755                instanceLayout.getConfDirectory() ) );
756        }
757    }
758
759
760    public void setShutdownHookEnabled( boolean shutdownHookEnabled )
761    {
762        this.shutdownHookEnabled = shutdownHookEnabled;
763    }
764
765
766    public boolean isShutdownHookEnabled()
767    {
768        return shutdownHookEnabled;
769    }
770
771
772    public void setExitVmOnShutdown( boolean exitVmOnShutdown )
773    {
774        this.exitVmOnShutdown = exitVmOnShutdown;
775    }
776
777
778    public boolean isExitVmOnShutdown()
779    {
780        return exitVmOnShutdown;
781    }
782
783
784    public void setSystemPartition( Partition systemPartition )
785    {
786        this.systemPartition = systemPartition;
787    }
788
789
790    public Partition getSystemPartition()
791    {
792        return systemPartition;
793    }
794
795
796    /**
797     * return true if the operational attributes must be normalized when returned
798     */
799    public boolean isDenormalizeOpAttrsEnabled()
800    {
801        return denormalizeOpAttrsEnabled;
802    }
803
804
805    /**
806     * Sets whether the operational attributes are denormalized when returned
807     * @param denormalizeOpAttrsEnabled The flag value
808     */
809    public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
810    {
811        this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled;
812    }
813
814
815    /**
816     * {@inheritDoc}
817     */
818    public ChangeLog getChangeLog()
819    {
820        return changeLog;
821    }
822
823
824    /**
825     * {@inheritDoc}
826     */
827    public Journal getJournal()
828    {
829        return journal;
830    }
831
832
833    /**
834     * {@inheritDoc}
835     */
836    public void setChangeLog( ChangeLog changeLog )
837    {
838        this.changeLog = changeLog;
839    }
840
841
842    /**
843     * {@inheritDoc}
844     */
845    public void setJournal( Journal journal )
846    {
847        this.journal = journal;
848    }
849
850
851    /**
852     * {@inheritDoc}
853     */
854    public void addPartition( Partition partition ) throws LdapException
855    {
856        partition.setSchemaManager( schemaManager );
857
858        // can be null when called before starting up
859        if ( partitionNexus != null )
860        {
861            partitionNexus.addContextPartition( partition );
862        }
863
864        // Now, add the partition to the set of managed partitions
865        partitions.add( partition );
866    }
867
868
869    /**
870     * {@inheritDoc}
871     */
872    public void removePartition( Partition partition ) throws LdapException
873    {
874        // Do the backend cleanup first
875        // can be null when called before starting up
876        if ( partitionNexus != null )
877        {
878            partitionNexus.removeContextPartition( partition.getSuffixDn().getNormName() );
879        }
880
881        // And update the set of managed partitions
882        partitions.remove( partition );
883    }
884
885
886    // ------------------------------------------------------------------------
887    // BackendSubsystem Interface Method Implementations
888    // ------------------------------------------------------------------------
889    /**
890     * Define a default list of interceptors that has to be used if no other
891     * configuration is defined.
892     */
893    private void setDefaultInterceptorConfigurations()
894    {
895        // Set default interceptor chains
896        List<Interceptor> list = new ArrayList<>();
897
898        list.add( new NormalizationInterceptor() );
899        list.add( new AuthenticationInterceptor() );
900        list.add( new ReferralInterceptor() );
901        list.add( new AciAuthorizationInterceptor() );
902        list.add( new DefaultAuthorizationInterceptor() );
903        list.add( new AdministrativePointInterceptor() );
904        list.add( new ExceptionInterceptor() );
905        list.add( new SchemaInterceptor() );
906        list.add( new OperationalAttributeInterceptor() );
907        list.add( new CollectiveAttributeInterceptor() );
908        list.add( new SubentryInterceptor() );
909        list.add( new EventInterceptor() );
910        list.add( new TriggerInterceptor() );
911        list.add( new ChangeLogInterceptor() );
912        list.add( new JournalInterceptor() );
913
914        setInterceptors( list );
915    }
916
917
918    public CoreSession getAdminSession()
919    {
920        return adminSession;
921    }
922
923
924    /**
925     * Get back an anonymous session
926     */
927    public CoreSession getSession()
928    {
929        return new DefaultCoreSession( new LdapPrincipal( schemaManager ), this );
930    }
931
932
933    /** 
934     * Get back a session for a given principal
935     */
936    public CoreSession getSession( LdapPrincipal principal )
937    {
938        return new DefaultCoreSession( principal, this );
939    }
940
941
942    /**
943     * Get back a session for the give user and credentials bound with Simple Bind
944     */
945    public CoreSession getSession( Dn principalDn, byte[] credentials ) throws LdapException
946    {
947        synchronized ( this )
948        {
949            if ( !started )
950            {
951                throw new IllegalStateException( "Service has not started." );
952            }
953        }
954
955        BindOperationContext bindContext = new BindOperationContext( null );
956        bindContext.setCredentials( credentials );
957        
958        if ( principalDn.isSchemaAware() )
959        {
960            bindContext.setDn( principalDn );
961        }
962        else
963        {
964            bindContext.setDn( new Dn( schemaManager, principalDn ) );
965        }
966        
967        bindContext.setInterceptors( getInterceptors( OperationEnum.BIND ) );
968
969        operationManager.bind( bindContext );
970
971        return bindContext.getSession();
972    }
973
974
975    /**
976     * Get back a session for a given user bound with SASL Bind
977     */
978    public CoreSession getSession( Dn principalDn, byte[] credentials, String saslMechanism, String saslAuthId )
979        throws LdapException
980    {
981        synchronized ( this )
982        {
983            if ( !started )
984            {
985                throw new IllegalStateException( "Service has not started." );
986
987            }
988        }
989
990        BindOperationContext bindContext = new BindOperationContext( null );
991        bindContext.setTransaction( partitionNexus.beginReadTransaction() );
992        bindContext.setCredentials( credentials );
993
994        if ( principalDn.isSchemaAware() )
995        {
996            bindContext.setDn( principalDn );
997        }
998        else
999        {
1000            bindContext.setDn( new Dn( schemaManager, principalDn ) );
1001        }
1002
1003        bindContext.setSaslMechanism( saslMechanism );
1004        bindContext.setInterceptors( getInterceptors( OperationEnum.BIND ) );
1005
1006        operationManager.bind( bindContext );
1007
1008        return bindContext.getSession();
1009    }
1010
1011
1012    public long revert() throws LdapException
1013    {
1014        if ( changeLog == null || !changeLog.isEnabled() )
1015        {
1016            throw new IllegalStateException( I18n.err( I18n.ERR_310 ) );
1017        }
1018
1019        Tag latest = changeLog.getLatest();
1020
1021        if ( null != latest )
1022        {
1023            if ( latest.getRevision() < changeLog.getCurrentRevision() )
1024            {
1025                return revert( latest.getRevision() );
1026            }
1027            else
1028            {
1029                LOG.info( "Ignoring request to revert without changes since the latest tag." );
1030                return changeLog.getCurrentRevision();
1031            }
1032        }
1033
1034        throw new IllegalStateException( I18n.err( I18n.ERR_311 ) );
1035    }
1036
1037
1038    /**
1039     * We handle the ModDN/ModRDN operation for the revert here.
1040     */
1041    private void moddn( Dn oldDn, Dn newDn, boolean delOldRdn ) throws LdapException
1042    {
1043        if ( oldDn.size() == 0 )
1044        {
1045            throw new LdapNoPermissionException( I18n.err( I18n.ERR_312 ) );
1046        }
1047
1048        // calculate parents
1049        Dn oldBase = oldDn.getParent();
1050        Dn newBase = newDn.getParent();
1051
1052        // Compute the Rdn for each of the Dn
1053        Rdn newRdn = newDn.getRdn();
1054        Rdn oldRdn = oldDn.getRdn();
1055
1056        /*
1057         * We need to determine if this rename operation corresponds to a simple
1058         * Rdn name change or a move operation.  If the two names are the same
1059         * except for the Rdn then it is a simple modifyRdn operation.  If the
1060         * names differ in size or have a different baseDN then the operation is
1061         * a move operation.  Furthermore if the Rdn in the move operation
1062         * changes it is both an Rdn change and a move operation.
1063         */
1064        if ( ( oldDn.size() == newDn.size() ) && oldBase.equals( newBase ) )
1065        {
1066            adminSession.rename( oldDn, newRdn, delOldRdn );
1067        }
1068        else
1069        {
1070            Dn target = newDn.getParent();
1071
1072            if ( newRdn.equals( oldRdn ) )
1073            {
1074                adminSession.move( oldDn, target );
1075            }
1076            else
1077            {
1078                adminSession.moveAndRename( oldDn, target, newRdn, delOldRdn );
1079            }
1080        }
1081    }
1082
1083
1084    public long revert( long revision ) throws LdapException
1085    {
1086        if ( changeLog == null || !changeLog.isEnabled() )
1087        {
1088            throw new IllegalStateException( I18n.err( I18n.ERR_310 ) );
1089        }
1090
1091        if ( revision < 0 )
1092        {
1093            throw new IllegalArgumentException( I18n.err( I18n.ERR_239 ) );
1094        }
1095
1096        if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() )
1097        {
1098            throw new IllegalArgumentException( I18n.err( I18n.ERR_314 ) );
1099        }
1100
1101        Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision );
1102
1103        /*
1104         * BAD, BAD, BAD!!!
1105         *
1106         * No synchronization no nothing.  Just getting this to work for now
1107         * so we can revert tests.  Any production grade use of this feature
1108         * needs to synchronize on all changes while the revert is in progress.
1109         *
1110         * How about making this operation transactional?
1111         *
1112         * First of all just stop using JNDI and construct the operations to
1113         * feed into the interceptor pipeline.
1114         *
1115         * TODO review this code.
1116         */
1117        PartitionTxn transaction = systemPartition.beginWriteTransaction();
1118        // Speedup the addition by using a global transaction
1119        adminSession.addTransaction( systemPartition, transaction );
1120        adminSession.beginSessionTransaction();
1121
1122        try
1123        {
1124            LOG.warn( PARTIAL_IMPL_WARNING );
1125            cursor.afterLast();
1126
1127            while ( cursor.previous() ) // apply ldifs in reverse order
1128            {
1129                ChangeLogEvent event = cursor.get();
1130                List<LdifEntry> reverses = event.getReverseLdifs();
1131
1132                for ( LdifEntry reverse : reverses )
1133                {
1134                    switch ( reverse.getChangeType().getChangeType() )
1135                    {
1136                        case ChangeType.ADD_ORDINAL:
1137                            adminSession.add(
1138                                new DefaultEntry( schemaManager, reverse.getEntry() ), true );
1139                            break;
1140
1141                        case ChangeType.DELETE_ORDINAL:
1142                            adminSession.delete( reverse.getDn(), true );
1143                            break;
1144
1145                        case ChangeType.MODIFY_ORDINAL:
1146                            List<Modification> mods = reverse.getModifications();
1147
1148                            adminSession.modify( reverse.getDn(), mods, true );
1149                            break;
1150
1151                        case ChangeType.MODDN_ORDINAL:
1152                            // NO BREAK - both ModDN and ModRDN handling is the same
1153
1154                        case ChangeType.MODRDN_ORDINAL:
1155                            Dn forwardDn = event.getForwardLdif().getDn();
1156                            Dn reverseDn = reverse.getDn();
1157
1158                            moddn( reverseDn, forwardDn, reverse.isDeleteOldRdn() );
1159
1160                            break;
1161
1162                        default:
1163                            LOG.error( I18n.err( I18n.ERR_75 ) );
1164                            throw new NotImplementedException( I18n.err( I18n.ERR_76, reverse.getChangeType() ) );
1165                    }
1166                }
1167                
1168                adminSession.endSessionTransaction( true );
1169            }
1170        }
1171        catch ( Exception e )
1172        {
1173            try
1174            {
1175                adminSession.endSessionTransaction( false );
1176            }
1177            catch ( IOException ioe )
1178            {
1179                throw new LdapOperationException( ioe.getMessage(), ioe );
1180            }
1181            
1182            throw new LdapOperationException( e.getMessage(), e );
1183        }
1184        finally
1185        {
1186            try
1187            {
1188                cursor.close();
1189            }
1190            catch ( Exception e )
1191            {
1192                throw new LdapOperationException( e.getMessage(), e );
1193            }
1194        }
1195
1196        return changeLog.getCurrentRevision();
1197    }
1198
1199
1200    public OperationManager getOperationManager()
1201    {
1202        return operationManager;
1203    }
1204
1205
1206    /**
1207     * @throws LdapException if the LDAP server cannot be started
1208     */
1209    public synchronized void startup() throws LdapException
1210    {
1211        if ( started )
1212        {
1213            return;
1214        }
1215
1216        lockWorkDir();
1217
1218        if ( shutdownHookEnabled )
1219        {
1220            Runtime.getRuntime().addShutdownHook( new Thread( new Runnable()
1221            {
1222                public void run()
1223                {
1224                    try
1225                    {
1226                        shutdown();
1227                    }
1228                    catch ( Exception e )
1229                    {
1230                        LOG.warn( "Failed to shut down the directory service: "
1231                            + DefaultDirectoryService.this.instanceId, e );
1232                    }
1233                }
1234            }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) );
1235
1236            LOG.info( "ApacheDS shutdown hook has been registered with the runtime." );
1237        }
1238        else if ( LOG.isWarnEnabled() )
1239        {
1240            LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime."
1241                + "  This default setting for standalone operation has been overriden." );
1242        }
1243
1244        initialize();
1245        showSecurityWarnings();
1246
1247        started = true;
1248
1249        if ( !testEntries.isEmpty() )
1250        {
1251            createTestEntries();
1252        }
1253    }
1254
1255
1256    public synchronized void sync() throws LdapException
1257    {
1258        if ( !started )
1259        {
1260            return;
1261        }
1262
1263        this.changeLog.sync();
1264        this.partitionNexus.sync();
1265    }
1266
1267
1268    public synchronized void shutdown() throws LdapException
1269    {
1270        LOG.debug( "+++ DirectoryService Shutdown required" );
1271
1272        if ( !started )
1273        {
1274            return;
1275        }
1276
1277        // --------------------------------------------------------------------
1278        // Shutdown the sync thread
1279        // --------------------------------------------------------------------
1280        LOG.debug( "--- Syncing the nexus " );
1281        LOG.debug( "--- Flushing everything before quitting" );
1282        operationManager.lockWrite();
1283        partitionNexus.sync();
1284        operationManager.unlockWrite();
1285
1286        // --------------------------------------------------------------------
1287        // Shutdown the changelog
1288        // --------------------------------------------------------------------
1289        LOG.debug( "--- Syncing the changeLog " );
1290        changeLog.sync();
1291        changeLog.destroy();
1292
1293        // --------------------------------------------------------------------
1294        // Shutdown the journal if enabled
1295        // --------------------------------------------------------------------
1296        if ( journal.isEnabled() )
1297        {
1298            LOG.debug( "--- Destroying the journal " );
1299            journal.destroy();
1300        }
1301
1302        
1303        // --------------------------------------------------------------------
1304        // Shutdown the partition
1305        // --------------------------------------------------------------------
1306
1307        LOG.debug( "--- Destroying the nexus" );
1308        partitionNexus.destroy( null );
1309        
1310        // --------------------------------------------------------------------
1311        // Shutdown the interceptors
1312        // --------------------------------------------------------------------
1313        LOG.debug( "--- Destroying the interceptors" );
1314        
1315        for ( Interceptor interceptor : interceptors )
1316        {
1317            interceptor.destroy();
1318        }
1319
1320        // --------------------------------------------------------------------
1321        // And shutdown the server
1322        // --------------------------------------------------------------------
1323        LOG.debug( "---Deleting the DnCache" );
1324        dnFactory = null;
1325
1326        if ( lockFile != null )
1327        {
1328            try
1329            {
1330                lockFile.close();
1331                // no need to delete the lock file
1332            }
1333            catch ( IOException e )
1334            {
1335                LOG.warn( "couldn't delete the lock file {}", LOCK_FILE_NAME );
1336            }
1337        }
1338
1339        LOG.debug( "+++ DirectoryService stopped" );
1340        started = false;
1341    }
1342
1343
1344    /**
1345     * @return The referral manager
1346     */
1347    public ReferralManager getReferralManager()
1348    {
1349        return referralManager;
1350    }
1351
1352
1353    /**
1354     * Set the referralManager
1355     * @param referralManager The initialized referralManager
1356     */
1357    public void setReferralManager( ReferralManager referralManager )
1358    {
1359        this.referralManager = referralManager;
1360    }
1361
1362
1363    /**
1364     * @return the SchemaManager
1365     */
1366    public SchemaManager getSchemaManager()
1367    {
1368        return schemaManager;
1369    }
1370
1371
1372    /**
1373     * Set the SchemaManager instance.
1374     *
1375     * @param schemaManager The server schemaManager
1376     */
1377    public void setSchemaManager( SchemaManager schemaManager )
1378    {
1379        this.schemaManager = schemaManager;
1380    }
1381
1382
1383    public LdapApiService getLdapCodecService()
1384    {
1385        return ldapCodecService;
1386    }
1387
1388
1389    /**
1390     * {@inheritDoc}
1391     */
1392    public SchemaPartition getSchemaPartition()
1393    {
1394        return schemaPartition;
1395    }
1396
1397
1398    /**
1399     * {@inheritDoc}
1400     */
1401    public void setSchemaPartition( SchemaPartition schemaPartition )
1402    {
1403        this.schemaPartition = schemaPartition;
1404    }
1405
1406
1407    public DefaultPartitionNexus getPartitionNexus()
1408    {
1409        return partitionNexus;
1410    }
1411
1412
1413    public boolean isFirstStart()
1414    {
1415        return firstStart;
1416    }
1417
1418
1419    public synchronized boolean isStarted()
1420    {
1421        return started;
1422    }
1423
1424
1425    public Entry newEntry( Dn dn )
1426    {
1427        return new DefaultEntry( schemaManager, dn );
1428    }
1429    
1430    
1431    /**
1432     * Add a new entry into the server
1433     */
1434    private void addEntry( Entry serverEntry ) throws LdapException
1435    {
1436        Partition partition = partitionNexus.getPartition( serverEntry.getDn() );
1437        AddOperationContext addContext = new AddOperationContext( adminSession, serverEntry );
1438        PartitionTxn partitionTxn = null;
1439
1440        try
1441        {
1442            partitionTxn = partition.beginWriteTransaction();
1443            addContext.setTransaction( partitionTxn );
1444            addContext.setPartition( partition );
1445            partitionNexus.add( addContext );
1446            partitionTxn.commit();
1447        }
1448        catch ( LdapException le )
1449        {
1450            try
1451            {
1452                partitionTxn.abort();
1453            }
1454            catch ( IOException ioe )
1455            {
1456                throw new LdapOtherException( ioe.getMessage(), ioe );
1457            }
1458            
1459            throw le;
1460        }
1461        catch ( IOException ioe )
1462        {
1463            try
1464            {
1465                partitionTxn.abort();
1466            }
1467            catch ( IOException ioe2 )
1468            {
1469                throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1470            }
1471            
1472            throw new LdapOtherException( ioe.getMessage(), ioe );
1473        }
1474    }
1475
1476
1477    /**
1478     * Returns true if we had to create the bootstrap entries on the first
1479     * start of the server.  Otherwise if all entries exist, meaning none
1480     * had to be created, then we are not starting for the first time.
1481     *
1482     * @return true if the bootstrap entries had to be created, false otherwise
1483     * @throws LdapException if entries cannot be created
1484     */
1485    private boolean createBootstrapEntries() throws LdapException, IOException
1486    {
1487        boolean firstStart = false;
1488
1489        // -------------------------------------------------------------------
1490        // create admin entry
1491        // -------------------------------------------------------------------
1492
1493        /*
1494         * If the admin entry is there, then the database was already created
1495         */
1496        Partition partition = partitionNexus.getPartition( adminDn );
1497        
1498        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1499        {
1500            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, adminDn );
1501            hasEntryContext.setPartition( partition );
1502            hasEntryContext.setTransaction( partitionTxn );
1503            
1504            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1505            {
1506                firstStart = true;
1507    
1508                Entry serverEntry = new DefaultEntry( schemaManager, adminDn );
1509    
1510                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1511                    SchemaConstants.TOP_OC,
1512                    SchemaConstants.PERSON_OC,
1513                    SchemaConstants.ORGANIZATIONAL_PERSON_OC,
1514                    SchemaConstants.INET_ORG_PERSON_OC );
1515    
1516                serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID );
1517                serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES );
1518                serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
1519                serverEntry.put( SchemaConstants.CN_AT, "system administrator" );
1520                serverEntry.put( SchemaConstants.SN_AT, "administrator" );
1521                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1522                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1523                serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
1524                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1525                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1526    
1527                addEntry( serverEntry );
1528            }
1529        }
1530
1531        // -------------------------------------------------------------------
1532        // create system users area
1533        // -------------------------------------------------------------------
1534
1535        Dn userDn = getDnFactory().create( ServerDNConstants.USERS_SYSTEM_DN );
1536        partition = partitionNexus.getPartition( userDn );
1537
1538        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1539        {
1540            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, userDn );
1541            hasEntryContext.setPartition( partition );
1542            hasEntryContext.setTransaction( partitionTxn );
1543            
1544            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1545            {
1546                firstStart = true;
1547    
1548                Entry serverEntry = new DefaultEntry( schemaManager, userDn );
1549    
1550                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1551                    SchemaConstants.TOP_OC,
1552                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1553    
1554                serverEntry.put( SchemaConstants.OU_AT, "users" );
1555                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1556                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1557                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1558                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1559                
1560                addEntry( serverEntry );
1561            }
1562        }
1563
1564        // -------------------------------------------------------------------
1565        // create system groups area
1566        // -------------------------------------------------------------------
1567
1568        Dn groupDn = getDnFactory().create( ServerDNConstants.GROUPS_SYSTEM_DN );
1569        partition = partitionNexus.getPartition( groupDn );
1570
1571        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1572        {
1573            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, groupDn );
1574            hasEntryContext.setPartition( partition );
1575            hasEntryContext.setTransaction( partitionTxn );
1576            
1577            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1578            {
1579                firstStart = true;
1580    
1581                Entry serverEntry = new DefaultEntry( schemaManager, groupDn );
1582    
1583                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1584                    SchemaConstants.TOP_OC,
1585                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1586    
1587                serverEntry.put( SchemaConstants.OU_AT, "groups" );
1588                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1589                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1590                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1591                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1592    
1593                addEntry( serverEntry );
1594            }
1595        }
1596        
1597        // -------------------------------------------------------------------
1598        // create administrator group
1599        // -------------------------------------------------------------------
1600
1601        Dn name = getDnFactory().create( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
1602        partition = partitionNexus.getPartition( name );
1603
1604        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1605        {
1606            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, name );
1607            hasEntryContext.setPartition( partition );
1608            hasEntryContext.setTransaction( partitionTxn );
1609            
1610            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1611            {
1612                firstStart = true;
1613    
1614                Entry serverEntry = new DefaultEntry( schemaManager, name );
1615    
1616                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1617                    SchemaConstants.TOP_OC,
1618                    SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC );
1619    
1620                serverEntry.put( SchemaConstants.CN_AT, "Administrators" );
1621                serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1622                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1623                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1624                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1625                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1626    
1627                addEntry( serverEntry );
1628            }
1629        }
1630
1631        // -------------------------------------------------------------------
1632        // create system configuration area
1633        // -------------------------------------------------------------------
1634
1635        Dn configurationDn = getDnFactory().create( "ou=configuration,ou=system" );
1636        partition = partitionNexus.getPartition( configurationDn );
1637
1638        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1639        {
1640            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, configurationDn );
1641            hasEntryContext.setPartition( partition );
1642            hasEntryContext.setTransaction( partitionTxn );
1643            
1644            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1645            {
1646                firstStart = true;
1647    
1648                Entry serverEntry = new DefaultEntry( schemaManager, configurationDn );
1649                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
1650                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1651    
1652                serverEntry.put( SchemaConstants.OU_AT, "configuration" );
1653                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1654                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1655                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1656                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1657    
1658                addEntry( serverEntry );
1659            }
1660        }
1661
1662        // -------------------------------------------------------------------
1663        // create system configuration area for partition information
1664        // -------------------------------------------------------------------
1665
1666        Dn partitionsDn = getDnFactory().create( "ou=partitions,ou=configuration,ou=system" );
1667        partition = partitionNexus.getPartition( partitionsDn );
1668
1669        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1670        {
1671            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, partitionsDn );
1672            hasEntryContext.setPartition( partition );
1673            hasEntryContext.setTransaction( partitionTxn );
1674            
1675            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1676            {
1677                firstStart = true;
1678    
1679                Entry serverEntry = new DefaultEntry( schemaManager, partitionsDn );
1680                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
1681                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1682                serverEntry.put( SchemaConstants.OU_AT, "partitions" );
1683                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1684                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1685                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1686                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1687    
1688                addEntry( serverEntry );
1689            }
1690        }
1691
1692        // -------------------------------------------------------------------
1693        // create system configuration area for services
1694        // -------------------------------------------------------------------
1695
1696        Dn servicesDn = getDnFactory().create( "ou=services,ou=configuration,ou=system" );
1697        partition = partitionNexus.getPartition( servicesDn );
1698
1699        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1700        {
1701            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, servicesDn );
1702            hasEntryContext.setPartition( partition );
1703            hasEntryContext.setTransaction( partitionTxn );
1704            
1705            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1706            {
1707                firstStart = true;
1708    
1709                Entry serverEntry = new DefaultEntry( schemaManager, servicesDn );
1710                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
1711                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1712    
1713                serverEntry.put( SchemaConstants.OU_AT, "services" );
1714                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1715                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1716                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1717                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1718    
1719                addEntry( serverEntry );
1720            }
1721        }
1722
1723        // -------------------------------------------------------------------
1724        // create system configuration area for interceptors
1725        // -------------------------------------------------------------------
1726
1727        Dn interceptorsDn = getDnFactory().create( "ou=interceptors,ou=configuration,ou=system" );
1728        partition = partitionNexus.getPartition( interceptorsDn );
1729
1730        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1731        {
1732            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, interceptorsDn );
1733            hasEntryContext.setPartition( partition );
1734            hasEntryContext.setTransaction( partitionTxn );
1735            
1736            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1737            {
1738                firstStart = true;
1739    
1740                Entry serverEntry = new DefaultEntry( schemaManager, interceptorsDn );
1741                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
1742                    SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1743    
1744                serverEntry.put( SchemaConstants.OU_AT, "interceptors" );
1745                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1746                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1747                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1748                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1749    
1750                addEntry( serverEntry );
1751            }
1752        }
1753
1754        // -------------------------------------------------------------------
1755        // create system preferences area
1756        // -------------------------------------------------------------------
1757
1758        Dn sysPrefRootDn = getDnFactory().create( ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
1759        partition = partitionNexus.getPartition( sysPrefRootDn );
1760
1761        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1762        {
1763            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( adminSession, sysPrefRootDn );
1764            hasEntryContext.setPartition( partition );
1765            hasEntryContext.setTransaction( partitionTxn );
1766            
1767            if ( !partitionNexus.hasEntry( hasEntryContext ) )
1768            {
1769                firstStart = true;
1770    
1771                Entry serverEntry = new DefaultEntry( schemaManager, sysPrefRootDn );
1772                serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1773                    SchemaConstants.TOP_OC,
1774                    SchemaConstants.ORGANIZATIONAL_UNIT_OC,
1775                    SchemaConstants.EXTENSIBLE_OBJECT_OC );
1776    
1777                serverEntry.put( "prefNodeName", "sysPrefRoot" );
1778                serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1779                serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1780                serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1781                serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1782    
1783                addEntry( serverEntry );
1784            }
1785        }
1786
1787        return firstStart;
1788    }
1789
1790
1791    /**
1792     * Displays security warning messages if any possible secutiry issue is found.
1793     * @throws LdapException if there are failures parsing and accessing internal structures
1794     */
1795    // made protected as per the request in DIRSERVER-1920
1796    protected void showSecurityWarnings() throws LdapException
1797    {
1798        // Warn if the default password is not changed.
1799        boolean needToChangeAdminPassword;
1800
1801        Dn admin = getDnFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN );
1802        Partition partition = partitionNexus.getPartition( admin );
1803        LookupOperationContext lookupContext = new LookupOperationContext( adminSession, admin );
1804        lookupContext.setPartition( partition );
1805        
1806        Entry adminEntry;
1807
1808        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1809        {
1810            lookupContext.setTransaction( partitionTxn );
1811            adminEntry = partitionNexus.lookup( lookupContext );
1812        }
1813        catch ( IOException ioe )
1814        {
1815            throw new LdapOtherException( ioe.getMessage(), ioe );
1816        }
1817        
1818        Value userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get();
1819        needToChangeAdminPassword = MessageDigest.isEqual( PartitionNexus.ADMIN_PASSWORD_BYTES, userPassword.getBytes() );
1820
1821        if ( needToChangeAdminPassword )
1822        {
1823            LOG.warn( "You didn't change the admin password of directory service instance '{}'.  "
1824                + "Please update the admin password as soon as possible to prevent a possible security breach.", instanceId );
1825        }
1826    }
1827
1828
1829    /**
1830     * Adds test entries into the core.
1831     *
1832     * TODO this may no longer be needed when JNDI is not used for bootstrapping
1833     *
1834     * @throws LdapException if the creation of test entries fails.
1835     */
1836    private void createTestEntries() throws LdapException
1837    {
1838        for ( LdifEntry testEntry : testEntries )
1839        {
1840            try
1841            {
1842                LdifEntry ldifEntry = testEntry.clone();
1843                Entry entry = ldifEntry.getEntry();
1844                String dn = ldifEntry.getDn().getName();
1845
1846                try
1847                {
1848                    getAdminSession().add( new DefaultEntry( schemaManager, entry ) );
1849                }
1850                catch ( Exception e )
1851                {
1852                    LOG.warn( dn + " test entry already exists.", e );
1853                }
1854            }
1855            catch ( CloneNotSupportedException cnse )
1856            {
1857                LOG.warn( "Cannot clone the entry ", cnse );
1858            }
1859        }
1860    }
1861
1862
1863    private void initializeSystemPartition() throws LdapException, IOException
1864    {
1865        Partition system = getSystemPartition();
1866
1867        // Add root context entry for system partition
1868        Dn systemSuffixDn = getDnFactory().create( ServerDNConstants.SYSTEM_DN );
1869        CoreSession admin = getAdminSession();
1870
1871        HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( admin, systemSuffixDn );
1872        Partition partition = getPartitionNexus().getPartition( systemSuffixDn );
1873        hasEntryContext.setPartition( partition );
1874        
1875        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
1876        {
1877            hasEntryContext.setTransaction( partitionTxn );
1878            
1879            if ( !system.hasEntry( hasEntryContext ) )
1880            {
1881                Entry systemEntry = new DefaultEntry( schemaManager, systemSuffixDn );
1882    
1883                // Add the ObjectClasses
1884                systemEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
1885                    SchemaConstants.ORGANIZATIONAL_UNIT_OC, SchemaConstants.EXTENSIBLE_OBJECT_OC );
1886    
1887                // Add some operational attributes
1888                systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
1889                systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime( getTimeProvider() ) );
1890                systemEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1891                systemEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1892                systemEntry.put( DnUtils.getRdnAttributeType( ServerDNConstants.SYSTEM_DN ), DnUtils
1893                    .getRdnValue( ServerDNConstants.SYSTEM_DN ) );
1894    
1895                AddOperationContext addOperationContext = new AddOperationContext( admin, systemEntry );
1896                addOperationContext.setPartition( partition );
1897                
1898                PartitionTxn writeTxn = null;
1899                
1900                try
1901                {
1902                    writeTxn = partition.beginWriteTransaction();
1903                    addOperationContext.setTransaction( writeTxn );
1904                    system.add( addOperationContext );
1905                    
1906                    writeTxn.commit();
1907                }
1908                catch ( LdapException le )
1909                {
1910                    try
1911                    {
1912                        writeTxn.abort();
1913                    }
1914                    catch ( IOException ioe )
1915                    {
1916                        throw new LdapOtherException( ioe.getMessage(), ioe );
1917                    }
1918                    
1919                    throw le;
1920                }
1921                catch ( IOException ioe )
1922                {
1923                    try
1924                    {
1925                        writeTxn.abort();
1926                    }
1927                    catch ( IOException ioe2 )
1928                    {
1929                        throw new LdapOtherException( ioe2.getMessage(), ioe2 );
1930                    }
1931                    
1932                    throw new LdapOtherException( ioe.getMessage(), ioe );
1933                }
1934            }
1935        }
1936    }
1937
1938
1939    /**
1940     * Kicks off the initialization of the entire system.
1941     *
1942     * @throws LdapException if there are problems along the way
1943     */
1944    private void initialize() throws LdapException
1945    {
1946        if ( LOG.isDebugEnabled() )
1947        {
1948            LOG.debug( "---> Initializing the DefaultDirectoryService " );
1949        }
1950
1951        csnFactory.setReplicaId( replicaId );
1952
1953        // If no interceptor list is defined, setup a default list
1954        if ( interceptors == null )
1955        {
1956            setDefaultInterceptorConfigurations();
1957        }
1958
1959        // Initialize the AP caches
1960        accessControlAPCache = new DnNode<>();
1961        collectiveAttributeAPCache = new DnNode<>();
1962        subschemaAPCache = new DnNode<>();
1963        triggerExecutionAPCache = new DnNode<>();
1964
1965        if ( dnFactory == null )
1966        {
1967            dnFactory = new DefaultDnFactory( schemaManager, 10000 );
1968        }
1969
1970        // triggers partition to load schema fully from schema partition
1971        schemaPartition.initialize();
1972        partitions.add( schemaPartition );
1973        
1974        if ( !systemPartition.getSuffixDn().isSchemaAware() )
1975        {
1976            systemPartition.setSuffixDn( new Dn( schemaManager, systemPartition.getSuffixDn() ) );
1977        }
1978
1979        adminDn = getDnFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN );
1980        adminSession = new DefaultCoreSession( new LdapPrincipal( schemaManager, adminDn, AuthenticationLevel.STRONG ),
1981            this );
1982
1983        // TODO - NOTE: Need to find a way to instantiate without dependency on DPN
1984        partitionNexus = new DefaultPartitionNexus( new DefaultEntry( schemaManager, Dn.ROOT_DSE ) );
1985        partitionNexus.setDirectoryService( this );
1986        partitionNexus.initialize();
1987
1988        try
1989        {
1990            initializeSystemPartition();
1991        }
1992        catch ( IOException ioe )
1993        {
1994            throw new LdapException( ioe.getMessage(), ioe );
1995        }
1996
1997        // --------------------------------------------------------------------
1998        // Create all the bootstrap entries before initializing chain
1999        // --------------------------------------------------------------------
2000
2001        try
2002        {
2003            firstStart = createBootstrapEntries();
2004        }
2005        catch ( IOException ioe )
2006        {
2007            throw new LdapException( ioe.getMessage(), ioe );
2008        }
2009
2010        // initialize schema providers
2011        atProvider = new AttributeTypeProvider( schemaManager );
2012        ocProvider = new ObjectClassProvider( schemaManager );
2013
2014        // Initialize the interceptors
2015        initInterceptors();
2016
2017        // --------------------------------------------------------------------
2018        // Initialize the changeLog if it's enabled
2019        // --------------------------------------------------------------------
2020
2021        if ( changeLog.isEnabled() )
2022        {
2023            changeLog.init( this );
2024
2025            if ( changeLog.isExposed() && changeLog.isTagSearchSupported() )
2026            {
2027                String clSuffix = ( ( TaggableSearchableChangeLogStore ) changeLog.getChangeLogStore() ).getPartition()
2028                    .getSuffixDn().getName();
2029                partitionNexus.getRootDse( null ).add( ApacheSchemaConstants.CHANGELOG_CONTEXT_AT, clSuffix );
2030            }
2031        }
2032
2033        // --------------------------------------------------------------------
2034        // Initialize the journal if it's enabled
2035        // --------------------------------------------------------------------
2036        if ( journal.isEnabled() )
2037        {
2038            journal.init( this );
2039        }
2040
2041        if ( LOG.isDebugEnabled() )
2042        {
2043            LOG.debug( "<--- DefaultDirectoryService initialized" );
2044        }
2045    }
2046
2047
2048    /**
2049     * Read an entry (without Dn)
2050     *
2051     * @param text The ldif format file
2052     * @return An entry.
2053     */
2054    private Entry readEntry( String text )
2055    {
2056        StringReader strIn = new StringReader( text );
2057        BufferedReader in = new BufferedReader( strIn );
2058
2059        String line = null;
2060        Entry entry = new DefaultEntry();
2061
2062        try
2063        {
2064            while ( ( line = in.readLine() ) != null )
2065            {
2066                if ( line.length() == 0 )
2067                {
2068                    continue;
2069                }
2070
2071                String addedLine = line.trim();
2072
2073                if ( Strings.isEmpty( addedLine ) )
2074                {
2075                    continue;
2076                }
2077
2078                Attribute attribute = LdifReader.parseAttributeValue( addedLine );
2079                Attribute oldAttribute = entry.get( attribute.getId() );
2080
2081                if ( oldAttribute != null )
2082                {
2083                    try
2084                    {
2085                        oldAttribute.add( attribute.get() );
2086                        entry.put( oldAttribute );
2087                    }
2088                    catch ( LdapException ne )
2089                    {
2090                        // Do nothing
2091                    }
2092                }
2093                else
2094                {
2095                    try
2096                    {
2097                        entry.put( attribute );
2098                    }
2099                    catch ( LdapException ne )
2100                    {
2101                        // TODO do nothing ...
2102                    }
2103                }
2104            }
2105        }
2106        catch ( IOException ioe )
2107        {
2108            // Do nothing : we can't reach this point !
2109        }
2110
2111        return entry;
2112    }
2113
2114
2115    /**
2116     * Create a new Entry
2117     *
2118     * @param ldif The String representing the attributes, as a LDIF file
2119     * @param dn The Dn for this new entry
2120     */
2121    public Entry newEntry( String ldif, String dn )
2122    {
2123        try
2124        {
2125            Entry entry = readEntry( ldif );
2126            Dn newDn = getDnFactory().create( dn );
2127
2128            entry.setDn( newDn );
2129
2130            return new DefaultEntry( schemaManager, entry );
2131        }
2132        catch ( Exception e )
2133        {
2134            LOG.error( I18n.err( I18n.ERR_78, ldif, dn ) );
2135            // do nothing
2136            return null;
2137        }
2138    }
2139
2140
2141    public EventService getEventService()
2142    {
2143        return eventService;
2144    }
2145
2146
2147    public void setEventService( EventService eventService )
2148    {
2149        this.eventService = eventService;
2150    }
2151
2152
2153    /**
2154     * {@inheritDoc}
2155     */
2156    public boolean isPasswordHidden()
2157    {
2158        return passwordHidden;
2159    }
2160
2161
2162    /**
2163     * {@inheritDoc}
2164     */
2165    public void setPasswordHidden( boolean passwordHidden )
2166    {
2167        this.passwordHidden = passwordHidden;
2168    }
2169
2170
2171    /**
2172     * @return The maximum allowed size for an incoming PDU
2173     */
2174    public int getMaxPDUSize()
2175    {
2176        return maxPDUSize;
2177    }
2178
2179
2180    /**
2181     * Set the maximum allowed size for an incoming PDU
2182     * @param maxPDUSize A positive number of bytes for the PDU. A negative or
2183     * null value will be transformed to {@link Integer#MAX_VALUE}
2184     */
2185    public void setMaxPDUSize( int maxPDUSize )
2186    {
2187        if ( maxPDUSize <= 0 )
2188        {
2189            maxPDUSize = Integer.MAX_VALUE;
2190        }
2191
2192        this.maxPDUSize = maxPDUSize;
2193    }
2194
2195
2196    /**
2197     * {@inheritDoc}
2198     */
2199    public Interceptor getInterceptor( String interceptorName )
2200    {
2201        return interceptorNames.get( interceptorName );
2202    }
2203
2204
2205    /**
2206     * {@inheritDoc}
2207     */
2208    public void addFirst( Interceptor interceptor ) throws LdapException
2209    {
2210        addInterceptor( interceptor, 0 );
2211    }
2212
2213
2214    /**
2215     * {@inheritDoc}
2216     */
2217    public void addLast( Interceptor interceptor ) throws LdapException
2218    {
2219        addInterceptor( interceptor, -1 );
2220    }
2221
2222
2223    /**
2224     * {@inheritDoc}
2225     */
2226    public void addAfter( String interceptorName, Interceptor interceptor )
2227    {
2228        writeLock.lock();
2229
2230        try
2231        {
2232            int position = 0;
2233
2234            // Find the position
2235            for ( Interceptor inter : interceptors )
2236            {
2237                if ( interceptorName.equals( inter.getName() ) )
2238                {
2239                    break;
2240                }
2241
2242                position++;
2243            }
2244
2245            if ( position == interceptors.size() )
2246            {
2247                interceptors.add( interceptor );
2248            }
2249            else
2250            {
2251                interceptors.add( position, interceptor );
2252            }
2253        }
2254        finally
2255        {
2256            writeLock.unlock();
2257        }
2258    }
2259
2260
2261    /**
2262     * {@inheritDoc}
2263     */
2264    public void remove( String interceptorName )
2265    {
2266        removeOperationsList( interceptorName );
2267    }
2268
2269
2270    /**
2271     * Get a new CSN
2272     * @return The CSN generated for this directory service
2273     */
2274    public Csn getCSN()
2275    {
2276        return csnFactory.newInstance();
2277    }
2278
2279
2280    /**
2281     * @return the replicaId
2282     */
2283    public int getReplicaId()
2284    {
2285        return replicaId;
2286    }
2287
2288
2289    /**
2290     * @param replicaId the replicaId to set
2291     */
2292    public void setReplicaId( int replicaId )
2293    {
2294        if ( ( replicaId < 0 ) || ( replicaId > 999 ) )
2295        {
2296            LOG.error( I18n.err( I18n.ERR_79 ) );
2297            this.replicaId = 0;
2298        }
2299        else
2300        {
2301            this.replicaId = replicaId;
2302        }
2303    }
2304
2305
2306    /**
2307     * {@inheritDoc}
2308     */
2309    public long getSyncPeriodMillis()
2310    {
2311        return syncPeriodMillis;
2312    }
2313
2314
2315    /**
2316     * {@inheritDoc}
2317     */
2318    public void setSyncPeriodMillis( long syncPeriodMillis )
2319    {
2320        this.syncPeriodMillis = syncPeriodMillis;
2321    }
2322
2323
2324    /**
2325     * checks if the working directory is already in use by some other directory service, if yes
2326     * then throws a runtime exception else will obtain the lock on the working directory
2327     */
2328    private void lockWorkDir()
2329    {
2330        FileLock fileLock = null;
2331
2332        try
2333        {
2334            lockFile = new RandomAccessFile( new File( instanceLayout.getInstanceDirectory(), LOCK_FILE_NAME ), "rw" );
2335            try
2336            {
2337                fileLock = lockFile.getChannel().tryLock( 0, 1, false );
2338            }
2339            catch ( IOException e )
2340            {
2341                // shoudn't happen, but log
2342                LOG.error( "failed to lock the work directory", e );
2343            }
2344            catch ( OverlappingFileLockException e ) // thrown if we can't get a lock
2345            {
2346                fileLock = null;
2347            }
2348        }
2349        catch ( FileNotFoundException e )
2350        {
2351            // shouldn't happen, but log anyway
2352            LOG.error( "failed to lock the work directory", e );
2353        }
2354
2355        if ( ( fileLock == null ) || ( !fileLock.isValid() ) )
2356        {
2357            String message = "the working directory " + instanceLayout.getRunDirectory()
2358                + " has been locked by another directory service.";
2359            LOG.error( message );
2360            throw new RuntimeException( message );
2361        }
2362
2363    }
2364
2365
2366    /**
2367     * {@inheritDoc}
2368     */
2369    public DnNode<AccessControlAdministrativePoint> getAccessControlAPCache()
2370    {
2371        return accessControlAPCache;
2372    }
2373
2374
2375    /**
2376     * {@inheritDoc}
2377     */
2378    public DnNode<CollectiveAttributeAdministrativePoint> getCollectiveAttributeAPCache()
2379    {
2380        return collectiveAttributeAPCache;
2381    }
2382
2383
2384    /**
2385     * {@inheritDoc}
2386     */
2387    public DnNode<SubschemaAdministrativePoint> getSubschemaAPCache()
2388    {
2389        return subschemaAPCache;
2390    }
2391
2392
2393    /**
2394     * {@inheritDoc}
2395     */
2396    public DnNode<TriggerExecutionAdministrativePoint> getTriggerExecutionAPCache()
2397    {
2398        return triggerExecutionAPCache;
2399    }
2400
2401
2402    /**
2403     * {@inheritDoc}
2404     */
2405    public boolean isPwdPolicyEnabled()
2406    {
2407        AuthenticationInterceptor authenticationInterceptor = ( AuthenticationInterceptor ) getInterceptor( InterceptorEnum.AUTHENTICATION_INTERCEPTOR
2408            .getName() );
2409
2410        if ( authenticationInterceptor == null )
2411        {
2412            return false;
2413        }
2414
2415        PpolicyConfigContainer pwdPolicyContainer = authenticationInterceptor.getPwdPolicyContainer();
2416
2417        return ( ( pwdPolicyContainer != null )
2418        && ( ( pwdPolicyContainer.getDefaultPolicy() != null )
2419        || ( pwdPolicyContainer.hasCustomConfigs() ) ) );
2420    }
2421
2422
2423    /**
2424     * {@inheritDoc}
2425     */
2426    public DnFactory getDnFactory()
2427    {
2428        return dnFactory;
2429    }
2430
2431
2432    /**
2433     * {@inheritDoc}
2434     */
2435    public void setDnFactory( DnFactory dnFactory )
2436    {
2437        this.dnFactory = dnFactory;
2438    }
2439
2440
2441    /**
2442     * {@inheritDoc}
2443     */
2444    public SubentryCache getSubentryCache()
2445    {
2446        return subentryCache;
2447    }
2448
2449
2450    /**
2451     * {@inheritDoc}
2452     */
2453    public SubtreeEvaluator getEvaluator()
2454    {
2455        return evaluator;
2456    }
2457
2458
2459    /**
2460     * {@inheritDoc}
2461     */
2462    @Override
2463    public AttributeTypeProvider getAtProvider()
2464    {
2465        return atProvider;
2466    }
2467
2468
2469    /**
2470     * {@inheritDoc}
2471     */
2472    @Override
2473    public ObjectClassProvider getOcProvider()
2474    {
2475        return ocProvider;
2476    }
2477
2478
2479    /**
2480     * {@inheritDoc}
2481     */
2482    @Override
2483    public TimeProvider getTimeProvider()
2484    {
2485        return timeProvider;
2486    }
2487
2488
2489    /**
2490     * {@inheritDoc}
2491     */
2492    @Override
2493    public void setTimeProvider( TimeProvider timeProvider )
2494    {
2495        this.timeProvider = timeProvider;
2496    }
2497}