# SchemaLoad.java

    /*
    ** Module   : SchemaLoad.java
    ** Abstract : helper class to load the directory schema
    **
    ** Copyright (c) 2010-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ----------------Description-----------------
    ** 001 GES 20101116 Created initial version.
    ** 002 HC  20180821 Added double to the list of required object classes.
    ** 003 GBB 20230825 Static final field `required` to uppercase.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.directory;

    import java.io.*;
    import java.util.*;
    import java.net.*;
    import com.goldencode.p2j.cfg.*;

    /**
     * Provides a bootstrap loader for the directory schema.  This class defines
     * the filenames of the resources that are loaded and the order in which those
     * resources are loaded.
     * <p>
     * The directory schema can be read from multiple input files.  The main
     * schema (which defines the standard object classes that must always exist for
     * P2J) will be loaded from {@link #SYS_SCHEMA_NAME}.  The J2SE method
     * <code>ClassLoader.getSystemResource()</code> will be used to search the
     * classpath and load the first instance of this file.  The file should be
     * found in the jar for P2J and it should not be modified since the P2J code
     * has heavy dependencies upon those definitions.
     * <p>
     * Applications may optionally add their own extensions to the directory schema.
     * This is done for such purposes as supporting custom security plugins using
     * data stored in the directory.  Application schema extensions are loaded from
     * {@link #EXT_SCHEMA_NAME} using <code>ClassLoader.getSystemResources()</code>
     * which will return the list of all found resources.  There can be more than
     * one, if there are enough jar files in the classpath that each contain that
     * file.  It is valid for this file to be missing, it is completely optional.
     * <p>
     * Since there is no pathing information in either resource name, the associated
     * files must be present in the root directory of the jar files being searched
     * OR in the topmost directory of a path in the CLASSPATH.
     * <p>
     * This search process occurs regardless of any configuration.  The core P2J
     * schema will be read first and then any application extensions will be read.
     * This is done very early in the initialization of the runtime environment,
     * before the directory itself is active.  The reason loading from resources is
     * a reasonable approach is that the source code for P2J (and optionally for
     * applications that have their own directory schema extensions) is already
     * hard coded to the specific schema.  For that reason, the schema upon which
     * the code is dependent should be included with the code and there should be
     * no need to require configuration in order to find it.
     * <p>
     * The format for both files is the same (see above), but the contents should
     * generally not overlap. If the application extension specifies an object class
     * name that is the same as one that is already defined, it will replace that
     * object class. Please note that this is VERY DANGEROUS unless you know exactly
     * what you are doing.
     */
    class SchemaLoad
    {
       /** File name for the system directory schema. */
       public static final String SYS_SCHEMA_NAME = "dir_schema.xml";

       /** File name for directory schema extensions. */
       public static final String EXT_SCHEMA_NAME = "dir_schema_ext.xml";

       /** List of object classes that must be present in the schema. */
       public static final String[] REQUIRED =
       {
          "authMode", "auditResource", "bytes", "binding", "boolean",
          "container", "dates", "double", "group", "integer", "process", "strings",
          "systemRights", "string", "user"
       };

       /**
        * Initialize the directory schema.
        *
        * @throws  ConfigurationException
        *          in case of configuration error.
        */
       static void initSchema()
       throws ConfigurationException
       {
          URL[] schemas = null;

          URL system  = ClassLoader.getSystemResource(SYS_SCHEMA_NAME);

          if (system == null)
          {
             String spec1 = "System directory schema (%s) cannot be found.";

             throw new ConfigurationException(String.format(spec1,
                                                            SYS_SCHEMA_NAME));
          }

          try
          {
             Enumeration<URL> ext = ClassLoader.getSystemResources(EXT_SCHEMA_NAME);

             if (ext != null)
             {
                List<URL> list = Collections.list(ext);

                schemas = new URL[list.size() + 1];
                schemas[0] = system;

                int i = 1;

                for (URL next : list)
                {
                   schemas[i++] = next;
                }
             }
             else
             {
                schemas = new URL[1];
                schemas[0] = system;
             }
          }

          catch (IOException ioe)
          {
             String spec2 = "Failure loading directory schema extensions (%s).";
             throw new ConfigurationException(String.format(spec2,
                                                            EXT_SCHEMA_NAME));
          }

          try
          {
             SchemaStorage.initSchema(schemas, REQUIRED);
          }

          catch (Exception e)
          {
             throw new ConfigurationException("Can't initialise directory schema.",
                                              e);
          }
       }
    }