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