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 }