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 2008-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2017 ForgeRock AS.
016 */
017package org.opends.guitools.controlpanel.util;
018
019import static org.opends.messages.ConfigMessages.*;
020
021import java.io.File;
022import java.io.FileFilter;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.List;
026
027import org.forgerock.i18n.LocalizableMessage;
028import org.forgerock.i18n.LocalizedIllegalArgumentException;
029import org.forgerock.opendj.config.server.ConfigException;
030import org.forgerock.opendj.ldap.ResultCode;
031import org.forgerock.opendj.ldap.schema.AttributeType;
032import org.forgerock.opendj.ldap.schema.MatchingRule;
033import org.forgerock.opendj.ldap.schema.ObjectClass;
034import org.forgerock.opendj.ldap.schema.SchemaBuilder;
035import org.forgerock.opendj.ldap.schema.Syntax;
036import org.opends.server.config.ConfigConstants;
037import org.opends.server.core.DirectoryServer;
038import org.opends.server.core.SchemaConfigManager;
039import org.opends.server.schema.SchemaConstants;
040import org.opends.server.types.DirectoryException;
041import org.opends.server.types.InitializationException;
042import org.opends.server.types.Schema;
043
044import com.forgerock.opendj.util.OperatingSystem;
045
046/** Class used to retrieve the schema from the schema files. */
047public class SchemaLoader
048{
049  private Schema schema;
050  private static final String[] ATTRIBUTES_TO_KEEP = {
051    ConfigConstants.ATTR_ATTRIBUTE_TYPES_LC,
052    ConfigConstants.ATTR_OBJECTCLASSES_LC,
053    ConfigConstants.ATTR_NAME_FORMS_LC,
054    ConfigConstants.ATTR_DIT_CONTENT_RULES_LC,
055    ConfigConstants.ATTR_DIT_STRUCTURE_RULES_LC,
056    ConfigConstants.ATTR_MATCHING_RULE_USE_LC };
057  private static final String[] OBJECTCLASS_TO_KEEP = { SchemaConstants.TOP_OBJECTCLASS_NAME };
058
059  private final List<ObjectClass> objectclassesToKeep = new ArrayList<>();
060  private final List<AttributeType> attributesToKeep = new ArrayList<>();
061  /** List of matching rules to keep in the schema. */
062  protected final List<MatchingRule> matchingRulesToKeep = new ArrayList<>();
063  /** List of attribute syntaxes to keep in the schema. */
064  protected final List<Syntax> syntaxesToKeep = new ArrayList<>();
065
066  /** Constructor. */
067  public SchemaLoader()
068  {
069    Schema sc = DirectoryServer.getSchema();
070    for (String name : OBJECTCLASS_TO_KEEP)
071    {
072      ObjectClass oc = sc.getObjectClass(name);
073      if (!oc.isPlaceHolder())
074      {
075        objectclassesToKeep.add(oc);
076      }
077    }
078    for (String name : ATTRIBUTES_TO_KEEP)
079    {
080      if (sc.hasAttributeType(name))
081      {
082        attributesToKeep.add(sc.getAttributeType(name));
083      }
084    }
085    matchingRulesToKeep.addAll(sc.getMatchingRules());
086    syntaxesToKeep.addAll(sc.getSyntaxes());
087  }
088
089  private static String getSchemaDirectoryPath()
090  {
091    File schemaDir = DirectoryServer.getEnvironmentConfig().getSchemaDirectory();
092    return schemaDir != null ? schemaDir.getAbsolutePath() : null;
093  }
094
095  /**
096   * Reads the schema.
097   *
098   * @throws ConfigException
099   *           if an error occurs reading the schema.
100   * @throws InitializationException
101   *           if an error occurs trying to find out the schema files.
102   * @throws DirectoryException
103   *           if there is an error registering the minimal objectclasses.
104   */
105  public void readSchema() throws DirectoryException, ConfigException, InitializationException
106  {
107    schema = getBaseSchema();
108
109    List<String> fileNames;
110    String schemaDirPath = getSchemaDirectoryPath();
111    try
112    {
113      // Load install directory schema
114      File schemaDir = new File(schemaDirPath);
115      if (schemaDirPath == null || !schemaDir.exists())
116      {
117        LocalizableMessage message = ERR_CONFIG_SCHEMA_NO_SCHEMA_DIR.get(schemaDirPath);
118        throw new InitializationException(message);
119      }
120      else if (!schemaDir.isDirectory())
121      {
122        LocalizableMessage message = ERR_CONFIG_SCHEMA_DIR_NOT_DIRECTORY.get(schemaDirPath);
123        throw new InitializationException(message);
124      }
125      FileFilter ldifFilesFilter = new FileFilter()
126      {
127        @Override
128        public boolean accept(File f)
129        {
130          if (f != null)
131          {
132            if (f.isDirectory())
133            {
134              return true;
135            }
136            return OperatingSystem.isWindows() ? f.getName().toLowerCase().endsWith(".ldif")
137                                               : f.getName().endsWith(".ldif");
138          }
139          return false;
140        }
141      };
142      File[] schemaFiles = schemaDir.listFiles(ldifFilesFilter);
143      fileNames = new ArrayList<>(schemaFiles.length);
144      for (File f : schemaFiles)
145      {
146        if (f.isFile())
147        {
148          fileNames.add(f.getName());
149        }
150      }
151
152      Collections.sort(fileNames);
153    }
154    catch (InitializationException ie)
155    {
156      throw ie;
157    }
158    catch (Exception e)
159    {
160      throw new InitializationException(ERR_CONFIG_SCHEMA_CANNOT_LIST_FILES.get(schemaDirPath, e.getMessage()), e);
161    }
162
163    // Iterate through the schema files and read them as an LDIF file containing a single entry.
164    // Then get the attributeTypes and objectClasses attributes from that entry
165    // and parse them to initialize the server schema.
166    for (String schemaFile : fileNames)
167    {
168      SchemaConfigManager.loadSchemaFile(schema, null, schemaFile);
169    }
170  }
171
172  /**
173   * Returns a basic version of the schema. The schema is created and contains
174   * enough definitions for the schema to be loaded.
175   *
176   * @return a basic version of the schema.
177   * @throws DirectoryException
178   *           if there is an error registering the minimal objectclasses.
179   */
180  protected Schema getBaseSchema() throws DirectoryException
181  {
182    try
183    {
184      SchemaBuilder builder = new SchemaBuilder(org.forgerock.opendj.ldap.schema.Schema.getDefaultSchema());
185      for (Syntax syntax : syntaxesToKeep)
186      {
187        builder.buildSyntax(syntax).addToSchemaOverwrite();
188      }
189      for (MatchingRule mr : matchingRulesToKeep)
190      {
191        builder.buildMatchingRule(mr).addToSchemaOverwrite();
192      }
193      for (AttributeType attr : attributesToKeep)
194      {
195        builder.buildAttributeType(attr).addToSchemaOverwrite();
196      }
197      for (ObjectClass oc : objectclassesToKeep)
198      {
199        builder.buildObjectClass(oc).addToSchemaOverwrite();
200      }
201      return new Schema(builder.toSchema());
202    }
203    catch (LocalizedIllegalArgumentException e)
204    {
205      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e);
206    }
207  }
208
209  /**
210   * Returns the schema that was read.
211   *
212   * @return the schema that was read.
213   */
214  public Schema getSchema()
215  {
216    return schema;
217  }
218}