001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.core;
018
019import java.io.File;
020import java.io.FilenameFilter;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.LinkedList;
024import java.util.List;
025
026import org.forgerock.i18n.LocalizableMessage;
027import org.forgerock.i18n.slf4j.LocalizedLogger;
028import org.forgerock.opendj.config.server.ConfigException;
029import org.forgerock.opendj.ldap.ByteString;
030import org.forgerock.opendj.ldap.ModificationType;
031import org.forgerock.opendj.ldap.ResultCode;
032import org.forgerock.opendj.ldap.schema.AttributeType;
033import org.forgerock.opendj.ldap.schema.CoreSchema;
034import org.forgerock.opendj.ldap.schema.Syntax;
035import org.opends.server.schema.DITContentRuleSyntax;
036import org.opends.server.schema.DITStructureRuleSyntax;
037import org.opends.server.schema.MatchingRuleUseSyntax;
038import org.opends.server.schema.NameFormSyntax;
039import org.opends.server.schema.ObjectClassSyntax;
040import org.opends.server.types.Attribute;
041import org.opends.server.types.DITContentRule;
042import org.opends.server.types.DITStructureRule;
043import org.opends.server.types.DirectoryException;
044import org.opends.server.types.Entry;
045import org.opends.server.types.InitializationException;
046import org.opends.server.types.LDIFImportConfig;
047import org.opends.server.types.MatchingRuleUse;
048import org.opends.server.types.Modification;
049import org.opends.server.types.NameForm;
050import org.opends.server.types.ObjectClass;
051import org.opends.server.types.Schema;
052import org.opends.server.util.LDIFReader;
053import org.opends.server.util.StaticUtils;
054
055import static org.opends.messages.ConfigMessages.*;
056import static org.opends.server.config.ConfigConstants.*;
057import static org.opends.server.schema.SchemaConstants.*;
058import static org.opends.server.types.CommonSchemaElements.*;
059import static org.opends.server.util.ServerConstants.*;
060import static org.opends.server.util.StaticUtils.*;
061
062/**
063 * This class defines a utility that will be used to manage the interaction with
064 * the Directory Server schema.  It will be used to initially load all of the
065 * matching rules and attribute syntaxes that have been defined in the
066 * configuration, and will then read the actual schema definitions.  At present,
067 * only attribute types and objectclasses are supported in the schema config
068 * files.  Other components like DIT content rules, DIT structure rules, name
069 * forms, and matching rule use definitions will be ignored.
070 */
071public class SchemaConfigManager
072{
073  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
074
075  /** The schema that has been parsed from the server configuration. */
076  private Schema schema;
077
078  private final ServerContext serverContext;
079
080  /**
081   * Creates a new instance of this schema config manager.
082   *
083   * @param serverContext
084   *            The server context.
085   */
086  public SchemaConfigManager(ServerContext serverContext)
087  {
088    this.serverContext = serverContext;
089    try
090    {
091      // the manager will build the schema from scratch, but we need to start from core schema for SDK schema
092      schema = new Schema(org.forgerock.opendj.ldap.schema.Schema.getCoreSchema());
093    }
094    catch (DirectoryException unexpected)
095    {
096      // the core schema should not have any warning
097      throw new RuntimeException(unexpected);
098    }
099  }
100
101
102
103  /**
104   * Retrieves the path to the directory containing the server schema files.
105   *
106   * @return  The path to the directory containing the server schema files.
107   */
108  public static String getSchemaDirectoryPath()
109  {
110    File schemaDir =
111        DirectoryServer.getEnvironmentConfig().getSchemaDirectory();
112    if (schemaDir != null) {
113      return schemaDir.getAbsolutePath();
114    }
115    return null;
116  }
117
118
119
120  /**
121   * Retrieves a reference to the schema information that has been read from the
122   * server configuration.  Note that this information will not be complete
123   * until the <CODE>initializeMatchingRules</CODE>,
124   * <CODE>initializeAttributeSyntaxes</CODE>, and
125   * <CODE>initializeAttributeTypesAndObjectClasses</CODE> methods have been
126   * called.
127   *
128   * @return  A reference to the schema information that has been read from the
129   *          server configuration.
130   */
131  public Schema getSchema()
132  {
133    return schema;
134  }
135
136
137
138  /**
139   * Initializes all the matching rules defined in the Directory Server
140   * configuration.  This should only be called at Directory Server startup.
141   *
142   * @throws  ConfigException  If a configuration problem causes the matching
143   *                           rule initialization process to fail.
144   *
145   * @throws  InitializationException  If a problem occurs while initializing
146   *                                   the matching rules that is not related to
147   *                                   the server configuration.
148   */
149  public void initializeMatchingRules()
150         throws ConfigException, InitializationException
151  {
152    MatchingRuleConfigManager matchingRuleConfigManager = new MatchingRuleConfigManager(serverContext);
153    matchingRuleConfigManager.initializeMatchingRules();
154  }
155
156
157
158  /**
159   * Initializes all the attribute syntaxes defined in the Directory Server
160   * configuration.  This should only be called at Directory Server startup.
161   *
162   * @throws  ConfigException  If a configuration problem causes the syntax
163   *                           initialization process to fail.
164   *
165   * @throws  InitializationException  If a problem occurs while initializing
166   *                                   the syntaxes that is not related to the
167   *                                   server configuration.
168   */
169  public void initializeAttributeSyntaxes()
170         throws ConfigException, InitializationException
171  {
172    AttributeSyntaxConfigManager syntaxConfigManager =
173         new AttributeSyntaxConfigManager(serverContext);
174    syntaxConfigManager.initializeAttributeSyntaxes();
175  }
176
177
178
179  /**
180   * Filter implementation that accepts only ldif files.
181   */
182  public static class SchemaFileFilter implements FilenameFilter
183  {
184    /** {@inheritDoc} */
185    @Override
186    public boolean accept(File directory, String filename)
187    {
188      return filename.endsWith(".ldif");
189    }
190  }
191
192
193
194  /**
195   * Initializes all the attribute type, object class, name form, DIT content
196   * rule, DIT structure rule, and matching rule use definitions by reading the
197   * server schema files.  These files will be located in a single directory and
198   * will be processed in lexicographic order.  However, to make the order
199   * easier to understand, they may be prefixed with a two digit number (with a
200   * leading zero if necessary) so that they will be read in numeric order.
201   * This should only be called at Directory Server startup.
202   *
203   * @throws  ConfigException  If a configuration problem causes the schema
204   *                           element initialization to fail.
205   *
206   * @throws  InitializationException  If a problem occurs while initializing
207   *                                   the schema elements that is not related
208   *                                   to the server configuration.
209   */
210  public void initializeSchemaFromFiles()
211         throws ConfigException, InitializationException
212  {
213    // Construct the path to the directory that should contain the schema files
214    // and make sure that it exists and is a directory.  Get a list of the files
215    // in that directory sorted in alphabetic order.
216    String schemaInstanceDirPath  = getSchemaDirectoryPath();
217    File schemaInstanceDir        = null;
218
219    try
220    {
221      if (schemaInstanceDirPath != null)
222      {
223        schemaInstanceDir = new File(schemaInstanceDirPath);
224      }
225    } catch (Exception e)
226    {
227      schemaInstanceDir = null;
228    }
229    long oldestModificationTime   = -1L;
230    long youngestModificationTime = -1L;
231    String[] fileNames;
232
233    try
234    {
235      if (schemaInstanceDir == null || ! schemaInstanceDir.exists())
236      {
237        LocalizableMessage message =
238          ERR_CONFIG_SCHEMA_NO_SCHEMA_DIR.get(schemaInstanceDirPath);
239        throw new InitializationException(message);
240      }
241      if (! schemaInstanceDir.isDirectory())
242      {
243        LocalizableMessage message =
244            ERR_CONFIG_SCHEMA_DIR_NOT_DIRECTORY.get(schemaInstanceDirPath);
245        throw new InitializationException(message);
246      }
247
248
249      FilenameFilter filter = new SchemaFileFilter();
250      File[] schemaInstanceDirFiles =
251                schemaInstanceDir.listFiles(filter);
252      int fileNumber = schemaInstanceDirFiles.length ;
253      ArrayList<String> fileList = new ArrayList<>(fileNumber);
254
255      for (File f : schemaInstanceDirFiles)
256      {
257        if (f.isFile())
258        {
259          fileList.add(f.getName());
260        }
261
262        long modificationTime = f.lastModified();
263        if (oldestModificationTime <= 0L ||
264            modificationTime < oldestModificationTime)
265        {
266          oldestModificationTime = modificationTime;
267        }
268
269        if (youngestModificationTime <= 0 ||
270            modificationTime > youngestModificationTime)
271        {
272          youngestModificationTime = modificationTime;
273        }
274      }
275
276      fileNames = new String[fileList.size()];
277      fileList.toArray(fileNames);
278      Arrays.sort(fileNames);
279    }
280    catch (InitializationException ie)
281    {
282      logger.traceException(ie);
283
284      throw ie;
285    }
286    catch (Exception e)
287    {
288      logger.traceException(e);
289
290      LocalizableMessage message = ERR_CONFIG_SCHEMA_CANNOT_LIST_FILES.get(
291          schemaInstanceDirPath, getExceptionMessage(e));
292      throw new InitializationException(message, e);
293    }
294
295
296    // If the oldest and youngest modification timestamps didn't get set for
297    // some reason, then set them to the current time.
298    if (oldestModificationTime <= 0)
299    {
300      oldestModificationTime = System.currentTimeMillis();
301    }
302
303    if (youngestModificationTime <= 0)
304    {
305      youngestModificationTime = oldestModificationTime;
306    }
307
308    schema.setOldestModificationTime(oldestModificationTime);
309    schema.setYoungestModificationTime(youngestModificationTime);
310
311
312    // Iterate through the schema files and read them as an LDIF file containing
313    // a single entry.  Then get the attributeTypes and objectClasses attributes
314    // from that entry and parse them to initialize the server schema.
315    for (String schemaFile : fileNames)
316    {
317      loadSchemaFile(schema, schemaFile, false);
318    }
319  }
320
321
322
323  /**
324   * Loads the contents of the specified schema file into the provided schema.
325   *
326   * @param  schema      The schema in which the contents of the schema file are
327   *                     to be loaded.
328   * @param  schemaFile  The name of the schema file to be loaded into the
329   *                     provided schema.
330   *
331   * @return  A list of the modifications that could be performed in order to
332   *          obtain the contents of the file.
333   *
334   * @throws  ConfigException  If a configuration problem causes the schema
335   *                           element initialization to fail.
336   *
337   * @throws  InitializationException  If a problem occurs while initializing
338   *                                   the schema elements that is not related
339   *                                   to the server configuration.
340   */
341  public static List<Modification> loadSchemaFile(Schema schema, String schemaFile)
342         throws ConfigException, InitializationException
343  {
344    return loadSchemaFile(schema, schemaFile, true);
345  }
346
347
348
349  /**
350   * Loads the contents of the specified schema file into the provided schema.
351   *
352   * @param  schema       The schema in which the contents of the schema file
353   *                      are to be loaded.
354   * @param  schemaFile   The name of the schema file to be loaded into the
355   *                      provided schema.
356   * @param  failOnError  If {@code true}, indicates that this method should
357   *                      throw an exception if certain kinds of errors occur.
358   *                      If {@code false}, indicates that this method should
359   *                      log an error message and return without an exception.
360   *                      This should only be {@code false} when called from
361   *                      {@code initializeSchemaFromFiles}.
362   *
363   * @return  A list of the modifications that could be performed in order to
364   *          obtain the contents of the file, or {@code null} if a problem
365   *          occurred and {@code failOnError} is {@code false}.
366   *
367   * @throws  ConfigException  If a configuration problem causes the schema
368   *                           element initialization to fail.
369   *
370   * @throws  InitializationException  If a problem occurs while initializing
371   *                                   the schema elements that is not related
372   *                                   to the server configuration.
373   */
374  private static List<Modification> loadSchemaFile(Schema schema, String schemaFile,
375      boolean failOnError) throws ConfigException, InitializationException
376  {
377    // Create an LDIF reader to use when reading the files.
378    String schemaDirPath = getSchemaDirectoryPath();
379    File f = new File(schemaDirPath, schemaFile);
380    LDIFReader reader;
381    try
382    {
383      reader = new LDIFReader(new LDIFImportConfig(f.getAbsolutePath()));
384    }
385    catch (Exception e)
386    {
387      logger.traceException(e);
388
389      LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_OPEN_FILE.get(
390              schemaFile, schemaDirPath, getExceptionMessage(e));
391
392      if (failOnError)
393      {
394        throw new ConfigException(message);
395      }
396      else
397      {
398        logger.error(message);
399        return null;
400      }
401    }
402
403
404    // Read the LDIF entry from the file and close the file.
405    Entry entry;
406    try
407    {
408      entry = reader.readEntry(false);
409
410      if (entry == null)
411      {
412        // The file was empty -- skip it.
413        reader.close();
414        return new LinkedList<>();
415      }
416    }
417    catch (Exception e)
418    {
419      logger.traceException(e);
420
421      LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_READ_LDIF_ENTRY.get(
422              schemaFile, schemaDirPath, getExceptionMessage(e));
423
424      if (failOnError)
425      {
426        throw new InitializationException(message, e);
427      }
428      else
429      {
430        logger.error(message);
431        StaticUtils.close(reader);
432        return null;
433      }
434    }
435
436    // If there are any more entries in the file, then print a warning message.
437    try
438    {
439      Entry e = reader.readEntry(false);
440      if (e != null)
441      {
442        logger.warn(WARN_CONFIG_SCHEMA_MULTIPLE_ENTRIES_IN_FILE, schemaFile, schemaDirPath);
443      }
444    }
445    catch (Exception e)
446    {
447      logger.traceException(e);
448
449      logger.warn(WARN_CONFIG_SCHEMA_UNPARSEABLE_EXTRA_DATA_IN_FILE, schemaFile, schemaDirPath, getExceptionMessage(e));
450    }
451    finally
452    {
453      StaticUtils.close(reader);
454    }
455
456    // Get the attributeTypes attribute from the entry.
457    List<Modification> mods = new LinkedList<>();
458
459    //parse the syntaxes first because attributes rely on these.
460    List<Attribute> ldapSyntaxList = getLdapSyntaxesAttributes(schema, entry, mods);
461    List<Attribute> attrList = getAttributeTypeAttributes(schema, entry, mods);
462    List<Attribute> ocList = getObjectClassesAttributes(schema, entry, mods);
463    List<Attribute> nfList = getNameFormsAttributes(schema, entry, mods);
464    List<Attribute> dcrList = getDITContentRulesAttributes(schema, entry, mods);
465    List<Attribute> dsrList = getDITStructureRulesAttributes(schema, entry, mods);
466    List<Attribute> mruList = getMatchingRuleUsesAttributes(schema, entry, mods);
467
468    // Loop on all the attribute of the schema entry to
469    // find the extra attribute that should be loaded in the Schema.
470    for (Attribute attribute : entry.getAttributes())
471    {
472      if (!isSchemaAttribute(attribute))
473      {
474        schema.addExtraAttribute(attribute.getName(), attribute);
475      }
476    }
477
478    parseLdapSyntaxesDefinitions(schema, schemaFile, failOnError, ldapSyntaxList);
479    parseAttributeTypeDefinitions(schema, schemaFile, failOnError, attrList);
480    parseObjectclassDefinitions(schema, schemaFile, failOnError, ocList);
481    parseNameFormDefinitions(schema, schemaFile, failOnError, nfList);
482    parseDITContentRuleDefinitions(schema, schemaFile, failOnError, dcrList);
483    parseDITStructureRuleDefinitions(schema, schemaFile, failOnError, dsrList);
484    parseMatchingRuleUseDefinitions(schema, schemaFile, failOnError, mruList);
485
486    return mods;
487  }
488
489  private static List<Attribute> getLdapSyntaxesAttributes(Schema schema,
490      Entry entry, List<Modification> mods) throws ConfigException
491  {
492    Syntax syntax = schema.getSyntax(SYNTAX_LDAP_SYNTAX_OID);
493    if (syntax == null)
494    {
495      syntax = CoreSchema.getLDAPSyntaxDescriptionSyntax();
496    }
497
498    AttributeType ldapSyntaxAttrType = schema.getAttributeType(ATTR_LDAP_SYNTAXES, syntax);
499    return createAddModifications(entry, mods, ldapSyntaxAttrType);
500  }
501
502  private static List<Attribute> getAttributeTypeAttributes(Schema schema,
503      Entry entry, List<Modification> mods) throws ConfigException,
504      InitializationException
505  {
506    Syntax syntax = schema.getSyntax(SYNTAX_ATTRIBUTE_TYPE_OID);
507    if (syntax == null)
508    {
509      syntax = CoreSchema.getAttributeTypeDescriptionSyntax();
510    }
511    AttributeType attributeAttrType = schema.getAttributeType(ATTR_ATTRIBUTE_TYPES, syntax);
512    return createAddModifications(entry, mods, attributeAttrType);
513  }
514
515  /** Get the objectClasses attribute from the entry. */
516  private static List<Attribute> getObjectClassesAttributes(Schema schema,
517      Entry entry, List<Modification> mods) throws ConfigException,
518      InitializationException
519  {
520    Syntax syntax = schema.getSyntax(SYNTAX_OBJECTCLASS_OID);
521    if (syntax == null)
522    {
523      syntax = CoreSchema.getObjectClassDescriptionSyntax();
524    }
525    AttributeType objectclassAttrType = schema.getAttributeType(ATTR_OBJECTCLASSES, syntax);
526    return createAddModifications(entry, mods, objectclassAttrType);
527  }
528
529  /** Get the name forms attribute from the entry. */
530  private static List<Attribute> getNameFormsAttributes(Schema schema,
531      Entry entry, List<Modification> mods) throws ConfigException,
532      InitializationException
533  {
534    Syntax syntax = schema.getSyntax(SYNTAX_NAME_FORM_OID);
535    if (syntax == null)
536    {
537      syntax = CoreSchema.getNameFormDescriptionSyntax();
538    }
539    AttributeType nameFormAttrType = schema.getAttributeType(ATTR_NAME_FORMS, syntax);
540    return createAddModifications(entry, mods, nameFormAttrType);
541  }
542
543  /** Get the DIT content rules attribute from the entry. */
544  private static List<Attribute> getDITContentRulesAttributes(Schema schema,
545      Entry entry, List<Modification> mods) throws ConfigException,
546      InitializationException
547  {
548    Syntax syntax = schema.getSyntax(SYNTAX_DIT_CONTENT_RULE_OID);
549    if (syntax == null)
550    {
551      syntax = CoreSchema.getDITContentRuleDescriptionSyntax();
552    }
553    AttributeType dcrAttrType = schema.getAttributeType(ATTR_DIT_CONTENT_RULES, syntax);
554    return createAddModifications(entry, mods, dcrAttrType);
555  }
556
557  /** Get the DIT structure rules attribute from the entry. */
558  private static List<Attribute> getDITStructureRulesAttributes(Schema schema,
559      Entry entry, List<Modification> mods) throws ConfigException,
560      InitializationException
561  {
562    Syntax syntax = schema.getSyntax(SYNTAX_DIT_STRUCTURE_RULE_OID);
563    if (syntax == null)
564    {
565      syntax = CoreSchema.getDITStructureRuleDescriptionSyntax();
566    }
567    AttributeType dsrAttrType = schema.getAttributeType(ATTR_DIT_STRUCTURE_RULES, syntax);
568    return createAddModifications(entry, mods, dsrAttrType);
569  }
570
571  /** Get the matching rule uses attribute from the entry. */
572  private static List<Attribute> getMatchingRuleUsesAttributes(Schema schema,
573      Entry entry, List<Modification> mods) throws ConfigException,
574      InitializationException
575  {
576    Syntax syntax = schema.getSyntax(SYNTAX_MATCHING_RULE_USE_OID);
577    if (syntax == null)
578    {
579      syntax = CoreSchema.getMatchingRuleUseDescriptionSyntax();
580    }
581    AttributeType mruAttrType = schema.getAttributeType(ATTR_MATCHING_RULE_USE, syntax);
582    return createAddModifications(entry, mods, mruAttrType);
583  }
584
585  private static List<Attribute> createAddModifications(Entry entry,
586      List<Modification> mods, AttributeType attrType)
587  {
588    List<Attribute> attributes = entry.getAttribute(attrType);
589    for (Attribute a : attributes)
590    {
591      mods.add(new Modification(ModificationType.ADD, a));
592    }
593    return attributes;
594  }
595
596  /** Parse the ldapsyntaxes definitions if there are any. */
597  private static void parseLdapSyntaxesDefinitions(Schema schema,
598      String schemaFile, boolean failOnError, List<Attribute> ldapSyntaxList)
599      throws ConfigException
600  {
601    if (ldapSyntaxList != null)
602    {
603      for (Attribute a : ldapSyntaxList)
604      {
605        for (ByteString v : a)
606        {
607          final String definition = Schema.addSchemaFileToElementDefinitionIfAbsent(v.toString(), schemaFile);
608          try
609          {
610            schema.registerLdapSyntaxDescription(definition, failOnError);
611          }
612          catch (DirectoryException de)
613          {
614            logger.traceException(de);
615
616            if (de.getResultCode().equals(ResultCode.CONSTRAINT_VIOLATION))
617            {
618              // Register it with the schema.  We will allow duplicates, with the
619              // later definition overriding any earlier definition, but we want
620              // to trap them and log a warning.
621              logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_LDAP_SYNTAX, schemaFile, de.getMessageObject());
622              try
623              {
624                schema.registerLdapSyntaxDescription(definition, true);
625              }
626              catch (Exception e)
627              {
628                // This should never happen.
629                logger.traceException(e);
630              }
631            }
632            else
633            {
634              LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX.get(
635                  schemaFile, de.getMessageObject());
636              reportError(failOnError, de, message);
637            }
638          }
639        }
640      }
641    }
642  }
643
644  /** Parse the attribute type definitions if there are any. */
645  private static void parseAttributeTypeDefinitions(
646      Schema schema, String schemaFile, boolean failOnError, List<Attribute> attrList)
647          throws ConfigException
648  {
649    if (attrList != null)
650    {
651      List<String> definitions = new ArrayList<>();
652      for (Attribute a : attrList)
653      {
654        for (ByteString v : a)
655        {
656          definitions.add(v.toString());
657        }
658      }
659      try
660      {
661        schema.registerAttributeTypes(definitions, schemaFile, !failOnError);
662      }
663      catch (DirectoryException de)
664      {
665        logger.traceException(de);
666
667        if (de.getResultCode().equals(ResultCode.CONSTRAINT_VIOLATION))
668        {
669          // Register it with the schema. We will allow duplicates, with the
670          // later definition overriding any earlier definition, but we want
671          // to trap them and log a warning.
672          logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_ATTR_TYPE, schemaFile, de.getMessageObject());
673          try
674          {
675            schema.registerAttributeTypes(definitions, schemaFile, true);
676          }
677          catch (DirectoryException e)
678          {
679            // This should never happen
680            logger.traceException(e);
681          }
682        }
683        else
684        {
685          LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_ATTR_TYPE.get(schemaFile, de.getMessageObject());
686          reportError(failOnError, de, message);
687        }
688      }
689    }
690  }
691
692  /** Parse the objectclass definitions if there are any. */
693  private static void parseObjectclassDefinitions(Schema schema,
694      String schemaFile, boolean failOnError, List<Attribute> ocList)
695      throws ConfigException
696  {
697    if (ocList != null)
698    {
699      for (Attribute a : ocList)
700      {
701        for (ByteString v : a)
702        {
703          // Parse the objectclass.
704          ObjectClass oc;
705          try
706          {
707            oc = ObjectClassSyntax.decodeObjectClass(v, schema, false);
708            setExtraProperty(oc, SCHEMA_PROPERTY_FILENAME, null);
709            setSchemaFile(oc, schemaFile);
710          }
711          catch (DirectoryException de)
712          {
713            logger.traceException(de);
714
715            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_OC.get(
716                    schemaFile,
717                    de.getMessageObject());
718            reportError(failOnError, de, message);
719            continue;
720          }
721          catch (Exception e)
722          {
723            logger.traceException(e);
724
725            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_OC.get(
726                    schemaFile, v + ":  " + getExceptionMessage(e));
727            reportError(failOnError, e, message);
728            continue;
729          }
730
731          // Register it with the schema.  We will allow duplicates, with the
732          // later definition overriding any earlier definition, but we want
733          // to trap them and log a warning.
734          try
735          {
736            schema.registerObjectClass(oc, failOnError);
737          }
738          catch (DirectoryException de)
739          {
740            logger.traceException(de);
741
742            logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_OC, schemaFile, de.getMessageObject());
743
744            try
745            {
746              schema.registerObjectClass(oc, true);
747            }
748            catch (Exception e)
749            {
750              // This should never happen.
751              logger.traceException(e);
752            }
753          }
754        }
755      }
756    }
757  }
758
759  /** Parse the name form definitions if there are any. */
760  private static void parseNameFormDefinitions(Schema schema,
761      String schemaFile, boolean failOnError, List<Attribute> nfList)
762      throws ConfigException
763  {
764    if (nfList != null)
765    {
766      for (Attribute a : nfList)
767      {
768        for (ByteString v : a)
769        {
770          // Parse the name form.
771          NameForm nf;
772          try
773          {
774            nf = NameFormSyntax.decodeNameForm(v, schema, false);
775            nf.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
776            setSchemaFile(nf, schemaFile);
777          }
778          catch (DirectoryException de)
779          {
780            logger.traceException(de);
781
782            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM.get(
783                    schemaFile, de.getMessageObject());
784            reportError(failOnError, de, message);
785            continue;
786          }
787          catch (Exception e)
788          {
789            logger.traceException(e);
790
791            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM.get(
792                    schemaFile,  v + ":  " + getExceptionMessage(e));
793            reportError(failOnError, e, message);
794            continue;
795          }
796
797          // Register it with the schema.  We will allow duplicates, with the
798          // later definition overriding any earlier definition, but we want
799          // to trap them and log a warning.
800          try
801          {
802            schema.registerNameForm(nf, failOnError);
803          }
804          catch (DirectoryException de)
805          {
806            logger.traceException(de);
807
808            logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_NAME_FORM, schemaFile, de.getMessageObject());
809
810            try
811            {
812              schema.registerNameForm(nf, true);
813            }
814            catch (Exception e)
815            {
816              // This should never happen.
817              logger.traceException(e);
818            }
819          }
820        }
821      }
822    }
823  }
824
825  /** Parse the DIT content rule definitions if there are any. */
826  private static void parseDITContentRuleDefinitions(Schema schema,
827      String schemaFile, boolean failOnError, List<Attribute> dcrList)
828      throws ConfigException
829  {
830    if (dcrList != null)
831    {
832      for (Attribute a : dcrList)
833      {
834        for (ByteString v : a)
835        {
836          // Parse the DIT content rule.
837          DITContentRule dcr;
838          try
839          {
840            dcr = DITContentRuleSyntax.decodeDITContentRule(v, schema, false);
841            dcr.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
842            setSchemaFile(dcr, schemaFile);
843          }
844          catch (DirectoryException de)
845          {
846            logger.traceException(de);
847
848            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DCR.get(
849                    schemaFile, de.getMessageObject());
850            reportError(failOnError, de, message);
851            continue;
852          }
853          catch (Exception e)
854          {
855            logger.traceException(e);
856
857            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DCR.get(
858                    schemaFile, v + ":  " + getExceptionMessage(e));
859            reportError(failOnError, e, message);
860            continue;
861          }
862
863          // Register it with the schema.  We will allow duplicates, with the
864          // later definition overriding any earlier definition, but we want
865          // to trap them and log a warning.
866          try
867          {
868            schema.registerDITContentRule(dcr, failOnError);
869          }
870          catch (DirectoryException de)
871          {
872            logger.traceException(de);
873
874            logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_DCR, schemaFile, de.getMessageObject());
875
876            try
877            {
878              schema.registerDITContentRule(dcr, true);
879            }
880            catch (Exception e)
881            {
882              // This should never happen.
883              logger.traceException(e);
884            }
885          }
886        }
887      }
888    }
889  }
890
891  private static void reportError(boolean failOnError, Exception e,
892      LocalizableMessage message) throws ConfigException
893  {
894    if (failOnError)
895    {
896      throw new ConfigException(message, e);
897    }
898    logger.error(message);
899  }
900
901  /** Parse the DIT structure rule definitions if there are any. */
902  private static void parseDITStructureRuleDefinitions(Schema schema,
903      String schemaFile, boolean failOnError, List<Attribute> dsrList)
904      throws ConfigException
905  {
906    if (dsrList != null)
907    {
908      for (Attribute a : dsrList)
909      {
910        for (ByteString v : a)
911        {
912          // Parse the DIT content rule.
913          DITStructureRule dsr;
914          try
915          {
916            dsr = DITStructureRuleSyntax.decodeDITStructureRule(v, schema, false);
917            dsr.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
918            setSchemaFile(dsr, schemaFile);
919          }
920          catch (DirectoryException de)
921          {
922            logger.traceException(de);
923
924            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DSR.get(
925                    schemaFile, de.getMessageObject());
926            reportError(failOnError, de, message);
927            continue;
928          }
929          catch (Exception e)
930          {
931            logger.traceException(e);
932
933            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DSR.get(
934                    schemaFile, v + ":  " + getExceptionMessage(e));
935            reportError(failOnError, e, message);
936            continue;
937          }
938
939          // Register it with the schema.  We will allow duplicates, with the
940          // later definition overriding any earlier definition, but we want
941          // to trap them and log a warning.
942          try
943          {
944            schema.registerDITStructureRule(dsr, failOnError);
945          }
946          catch (DirectoryException de)
947          {
948            logger.traceException(de);
949
950            logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_DSR, schemaFile, de.getMessageObject());
951
952            try
953            {
954              schema.registerDITStructureRule(dsr, true);
955            }
956            catch (Exception e)
957            {
958              // This should never happen.
959              logger.traceException(e);
960            }
961          }
962        }
963      }
964    }
965  }
966
967  /** Parse the matching rule use definitions if there are any. */
968  private static void parseMatchingRuleUseDefinitions(Schema schema,
969      String schemaFile, boolean failOnError, List<Attribute> mruList)
970      throws ConfigException
971  {
972    if (mruList != null)
973    {
974      for (Attribute a : mruList)
975      {
976        for (ByteString v : a)
977        {
978          // Parse the matching rule use definition.
979          MatchingRuleUse mru;
980          try
981          {
982            mru = MatchingRuleUseSyntax.decodeMatchingRuleUse(v, schema, false);
983            mru.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
984            setSchemaFile(mru, schemaFile);
985          }
986          catch (DirectoryException de)
987          {
988            logger.traceException(de);
989
990            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_MRU.get(
991                    schemaFile, de.getMessageObject());
992            reportError(failOnError, de, message);
993            continue;
994          }
995          catch (Exception e)
996          {
997            logger.traceException(e);
998
999            LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_MRU.get(
1000                    schemaFile, v + ":  " + getExceptionMessage(e));
1001            reportError(failOnError, e, message);
1002            continue;
1003          }
1004
1005          // Register it with the schema.  We will allow duplicates, with the
1006          // later definition overriding any earlier definition, but we want
1007          // to trap them and log a warning.
1008          try
1009          {
1010            schema.registerMatchingRuleUse(mru, failOnError);
1011          }
1012          catch (DirectoryException de)
1013          {
1014            logger.traceException(de);
1015
1016            logger.warn(WARN_CONFIG_SCHEMA_CONFLICTING_MRU, schemaFile, de.getMessageObject());
1017
1018            try
1019            {
1020              schema.registerMatchingRuleUse(mru, true);
1021            }
1022            catch (Exception e)
1023            {
1024              // This should never happen.
1025              logger.traceException(e);
1026            }
1027          }
1028        }
1029      }
1030    }
1031  }
1032
1033
1034
1035  /**
1036   * This method checks if a given attribute is an attribute that
1037   * is used by the definition of the schema.
1038   *
1039   * @param attribute   The attribute to be checked.
1040   * @return            true if the attribute is part of the schema definition,
1041   *                    false if the attribute is not part of the schema
1042   *                    definition.
1043   */
1044  public static boolean isSchemaAttribute(Attribute attribute)
1045  {
1046    String attributeOid = attribute.getAttributeDescription().getAttributeType().getOID();
1047    return attributeOid.equals("2.5.21.1") ||
1048        attributeOid.equals("2.5.21.2") ||
1049        attributeOid.equals("2.5.21.4") ||
1050        attributeOid.equals("2.5.21.5") ||
1051        attributeOid.equals("2.5.21.6") ||
1052        attributeOid.equals("2.5.21.7") ||
1053        attributeOid.equals("2.5.21.8") ||
1054        attributeOid.equals("2.5.4.3") ||
1055        attributeOid.equals("1.3.6.1.4.1.1466.101.120.16") ||
1056        attributeOid.equals("cn-oid") ||
1057        attributeOid.equals("attributetypes-oid") ||
1058        attributeOid.equals("objectclasses-oid") ||
1059        attributeOid.equals("matchingrules-oid") ||
1060        attributeOid.equals("matchingruleuse-oid") ||
1061        attributeOid.equals("nameformdescription-oid") ||
1062        attributeOid.equals("ditcontentrules-oid") ||
1063        attributeOid.equals("ditstructurerules-oid") ||
1064        attributeOid.equals("ldapsyntaxes-oid");
1065  }
1066}
1067