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;
022
023
024import java.lang.reflect.Array;
025import java.lang.reflect.Constructor;
026import java.lang.reflect.Field;
027import java.lang.reflect.InvocationTargetException;
028import java.lang.reflect.Method;
029import java.lang.reflect.ParameterizedType;
030import java.lang.reflect.Type;
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.HashSet;
034import java.util.List;
035import java.util.Set;
036
037import org.apache.directory.api.ldap.model.constants.SchemaConstants;
038import org.apache.directory.api.ldap.model.cursor.Cursor;
039import org.apache.directory.api.ldap.model.entry.Attribute;
040import org.apache.directory.api.ldap.model.entry.Entry;
041import org.apache.directory.api.ldap.model.entry.Value;
042import org.apache.directory.api.ldap.model.exception.LdapException;
043import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
044import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
045import org.apache.directory.api.ldap.model.filter.EqualityNode;
046import org.apache.directory.api.ldap.model.message.AliasDerefMode;
047import org.apache.directory.api.ldap.model.message.SearchScope;
048import org.apache.directory.api.ldap.model.name.Dn;
049import org.apache.directory.api.ldap.model.name.Rdn;
050import org.apache.directory.api.ldap.model.schema.AttributeType;
051import org.apache.directory.api.ldap.model.schema.ObjectClass;
052import org.apache.directory.api.ldap.model.schema.SchemaManager;
053import org.apache.directory.api.util.Strings;
054import org.apache.directory.server.config.beans.AdsBaseBean;
055import org.apache.directory.server.config.beans.ConfigBean;
056import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
057import org.apache.directory.server.core.api.partition.PartitionTxn;
058import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
059import org.apache.directory.server.i18n.I18n;
060import org.apache.directory.server.xdbm.IndexEntry;
061import org.apache.directory.server.xdbm.search.PartitionSearchResult;
062import org.apache.directory.server.xdbm.search.SearchEngine;
063import org.slf4j.Logger;
064import org.slf4j.LoggerFactory;
065
066
067/**
068 * A class used for reading the configuration present in a Partition
069 * and instantiate the necessary objects like DirectoryService, Interceptors etc.
070 *
071 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
072 */
073public class ConfigPartitionReader
074{
075    /** The logger for this class */
076    private static final Logger LOG = LoggerFactory.getLogger( ConfigPartitionReader.class );
077
078    /** the partition which holds the configuration data */
079    private AbstractBTreePartition configPartition;
080
081    /** the search engine of the partition */
082    private SearchEngine se;
083
084    /** the schema manager set in the config partition */
085    private SchemaManager schemaManager;
086
087    /** The prefix for all the configuration ObjectClass names */
088    private static final String ADS_PREFIX = "ads-";
089
090    /** The suffix for the bean */
091    private static final String ADS_SUFFIX = "Bean";
092
093
094    /**
095     * 
096     * Creates a new instance of ConfigPartitionReader.
097     *
098     * @param configPartition the non null config partition
099     */
100    public ConfigPartitionReader( AbstractBTreePartition configPartition )
101    {
102        if ( configPartition == null )
103        {
104            throw new IllegalArgumentException( I18n.err( I18n.ERR_503 ) );
105        }
106
107        if ( !configPartition.isInitialized() )
108        {
109            throw new IllegalStateException( I18n.err( I18n.ERR_504 ) );
110        }
111
112        this.configPartition = configPartition;
113        se = configPartition.getSearchEngine();
114        this.schemaManager = configPartition.getSchemaManager();
115    }
116
117
118    /**
119     * Find the upper objectclass in a hierarchy. All the inherited ObjectClasses
120     * will be removed.
121     */
122    private ObjectClass findObjectClass( Attribute objectClass ) throws Exception
123    {
124        Set<ObjectClass> candidates = new HashSet<>();
125
126        // Create the set of candidates
127        for ( Value ocValue : objectClass )
128        {
129            String ocName = ocValue.getString();
130            String ocOid = schemaManager.getObjectClassRegistry().getOidByName( ocName );
131            ObjectClass oc = schemaManager.getObjectClassRegistry().get( ocOid );
132
133            if ( oc.isStructural() )
134            {
135                candidates.add( oc );
136            }
137        }
138
139        // Now find the parent OC
140        for ( Value ocValue : objectClass )
141        {
142            String ocName = ocValue.getString();
143            String ocOid = schemaManager.getObjectClassRegistry().getOidByName( ocName );
144            ObjectClass oc = schemaManager.getObjectClassRegistry().get( ocOid );
145
146            for ( ObjectClass superior : oc.getSuperiors() )
147            {
148                if ( oc.isStructural() && candidates.contains( superior ) )
149                {
150                    candidates.remove( superior );
151                }
152            }
153        }
154
155        // The remaining OC in the candidates set is the one we are looking for
156        ObjectClass result = candidates.toArray( new ObjectClass[]
157            {} )[0];
158
159        LOG.debug( "The top level object class is {}", result.getName() );
160        return result;
161    }
162
163
164    /**
165     * Create the base Bean from the ObjectClass name.
166     * The bean name is constructed using the OjectClass name, by
167     * removing the ADS prefix, upper casing the first letter and adding "Bean" at the end.
168     * 
169     * For instance, ads-directoryService wil become DirectoryServiceBean
170     */
171    private AdsBaseBean createBean( ObjectClass objectClass ) throws ConfigurationException
172    {
173        // The remaining OC in the candidates set is the one we are looking for
174        String objectClassName = objectClass.getName();
175
176        // Now, let's instantiate the associated bean. Get rid of the 'ads-' in front of the name,
177        // and uppercase the first letter. Finally add "Bean" at the end and add the package.
178        String beanName = this.getClass().getPackage().getName() + ".beans."
179            + Character.toUpperCase( objectClassName.charAt( ADS_PREFIX.length() ) )
180            + objectClassName.substring( ADS_PREFIX.length() + 1 ) + ADS_SUFFIX;
181
182        try
183        {
184            Class<?> clazz = Class.forName( beanName );
185            Constructor<?> constructor = clazz.getConstructor();
186            AdsBaseBean bean = ( AdsBaseBean ) constructor.newInstance();
187
188            LOG.debug( "Bean {} created for ObjectClass {}", beanName, objectClassName );
189
190            return bean;
191        }
192        catch ( ClassNotFoundException cnfe )
193        {
194            String message = "Cannot find a Bean class for the ObjectClass name " + objectClassName;
195            LOG.error( message );
196            throw new ConfigurationException( message );
197        }
198        catch ( SecurityException e )
199        {
200            String message = "Cannot access to the class " + beanName;
201            LOG.error( message );
202            throw new ConfigurationException( message );
203        }
204        catch ( NoSuchMethodException nsme )
205        {
206            String message = "Cannot find a constructor for the class " + beanName;
207            LOG.error( message );
208            throw new ConfigurationException( message );
209        }
210        catch ( InvocationTargetException ite )
211        {
212            String message = "Cannot invoke the class " + beanName + ", " + ite.getMessage();
213            LOG.error( message );
214            throw new ConfigurationException( message );
215        }
216        catch ( IllegalAccessException iae )
217        {
218            String message = "Cannot access to the constructor for class " + beanName;
219            LOG.error( message );
220            throw new ConfigurationException( message );
221        }
222        catch ( InstantiationException ie )
223        {
224            String message = "Cannot instantiate the class " + beanName + ", " + ie.getMessage();
225            LOG.error( message );
226            throw new ConfigurationException( message );
227        }
228    }
229
230
231    /**
232     * Read the single entry value for an AttributeType, and feed the Bean field with this value
233     */
234    private void readSingleValueField( AdsBaseBean bean, Field beanField, Attribute fieldAttr )
235        throws ConfigurationException
236    {
237        if ( fieldAttr == null )
238        {
239            return;
240        }
241        
242        
243        Value value = fieldAttr.get();
244        String valueStr = ""; 
245        
246        if ( value != null )
247        {
248            valueStr = value.getString();
249        }
250
251        Class<?> type = beanField.getType();
252
253        // Process the value accordingly to its type.
254        try
255        {
256            if ( type == String.class )
257            {
258                beanField.set( bean, valueStr );
259            }
260            else if ( type == byte[].class )
261            {
262                if ( value != null )
263                {
264                    beanField.set( bean, value.getBytes() );
265                }
266                else
267                {
268                    beanField.set( bean, Strings.EMPTY_BYTES );
269                }
270            }
271            else if ( type == int.class )
272            {
273                beanField.setInt( bean, Integer.parseInt( valueStr ) );
274            }
275            else if ( type == long.class )
276            {
277                beanField.setLong( bean, Long.parseLong( valueStr ) );
278            }
279            else if ( type == boolean.class )
280            {
281                beanField.setBoolean( bean, Boolean.parseBoolean( valueStr ) );
282            }
283            else if ( type == Dn.class )
284            {
285                try
286                {
287                    Dn dn = new Dn( valueStr );
288                    beanField.set( bean, dn );
289                }
290                catch ( LdapInvalidDnException lide )
291                {
292                    String message = "The Dn '" + valueStr + "' for attribute " + fieldAttr.getId()
293                        + " is not a valid Dn";
294                    LOG.error( message );
295                    throw new ConfigurationException( message );
296                }
297            }
298        }
299        catch ( IllegalArgumentException | IllegalAccessException e )
300        {
301            String message = "Cannot store '" + valueStr + "' into attribute " + fieldAttr.getId();
302            LOG.error( message );
303            throw new ConfigurationException( message );
304        }
305    }
306
307
308    /**
309     * Read the multiple entry value for an AttributeType, and feed the Bean field with this value
310     */
311    private void readMultiValuedField( AdsBaseBean bean, Field field, Attribute attribute )
312        throws ConfigurationException
313    {
314        if ( attribute == null )
315        {
316            return;
317        }
318
319        Class<?> type = field.getType();
320
321        String fieldName = field.getName();
322        String addMethodName = "add" + Character.toUpperCase( fieldName.charAt( 0 ) ) + fieldName.substring( 1 );
323
324        // loop on the values and inject them in the bean
325        for ( Value value : attribute )
326        {
327            String valueStr = value.getString();
328
329            try
330            {
331                if ( type == String.class )
332                {
333                    field.set( bean, valueStr );
334                }
335                else if ( type == int.class )
336                {
337                    field.setInt( bean, Integer.parseInt( valueStr ) );
338                }
339                else if ( type == long.class )
340                {
341                    field.setLong( bean, Long.parseLong( valueStr ) );
342                }
343                else if ( type == boolean.class )
344                {
345                    field.setBoolean( bean, Boolean.parseBoolean( valueStr ) );
346                }
347                else if ( type == Dn.class )
348                {
349                    try
350                    {
351                        Dn dn = new Dn( valueStr );
352                        field.set( bean, dn );
353                    }
354                    catch ( LdapInvalidDnException lide )
355                    {
356                        String message = "The Dn '" + valueStr + "' for attribute " + attribute.getId()
357                            + " is not a valid Dn";
358                        LOG.error( message );
359                        throw new ConfigurationException( message );
360                    }
361                }
362                else if ( ( type == Set.class ) || ( type == List.class ) )
363                {
364                    Type genericFieldType = field.getGenericType();
365                    Class<?> fieldArgClass = null;
366
367                    if ( genericFieldType instanceof ParameterizedType )
368                    {
369                        ParameterizedType parameterizedType = ( ParameterizedType ) genericFieldType;
370                        Type[] fieldArgTypes = parameterizedType.getActualTypeArguments();
371
372                        for ( Type fieldArgType : fieldArgTypes )
373                        {
374                            fieldArgClass = ( Class<?> ) fieldArgType;
375                        }
376                    }
377
378                    Method method = bean.getClass().getMethod( addMethodName,
379                        Array.newInstance( fieldArgClass, 0 ).getClass() );
380
381                    method.invoke( bean, new Object[] { new String[] { valueStr } } );
382                }
383            }
384            catch ( IllegalArgumentException | IllegalAccessException e )
385            {
386                String message = "Cannot store '" + valueStr + "' into attribute " + attribute.getId();
387                LOG.error( message );
388                throw new ConfigurationException( message );
389            }
390            catch ( SecurityException e )
391            {
392                String message = "Cannot access to the class " + bean.getClass().getName();
393                LOG.error( message );
394                throw new ConfigurationException( message );
395            }
396            catch ( NoSuchMethodException nsme )
397            {
398                String message = "Cannot find a method " + addMethodName + " in the class " + bean.getClass().getName();
399                LOG.error( message );
400                throw new ConfigurationException( message );
401            }
402            catch ( InvocationTargetException ite )
403            {
404                String message = "Cannot invoke the class " + bean.getClass().getName() + ", " + ite.getMessage();
405                LOG.error( message );
406                throw new ConfigurationException( message );
407            }
408            catch ( NegativeArraySizeException nase )
409            {
410                // No way that can happen...
411            }
412        }
413    }
414
415
416    private void readFieldValue( AdsBaseBean bean, Field field, Entry entry, String attributeTypeName, boolean mandatory )
417        throws ConfigurationException
418    {
419        // Get the entry attribute for this attribute type
420        Attribute attribute = entry.get( attributeTypeName );
421
422        if ( attribute != null )
423        {
424            if ( attribute.size() > 0 )
425            {
426                if ( !isMultiple( field.getType() ) )
427                {
428                    readSingleValueField( bean, field, attribute );
429                }
430                else
431                {
432                    readMultiValuedField( bean, field, attribute );
433                }
434            }
435            else if ( attribute.size() == 0 )
436            {
437                // No value ? May be valid
438                readSingleValueField( bean, field, attribute );
439            }
440            else if ( mandatory )
441            {
442                // the requested element is mandatory so let's throw an exception
443                String message = "No value was configured for entry with DN '"
444                    + entry.getDn() + "' and attribute type '" + attributeTypeName + "'.";
445                LOG.error( message );
446                throw new ConfigurationException( message );
447            }
448        }
449        else
450        {
451            if ( mandatory )
452            {
453                // the requested element is mandatory so let's throw an exception
454                String message = "No value was configured for entry with DN '"
455                    + entry.getDn() + "' and attribute type '" + attributeTypeName + "'.";
456                LOG.error( message );
457                throw new ConfigurationException( message );
458            }
459        }
460    }
461
462
463    /**
464     * Read some configuration element from the DIT using its name
465     * 
466     * @param baseDn The base Dn in the DIT where the configuration is stored
467     * @param name The element to read
468     * @param scope The search scope
469     * @param mandatory If the element is mandatory or not
470     * @return The list of beans read
471     * @throws ConfigurationException If the configuration cannot be read 
472     */
473    public List<AdsBaseBean> read( Dn baseDn, String name, SearchScope scope, boolean mandatory )
474        throws ConfigurationException
475    {
476        LOG.debug( "Reading from '{}', objectClass '{}'", baseDn, name );
477
478        // Search for the element starting at some point in the DIT
479        // Prepare the search request
480        AttributeType ocAt = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
481        EqualityNode<String> filter = null;
482        
483        try
484        {
485            filter = new EqualityNode<>( ocAt, new Value( ocAt, name ) );
486        }
487        catch ( LdapInvalidAttributeValueException liave )
488        {
489            throw new ConfigurationException( liave.getMessage() );
490        }
491        
492        Cursor<IndexEntry<String, String>> cursor = null;
493
494        // Create a container for all the read beans
495        List<AdsBaseBean> beansList = new ArrayList<>();
496
497        try
498        {
499            // Do the search
500            
501            try ( PartitionTxn partitionTxn = configPartition.beginReadTransaction() )
502            {
503                SearchOperationContext searchContext = new SearchOperationContext( null );
504                searchContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
505                searchContext.setDn( baseDn );
506                searchContext.setFilter( filter );
507                searchContext.setScope( scope );
508                searchContext.setPartition( configPartition );
509                searchContext.setTransaction( partitionTxn );
510                PartitionSearchResult searchResult = se.computeResult( partitionTxn, schemaManager, searchContext );
511    
512                cursor = searchResult.getResultSet();
513    
514                // First, check if we have some entries to process.
515                if ( !cursor.next() )
516                {
517                    if ( mandatory )
518                    {
519                        cursor.close();
520    
521                        // the requested element is mandatory so let's throw an exception
522                        String message = "No instance was configured under the DN '"
523                            + baseDn + "' for the objectClass '" + name + "'.";
524                        LOG.error( message );
525                        throw new ConfigurationException( message );
526                    }
527                    else
528                    {
529                        return null;
530                    }
531                }
532    
533                // Loop on all the found elements
534                do
535                {
536                    IndexEntry<String, String> forwardEntry = cursor.get();
537    
538                    // Now, get the entry
539                    Entry entry = configPartition.fetch( partitionTxn, forwardEntry.getId() );
540                    LOG.debug( "Entry read : {}", entry );
541    
542                    AdsBaseBean bean = readConfig( entry );
543                    // Adding the bean to the list
544                    beansList.add( bean );
545                }
546                while ( cursor.next() );
547            }
548        }
549        catch ( ConfigurationException ce )
550        {
551            throw ce;
552        }
553        catch ( Exception e )
554        {
555            String message = "An error occured while reading the configuration DN '"
556                + baseDn + "' for the objectClass '" + name + "':\n" + e.getMessage();
557            LOG.error( message );
558            throw new ConfigurationException( message, e );
559        }
560        finally
561        {
562            if ( cursor != null )
563            {
564                try
565                {
566                    cursor.close();
567                }
568                catch ( Exception e )
569                {
570                    // So ??? If the cursor can't be close, there is nothing we can do
571                    // but rethrow the exception
572                    throw new ConfigurationException( e.getMessage(), e.getCause() );
573                }
574            }
575        }
576
577        return beansList;
578    }
579
580
581    /**
582     * Creates a configuration bean from the given entry.
583     * 
584     * @param entry any configuration entry of the type "ads-base"
585     * @return The ApacheDS base configuration
586     * @throws Exception If the configuration cannot be read
587     */
588    public AdsBaseBean readConfig( Entry entry ) throws Exception
589    {
590        // Let's instantiate the bean we need. The upper ObjectClass's name
591        // will be used to do that
592        ObjectClass objectClass = findObjectClass( entry.get( SchemaConstants.OBJECT_CLASS_AT ) );
593
594        // Instantiating the bean
595        AdsBaseBean bean = createBean( objectClass );
596
597        // Setting its DN
598        bean.setDn( entry.getDn() );
599
600        // Getting the class of the bean
601        Class<?> beanClass = bean.getClass();
602
603        // A flag to know when we reached the 'AdsBaseBean' class when 
604        // looping on the class hierarchy of the bean
605        boolean adsBaseBeanClassFound = false;
606
607        // Looping until the 'AdsBaseBean' class has been found
608        while ( !adsBaseBeanClassFound )
609        {
610            // Checking if we reached the 'AdsBaseBean' class
611            if ( beanClass == AdsBaseBean.class )
612            {
613                adsBaseBeanClassFound = true;
614            }
615
616            // Looping on all fields of the bean
617            Field[] fields = beanClass.getDeclaredFields();
618            for ( Field field : fields )
619            {
620                // Making the field accessible (we get an exception if we don't do that)
621                field.setAccessible( true );
622
623                // Getting the class of the field
624                Class<?> fieldClass = field.getType();
625
626                // Looking for the @ConfigurationElement annotation
627                ConfigurationElement configurationElement = field.getAnnotation( ConfigurationElement.class );
628                if ( configurationElement != null )
629                {
630                    // Getting the annotation's values
631                    String fieldAttributeType = configurationElement.attributeType();
632                    String fieldObjectClass = configurationElement.objectClass();
633                    String container = configurationElement.container();
634                    boolean isOptional = configurationElement.isOptional();
635
636                    // Checking if we have a value for the attribute type
637                    if ( ( fieldAttributeType != null ) && ( !"".equals( fieldAttributeType ) ) )
638                    {
639                        readFieldValue( bean, field, entry, fieldAttributeType, !isOptional );
640                    }
641                    // Checking if we have a value for the object class
642                    else if ( ( fieldObjectClass != null ) && ( !"".equals( fieldObjectClass ) ) )
643                    {
644                        // Checking if this is a multi-valued field (which values are stored in a container)
645                        if ( isMultiple( fieldClass ) && ( container != null )
646                            && ( !"".equals( container ) ) )
647                        {
648                            // Creating the DN of the container
649                            Dn newBase = entry.getDn().add( "ou=" + container );
650
651                            // Looking for the field values
652                            Collection<AdsBaseBean> fieldValues = read( newBase, fieldObjectClass,
653                                SearchScope.ONELEVEL, !isOptional );
654
655                            // Setting the values to the field
656                            if ( ( fieldValues != null ) && !fieldValues.isEmpty() )
657                            {
658                                field.set( bean, fieldValues );
659                            }
660                        }
661                        // This is a single-value field
662                        else
663                        {
664                            // Looking for the field values
665                            List<AdsBaseBean> fieldValues = read( entry.getDn(), fieldObjectClass,
666                                SearchScope.ONELEVEL, !isOptional );
667
668                            // Setting the value to the field
669                            if ( ( fieldValues != null ) && !fieldValues.isEmpty() )
670                            {
671                                field.set( bean, fieldValues.get( 0 ) );
672                            }
673                        }
674                    }
675                }
676            }
677
678            // Moving to the upper class in the class hierarchy
679            beanClass = beanClass.getSuperclass();
680        }
681        
682        return bean;
683    }
684    
685    
686    /**
687     * Indicates the given type is multiple.
688     *
689     * @param clazz
690     *      the class
691     * @return
692     *      <code>true</code> if the given is multiple,
693     *      <code>false</code> if not.
694     */
695    private boolean isMultiple( Class<?> clazz )
696    {
697        return Collection.class.isAssignableFrom( clazz );
698    }
699
700
701    /**
702     * Read the configuration from the DIT, returning a bean containing all of it.
703     * <p>
704     * This method implicitly uses <em>"ou=config"</em> as base Dn
705     * 
706     * @return The Config bean, containing the whole configuration
707     * @throws ConfigurationException If we had some issue reading the configuration
708     */
709    public ConfigBean readConfig() throws LdapException
710    {
711        // The starting point is the DirectoryService element
712        return readConfig( new Dn( new Rdn( SchemaConstants.OU_AT, "config" ) ) );
713    }
714
715
716    /**
717     * Read the configuration from the DIT, returning a bean containing all of it.
718     * 
719     * @param baseDn The base Dn in the DIT where the configuration is stored
720     * @return The Config bean, containing the whole configuration
721     * @throws ConfigurationException If we had some issue reading the configuration
722     */
723    public ConfigBean readConfig( String baseDn ) throws LdapException
724    {
725        // The starting point is the DirectoryService element
726        return readConfig( new Dn( baseDn ) );
727    }
728
729
730    /**
731     * Read the configuration from the DIT, returning a bean containing all of it.
732     * 
733     * @param baseDn The base Dn in the DIT where the configuration is stored
734     * @return The Config bean, containing the whole configuration
735     * @throws ConfigurationException If we had some issue reading the configuration
736     */
737    public ConfigBean readConfig( Dn baseDn ) throws ConfigurationException
738    {
739        // The starting point is the DirectoryService element
740        return readConfig( baseDn, ConfigSchemaConstants.ADS_DIRECTORY_SERVICE_OC.getValue() );
741    }
742
743
744    /**
745     * Read the configuration from the DIT, returning a bean containing all of it.
746     * 
747     * @param baseDn The base Dn in the DIT where the configuration is stored
748     * @param objectClass The element to read from the DIT
749     * @return The bean containing the configuration for the required element
750     * @throws ConfigurationException If the configuration cannot be read
751     */
752    public ConfigBean readConfig( String baseDn, String objectClass ) throws LdapException
753    {
754        return readConfig( new Dn( baseDn ), objectClass );
755    }
756
757
758    /**
759     * Read the configuration from the DIT, returning a bean containing all of it.
760     * 
761     * @param baseDn The base Dn in the DIT where the configuration is stored
762     * @param objectClass The element to read from the DIT
763     * @return The bean containing the configuration for the required element
764     * @throws ConfigurationException If the configuration cannot be read
765     */
766    public ConfigBean readConfig( Dn baseDn, String objectClass ) throws ConfigurationException
767    {
768        LOG.debug( "Reading configuration for the {} element, from {} ", objectClass, baseDn );
769        ConfigBean configBean = new ConfigBean();
770
771        if ( baseDn == null )
772        {
773            baseDn = configPartition.getSuffixDn();
774        }
775
776        List<AdsBaseBean> beans = read( baseDn, objectClass, SearchScope.ONELEVEL, true );
777
778        if ( LOG.isDebugEnabled() )
779        {
780            if ( ( beans == null ) || beans.isEmpty() )
781            {
782                LOG.debug( "No {} element to read", objectClass );
783            }
784            else
785            {
786                LOG.debug( beans.get( 0 ).toString() );
787            }
788        }
789
790        configBean.setDirectoryServiceBeans( beans );
791
792        return configBean;
793    }
794}