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);
      }
   }
}