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 */
020
021package org.apache.directory.server.config.builder;
022
023
024import java.io.File;
025import java.io.FilenameFilter;
026import java.io.IOException;
027import java.lang.reflect.Constructor;
028import java.util.ArrayList;
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034import java.util.TreeSet;
035
036import org.apache.directory.api.ldap.model.exception.LdapException;
037import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
038import org.apache.directory.api.ldap.model.ldif.LdapLdifException;
039import org.apache.directory.api.ldap.model.ldif.LdifEntry;
040import org.apache.directory.api.ldap.model.ldif.LdifReader;
041import org.apache.directory.api.ldap.model.message.AliasDerefMode;
042import org.apache.directory.api.ldap.model.message.ExtendedRequest;
043import org.apache.directory.api.ldap.model.message.ExtendedResponse;
044import org.apache.directory.api.ldap.model.message.SearchScope;
045import org.apache.directory.api.ldap.model.name.Dn;
046import org.apache.directory.api.ldap.model.schema.SchemaManager;
047import org.apache.directory.api.util.Strings;
048import org.apache.directory.server.config.ConfigurationException;
049import org.apache.directory.server.config.beans.AuthenticationInterceptorBean;
050import org.apache.directory.server.config.beans.AuthenticatorBean;
051import org.apache.directory.server.config.beans.AuthenticatorImplBean;
052import org.apache.directory.server.config.beans.ChangeLogBean;
053import org.apache.directory.server.config.beans.ChangePasswordServerBean;
054import org.apache.directory.server.config.beans.DelegatingAuthenticatorBean;
055import org.apache.directory.server.config.beans.DirectoryServiceBean;
056import org.apache.directory.server.config.beans.ExtendedOpHandlerBean;
057import org.apache.directory.server.config.beans.HttpServerBean;
058import org.apache.directory.server.config.beans.HttpWebAppBean;
059import org.apache.directory.server.config.beans.IndexBean;
060import org.apache.directory.server.config.beans.InterceptorBean;
061import org.apache.directory.server.config.beans.JdbmIndexBean;
062import org.apache.directory.server.config.beans.JdbmPartitionBean;
063import org.apache.directory.server.config.beans.JournalBean;
064import org.apache.directory.server.config.beans.KdcServerBean;
065import org.apache.directory.server.config.beans.LdapServerBean;
066import org.apache.directory.server.config.beans.MavibotIndexBean;
067import org.apache.directory.server.config.beans.MavibotPartitionBean;
068import org.apache.directory.server.config.beans.NtpServerBean;
069import org.apache.directory.server.config.beans.PartitionBean;
070import org.apache.directory.server.config.beans.PasswordPolicyBean;
071import org.apache.directory.server.config.beans.ReplConsumerBean;
072import org.apache.directory.server.config.beans.SaslMechHandlerBean;
073import org.apache.directory.server.config.beans.TcpTransportBean;
074import org.apache.directory.server.config.beans.TransportBean;
075import org.apache.directory.server.constants.ApacheSchemaConstants;
076import org.apache.directory.server.core.DefaultDirectoryService;
077import org.apache.directory.server.core.api.DirectoryService;
078import org.apache.directory.server.core.api.InstanceLayout;
079import org.apache.directory.server.core.api.authn.ppolicy.CheckQualityEnum;
080import org.apache.directory.server.core.api.authn.ppolicy.DefaultPasswordValidator;
081import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyConfiguration;
082import org.apache.directory.server.core.api.authn.ppolicy.PasswordValidator;
083import org.apache.directory.server.core.api.changelog.ChangeLog;
084import org.apache.directory.server.core.api.interceptor.Interceptor;
085import org.apache.directory.server.core.api.journal.Journal;
086import org.apache.directory.server.core.api.journal.JournalStore;
087import org.apache.directory.server.core.api.partition.AbstractPartition;
088import org.apache.directory.server.core.api.partition.Partition;
089import org.apache.directory.server.core.authn.AuthenticationInterceptor;
090import org.apache.directory.server.core.authn.Authenticator;
091import org.apache.directory.server.core.authn.DelegatingAuthenticator;
092import org.apache.directory.server.core.authn.ppolicy.PpolicyConfigContainer;
093import org.apache.directory.server.core.changelog.DefaultChangeLog;
094import org.apache.directory.server.core.journal.DefaultJournal;
095import org.apache.directory.server.core.journal.DefaultJournalStore;
096import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmDnIndex;
097import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
098import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
099import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmRdnIndex;
100import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotDnIndex;
101import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotIndex;
102import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotPartition;
103import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotRdnIndex;
104import org.apache.directory.server.i18n.I18n;
105import org.apache.directory.server.integration.http.HttpServer;
106import org.apache.directory.server.integration.http.WebApp;
107import org.apache.directory.server.kerberos.ChangePasswordConfig;
108import org.apache.directory.server.kerberos.KerberosConfig;
109import org.apache.directory.server.kerberos.changepwd.ChangePasswordServer;
110import org.apache.directory.server.kerberos.kdc.KdcServer;
111import org.apache.directory.server.ldap.ExtendedOperationHandler;
112import org.apache.directory.server.ldap.LdapServer;
113import org.apache.directory.server.ldap.handlers.sasl.MechanismHandler;
114import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler;
115import org.apache.directory.server.ldap.replication.SyncReplConfiguration;
116import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumer;
117import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumerImpl;
118import org.apache.directory.server.ldap.replication.provider.ReplicationRequestHandler;
119import org.apache.directory.server.ldap.replication.provider.SyncReplRequestHandler;
120import org.apache.directory.server.ntp.NtpServer;
121import org.apache.directory.server.protocol.shared.transport.TcpTransport;
122import org.apache.directory.server.protocol.shared.transport.Transport;
123import org.apache.directory.server.protocol.shared.transport.UdpTransport;
124import org.apache.directory.server.xdbm.Index;
125import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
126import org.slf4j.Logger;
127import org.slf4j.LoggerFactory;
128
129
130/**
131 * A class used for reading the configuration present in a Partition
132 * and instantiate the necessary objects like DirectoryService, Interceptors etc.
133 *
134 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
135 */
136public final class ServiceBuilder
137{
138    /** The logger for this class */
139    private static final Logger LOG = LoggerFactory.getLogger( ServiceBuilder.class );
140
141    /** LDIF file filter */
142    private static FilenameFilter ldifFilter = new FilenameFilter()
143    {
144        public boolean accept( File file, String name )
145        {
146            if ( file.isDirectory() )
147            {
148                return true;
149            }
150
151            return Strings.toLowerCaseAscii( file.getName() ).endsWith( ".ldif" );
152        }
153    };
154
155
156    private ServiceBuilder()
157    {
158    }
159
160
161    /**
162     * Creates the Interceptor instances from the configuration
163     *
164     * @param interceptorBeans The Interceptors configuration used to create Interceptors
165     * @return a list of instantiated Interceptor objects
166     * @throws LdapException If the instanciation failed
167     */
168    public static List<Interceptor> createInterceptors( List<InterceptorBean> interceptorBeans ) throws LdapException
169    {
170        List<Interceptor> interceptors = new ArrayList<>( interceptorBeans.size() );
171
172        // First order the interceptorBeans
173        Set<InterceptorBean> orderedInterceptorBeans = new TreeSet<>();
174
175        for ( InterceptorBean interceptorBean : interceptorBeans )
176        {
177            if ( interceptorBean.isEnabled() )
178            {
179                orderedInterceptorBeans.add( interceptorBean );
180            }
181        }
182
183        // Instantiate the interceptors now
184        for ( InterceptorBean interceptorBean : orderedInterceptorBeans )
185        {
186            try
187            {
188                LOG.debug( "loading the interceptor class {} and instantiating",
189                    interceptorBean.getInterceptorClassName() );
190                Class<?> clazz = Class.forName( interceptorBean.getInterceptorClassName() );
191                Interceptor interceptor = null;
192                try
193                {
194                    Constructor<?> constructor = clazz.getDeclaredConstructor( interceptorBean.getClass() );
195                    interceptor = ( Interceptor ) constructor.newInstance( interceptorBean );
196                }
197                catch ( NoSuchMethodException e )
198                {
199                    interceptor = ( Interceptor ) Class.forName( interceptorBean.getInterceptorClassName() )
200                        .newInstance();
201                }
202
203                if ( interceptorBean instanceof AuthenticationInterceptorBean )
204                {
205                    // Transports
206                    Authenticator[] authenticators = createAuthenticators( ( ( AuthenticationInterceptorBean ) interceptorBean )
207                        .getAuthenticators() );
208                    ( ( AuthenticationInterceptor ) interceptor ).setAuthenticators( authenticators );
209
210                    // password policies
211                    List<PasswordPolicyBean> ppolicyBeans = ( ( AuthenticationInterceptorBean ) interceptorBean )
212                        .getPasswordPolicies();
213                    PpolicyConfigContainer ppolicyContainer = new PpolicyConfigContainer();
214
215                    for ( PasswordPolicyBean ppolicyBean : ppolicyBeans )
216                    {
217                        PasswordPolicyConfiguration ppolicyConfig = createPwdPolicyConfig( ppolicyBean );
218
219                        if ( ppolicyConfig != null )
220                        {
221                            ppolicyContainer.addPolicy( ppolicyBean.getDn(), ppolicyConfig );
222
223                            // the name should be strictly 'default', the default policy can't be enforced by defining a new AT
224                            if ( ppolicyBean.getPwdId().equalsIgnoreCase( "default" ) )
225                            {
226                                ppolicyContainer.setDefaultPolicyDn( ppolicyBean.getDn() );
227                            }
228                        }
229                    }
230
231                    ( ( AuthenticationInterceptor ) interceptor ).setPwdPolicies( ppolicyContainer );
232                }
233
234                interceptors.add( interceptor );
235            }
236            catch ( Exception e )
237            {
238                String message = "Cannot initialize the " + interceptorBean.getInterceptorClassName() + ", error : "
239                    + e;
240                LOG.error( message );
241                throw new ConfigurationException( message );
242            }
243        }
244
245        return interceptors;
246    }
247
248
249    /**
250     * creates the PassworddPolicyConfiguration object after reading the config entry containing pwdPolicy OC
251     *
252     * @param passwordPolicyBean The Bean containing the PasswordPolicy configuration
253     * @return the {@link PasswordPolicyConfiguration} object, null if the pwdPolicy entry is not present or disabled
254     */
255    public static PasswordPolicyConfiguration createPwdPolicyConfig( PasswordPolicyBean passwordPolicyBean )
256    {
257        if ( ( passwordPolicyBean == null ) || passwordPolicyBean.isDisabled() )
258        {
259            return null;
260        }
261
262        PasswordPolicyConfiguration passwordPolicy = new PasswordPolicyConfiguration();
263
264        passwordPolicy.setPwdAllowUserChange( passwordPolicyBean.isPwdAllowUserChange() );
265        passwordPolicy.setPwdAttribute( passwordPolicyBean.getPwdAttribute() );
266        passwordPolicy.setPwdCheckQuality( CheckQualityEnum.getCheckQuality( passwordPolicyBean.getPwdCheckQuality() ) );
267        passwordPolicy.setPwdExpireWarning( passwordPolicyBean.getPwdExpireWarning() );
268        passwordPolicy.setPwdFailureCountInterval( passwordPolicyBean.getPwdFailureCountInterval() );
269        passwordPolicy.setPwdGraceAuthNLimit( passwordPolicyBean.getPwdGraceAuthNLimit() );
270        passwordPolicy.setPwdGraceExpire( passwordPolicyBean.getPwdGraceExpire() );
271        passwordPolicy.setPwdInHistory( passwordPolicyBean.getPwdInHistory() );
272        passwordPolicy.setPwdLockout( passwordPolicyBean.isPwdLockout() );
273        passwordPolicy.setPwdLockoutDuration( passwordPolicyBean.getPwdLockoutDuration() );
274        passwordPolicy.setPwdMaxAge( passwordPolicyBean.getPwdMaxAge() );
275        passwordPolicy.setPwdMaxDelay( passwordPolicyBean.getPwdMaxDelay() );
276        passwordPolicy.setPwdMaxFailure( passwordPolicyBean.getPwdMaxFailure() );
277        passwordPolicy.setPwdMaxIdle( passwordPolicyBean.getPwdMaxIdle() );
278        passwordPolicy.setPwdMaxLength( passwordPolicyBean.getPwdMaxLength() );
279        passwordPolicy.setPwdMinAge( passwordPolicyBean.getPwdMinAge() );
280        passwordPolicy.setPwdMinDelay( passwordPolicyBean.getPwdMinDelay() );
281        passwordPolicy.setPwdMinLength( passwordPolicyBean.getPwdMinLength() );
282        passwordPolicy.setPwdMustChange( passwordPolicyBean.isPwdMustChange() );
283        passwordPolicy.setPwdSafeModify( passwordPolicyBean.isPwdSafeModify() );
284
285        PasswordValidator validator = null;
286
287        try
288        {
289            String className = passwordPolicyBean.getPwdValidator();
290
291            if ( className != null )
292            {
293                Class<?> cls = Class.forName( className );
294                validator = ( PasswordValidator ) cls.newInstance();
295            }
296        }
297        catch ( Exception e )
298        {
299            LOG.warn(
300                "Failed to load and instantiate the custom password validator for password policy config {}, using the default validator",
301                passwordPolicyBean.getDn(), e );
302        }
303
304        if ( validator == null )
305        {
306            validator = new DefaultPasswordValidator();
307        }
308
309        passwordPolicy.setPwdValidator( validator );
310
311        return passwordPolicy;
312    }
313
314
315    /**
316     * Read the configuration for the ChangeLog system
317     * 
318     * @param changeLogBean The Bean containing the ChangeLog configuration
319     * @return The instantiated ChangeLog element
320     */
321    public static ChangeLog createChangeLog( ChangeLogBean changeLogBean )
322    {
323        if ( ( changeLogBean == null ) || changeLogBean.isDisabled() )
324        {
325            return null;
326        }
327
328        ChangeLog changeLog = new DefaultChangeLog();
329
330        changeLog.setEnabled( changeLogBean.isEnabled() );
331        changeLog.setExposed( changeLogBean.isChangeLogExposed() );
332
333        return changeLog;
334    }
335
336
337    /**
338     * Instantiate the Journal object from the stored configuration
339     * 
340     * @param journalBean The Bean containing the Journal configuration
341     * @return An instance of Journal
342     */
343    public static Journal createJournal( JournalBean journalBean )
344    {
345        if ( ( journalBean == null ) || journalBean.isDisabled() )
346        {
347            return null;
348        }
349
350        Journal journal = new DefaultJournal();
351
352        journal.setRotation( journalBean.getJournalRotation() );
353        journal.setEnabled( journalBean.isEnabled() );
354
355        JournalStore store = new DefaultJournalStore();
356
357        store.setFileName( journalBean.getJournalFileName() );
358        store.setWorkingDirectory( journalBean.getJournalWorkingDir() );
359
360        journal.setJournalStore( store );
361
362        return journal;
363    }
364
365
366    /**
367     * Load the Test entries
368     * 
369     * @param entryFilePath The place on disk where the test entries are stored
370     * @return A list of LdifEntry elements
371     * @throws ConfigurationException If we weren't able to read the entries
372     */
373    public static List<LdifEntry> readTestEntries( String entryFilePath ) throws ConfigurationException
374    {
375        List<LdifEntry> entries = new ArrayList<>();
376
377        File file = new File( entryFilePath );
378
379        if ( !file.exists() )
380        {
381            LOG.warn( "LDIF test entry file path doesn't exist {}", entryFilePath );
382        }
383        else
384        {
385            LOG.debug( "parsing the LDIF file(s) present at the path {}", entryFilePath );
386
387            try
388            {
389                loadEntries( file, entries );
390            }
391            catch ( LdapLdifException e )
392            {
393                String message = "Error while parsing a LdifEntry : " + e.getMessage();
394                LOG.error( message );
395                throw new ConfigurationException( message );
396            }
397            catch ( IOException e )
398            {
399                String message = "cannot read the Ldif entries from the " + entryFilePath + " location";
400                LOG.error( message );
401                throw new ConfigurationException( message );
402            }
403        }
404
405        return entries;
406    }
407
408
409    /**
410     * Load the entries from a Ldif file recursively
411     * @throws LdapLdifException
412     * @throws IOException
413     */
414    private static void loadEntries( File ldifFile, List<LdifEntry> entries ) throws LdapLdifException, IOException
415    {
416        if ( ldifFile.isDirectory() )
417        {
418            File[] files = ldifFile.listFiles( ldifFilter );
419
420            for ( File f : files )
421            {
422                loadEntries( f, entries );
423            }
424        }
425        else
426        {
427            LdifReader reader = new LdifReader();
428
429            try
430            {
431                entries.addAll( reader.parseLdifFile( ldifFile.getAbsolutePath() ) );
432            }
433            finally
434            {
435                reader.close();
436            }
437        }
438    }
439
440
441    /**
442     * Loads and instantiates a MechanismHandler from the configuration entry
443     *
444     * @param saslMechHandlerBean The SaslMechHandler configuration used to create MechanismHandler instance
445     * @return an instance of the MechanismHandler type
446     * @throws ConfigurationException if the SASL mechanism handler cannot be created
447     */
448    public static MechanismHandler createSaslMechHandler( SaslMechHandlerBean saslMechHandlerBean )
449        throws ConfigurationException
450    {
451        if ( ( saslMechHandlerBean == null ) || saslMechHandlerBean.isDisabled() )
452        {
453            return null;
454        }
455
456        String mechClassName = saslMechHandlerBean.getSaslMechClassName();
457
458        Class<?> mechClass = null;
459
460        try
461        {
462            mechClass = Class.forName( mechClassName );
463        }
464        catch ( ClassNotFoundException e )
465        {
466            String message = "Cannot find the class " + mechClassName;
467            LOG.error( message );
468            throw new ConfigurationException( message );
469        }
470
471        MechanismHandler handler = null;
472
473        try
474        {
475            handler = ( MechanismHandler ) mechClass.newInstance();
476        }
477        catch ( InstantiationException e )
478        {
479            String message = "Cannot instantiate the class : " + mechClassName;
480            LOG.error( message );
481            throw new ConfigurationException( message );
482        }
483        catch ( IllegalAccessException e )
484        {
485            String message = "Cnnot invoke the class' constructor for " + mechClassName;
486            LOG.error( message );
487            throw new ConfigurationException( message );
488        }
489
490        if ( mechClass == NtlmMechanismHandler.class )
491        {
492            NtlmMechanismHandler ntlmHandler = ( NtlmMechanismHandler ) handler;
493            ntlmHandler.setNtlmProviderFqcn( saslMechHandlerBean.getNtlmMechProvider() );
494        }
495
496        return handler;
497    }
498
499
500    /**
501     * Creates a Authenticator from the configuration
502     * 
503     * @param authenticatorBean The created instance of authenticator
504     * @return An instance of authenticator if the given authenticatorBean is not disabled
505     * @throws ConfigurationException If the Authenticator cannot be created
506     */
507    public static Authenticator createAuthenticator( AuthenticatorBean authenticatorBean )
508        throws ConfigurationException
509    {
510        if ( authenticatorBean.isDisabled() )
511        {
512            return null;
513        }
514
515        Authenticator authenticator = null;
516
517        if ( authenticatorBean instanceof DelegatingAuthenticatorBean )
518        {
519            try
520            {
521                authenticator = new DelegatingAuthenticator(
522                    new Dn(
523                        ( ( DelegatingAuthenticatorBean ) authenticatorBean ).getBaseDn() ) );
524            }
525            catch ( LdapInvalidDnException e )
526            {
527                String errorMsg = "Failed to instantiate the configured authenticator "
528                    + authenticatorBean.getAuthenticatorId();
529                LOG.warn( errorMsg );
530                throw new ConfigurationException( errorMsg, e );
531            }
532
533            ( ( DelegatingAuthenticator ) authenticator )
534                .setDelegateHost( ( ( DelegatingAuthenticatorBean ) authenticatorBean ).getDelegateHost() );
535            ( ( DelegatingAuthenticator ) authenticator )
536                .setDelegatePort( ( ( DelegatingAuthenticatorBean ) authenticatorBean ).getDelegatePort() );
537        }
538        else if ( authenticatorBean instanceof AuthenticatorImplBean )
539        {
540            String fqcn = ( ( AuthenticatorImplBean ) authenticatorBean ).getAuthenticatorClass();
541
542            try
543            {
544                Class<?> authnImplClass = Class.forName( fqcn );
545                authenticator = ( Authenticator ) authnImplClass.newInstance();
546            }
547            catch ( Exception e )
548            {
549                String errorMsg = "Failed to instantiate the configured authenticator "
550                    + authenticatorBean.getAuthenticatorId();
551                LOG.warn( errorMsg );
552                throw new ConfigurationException( errorMsg, e );
553            }
554        }
555
556        return authenticator;
557    }
558
559
560    /**
561     * Creates a Transport from the configuration
562     * 
563     * @param transportBean The created instance of transport
564     * @return An instance of transport
565     */
566    public static Transport createTransport( TransportBean transportBean )
567    {
568        if ( ( transportBean == null ) || transportBean.isDisabled() )
569        {
570            return null;
571        }
572
573        Transport transport = null;
574
575        if ( transportBean instanceof TcpTransportBean )
576        {
577            transport = new TcpTransport();
578        }
579        else
580        {
581            transport = new UdpTransport();
582        }
583
584        transport.setPort( transportBean.getSystemPort() );
585        transport.setAddress( transportBean.getTransportAddress() );
586        transport.setBackLog( transportBean.getTransportBackLog() );
587        transport.setNbThreads( transportBean.getTransportNbThreads() );
588
589        if ( transport instanceof TcpTransport )
590        {
591            ( ( TcpTransport ) transport ).setEnableSSL( transportBean.isTransportEnableSSL() );
592
593            if ( ( ( TcpTransport ) transport ).isSSLEnabled() )
594            {
595                ( ( TcpTransport ) transport ).setNeedClientAuth( transportBean.getNeedClientAuth() );
596                ( ( TcpTransport ) transport ).setWantClientAuth( transportBean.getWantClientAuth() );
597                List<String> enabledProtocols = transportBean.getEnabledProtocols();
598
599                if ( ( enabledProtocols != null ) && !enabledProtocols.isEmpty() )
600                {
601                    ( ( TcpTransport ) transport ).setEnabledProtocols( enabledProtocols );
602                }
603
604                List<String> enabledCiphers = transportBean.getEnabledCiphers();
605
606                if ( ( enabledCiphers != null ) && !enabledCiphers.isEmpty() )
607                {
608                    ( ( TcpTransport ) transport ).setEnabledCiphers( enabledCiphers );
609                }
610            }
611        }
612
613        return transport;
614    }
615
616
617    /**
618     * Creates the array of authenticators
619     * 
620     * @param list The array of AuthenticatorBean configuration
621     * @return An array of Authenticator instance
622     * @throws ConfigurationException If one of theAuthenticator cannot be created 
623     */
624    public static Authenticator[] createAuthenticators( List<AuthenticatorBean> list ) throws ConfigurationException
625    {
626        Set<Authenticator> authenticators = new HashSet<>( list.size() );
627
628        for ( AuthenticatorBean authenticatorBean : list )
629        {
630            if ( authenticatorBean.isEnabled() )
631            {
632                authenticators.add( createAuthenticator( authenticatorBean ) );
633            }
634        }
635
636        return authenticators.toArray( new Authenticator[]
637            {} );
638    }
639
640
641    /**
642     * Creates the array of transports read from the DIT
643     * 
644     * @param transportBeans The array of Transport configuration
645     * @return An arry of Transport instance
646     */
647    public static Transport[] createTransports( TransportBean[] transportBeans )
648    {
649        List<Transport> transports = new ArrayList<>();
650
651        for ( TransportBean transportBean : transportBeans )
652        {
653            if ( transportBean.isEnabled() )
654            {
655                transports.add( createTransport( transportBean ) );
656            }
657        }
658
659        return transports.toArray( new Transport[transports.size()] );
660    }
661
662
663    /**
664     * Helper method to create an Array of EncryptionTypes from an array of Strings
665     */
666    private static EncryptionType[] createEncryptionTypes( List<String> encryptionTypes )
667    {
668        if ( ( encryptionTypes == null ) || encryptionTypes.isEmpty() )
669        {
670            return new EncryptionType[0];
671        }
672
673        List<EncryptionType> types = new ArrayList<>();
674
675        for ( String encryptionType : encryptionTypes )
676        {
677            EncryptionType et = EncryptionType.getByName( encryptionType );
678            if ( et == EncryptionType.UNKNOWN )
679            {
680                LOG.warn( "Unknown encryption type {}", encryptionType );
681            }
682            else
683            {
684                types.add( et );
685            }
686        }
687
688        return types.toArray( new EncryptionType[0] );
689    }
690
691
692    /**
693     * Instantiates a NtpServer based on the configuration present in the partition
694     *
695     * @param ntpServerBean The NtpServerBean containing the NtpServer configuration
696     * @param directoryService The DirectoryService instance
697     * @return Instance of NtpServer
698     * @throws LdapException If the NtpServer instance cannot be created
699     */
700    public static NtpServer createNtpServer( NtpServerBean ntpServerBean, DirectoryService directoryService )
701    {
702        // Fist, do nothing if the NtpServer is disabled
703        if ( ( ntpServerBean == null ) || ntpServerBean.isDisabled() )
704        {
705            return null;
706        }
707
708        NtpServer ntpServer = new NtpServer();
709
710        // The service ID
711        ntpServer.setServiceId( ntpServerBean.getServerId() );
712
713        // The transports
714        Transport[] transports = createTransports( ntpServerBean.getTransports() );
715        ntpServer.setTransports( transports );
716
717        return ntpServer;
718    }
719
720
721    /*
722     * Instantiates a DhcpServer based on the configuration present in the partition
723     *
724     * @param dhcpServerBean The DhcpServerBean containing the DhcpServer configuration
725     * @return Instance of DhcpServer
726     * @throws LdapException
727     *
728    public static DhcpServer createDhcpServer( DhcpServerBean dhcpServerBean, DirectoryService directoryService ) throws LdapException
729    {
730        // Fist, do nothing if the DhcpServer is disabled
731        if ( !dhcpServerBean.isEnabled() )
732        {
733            return null;
734        }
735
736        DhcpServer dhcpServer = new DhcpServer();
737        
738        // The service ID
739        dhcpServer.setServiceId( dhcpServerBean.getServerId() );
740        
741        // The transports
742        Transport[] transports = createTransports( dhcpServerBean.getTransports() );
743        dhcpServer.setTransports( transports );
744        
745        return dhcpServer;
746    }
747
748
749    /**
750     * Instantiates a KdcServer based on the configuration present in the partition
751     *
752     * @param directoryServiceBean The DirectoryServiceBean containing the KdcServer configuration
753     * @param directoryService The DirectoryService instance
754     * @return Instance of KdcServer
755     * @throws LdapException If the KdcServce cannot be created
756     */
757    public static KdcServer createKdcServer( DirectoryServiceBean directoryServiceBean, DirectoryService directoryService )
758    {
759        KdcServerBean kdcServerBean = directoryServiceBean.getKdcServerBean();
760
761        // Fist, do nothing if the KdcServer is disabled
762        if ( ( kdcServerBean == null ) || kdcServerBean.isDisabled() )
763        {
764            return null;
765        }
766
767        KerberosConfig kdcConfig = new KerberosConfig();
768
769        // AllowableClockSkew
770        kdcConfig.setAllowableClockSkew( kdcServerBean.getKrbAllowableClockSkew() );
771
772        // BodyChecksumVerified
773        kdcConfig.setBodyChecksumVerified( kdcServerBean.isKrbBodyChecksumVerified() );
774
775        // EmptyAddressesAllowed
776        kdcConfig.setEmptyAddressesAllowed( kdcServerBean.isKrbEmptyAddressesAllowed() );
777
778        // EncryptionType
779        EncryptionType[] encryptionTypes = createEncryptionTypes( kdcServerBean.getKrbEncryptionTypes() );
780        kdcConfig.setEncryptionTypes( encryptionTypes );
781
782        // ForwardableAllowed
783        kdcConfig.setForwardableAllowed( kdcServerBean.isKrbForwardableAllowed() );
784
785        // KdcPrincipal
786        kdcConfig.setServicePrincipal( "krbtgt/" + kdcServerBean.getKrbPrimaryRealm() + "@"
787            + kdcServerBean.getKrbPrimaryRealm() );
788
789        // MaximumRenewableLifetime
790        kdcConfig.setMaximumRenewableLifetime( kdcServerBean.getKrbMaximumRenewableLifetime() );
791
792        // MaximumTicketLifetime
793        kdcConfig.setMaximumTicketLifetime( kdcServerBean.getKrbMaximumTicketLifetime() );
794
795        // PaEncTimestampRequired
796        kdcConfig.setPaEncTimestampRequired( kdcServerBean.isKrbPaEncTimestampRequired() );
797
798        // PostdatedAllowed
799        kdcConfig.setPostdatedAllowed( kdcServerBean.isKrbPostdatedAllowed() );
800
801        // PrimaryRealm
802        kdcConfig.setPrimaryRealm( kdcServerBean.getKrbPrimaryRealm() );
803
804        // ProxiableAllowed
805        kdcConfig.setProxiableAllowed( kdcServerBean.isKrbProxiableAllowed() );
806
807        // RenewableAllowed
808        kdcConfig.setRenewableAllowed( kdcServerBean.isKrbRenewableAllowed() );
809
810        // searchBaseDn
811        kdcConfig.setSearchBaseDn( kdcServerBean.getSearchBaseDn().getName() );
812
813        KdcServer kdcServer = new KdcServer( kdcConfig );
814
815        kdcServer.setDirectoryService( directoryService );
816        kdcServer.setEnabled( true );
817
818        // The ID
819        kdcServer.setServiceId( kdcServerBean.getServerId() );
820
821        // The transports
822        Transport[] transports = createTransports( kdcServerBean.getTransports() );
823        kdcServer.setTransports( transports );
824
825        ChangePasswordServerBean changePasswordServerBean = directoryServiceBean.getChangePasswordServerBean();
826
827        // Fist, do nothing if the ChangePasswordServer is disabled
828        if ( ( changePasswordServerBean != null ) && !changePasswordServerBean.isDisabled() )
829        {
830            ChangePasswordServer changePasswordServer = new ChangePasswordServer( new ChangePasswordConfig( kdcConfig ) );
831            changePasswordServer.setEnabled( true );
832            changePasswordServer.setDirectoryService( directoryService );
833
834            // Transports
835            Transport[] chngPwdTransports = createTransports( changePasswordServerBean.getTransports() );
836            changePasswordServer.setTransports( chngPwdTransports );
837
838            kdcServer.setChangePwdServer( changePasswordServer );
839        }
840
841        return kdcServer;
842    }
843
844
845    /**
846     * Instantiates the HttpWebApps based on the configuration present in the partition
847     *
848     * @param httpWebAppBeans The list of HttpWebAppBeans containing the HttpWebAppBeans configuration
849     * @param directoryService The DirectoryService instance
850     * @return Instances of HttpWebAppBean
851     * @throws LdapException If the HttpWebApps instance cannot be created
852     */
853    public static Set<WebApp> createHttpWebApps( List<HttpWebAppBean> httpWebAppBeans, DirectoryService directoryService )
854    {
855        Set<WebApp> webApps = new HashSet<>();
856
857        if ( httpWebAppBeans == null )
858        {
859            return webApps;
860        }
861
862        for ( HttpWebAppBean httpWebAppBean : httpWebAppBeans )
863        {
864            if ( httpWebAppBean.isDisabled() )
865            {
866                continue;
867            }
868
869            WebApp webApp = new WebApp();
870
871            // HttpAppCtxPath
872            webApp.setContextPath( httpWebAppBean.getHttpAppCtxPath() );
873
874            // HttpWarFile
875            webApp.setWarFile( httpWebAppBean.getHttpWarFile() );
876
877            webApps.add( webApp );
878        }
879
880        return webApps;
881    }
882
883
884    /**
885     * Instantiates a HttpServer based on the configuration present in the partition
886     *
887     * @param httpServerBean The HttpServerBean containing the HttpServer configuration
888     * @param directoryService The DirectoryService instance
889     * @return Instance of LdapServer
890     * @throws LdapException If the HttpServer cannot be created
891     */
892    public static HttpServer createHttpServer( HttpServerBean httpServerBean, DirectoryService directoryService )
893    {
894        // Fist, do nothing if the HttpServer is disabled
895        if ( ( httpServerBean == null ) || httpServerBean.isDisabled() )
896        {
897            return null;
898        }
899
900        HttpServer httpServer = new HttpServer();
901
902        // HttpConfFile
903        httpServer.setConfFile( httpServerBean.getHttpConfFile() );
904
905        // The transports
906        TransportBean[] transports = httpServerBean.getTransports();
907
908        for ( TransportBean transportBean : transports )
909        {
910            if ( transportBean.isDisabled() )
911            {
912                continue;
913            }
914
915            if ( transportBean instanceof TcpTransportBean )
916            {
917                TcpTransport transport = new TcpTransport( transportBean.getSystemPort() );
918                transport.setAddress( transportBean.getTransportAddress() );
919
920                if ( transportBean.getTransportId().equalsIgnoreCase( HttpServer.HTTP_TRANSPORT_ID ) )
921                {
922                    httpServer.setHttpTransport( transport );
923                }
924                else if ( transportBean.getTransportId().equalsIgnoreCase( HttpServer.HTTPS_TRANSPORT_ID ) )
925                {
926                    httpServer.setHttpsTransport( transport );
927                }
928                else
929                {
930                    LOG.warn( "Transport ids of HttpServer should be either 'http' or 'https'" );
931                }
932            }
933        }
934
935        // The webApps
936        httpServer.setWebApps( createHttpWebApps( httpServerBean.getHttpWebApps(), directoryService ) );
937
938        return httpServer;
939    }
940
941
942    /**
943     * Instantiates a ChangePasswordServer based on the configuration present in the partition
944     *
945     * @param ldapServerBean The ChangePasswordServerBean containing the ChangePasswordServer configuration
946     * @return Instance of ChangePasswordServer
947     * @throws LdapException
948     *
949    public static ChangePasswordServer createChangePasswordServer( ChangePasswordServerBean changePasswordServerBean, DirectoryService directoryService ) throws LdapException
950    {
951        // Fist, do nothing if the LdapServer is disabled
952        if ( ( changePasswordServerBean == null ) || changePasswordServerBean.isDisabled() )
953        {
954            return null;
955        }
956
957        ChangePasswordServer changePasswordServer = new ChangePasswordServer();
958        changePasswordServer.setEnabled( true );
959        changePasswordServer.setDirectoryService( directoryService );
960
961        // AllowableClockSkew
962        changePasswordServer.setAllowableClockSkew( changePasswordServerBean.getKrbAllowableClockSkew() );
963
964        // TODO CatalogBased
965        //changePasswordServer.setCatalogBased( changePasswordServerBean.isCatalogBase() );
966
967        // EmptyAddressesAllowed
968        changePasswordServer.setEmptyAddressesAllowed( changePasswordServerBean.isKrbEmptyAddressesAllowed() );
969
970        // EncryptionTypes
971        EncryptionType[] encryptionTypes = createEncryptionTypes( changePasswordServerBean.getKrbEncryptionTypes() );
972        changePasswordServer.setEncryptionTypes( encryptionTypes );
973
974        // PolicyCategoryCount
975        changePasswordServer.setPolicyCategoryCount( changePasswordServerBean.getChgPwdPolicyCategoryCount() );
976
977        // PolicyPasswordLength
978        changePasswordServer.setPolicyPasswordLength( changePasswordServerBean.getChgPwdPolicyPasswordLength() );
979
980        // policyTokenSize
981        changePasswordServer.setPolicyTokenSize( changePasswordServerBean.getChgPwdPolicyTokenSize() );
982
983        // PrimaryRealm
984        changePasswordServer.setPrimaryRealm( changePasswordServerBean.getKrbPrimaryRealm() );
985
986        // SearchBaseDn
987        changePasswordServer.setSearchBaseDn( changePasswordServerBean.getSearchBaseDn().getName() );
988
989        // Id/Name
990        changePasswordServer.setServiceName( changePasswordServerBean.getServerId() );
991        changePasswordServer.setServiceId( changePasswordServerBean.getServerId() );
992
993        // ServicePrincipal
994        changePasswordServer.setServicePrincipal( changePasswordServerBean.getChgPwdServicePrincipal() );
995
996        // Transports
997        Transport[] transports = createTransports( changePasswordServerBean.getTransports() );
998        changePasswordServer.setTransports( transports );
999        
1000        return changePasswordServer;
1001    }
1002    */
1003
1004    /**
1005     * Instantiates a LdapServer based on the configuration present in the partition
1006     *
1007     * @param ldapServerBean The LdapServerBean containing the LdapServer configuration
1008     * @param directoryService The DirectoryService instance
1009     * @return Instance of LdapServer
1010     * @throws LdapException If the LdapServer cannot be created
1011     */
1012    public static LdapServer createLdapServer( LdapServerBean ldapServerBean, DirectoryService directoryService )
1013        throws LdapException
1014    {
1015        // Fist, do nothing if the LdapServer is disabled
1016        if ( ( ldapServerBean == null ) || ldapServerBean.isDisabled() )
1017        {
1018            return null;
1019        }
1020
1021        LdapServer ldapServer = new LdapServer();
1022
1023        ldapServer.setDirectoryService( directoryService );
1024        ldapServer.setEnabled( true );
1025
1026        // The ID
1027        ldapServer.setServiceId( ldapServerBean.getServerId() );
1028
1029        // SearchBaseDN
1030        ldapServer.setSearchBaseDn( ldapServerBean.getSearchBaseDn().getName() );
1031
1032        // KeyStore
1033        ldapServer.setKeystoreFile( ldapServerBean.getLdapServerKeystoreFile() );
1034
1035        // Certificate password
1036        ldapServer.setCertificatePassword( ldapServerBean.getLdapServerCertificatePassword() );
1037
1038        // ConfidentialityRequired
1039        ldapServer.setConfidentialityRequired( ldapServerBean.isLdapServerConfidentialityRequired() );
1040
1041        // Max size limit
1042        ldapServer.setMaxSizeLimit( ldapServerBean.getLdapServerMaxSizeLimit() );
1043
1044        // Max time limit
1045        ldapServer.setMaxTimeLimit( ldapServerBean.getLdapServerMaxTimeLimit() );
1046
1047        // MaxPDUSize
1048        ldapServer.setMaxPDUSize( ldapServerBean.getMaxPDUSize() );
1049
1050        // Sasl Host
1051        ldapServer.setSaslHost( ldapServerBean.getLdapServerSaslHost() );
1052
1053        // Sasl Principal
1054        ldapServer.setSaslPrincipal( ldapServerBean.getLdapServerSaslPrincipal() );
1055
1056        // Sasl realm
1057        ldapServer.setSaslRealms( ldapServerBean.getLdapServerSaslRealms() );
1058
1059        // Relplication pinger thread sleep time
1060        ldapServer.setReplPingerSleepTime( ldapServerBean.getReplPingerSleep() );
1061
1062        // The transports
1063        Transport[] transports = createTransports( ldapServerBean.getTransports() );
1064        ldapServer.setTransports( transports );
1065
1066        // SaslMechs
1067        for ( SaslMechHandlerBean saslMechHandlerBean : ldapServerBean.getSaslMechHandlers() )
1068        {
1069            if ( saslMechHandlerBean.isEnabled() )
1070            {
1071                String mechanism = saslMechHandlerBean.getSaslMechName();
1072                ldapServer.addSaslMechanismHandler( mechanism, createSaslMechHandler( saslMechHandlerBean ) );
1073            }
1074        }
1075
1076        // ExtendedOpHandlers
1077        for ( ExtendedOpHandlerBean extendedpHandlerBean : ldapServerBean.getExtendedOps() )
1078        {
1079            if ( extendedpHandlerBean.isEnabled() )
1080            {
1081                try
1082                {
1083                    Class<?> extendedOpClass = Class.forName( extendedpHandlerBean.getExtendedOpHandlerClass() );
1084                    ExtendedOperationHandler<ExtendedRequest, ExtendedResponse> extOpHandler =
1085                        ( ExtendedOperationHandler<ExtendedRequest, ExtendedResponse> ) extendedOpClass.newInstance();
1086                    ldapServer.addExtendedOperationHandler( extOpHandler );
1087                }
1088                catch ( Exception e )
1089                {
1090                    String message = "Failed to load and instantiate ExtendedOperationHandler implementation "
1091                        + extendedpHandlerBean.getExtendedOpId() + ": " + e.getMessage();
1092                    LOG.error( message );
1093                    throw new ConfigurationException( message );
1094                }
1095            }
1096        }
1097
1098        // ReplReqHandler
1099        boolean replicationEnabled = ldapServerBean.isReplEnabled();
1100
1101        if ( replicationEnabled )
1102        {
1103            String fqcn = ldapServerBean.getReplReqHandler();
1104
1105            if ( fqcn != null )
1106            {
1107                try
1108                {
1109                    Class<?> replProvImplClz = Class.forName( fqcn );
1110                    ReplicationRequestHandler rp = ( ReplicationRequestHandler ) replProvImplClz.newInstance();
1111                    ldapServer.setReplicationReqHandler( rp );
1112                }
1113                catch ( Exception e )
1114                {
1115                    String message = "Failed to load and instantiate ReplicationRequestHandler implementation : "
1116                        + fqcn;
1117                    LOG.error( message );
1118                    throw new ConfigurationException( message );
1119                }
1120            }
1121            else
1122            {
1123                // Try with the default handler
1124                ReplicationRequestHandler rp = new SyncReplRequestHandler();
1125                ldapServer.setReplicationReqHandler( rp );
1126            }
1127        }
1128
1129        ldapServer.setReplConsumers( createReplConsumers( ldapServerBean.getReplConsumers() ) );
1130
1131        return ldapServer;
1132    }
1133
1134
1135    /**
1136     * instantiate the ReplicationConsumers based on the configuration present in ReplConsumerBeans
1137     * 
1138     * @param replConsumerBeans the list of consumers configured
1139     * @return a list of ReplicationConsumer instances
1140     * @throws ConfigurationException If the replication consumer instance cannot be created
1141     */
1142    public static List<ReplicationConsumer> createReplConsumers( List<ReplConsumerBean> replConsumerBeans )
1143        throws ConfigurationException
1144    {
1145        List<ReplicationConsumer> lst = new ArrayList<>();
1146
1147        if ( replConsumerBeans == null )
1148        {
1149            return lst;
1150        }
1151
1152        for ( ReplConsumerBean replBean : replConsumerBeans )
1153        {
1154            if ( replBean.isDisabled() )
1155            {
1156                continue;
1157            }
1158
1159            String className = replBean.getReplConsumerImpl();
1160
1161            ReplicationConsumer consumer = null;
1162            Class<?> consumerClass = null;
1163            SyncReplConfiguration config = null;
1164
1165            try
1166            {
1167                if ( className == null )
1168                {
1169                    consumer = new ReplicationConsumerImpl();
1170                }
1171                else
1172                {
1173                    consumerClass = Class.forName( className );
1174                    consumer = ( ReplicationConsumer ) consumerClass.newInstance();
1175                }
1176
1177                // we don't support any other configuration impls atm, but this configuration should suffice for many needs
1178                config = new SyncReplConfiguration();
1179
1180                config.setBaseDn( replBean.getSearchBaseDn() );
1181                config.setRemoteHost( replBean.getReplProvHostName() );
1182                config.setRemotePort( replBean.getReplProvPort() );
1183
1184                try
1185                {
1186                    config.setAliasDerefMode( AliasDerefMode.getDerefMode( replBean.getReplAliasDerefMode() ) );
1187                }
1188                catch ( IllegalArgumentException iae )
1189                {
1190                    LOG.error( "{}, defaulted to 'never'", iae.getMessage() );
1191                }
1192
1193                config.setAttributes( replBean.getReplAttributes().toArray( new String[0] ) );
1194                config.setRefreshInterval( replBean.getReplRefreshInterval() );
1195                config.setRefreshNPersist( replBean.isReplRefreshNPersist() );
1196
1197                int scope = SearchScope.getSearchScope( replBean.getReplSearchScope() );
1198                config.setSearchScope( SearchScope.getSearchScope( scope ) );
1199
1200                config.setFilter( replBean.getReplSearchFilter() );
1201                config.setSearchTimeout( replBean.getReplSearchTimeout() );
1202                config.setReplUserDn( replBean.getReplUserDn() );
1203                config.setReplUserPassword( replBean.getReplUserPassword() );
1204                config.setSearchSizeLimit( replBean.getReplSearchSizeLimit() );
1205
1206                config.setUseTls( replBean.isReplUseTls() );
1207                config.setStrictCertVerification( replBean.isReplStrictCertValidation() );
1208
1209                config.setConfigEntryDn( replBean.getDn() );
1210
1211                consumer.setConfig( config );
1212
1213                lst.add( consumer );
1214            }
1215            catch ( Exception e )
1216            {
1217                throw new ConfigurationException( "cannot configure the replication consumer with FQCN " + className, e );
1218            }
1219        }
1220
1221        return lst;
1222    }
1223
1224
1225    /**
1226     * Create a new instance of a JdbmIndex from an instance of JdbmIndexBean
1227     * 
1228     * @param partition The JdbmPartition instance
1229     * @param jdbmIndexBean The JdbmIndexBean to convert
1230     * @param directoryService The DirectoryService instance
1231     * @return An JdbmIndex instance
1232     */
1233    public static JdbmIndex<?> createJdbmIndex( JdbmPartition partition,
1234        JdbmIndexBean jdbmIndexBean, DirectoryService directoryService )
1235    {
1236        if ( ( jdbmIndexBean == null ) || jdbmIndexBean.isDisabled() )
1237        {
1238            return null;
1239        }
1240
1241        JdbmIndex<?> index = null;
1242
1243        boolean hasReverse = jdbmIndexBean.getIndexHasReverse();
1244
1245        if ( jdbmIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_RDN_AT )
1246            || jdbmIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_RDN_AT_OID ) )
1247        {
1248            index = new JdbmRdnIndex();
1249        }
1250        else if ( jdbmIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_ALIAS_AT )
1251            || jdbmIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_ALIAS_AT_OID ) )
1252        {
1253            index = new JdbmDnIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
1254        }
1255        else
1256        {
1257            index = new JdbmIndex<>( jdbmIndexBean.getIndexAttributeId(), hasReverse );
1258        }
1259
1260        index.setCacheSize( jdbmIndexBean.getIndexCacheSize() );
1261        index.setNumDupLimit( jdbmIndexBean.getIndexNumDupLimit() );
1262
1263        // Find the OID for this index
1264        if ( jdbmIndexBean.getIndexWorkingDir() != null )
1265        {
1266            index.setWkDirPath( new File( jdbmIndexBean.getIndexWorkingDir() ).toURI() );
1267        }
1268        else
1269        {
1270            // Set the Partition working dir as a default
1271            index.setWkDirPath( partition.getPartitionPath() );
1272        }
1273
1274        return index;
1275    }
1276
1277
1278    /**
1279     * Create the list of Index from the configuration
1280     */
1281    private static Set<Index<?, String>> createJdbmIndexes( JdbmPartition partition,
1282        List<IndexBean> indexesBeans,
1283        DirectoryService directoryService ) //throws Exception
1284    {
1285        Set<Index<?, String>> indexes = new HashSet<>();
1286
1287        for ( IndexBean indexBean : indexesBeans )
1288        {
1289            if ( indexBean.isEnabled() && ( indexBean instanceof JdbmIndexBean ) )
1290            {
1291                indexes.add( createJdbmIndex( partition, ( JdbmIndexBean ) indexBean, directoryService ) );
1292            }
1293        }
1294
1295        return indexes;
1296    }
1297
1298
1299    /**
1300     * Create a new instance of a JdbmPartition
1301     * 
1302     * @param directoryService The DirectoryService instance
1303     * @param jdbmPartitionBean the JdbmPartition bean
1304     * @return The instantiated JdbmPartition
1305     * @throws ConfigurationException If the instance cannot be created
1306     */
1307    public static JdbmPartition createJdbmPartition( DirectoryService directoryService,
1308        JdbmPartitionBean jdbmPartitionBean ) throws ConfigurationException
1309    {
1310        if ( ( jdbmPartitionBean == null ) || jdbmPartitionBean.isDisabled() )
1311        {
1312            return null;
1313        }
1314
1315        JdbmPartition jdbmPartition = new JdbmPartition( directoryService.getSchemaManager(),
1316            directoryService.getDnFactory() );
1317
1318        jdbmPartition.setCacheSize( jdbmPartitionBean.getPartitionCacheSize() );
1319        jdbmPartition.setId( jdbmPartitionBean.getPartitionId() );
1320        jdbmPartition.setOptimizerEnabled( jdbmPartitionBean.isJdbmPartitionOptimizerEnabled() );
1321        File partitionPath = new File( directoryService.getInstanceLayout().getPartitionsDirectory(),
1322            jdbmPartitionBean.getPartitionId() );
1323        jdbmPartition.setPartitionPath( partitionPath.toURI() );
1324
1325        try
1326        {
1327            jdbmPartition.setSuffixDn( jdbmPartitionBean.getPartitionSuffix() );
1328        }
1329        catch ( LdapInvalidDnException lide )
1330        {
1331            String message = "Cannot set the Dn " + jdbmPartitionBean.getPartitionSuffix() + ", " + lide.getMessage();
1332            LOG.error( message );
1333            throw new ConfigurationException( message );
1334        }
1335
1336        jdbmPartition.setSyncOnWrite( jdbmPartitionBean.isPartitionSyncOnWrite() );
1337        jdbmPartition.setIndexedAttributes( createJdbmIndexes( jdbmPartition, jdbmPartitionBean.getIndexes(),
1338            directoryService ) );
1339
1340        setContextEntry( jdbmPartitionBean, jdbmPartition );
1341
1342        return jdbmPartition;
1343    }
1344
1345
1346    /**
1347     * Create the a Partition instantiated from the configuration
1348     * 
1349     * @param directoryService The DirectoryService instance
1350     * @param partitionBean the Partition bean
1351     * @return The instantiated Partition
1352     * @throws ConfigurationException If we cannot process the Partition
1353     */
1354    public static Partition createPartition( DirectoryService directoryService, PartitionBean partitionBean )
1355        throws ConfigurationException
1356    {
1357        if ( ( partitionBean == null ) || partitionBean.isDisabled() )
1358        {
1359            return null;
1360        }
1361
1362        if ( partitionBean instanceof JdbmPartitionBean )
1363        {
1364            return createJdbmPartition( directoryService, ( JdbmPartitionBean ) partitionBean );
1365        }
1366        else if ( partitionBean instanceof MavibotPartitionBean )
1367        {
1368            return createMavibotPartition( directoryService, ( MavibotPartitionBean ) partitionBean );
1369        }
1370        else
1371        {
1372            return null;
1373        }
1374    }
1375
1376
1377    /**
1378     * Create the set of Partitions instantiated from the configuration
1379     * 
1380     * @param directoryService The DirectoryService instance
1381     * @param partitionBeans the list of Partition beans
1382     * @return A Map of all the instantiated partitions
1383     * @throws ConfigurationException If we cannot process some Partition
1384     */
1385    public static Map<String, Partition> createPartitions( DirectoryService directoryService,
1386        List<PartitionBean> partitionBeans ) throws ConfigurationException
1387    {
1388        Map<String, Partition> partitions = new HashMap<>( partitionBeans.size() );
1389
1390        for ( PartitionBean partitionBean : partitionBeans )
1391        {
1392            if ( partitionBean.isDisabled() )
1393            {
1394                continue;
1395            }
1396
1397            Partition partition = createPartition( directoryService, partitionBean );
1398
1399            if ( partition != null )
1400            {
1401                partitions.put( partitionBean.getPartitionId(), partition );
1402            }
1403        }
1404
1405        return partitions;
1406    }
1407
1408
1409    /**
1410     * Instantiates a DirectoryService based on the configuration present in the partition
1411     *
1412     * @param directoryServiceBean The bean containing the configuration
1413     * @param instanceLayout The InstanceLayout instance
1414     * @param schemaManager The SchemaManager instance
1415     * @return An instance of DirectoryService
1416     * @throws Exception If the DirectoryService cannot be created
1417     */
1418    public static DirectoryService createDirectoryService( DirectoryServiceBean directoryServiceBean,
1419        InstanceLayout instanceLayout, SchemaManager schemaManager ) throws Exception
1420    {
1421        DirectoryService directoryService = new DefaultDirectoryService();
1422
1423        // The schemaManager
1424        directoryService.setSchemaManager( schemaManager );
1425
1426        // MUST attributes
1427        // DirectoryService ID
1428        directoryService.setInstanceId( directoryServiceBean.getDirectoryServiceId() );
1429
1430        // Replica ID
1431        directoryService.setReplicaId( directoryServiceBean.getDsReplicaId() );
1432
1433        // WorkingDirectory
1434        directoryService.setInstanceLayout( instanceLayout );
1435
1436        // Interceptors
1437        List<Interceptor> interceptors = createInterceptors( directoryServiceBean.getInterceptors() );
1438        directoryService.setInterceptors( interceptors );
1439
1440        // Partitions
1441        Map<String, Partition> partitions = createPartitions( directoryService, directoryServiceBean.getPartitions() );
1442
1443        Partition systemPartition = partitions.remove( "system" );
1444
1445        if ( systemPartition == null )
1446        {
1447            throw new Exception( I18n.err( I18n.ERR_505 ) );
1448        }
1449
1450        directoryService.setSystemPartition( systemPartition );
1451        directoryService.setPartitions( new HashSet<Partition>( partitions.values() ) );
1452
1453        // MAY attributes
1454        // AccessControlEnabled
1455        directoryService.setAccessControlEnabled( directoryServiceBean.isDsAccessControlEnabled() );
1456
1457        // AllowAnonymousAccess
1458        directoryService.setAllowAnonymousAccess( directoryServiceBean.isDsAllowAnonymousAccess() );
1459
1460        // ChangeLog
1461        ChangeLog cl = createChangeLog( directoryServiceBean.getChangeLog() );
1462
1463        if ( cl != null )
1464        {
1465            directoryService.setChangeLog( cl );
1466        }
1467
1468        // DenormalizedOpAttrsEnabled
1469        directoryService.setDenormalizeOpAttrsEnabled( directoryServiceBean.isDsDenormalizeOpAttrsEnabled() );
1470
1471        // Journal
1472        Journal journal = createJournal( directoryServiceBean.getJournal() );
1473
1474        if ( journal != null )
1475        {
1476            directoryService.setJournal( journal );
1477        }
1478
1479        // PasswordHidden
1480        directoryService.setPasswordHidden( directoryServiceBean.isDsPasswordHidden() );
1481
1482        // SyncPeriodMillis
1483        directoryService.setSyncPeriodMillis( directoryServiceBean.getDsSyncPeriodMillis() );
1484
1485        // testEntries
1486        String entryFilePath = directoryServiceBean.getDsTestEntries();
1487
1488        if ( entryFilePath != null )
1489        {
1490            directoryService.setTestEntries( readTestEntries( entryFilePath ) );
1491        }
1492
1493        // Enabled
1494        // if ( !directoryServiceBean.isEnabled() )
1495        // TODO will only be useful if we ever allow more than one DS to be configured and
1496        // switch between them decide which one to use based on this flag
1497
1498        return directoryService;
1499    }
1500
1501
1502    public static MavibotPartition createMavibotPartition( DirectoryService directoryService,
1503        MavibotPartitionBean mvbtPartitionBean ) throws ConfigurationException
1504    {
1505        if ( ( mvbtPartitionBean == null ) || mvbtPartitionBean.isDisabled() )
1506        {
1507            return null;
1508        }
1509
1510        MavibotPartition mvbtPartition = new MavibotPartition( directoryService.getSchemaManager(),
1511            directoryService.getDnFactory() );
1512
1513        mvbtPartition.setId( mvbtPartitionBean.getPartitionId() );
1514        File partitionPath = new File( directoryService.getInstanceLayout().getPartitionsDirectory(),
1515            mvbtPartitionBean.getPartitionId() );
1516        mvbtPartition.setPartitionPath( partitionPath.toURI() );
1517
1518        try
1519        {
1520            mvbtPartition.setSuffixDn( mvbtPartitionBean.getPartitionSuffix() );
1521        }
1522        catch ( LdapInvalidDnException lide )
1523        {
1524            String message = "Cannot set the Dn " + mvbtPartitionBean.getPartitionSuffix() + ", " + lide.getMessage();
1525            LOG.error( message );
1526            throw new ConfigurationException( message );
1527        }
1528
1529        mvbtPartition.setSyncOnWrite( mvbtPartitionBean.isPartitionSyncOnWrite() );
1530        mvbtPartition.setIndexedAttributes( createMavibotIndexes( mvbtPartition, mvbtPartitionBean.getIndexes(),
1531            directoryService ) );
1532
1533        setContextEntry( mvbtPartitionBean, mvbtPartition );
1534
1535        return mvbtPartition;
1536    }
1537
1538
1539    /**
1540     * Create the list of MavibotIndex from the configuration
1541     */
1542    private static Set<Index<?, String>> createMavibotIndexes( MavibotPartition partition,
1543        List<IndexBean> indexesBeans,
1544        DirectoryService directoryService ) //throws Exception
1545    {
1546        Set<Index<?, String>> indexes = new HashSet<>();
1547
1548        for ( IndexBean indexBean : indexesBeans )
1549        {
1550            if ( indexBean.isEnabled() && ( indexBean instanceof MavibotIndexBean ) )
1551            {
1552                indexes.add( createMavibotIndex( partition, ( MavibotIndexBean ) indexBean, directoryService ) );
1553            }
1554        }
1555
1556        return indexes;
1557    }
1558
1559
1560    /**
1561     * Create a new instance of a MavibotIndex from an instance of MavibotIndexBean
1562     * 
1563     * @param partition The Mavibot partition instance
1564     * @param mavibotIndexBean The MavibotIndexBean to convert
1565     * @param directoryService The DirectoryService instance
1566     * @return An MavibotIndex instance
1567     */
1568    public static MavibotIndex<?> createMavibotIndex( MavibotPartition partition,
1569        MavibotIndexBean mavibotIndexBean, DirectoryService directoryService )
1570    {
1571        if ( ( mavibotIndexBean == null ) || mavibotIndexBean.isDisabled() )
1572        {
1573            return null;
1574        }
1575
1576        MavibotIndex<?> index = null;
1577
1578        boolean hasReverse = mavibotIndexBean.getIndexHasReverse();
1579
1580        if ( mavibotIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_RDN_AT )
1581            || mavibotIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_RDN_AT_OID ) )
1582        {
1583            index = new MavibotRdnIndex();
1584        }
1585        else if ( mavibotIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_ALIAS_AT )
1586            || mavibotIndexBean.getIndexAttributeId().equalsIgnoreCase( ApacheSchemaConstants.APACHE_ALIAS_AT_OID ) )
1587        {
1588            index = new MavibotDnIndex( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
1589        }
1590        else
1591        {
1592            index = new MavibotIndex<>( mavibotIndexBean.getIndexAttributeId(), hasReverse );
1593        }
1594
1595        index.setWkDirPath( partition.getPartitionPath() );
1596
1597        return index;
1598    }
1599
1600
1601    /**
1602     * Sets the configured context entry if present in the given partition bean 
1603     *
1604     * @param bean the partition configuration bean
1605     * @param partition the partition instance
1606     * @throws ConfigurationException
1607     */
1608    private static void setContextEntry( PartitionBean bean, AbstractPartition partition )
1609        throws ConfigurationException
1610    {
1611        String contextEntry = bean.getContextEntry();
1612
1613        if ( contextEntry != null )
1614        {
1615            try
1616            {
1617                // Replace '\n' to real LF
1618                String entryStr = contextEntry.replaceAll( "\\\\n", "\n" );
1619
1620                try ( LdifReader ldifReader = new LdifReader( partition.getSchemaManager() ) )
1621                {
1622                    List<LdifEntry> entries = ldifReader.parseLdif( entryStr );
1623    
1624                    if ( ( entries != null ) && !entries.isEmpty() )
1625                    {
1626                        LdifEntry entry = entries.get( 0 );
1627                        partition.setContextEntry( entry.getEntry() );
1628                    }
1629                }
1630                catch ( IOException ioe )
1631                {
1632                    LOG.error( "Cannot close the ldif reader" );
1633                }
1634            }
1635            catch ( LdapLdifException lle )
1636            {
1637                String message = "Cannot parse the context entry : " + contextEntry + ", " + lle.getMessage();
1638                LOG.error( message );
1639                throw new ConfigurationException( message );
1640            }
1641        }
1642    }
1643}