001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.shared.ldap.schemaloader;
021
022
023import java.io.File;
024import java.io.FileNotFoundException;
025import java.io.FilenameFilter;
026import java.io.IOException;
027import java.util.ArrayList;
028import java.util.List;
029
030import org.apache.directory.shared.i18n.I18n;
031import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
032import org.apache.directory.shared.ldap.model.entry.Entry;
033import org.apache.directory.shared.ldap.model.exception.LdapException;
034import org.apache.directory.shared.ldap.model.ldif.LdifEntry;
035import org.apache.directory.shared.ldap.model.ldif.LdifReader;
036import org.apache.directory.shared.ldap.model.schema.registries.Schema;
037import org.apache.directory.shared.ldap.model.schema.registries.AbstractSchemaLoader;
038import org.apache.directory.shared.util.Strings;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * Loads schema data from LDIF files containing entries representing schema
045 * objects, using the meta schema format.
046 *
047 * This class is used only for tests.
048 * 
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public class LdifSchemaLoader extends AbstractSchemaLoader
052{
053    /** ldif file extension used */
054    private static final String LDIF_EXT = "ldif";
055
056    /** ou=schema LDIF file name */
057    private static final String OU_SCHEMA_LDIF = "ou=schema." + LDIF_EXT;
058
059    /** static class logger */
060    private static final Logger LOG = LoggerFactory.getLogger( LdifSchemaLoader.class );
061
062    /** Speedup for DEBUG mode */
063    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
064
065    /** directory containing the schema LDIF file for ou=schema */
066    private final File baseDirectory;
067
068    /** a filter for listing all the LDIF files within a directory */
069    private final FilenameFilter ldifFilter = new FilenameFilter()
070    {
071        public boolean accept( File file, String name )
072        {
073            return name.endsWith( LDIF_EXT );
074        }
075    };
076
077
078    /**
079     * Creates a new LDIF based SchemaLoader. The constructor checks to make
080     * sure the supplied base directory exists and contains a schema.ldif file
081     * and if not complains about it.
082     *
083     * @param baseDirectory the schema LDIF base directory
084     * @throws Exception if the base directory does not exist or does not
085     * a valid schema.ldif file
086     */
087    public LdifSchemaLoader( File baseDirectory ) throws Exception
088    {
089        this.baseDirectory = baseDirectory;
090
091        if ( !baseDirectory.exists() )
092        {
093            String msg = "Provided baseDirectory '" + baseDirectory.getAbsolutePath() + "' does not exist.";
094            LOG.error( msg );
095            throw new IllegalArgumentException( msg );
096        }
097
098        File schemaLdif = new File( baseDirectory, OU_SCHEMA_LDIF );
099
100        if ( !schemaLdif.exists() )
101        {
102            String msg = I18n.err( I18n.ERR_10004, schemaLdif.getAbsolutePath() );
103            LOG.error( msg );
104            throw new FileNotFoundException( msg );
105        }
106
107        if ( IS_DEBUG )
108        {
109            LOG.debug( "Using '{}' as the base schema load directory.", baseDirectory );
110        }
111
112        initializeSchemas();
113    }
114
115
116    /**
117     * Scans for LDIF files just describing the various schema contained in
118     * the schema repository.
119     *
120     * @throws Exception
121     */
122    private void initializeSchemas() throws Exception
123    {
124        if ( IS_DEBUG )
125        {
126            LOG.debug( "Initializing schema" );
127        }
128
129        File schemaDirectory = new File( baseDirectory, SchemaConstants.OU_SCHEMA );
130        String[] ldifFiles = schemaDirectory.list( ldifFilter );
131
132        for ( String ldifFile : ldifFiles )
133        {
134            File file = new File( schemaDirectory, ldifFile );
135
136            try
137            {
138                LdifReader reader = new LdifReader( file );
139                LdifEntry entry = reader.next();
140                reader.close();
141                Schema schema = getSchema( entry.getEntry() );
142
143                if ( schema == null )
144                {
145                    // The entry was not a schema, skip it
146                    continue;
147                }
148
149                schemaMap.put( schema.getSchemaName(), schema );
150
151                if ( IS_DEBUG )
152                {
153                    LOG.debug( "Schema Initialized ... \n{}", schema );
154                }
155            }
156            catch ( Exception e )
157            {
158                LOG.error( I18n.err( I18n.ERR_10003, ldifFile ), e );
159                throw e;
160            }
161        }
162    }
163
164
165    /**
166     * {@inheritDoc}
167     *
168    public List<Throwable> loadWithDependencies( Schema schema, Registries registries, boolean check ) throws Exception
169    {
170        // Relax the controls at first
171        List<Throwable> errors = new ArrayList<Throwable>();
172        boolean wasRelaxed = registries.isRelaxed();
173        registries.setRelaxed( true );
174
175        Stack<String> beenthere = new Stack<String>();
176        Map<String,Schema> notLoaded = new HashMap<String,Schema>();
177        notLoaded.put( schema.getSchemaName(), schema );
178        super.loadDepsFirst( schema, beenthere, notLoaded, schema, registries );
179        
180        // At the end, check the registries if required
181        if ( check )
182        {
183            errors = registries.checkRefInteg();
184        }
185        
186        // Restore the Registries isRelaxed flag
187        registries.setRelaxed( wasRelaxed );
188        
189        return errors;
190    }
191
192
193    /**
194     * Loads a single schema if it has not been loaded already.  If the schema
195     * load request was made because some other schema depends on this one then
196     * the schema is checked to see if it is disabled.  If disabled it is 
197     * enabled with a write to disk and then loaded. Listeners are notified that
198     * the schema has been loaded.
199     * 
200     * {@inheritDoc}
201     *
202    public void load( Schema schema, Registries registries, boolean isDepLoad ) throws Exception
203    {
204        // if we're loading a dependency and it has not been enabled on 
205        // disk then enable it on disk before we proceed to load it
206        if ( schema.isDisabled() && isDepLoad )
207        {
208            enableSchema( schema );
209        }
210        
211        if ( registries.isSchemaLoaded( schema.getSchemaName() ) )
212        {
213            LOG.info( "Will not attempt to load already loaded '{}' " +
214                    "schema: \n{}", schema.getSchemaName(), schema );
215            return;
216        }
217        
218        LOG.info( "Loading {} schema: \n{}", schema.getSchemaName(), schema );
219        
220        registries.schemaLoaded( schema );
221        
222        loadComparators( schema );
223        loadNormalizers( schema, registries );
224        loadSyntaxCheckers( schema, registries );
225        loadSyntaxes( schema, registries );
226        loadMatchingRules( schema, registries );
227        loadAttributeTypes( schema, registries );
228        loadObjectClasses( schema, registries );
229        loadMatchingRuleUses( schema, registries );
230        loadDitContentRules( schema, registries );
231        loadNameForms( schema, registries );
232        loadDitStructureRules( schema, registries );
233
234        notifyListenerOrRegistries( schema, registries );
235    }
236
237    
238    /**
239     * Utility method to get the file for a schema directory.
240     *
241     * @param schema the schema to get the file for
242     * @return the file for the specific schema directory
243     */
244    private File getSchemaDirectory( Schema schema )
245    {
246        return new File( new File( baseDirectory, SchemaConstants.OU_SCHEMA ), "cn=" + Strings.lowerCase(schema.getSchemaName()) );
247    }
248
249
250    /**
251     * {@inheritDoc}
252     */
253    public List<Entry> loadComparators( Schema... schemas ) throws LdapException, IOException
254    {
255        List<Entry> comparatorList = new ArrayList<Entry>();
256
257        if ( schemas == null )
258        {
259            return comparatorList;
260        }
261
262        for ( Schema schema : schemas )
263        {
264            File comparatorsDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.COMPARATORS_PATH );
265
266            if ( !comparatorsDirectory.exists() )
267            {
268                return comparatorList;
269            }
270
271            File[] comparators = comparatorsDirectory.listFiles( ldifFilter );
272
273            for ( File ldifFile : comparators )
274            {
275                LdifReader reader = new LdifReader( ldifFile );
276                LdifEntry entry = reader.next();
277                reader.close();
278
279                comparatorList.add( entry.getEntry() );
280            }
281        }
282
283        return comparatorList;
284    }
285
286
287    /**
288     * {@inheritDoc}
289     */
290    public List<Entry> loadSyntaxCheckers( Schema... schemas ) throws LdapException, IOException
291    {
292        List<Entry> syntaxCheckerList = new ArrayList<Entry>();
293
294        if ( schemas == null )
295        {
296            return syntaxCheckerList;
297        }
298
299        for ( Schema schema : schemas )
300        {
301            File syntaxCheckersDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.SYNTAX_CHECKERS_PATH );
302
303            if ( !syntaxCheckersDirectory.exists() )
304            {
305                return syntaxCheckerList;
306            }
307
308            File[] syntaxCheckerFiles = syntaxCheckersDirectory.listFiles( ldifFilter );
309
310            for ( File ldifFile : syntaxCheckerFiles )
311            {
312                LdifReader reader = new LdifReader( ldifFile );
313                LdifEntry entry = reader.next();
314                reader.close();
315
316                syntaxCheckerList.add( entry.getEntry() );
317            }
318        }
319
320        return syntaxCheckerList;
321    }
322
323
324    /**
325     * {@inheritDoc}
326     */
327    public List<Entry> loadNormalizers( Schema... schemas ) throws LdapException, IOException
328    {
329        List<Entry> normalizerList = new ArrayList<Entry>();
330
331        if ( schemas == null )
332        {
333            return normalizerList;
334        }
335
336        for ( Schema schema : schemas )
337        {
338            File normalizersDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.NORMALIZERS_PATH );
339
340            if ( !normalizersDirectory.exists() )
341            {
342                return normalizerList;
343            }
344
345            File[] normalizerFiles = normalizersDirectory.listFiles( ldifFilter );
346
347            for ( File ldifFile : normalizerFiles )
348            {
349                LdifReader reader = new LdifReader( ldifFile );
350                LdifEntry entry = reader.next();
351                reader.close();
352
353                normalizerList.add( entry.getEntry() );
354            }
355        }
356
357        return normalizerList;
358    }
359
360
361    /**
362     * {@inheritDoc}
363     */
364    public List<Entry> loadMatchingRules( Schema... schemas ) throws LdapException, IOException
365    {
366        List<Entry> matchingRuleList = new ArrayList<Entry>();
367
368        if ( schemas == null )
369        {
370            return matchingRuleList;
371        }
372
373        for ( Schema schema : schemas )
374        {
375            File matchingRulesDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.MATCHING_RULES_PATH );
376
377            if ( !matchingRulesDirectory.exists() )
378            {
379                return matchingRuleList;
380            }
381
382            File[] matchingRuleFiles = matchingRulesDirectory.listFiles( ldifFilter );
383
384            for ( File ldifFile : matchingRuleFiles )
385            {
386                LdifReader reader = new LdifReader( ldifFile );
387                LdifEntry entry = reader.next();
388                reader.close();
389
390                matchingRuleList.add( entry.getEntry() );
391            }
392        }
393
394        return matchingRuleList;
395    }
396
397
398    /**
399     * {@inheritDoc}
400     */
401    public List<Entry> loadSyntaxes( Schema... schemas ) throws LdapException, IOException
402    {
403        List<Entry> syntaxList = new ArrayList<Entry>();
404
405        if ( schemas == null )
406        {
407            return syntaxList;
408        }
409
410        for ( Schema schema : schemas )
411        {
412            File syntaxesDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.SYNTAXES_PATH );
413
414            if ( !syntaxesDirectory.exists() )
415            {
416                return syntaxList;
417            }
418
419            File[] syntaxFiles = syntaxesDirectory.listFiles( ldifFilter );
420
421            for ( File ldifFile : syntaxFiles )
422            {
423                LdifReader reader = new LdifReader( ldifFile );
424                LdifEntry entry = reader.next();
425                reader.close();
426
427                syntaxList.add( entry.getEntry() );
428            }
429        }
430
431        return syntaxList;
432    }
433
434
435    /**
436     * {@inheritDoc}
437     */
438    public List<Entry> loadAttributeTypes( Schema... schemas ) throws LdapException, IOException
439    {
440        List<Entry> attributeTypeList = new ArrayList<Entry>();
441
442        if ( schemas == null )
443        {
444            return attributeTypeList;
445        }
446
447        for ( Schema schema : schemas )
448        {
449            // check that the attributeTypes directory exists for the schema
450            File attributeTypesDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.ATTRIBUTE_TYPES_PATH );
451
452            if ( !attributeTypesDirectory.exists() )
453            {
454                return attributeTypeList;
455            }
456
457            // get list of attributeType LDIF schema files in attributeTypes
458            File[] attributeTypeFiles = attributeTypesDirectory.listFiles( ldifFilter );
459
460            for ( File ldifFile : attributeTypeFiles )
461            {
462                LdifReader reader = new LdifReader( ldifFile );
463                LdifEntry entry = reader.next();
464                reader.close();
465
466                attributeTypeList.add( entry.getEntry() );
467            }
468        }
469
470        return attributeTypeList;
471    }
472
473
474    /**
475     * {@inheritDoc}
476     */
477    public List<Entry> loadMatchingRuleUses( Schema... schemas ) throws LdapException, IOException
478    {
479        List<Entry> matchingRuleUseList = new ArrayList<Entry>();
480
481        if ( schemas == null )
482        {
483            return matchingRuleUseList;
484        }
485
486        for ( Schema schema : schemas )
487        {
488            File matchingRuleUsesDirectory = new File( getSchemaDirectory( schema ),
489                SchemaConstants.MATCHING_RULE_USE_PATH );
490
491            if ( !matchingRuleUsesDirectory.exists() )
492            {
493                return matchingRuleUseList;
494            }
495
496            File[] matchingRuleUseFiles = matchingRuleUsesDirectory.listFiles( ldifFilter );
497
498            for ( File ldifFile : matchingRuleUseFiles )
499            {
500                LdifReader reader = new LdifReader( ldifFile );
501                LdifEntry entry = reader.next();
502                reader.close();
503
504                matchingRuleUseList.add( entry.getEntry() );
505            }
506        }
507
508        return matchingRuleUseList;
509    }
510
511
512    /**
513     * {@inheritDoc}
514     */
515    public List<Entry> loadNameForms( Schema... schemas ) throws LdapException, IOException
516    {
517        List<Entry> nameFormList = new ArrayList<Entry>();
518
519        if ( schemas == null )
520        {
521            return nameFormList;
522        }
523
524        for ( Schema schema : schemas )
525        {
526            File nameFormsDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.NAME_FORMS_PATH );
527
528            if ( !nameFormsDirectory.exists() )
529            {
530                return nameFormList;
531            }
532
533            File[] nameFormFiles = nameFormsDirectory.listFiles( ldifFilter );
534
535            for ( File ldifFile : nameFormFiles )
536            {
537                LdifReader reader = new LdifReader( ldifFile );
538                LdifEntry entry = reader.next();
539                reader.close();
540
541                nameFormList.add( entry.getEntry() );
542            }
543        }
544
545        return nameFormList;
546    }
547
548
549    /**
550     * {@inheritDoc}
551     */
552    public List<Entry> loadDitContentRules( Schema... schemas ) throws LdapException, IOException
553    {
554        List<Entry> ditContentRuleList = new ArrayList<Entry>();
555
556        if ( schemas == null )
557        {
558            return ditContentRuleList;
559        }
560
561        for ( Schema schema : schemas )
562        {
563            File ditContentRulesDirectory = new File( getSchemaDirectory( schema ),
564                SchemaConstants.DIT_CONTENT_RULES_PATH );
565
566            if ( !ditContentRulesDirectory.exists() )
567            {
568                return ditContentRuleList;
569            }
570
571            File[] ditContentRuleFiles = ditContentRulesDirectory.listFiles( ldifFilter );
572
573            for ( File ldifFile : ditContentRuleFiles )
574            {
575                LdifReader reader = new LdifReader( ldifFile );
576                LdifEntry entry = reader.next();
577                reader.close();
578
579                ditContentRuleList.add( entry.getEntry() );
580            }
581        }
582
583        return ditContentRuleList;
584    }
585
586
587    /**
588     * {@inheritDoc}
589     */
590    public List<Entry> loadDitStructureRules( Schema... schemas ) throws LdapException, IOException
591    {
592        List<Entry> ditStructureRuleList = new ArrayList<Entry>();
593
594        if ( schemas == null )
595        {
596            return ditStructureRuleList;
597        }
598
599        for ( Schema schema : schemas )
600        {
601            File ditStructureRulesDirectory = new File( getSchemaDirectory( schema ),
602                SchemaConstants.DIT_STRUCTURE_RULES_PATH );
603
604            if ( !ditStructureRulesDirectory.exists() )
605            {
606                return ditStructureRuleList;
607            }
608
609            File[] ditStructureRuleFiles = ditStructureRulesDirectory.listFiles( ldifFilter );
610
611            for ( File ldifFile : ditStructureRuleFiles )
612            {
613                LdifReader reader = new LdifReader( ldifFile );
614                LdifEntry entry = reader.next();
615                reader.close();
616
617                ditStructureRuleList.add( entry.getEntry() );
618            }
619        }
620
621        return ditStructureRuleList;
622    }
623
624
625    /**
626     * {@inheritDoc}
627     */
628    public List<Entry> loadObjectClasses( Schema... schemas ) throws LdapException, IOException
629    {
630        List<Entry> objectClassList = new ArrayList<Entry>();
631
632        if ( schemas == null )
633        {
634            return objectClassList;
635        }
636
637        for ( Schema schema : schemas )
638        {
639            // get objectClasses directory, check if exists, return if not
640            File objectClassesDirectory = new File( getSchemaDirectory( schema ), SchemaConstants.OBJECT_CLASSES_PATH );
641
642            if ( !objectClassesDirectory.exists() )
643            {
644                return objectClassList;
645            }
646
647            // get list of objectClass LDIF files from directory and load
648            File[] objectClassFiles = objectClassesDirectory.listFiles( ldifFilter );
649
650            for ( File ldifFile : objectClassFiles )
651            {
652                LdifReader reader = new LdifReader( ldifFile );
653                LdifEntry entry = reader.next();
654                reader.close();
655
656                objectClassList.add( entry.getEntry() );
657            }
658        }
659
660        return objectClassList;
661    }
662}