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