RemapTestDriver1.java

/*
** Module   : RemapTestDriver1.java
** Abstract : simplest test application
**
** Copyright (c) 2005-2022, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 SIY 20050224 ADD   @20049 Created initial version.
** 002 SIY 20050308 CHG   @20370 Test now is part of the package. 
** 003 SIY 20050317 CHG   @20586 Added missing tests for basic functionality. 
**                               Cleaned up code and fixed comments.         
** 004 SIY 20050429 CHG   @21028 Fixed comments and formatting, fixed object 
**                               classes to match current state, added batch 
**                               editing.
** 005 SIY 20050516 CHG   @21202 Updated documentation, added prompt before 
**                               starting work.
** 006 NVS 20050905 CHG   @22467 Initial log level is specified with directory
**                               service.
** 007 GES 20070112 CHG   @31810 Removed initial log level.
** 008 TJD 20220504              Java 11 compatibility minor changes
*/
/*
** 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 com.goldencode.p2j.cfg.*;

/**
 * Simple test application. This test performs simplest operations: it
 * instantiates <code>DirectoryService</code>, adds a number of nodes and
 * performs operations with them.
 * <p>
 * <b>WARNING: </b> THIS IS A TEST APPLICATION! UNDER NO CIRCUMSTANCES IT
 * SHOULD BE INVOKED AGAINST PRODUCTION DIRECTORY BECAUSE THIS WILL CAUSE
 * SEVERE DAMAGE OF THE DIRECTORY AND LOSS OF IMPORTANT SECURITY DATA!!!
 */
class RemapTestDriver1
{
   /** Total number of failed tests. */
   private static int failed = 0;

   /** Total number of performed tests. */
   private static int tests = 0;

   /**
    * Print text and operation result code.
    * 
    * @param   text
    *          Text banner for the test.
    * @param   rc
    *          Actual result
    * @param   expected
    *          Expected result
    */
   private static void prc(String text, boolean rc, boolean expected)
   {
      tests++;

      if (rc != expected)
         failed++;

      System.out.println("Testing " + text
         + ((rc == expected) ? " passed" : " failed"));
   }

   /**
    * Print provided array into stdout
    * 
    * @param   arr
    *          Array to print.
    */
   private static void printArray(Object[] arr)
   {
      if (arr == null)
      {
         System.out.println("null");
         return;
      }

      System.out.println("array of " + arr.length + " elements");

      for (int i = 0; i < arr.length; i++)
      {
         System.out.println("[" + i + "] = " + arr[i]);
      }

      System.out.println();
   }

   /**
    * Print testing summary report.
    */
   private static void printReport()
   {
      System.out.println("\nTotal: " + tests + " tests performed\n" + failed
         + " tests failed");
   }

   /**
    * Print directory tree.
    * 
    * @param   nodeId
    *          Root of the subtree which will be removed.
    */
   private static void printSubtree(String nodeId)
   {
      DirectoryService directory = DirectoryService.getInstance();

      String[] nodes = directory.enumerateNodes(nodeId);

      System.out.println(nodeId);

      for (int i = 0; nodes != null && i < nodes.length; i++)
      {
         if (nodeId.equals(""))
            printSubtree("/" + nodes[i] + "/");
         else
            printSubtree(nodeId + nodes[i] + "/");

      }
   }

   /**
    * Remove entire subtree. Note that all errors are ignored, so there is no
    * warranty that subtree will be removed. As a consequence it can be
    * invoked for "" or "/meta" subtree and everything that can be deleted
    * will be deleted.
    * 
    * @param   nodeId
    *          Root of the subtree which will be removed.
    */
   private static void pruneSubtree(String nodeId)
   {
      DirectoryService directory = DirectoryService.getInstance();

      String[] nodes = directory.enumerateNodes(nodeId);

      //System.out.println(nodeId);
      
      for (int i = 0; nodes != null && i < nodes.length; i++)
      {
         if (nodeId.equals(""))
            pruneSubtree("/" + nodes[i] + "/");
         else
            pruneSubtree(nodeId + nodes[i] + "/");
      }

      directory.deleteNode(nodeId);
//      boolean rc = directory.deleteNode(nodeId);
//      System.out.println("Removing [" + nodeId + "] => "
//         + (rc ? "success" : "failure"));
   }

   /**
    * Compare two byte arrays.
    * 
    * @param   one
    *          First array to compare.
    * @param   two
    *          Second array to compare.
    * @return  <code>true</code> if arrays are equal (i.e. have identical
    *          number of elements and all elements are equal).
    */
   private static boolean cmp(byte[] one, byte[] two)
   {
      if (one == null && two == null)
         return true;

      if (one == two)
         return true;

      if (one.length != two.length)
         return false;

      for (int i = 0; i < one.length; i++)
         if (one[i] != two[i])
            return false;

      return true;
   }

   /**
    * Main application entry point.
    * 
    * @param   args
    *          Application command line parameters.
    * @throws  ConfigurationException
    *          forwarded from <code>BootstrapConfig</code> constructor.
    */
   public static void main(String[] args)
   throws ConfigurationException
   {
      if (args.length != 2)
      {
         System.out.println("usage: java RemapTestDriver1 "
            + "<config.xml> <pw>");
         return;
      }

      try
      {
         System.out.println("WARNING! This test application is about to " +
                            "overwrite existing directory!");
         System.out.println("Press Enter to continue or Ctrl+C to break:");
         System.out.flush();
         
         int ch = System.in.read();
         
         if (ch < 0)
            return;
      }
      catch (IOException e)
      {
         return;
      }
      
      BootstrapConfig bc = null;

      if (args.length == 2)
      {
         bc = new BootstrapConfig(args[0],
                                  args[1].toCharArray(),
                                  null,
                                  null);
      }

      DirectoryService directory = DirectoryService.createInstance(bc);

      boolean bindRes = directory.bind();
      
      prc("Binding", bindRes, true);
      
      if (!bindRes)
      {
         System.out.println("Testing can't continue");
         return;
      }

      bindRes = directory.openBatch("");
      
      if (!bindRes)
      {
         System.out.println("Testing can't continue");
         return;
      }

      pruneSubtree("/Security/");
      pruneSubtree("/Application/");

      //Make Eclipse happy
      if (false)
      {
         printSubtree("");
         printArray(new String[0]);
      }

      //Prepare attributes
      //User1
      Attribute[] attrUser1 = new Attribute[]
      {
         new Attribute(directory.getClassNodeAttribute("user", "person"),
                       new Object[]
                       { new String("Gregory Shah") }),
         new Attribute(directory.getClassNodeAttribute("user", "groups"),
                       new Object[]
                       { new String("users"), new String("admins") }),
         new Attribute(directory.getClassNodeAttribute("user", "mode"),
                       new Object[]
                       { 0 }), };

      //User2
      Attribute[] attrUser2 = new Attribute[]
      {
         new Attribute(directory.getClassNodeAttribute("user", "person"),
                       new Object[]
                       { new String("Sergey Yevtushenko") }),
         new Attribute(directory.getClassNodeAttribute("user", "groups"),
                       new Object[]
                       { new String("users") }),
         new Attribute(directory.getClassNodeAttribute("user", "mode"),
                       new Object[]
                       { 0 }), };

      //boolean, booleanOption, booleans
      Attribute[] attrBoolean = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("boolean", "value"),
                      new Object[]
                      { true }) };

      //bytes, bytesOption, bytess
      Attribute[] attrBytes = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("bytes", "value"),
                      new Object[]
                      { new byte[]
                      { 0, 1, 2 } }) };

      //integer, integerOption, integers
      Attribute[] attrInteger = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("integer", "value"),
                      new Object[]
                      { 0 }) };

      //string, stringOption, strings
      Attribute[] attrString = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("string", "value"),
                      new Object[]
                      { new String("initial value") }) };

      //auditDecision, auditResource, authMode, directoryRights
      
      Attribute[] attrAuthResource = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("auditResource", "type"),
                      new Object[]
                      { "auditResource:type" }) };

      Attribute[] attrAuthMode = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("authMode", "mode"),
                      new Object[]
                      { 1 }) };

      Attribute[] attrDirectoryRights = new Attribute[]
      { new Attribute(directory.getClassNodeAttribute("directoryRights",
                                                      "permissions"),
                      new Object[]
                      { new BitField(8) }) };

      //binding, lock, process, terminal
      Attribute[] attrBinding = new Attribute[]
      {
         new Attribute(directory.getClassNodeAttribute("binding", "reftype"),
                       new Object[]
                       { false }),
         new Attribute(
                       directory.getClassNodeAttribute("binding", "reference"),
                       new Object[]
                       { new String("reference") }) };

      //Stage #1: base tree, and tests for container, user and group
      prc("Adding \"/security/test\" out of order", 
          directory.addNode("/Security/Test", "container", null), false);

      prc("Add \"/security\"", directory.addNode("/Security", "container",
                                                 null), true);
      prc("Add \"/security/groups\"", directory.addNode("/Security/Groups",
                                                        "container", null),
          true);
      prc("Add \"/security/groups/users\"",
          directory.addNode("/Security/Groups/Users", "group", null), true);
      prc("Add \"/security/groups/admins\"",
          directory.addNode("/SecuritY/GroupS/AdminS", "group", null), true);
      prc("Add \"/security/users\"", directory.addNode("/security/users/",
                                                       "container", null),
          true);

      prc("Add \"/security/users/ges\"",
          directory.addNode("/security/users/ges", "user", attrUser1), true);
      prc("Add \"/security/users/siy\"",
          directory.addNode("/security/users/siy", "user", attrUser2), true);

      //Stage #2:
      //Add different types of nodes

      //Root containers
      prc("Add \"/application\"", directory.addNode("/application",
                                                    "container", null), true);
      prc("Add \"/application/data\"", directory.addNode("/application/data",
                                                         "container", null),
          true);

      prc("Add \"/application/security\"",
          directory.addNode("/application/security", "container", null), true);

      //boolean, booleanOption, booleans

      prc("Class boolean", directory.addNode("/application/data/boolean",
                                             "boolean", attrBoolean), true);
      prc("Class booleanOption",
          directory.addNode("/application/data/booleanOption",
                            "booleanOption", null), true);
      prc("Class booleans", directory.addNode("/application/data/booleans",
                                              "booleans", null), true);

      //bytes, bytesOption, bytess

      prc("Class bytes attribute[0]", attrBytes[0].isValid(), true);

      prc("Class bytes", directory.addNode("/application/data/bytes",
                                           "bytes", attrBytes), true);
      prc("Class bytesOption",
          directory.addNode("/application/data/bytesOption", "bytesOption",
                            null), true);
      prc("Class bytess", directory.addNode("/application/data/bytess",
                                            "bytess", null), true);

      //integer, integerOption, integers

      prc("Class integer", directory.addNode("/application/data/integer",
                                             "integer", attrInteger), true);
      prc("Class integerOption",
          directory.addNode("/application/data/integerOption",
                            "integerOption", null), true);
      prc("Class integers", directory.addNode("/application/data/integers",
                                              "integers", null), true);

      //string, stringOption, strings
      prc("Class string", directory.addNode("/application/data/string",
                                            "string", attrString), true);
      prc("Class stringOption",
          directory.addNode("/application/data/stringOption", "stringOption",
                            null), true);
      prc("Class strings", directory.addNode("/application/data/strings",
                                             "strings", null), true);

      //auditDecision, auditResource, authMode, directoryRights

      prc("Class auditDecision",
          directory.addNode("/application/data/auditDecision",
                            "auditDecision", null), true);
      prc("Class auditResource",
          directory.addNode("/application/data/auditResource",
                            "auditResource", attrAuthResource), true);
      prc("Class authMode", directory.addNode("/application/data/authMode",
                                              "authMode", attrAuthMode), true);
      prc("Class directoryRights",
          directory.addNode("/application/data/directoryRights",
                            "directoryRights", attrDirectoryRights), true);

      //binding, lock, process, terminal
      prc("Class binding", directory.addNode("/application/data/binding",
                                             "binding", attrBinding), true);
      prc("Class lock", directory.addNode("/application/data/lock", "lock",
                                          null), true);
      prc("Class process", directory.addNode("/application/data/process",
                                             "process", null), true);
      prc("Class terminal", directory.addNode("/application/data/terminal",
                                              "terminal", null), true);

      //Stage #3
      //string (add/remove/change)
      prc("Add value to class string",
          directory.addNodeString("/application/data/string", "value",
                                  "new value"), false);

      System.out.println(directory.getNodeString("/application/data/string", 
                                                 "value", 0));
      
      prc("Remove value from the class string",
          directory.deleteNodeAttributeValue("/application/data/string",
                                             "value", 0), false);

      System.out.println(directory.getNodeString("/application/data/string", 
                                                 "value", 0));
      
      prc("Check value of the class string (before change)",
          "initial value".equals(
             directory.getNodeString("/application/data/string", "value", 0)), 
          true);

      prc("Change value of the class string",
          directory.setNodeString("/application/data/string", "value", 0,
                                  "new value"), true);

      prc("Check value of the class string (after change)",
          "new value".equals(
             directory.getNodeString("/application/data/string", "value", 0)), 
          true);

      //strings (multiple add/multiple change/multiple remove)
      prc("Add value to class strings",
          directory.addNodeString("/application/data/strings", "values",
                                  "value #0"), true);

      prc("Add value to class strings",
          directory.addNodeString("/application/data/strings", "values",
                                  "value #1"), true);

      prc("Check value #0 of the class strings",
          "value #0".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 0)), true);

      prc("Check value #1 of the class strings",
          "value #1".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 1)), true);

      prc("Remove value #1 from the class strings",
          directory.deleteNodeAttributeValue("/application/data/strings",
                                             "values", 1), true);

      prc("Check value #1 of the class strings",
          directory.getNodeString("/application/data/strings", "values", 1) 
             == null,
          true);

      prc("Check value #0 of the class strings",
          "value #0".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 0)), true);

      prc("Remove value #0 from the class strings",
          directory.deleteNodeAttributeValue("/application/data/strings",
                                             "values", 0), true);

      prc("Remove value #0 from the class strings",
          directory.deleteNodeAttributeValue("/application/data/strings",
                                             "values", 0), false);

      prc("Add multiple values to class strings",
          directory.setNodeStrings("/application/data/strings", "values",
                                   new String[]
                                   { "value #0", "value #1" }), true);

      prc("Check value #0 of the class strings",
          "value #0".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 0)), true);

      prc("Check value #1 of the class strings",
          "value #1".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 1)), true);

      prc("Remove value #0 from the class strings",
          directory.deleteNodeAttributeValue("/application/data/strings",
                                             "values", 0), true);

      prc("Check value #0 of the class strings",
          "value #1".equals(directory.getNodeString(
                                                  "/application/data/strings",
                                                  "values", 0)), true);

      //stringOption
      prc("Add value to class stringOption",
          directory.addNodeString("/application/data/stringOption", "option",
                                  "value #0"), true);

      prc("Add one more value to class stringOption",
          directory.addNodeString("/application/data/stringOption", "option",
                                  "value #1"), false);

      prc("Check value of the class stringOption (before change)",
          "value #0".equals(directory.getNodeString(
                                             "/application/data/stringOption",
                                             "option", 0)), true);

      prc("Change value of the class stringOption",
          directory.setNodeString("/application/data/stringOption", "option",
                                  0, "value #1"), true);

      prc("Check value of the class stringOption (after change)",
          "value #1".equals(directory.getNodeString(
                                             "/application/data/stringOption",
                                             "option", 0)), true);

      prc("Remove value from the class stringOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/stringOption",
                                             "option", 0), true);

      prc("Remove value from the class stringOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/stringOption",
                                             "option", 0), false);

      prc("Check value of the class stringOption",
          directory.getNodeString("/application/data/stringOption", "option",
                                  1) == null, true);

      //--------------------------------------------------------------
      //integer
      prc("Add value to class integer",
          directory.addNodeInteger("/application/data/integer", "value", 1),
          false);

      prc("Remove value from the class integer",
          directory.deleteNodeAttributeValue("/application/data/integer",
                                             "value", 0), false);

      prc("Check value of the class integer (before change)",
          (Integer.valueOf(0)).equals(directory.getNodeInteger(
                                                  "/application/data/integer",
                                                  "value", 0)), true);

      prc("Change value of the class integer",
          directory.setNodeInteger("/application/data/integer", "value", 0, 2),
          true);

      prc("Check value of the class integer (after change)",
          (Integer.valueOf(2)).equals(directory.getNodeInteger(
                                                  "/application/data/integer",
                                                  "value", 0)), true);

      //integers (multiple add/multiple change/multiple remove)
      prc("Add value to class integers",
          directory.addNodeInteger("/application/data/integers", "values", 1),
          true);

      prc("Add value to class integers",
          directory.addNodeInteger("/application/data/integers", "values", 2),
          true);

      prc("Check value #0 of the class integers",
          (Integer.valueOf(1)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 0)),
          true);

      prc("Check value #1 of the class integers",
          (Integer.valueOf(2)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 1)),
          true);

      prc("Remove value #1 from the class integers",
          directory.deleteNodeAttributeValue("/application/data/integers",
                                             "values", 1), true);

      prc("Check value #1 of the class integers",
          directory.getNodeInteger("/application/data/integers", "values", 1) 
             == null,
          true);

      prc("Check value #0 of the class integers",
          (Integer.valueOf(1)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 0)),
          true);

      prc("Remove value #0 from the class integers",
          directory.deleteNodeAttributeValue("/application/data/integers",
                                             "values", 0), true);

      prc("Remove value #0 from the class integers",
          directory.deleteNodeAttributeValue("/application/data/integers",
                                             "values", 0), false);

      prc("Add multiple values to class integers",
          directory.setNodeIntegers("/application/data/integers", "values",
                                    new int[]
                                    { 1, 2 }), true);

      Integer[] in = directory.getNodeIntegers("/application/data/integers",
                                               "values");
      prc("Check array of values of the class integers", in.length == 2
         && in[0].equals(Integer.valueOf(1)) && in[1].equals(Integer.valueOf(2)),
          true);

      prc("Check value #0 of the class integers",
          (Integer.valueOf(1)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 0)),
          true);

      prc("Check value #1 of the class integers",
          (Integer.valueOf(2)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 1)),
          true);

      prc("Remove value #0 from the class integers",
          directory.deleteNodeAttributeValue("/application/data/integers",
                                             "values", 0), true);

      prc("Check value #0 of the class integers",
          (Integer.valueOf(2)).equals(directory.getNodeInteger(
                                                 "/application/data/integers",
                                                 "values", 0)),
          true);

      //      integerOption
      prc("Add value to class integerOption",
          directory.addNodeInteger("/application/data/integerOption",
                                   "option", 1), true);

      prc("Add one more value to class integerOption",
          directory.addNodeInteger("/application/data/integerOption",
                                   "option", 2), false);

      prc("Check value of the class integerOption (before change)",
          (Integer.valueOf(1)).equals(directory.getNodeInteger(
                                            "/application/data/integerOption",
                                            "option", 0)),
          true);

      prc("Change value of the class integerOption",
          directory.setNodeInteger("/application/data/integerOption",
                                   "option", 0, 2), true);

      prc("Check value of the class integerOption (after change)",
          (Integer.valueOf(2)).equals(directory.getNodeInteger(
                                            "/application/data/integerOption",
                                            "option", 0)),
          true);

      prc("Remove value from the class integerOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/integerOption",
                                             "option", 0), true);

      prc("Remove value from the class integerOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/integerOption",
                                             "option", 0), false);

      prc("Check value of the class integerOption",
          directory.getNodeInteger("/application/data/integerOption",
                                   "option", 1) == null, true);
      //--------------------------------------------------------------
      //boolean
      prc("Add value to class boolean",
          directory.addNodeBoolean("/application/data/boolean", "value", true),
          false);

      prc("Remove value from the class boolean",
          directory.deleteNodeAttributeValue("/application/data/boolean",
                                             "value", 0), false);

      prc("Check value of the class boolean (before change)",
          (Boolean.TRUE).equals(directory.getNodeBoolean(
                                                  "/application/data/boolean",
                                                  "value", 0)),
          true);

      prc("Change value of the class boolean",
          directory.setNodeBoolean("/application/data/boolean", "value", 0,
                                   false), true);

      prc("Check value of the class boolean (after change)",
          (Boolean.FALSE).equals(directory.getNodeBoolean(
                                                  "/application/data/boolean",
                                                  "value", 0)),
          true);

      //booleans (multiple add/multiple change/multiple remove)
      prc("Add value to class booleans",
          directory.addNodeBoolean("/application/data/booleans", "values",
                                   true), true);

      prc("Add value to class booleans",
          directory.addNodeBoolean("/application/data/booleans", "values",
                                   false), true);

      prc("Check value #0 of the class booleans",
          (Boolean.TRUE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 0)),
          true);

      prc("Check value #1 of the class booleans",
          (Boolean.FALSE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 1)),
          true);

      prc("Remove value #1 from the class booleans",
          directory.deleteNodeAttributeValue("/application/data/booleans",
                                             "values", 1), true);

      prc("Check value #1 of the class booleans",
          directory.getNodeBoolean("/application/data/booleans", "values", 1) 
             == null,
          true);

      prc("Check value #0 of the class booleans",
          (Boolean.TRUE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 0)),
          true);

      prc("Remove value #0 from the class booleans",
          directory.deleteNodeAttributeValue("/application/data/booleans",
                                             "values", 0), true);

      prc("Remove value #0 from the class booleans",
          directory.deleteNodeAttributeValue("/application/data/booleans",
                                             "values", 0), false);

      prc("Add multiple values to class booleans",
          directory.setNodeBooleans("/application/data/booleans", "values",
                                    new boolean[]
                                    { true, false }), true);

      Boolean[] bo = directory.getNodeBooleans("/application/data/booleans",
                                               "values");
      prc("Check array of values of the class booleans", bo.length == 2
         && bo[0].equals(Boolean.TRUE)
         && bo[1].equals(Boolean.FALSE), true);

      prc("Check value #0 of the class booleans",
          (Boolean.TRUE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 0)),
          true);

      prc("Check value #1 of the class booleans",
          (Boolean.FALSE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 1)),
          true);

      prc("Remove value #0 from the class booleans",
          directory.deleteNodeAttributeValue("/application/data/booleans",
                                             "values", 0), true);

      prc("Check value #0 of the class booleans",
          (Boolean.FALSE).equals(directory.getNodeBoolean(
                                                 "/application/data/booleans",
                                                 "values", 0)),
          true);

      //booleanOption
      prc("Add value to class booleanOption",
          directory.addNodeBoolean("/application/data/booleanOption",
                                   "option", false), true);

      prc("Add one more value to class booleanOption",
          directory.addNodeBoolean("/application/data/booleanOption",
                                   "option", true), false);

      prc("Check value of the class booleanOption (before change)",
          (Boolean.FALSE).equals(directory.getNodeBoolean(
                                            "/application/data/booleanOption",
                                            "option", 0)),
          true);

      prc("Change value of the class booleanOption",
          directory.setNodeBoolean("/application/data/booleanOption",
                                   "option", 0, true), true);

      prc("Check value of the class booleanOption (after change)",
          (Boolean.TRUE).equals(directory.getNodeBoolean(
                                            "/application/data/booleanOption",
                                            "option", 0)),
          true);

      prc("Remove value from the class booleanOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/booleanOption",
                                             "option", 0), true);

      prc("Remove value from the class booleanOption",
          directory.deleteNodeAttributeValue(
                                             "/application/data/booleanOption",
                                             "option", 0), false);

      prc("Check value of the class booleanOption",
          directory.getNodeBoolean("/application/data/booleanOption",
                                   "option", 1) == null, true);

      //--------------------------------------------------------------
      //bytes
      prc("Add value to class bytes",
          directory.addNodeByteArray("/application/data/bytes", "value",
                                     new byte[]
                                     { 0, 1, 2 }), false);

      prc("Remove value from the class bytes",
          directory.deleteNodeAttributeValue("/application/data/bytes",
                                             "value", 0), false);

      prc("Check value of the class bytes (before change)", cmp(new byte[]
      { 0, 1, 2 }, directory.getNodeByteArray("/application/data/bytes",
                                              "value", 0)), true);

      prc("Change value of the class bytes",
          directory.setNodeByteArray("/application/data/bytes", "value", 0,
                                     new byte[]
                                     { 0, 1, 2 }), true);

      prc("Check value of the class bytes (after change)", cmp(new byte[]
      { 0, 1, 2 }, directory.getNodeByteArray("/application/data/bytes",
                                              "value", 0)), true);

      //bytess (multiple add/multiple change/multiple remove)
      prc("Add value to class bytess",
          directory.addNodeByteArray("/application/data/bytess", "values",
                                     new byte[]
                                     { 0, 1, 2 }), true);

      prc("Add value to class bytess",
          directory.addNodeByteArray("/application/data/bytess", "values",
                                     new byte[]
                                     { 3, 2, 1 }), true);

      prc("Check value #0 of the class bytess", cmp(new byte[]
      { 0, 1, 2 }, directory.getNodeByteArray("/application/data/bytess",
                                              "values", 0)), true);

      prc("Check value #1 of the class bytess", cmp(new byte[]
      { 3, 2, 1 }, directory.getNodeByteArray("/application/data/bytess",
                                              "values", 1)), true);

      prc("Remove value #1 from the class bytess",
          directory.deleteNodeAttributeValue("/application/data/bytess",
                                             "values", 1), true);

      prc("Check value #1 of the class bytess",
          directory.getNodeByteArray("/application/data/bytess", "values", 1) 
             == null,
          true);

      prc("Check value #0 of the class bytess", cmp(new byte[]
      { 0, 1, 2 }, directory.getNodeByteArray("/application/data/bytess",
                                              "values", 0)), true);

      prc("Remove value #0 from the class bytess",
          directory.deleteNodeAttributeValue("/application/data/bytess",
                                             "values", 0), true);

      prc("Remove value #0 from the class bytess",
          directory.deleteNodeAttributeValue("/application/data/bytess",
                                             "values", 0), false);

      prc("Add multiple values to class bytess",
          directory.setNodeByteArrays("/application/data/bytess", "values",
                                      new byte[][]
                                      { new byte[]
                                      { 0, 1, 2 }, new byte[]
                                      { 3, 2, 1 } }), true);

      byte[][] ba = directory.getNodeByteArrays("/application/data/bytess",
                                                "values");

      prc("Check array of values of the class bytess", 
          ba.length == 2 && 
          cmp(new byte[] { 0, 1, 2 }, ba[0]) && 
          cmp(new byte[] { 3, 2, 1 }, ba[1]), 
          true);

      prc("Check value #0 of the class bytess", 
          cmp(new byte[] { 0, 1, 2 }, 
              directory.getNodeByteArray("/application/data/bytess",
                                         "values", 0)), true);

      prc("Check value #1 of the class bytess", 
          cmp(new byte[] { 3, 2, 1 }, 
              directory.getNodeByteArray("/application/data/bytess",
                                         "values", 1)), true);

      prc("Remove value #0 from the class bytess",
          directory.deleteNodeAttributeValue("/application/data/bytess",
                                             "values", 0), true);

      prc("Check value #0 of the class bytess", 
          cmp(new byte[] { 3, 2, 1 }, 
              directory.getNodeByteArray("/application/data/bytess",
                                         "values", 0)), true);

      //bytesOption
      prc("Add value to class bytesOption",
          directory.addNodeByteArray("/application/data/bytesOption",
                                     "option", new byte[] { 0, 1, 2 }), true);

      prc("Add one more value to class bytesOption",
          directory.addNodeByteArray("/application/data/bytesOption",
                                     "option", new byte[] { 3, 2, 1 }), false);

      prc("Check value of the class bytesOption (before change)",
          cmp(new byte[] { 0, 1, 2 },
              directory.getNodeByteArray("/application/data/bytesOption",
                                         "option", 0)), true);

      prc("Change value of the class bytesOption",
          directory.setNodeByteArray("/application/data/bytesOption",
                                     "option", 0, new byte[] { 4, 5, 6 }), 
          true);

      prc("Check value of the class bytesOption (after change)",
          cmp(new byte[] { 4, 5, 6 },
              directory.getNodeByteArray("/application/data/bytesOption",
                                         "option", 0)), true);

      prc("Remove value from the class bytesOption",
          directory.deleteNodeAttributeValue("/application/data/bytesOption",
                                             "option", 0), true);

      prc("Remove value from the class bytesOption",
          directory.deleteNodeAttributeValue("/application/data/bytesOption",
                                             "option", 0), false);

      prc("Check value of the class bytesOption",
          directory.getNodeByteArray("/application/data/bytesOption",
                                     "option", 1) == null, true);
      //--------------------------------------------------------------
      //time, date
      //--------------------------------------------------------------
      prc("Add date value to \"/security/users/siy\"",
          directory.addNodeDate("/security/users/siy", "pwsetdate",
                                new DateValue(2005, 3, 15)), true);

      prc("Check date value of \"/security/users/siy\"",
          (new DateValue(2005, 3, 15)).equals(
                                directory.getNodeDate("/security/users/siy",
                                                      "pwsetdate", 0)), true);

      prc("Change date value of \"/security/users/siy\"",
          directory.setNodeDate("/security/users/siy", "pwsetdate", 0,
                                new DateValue(2005, 3, 16)), true);

      prc("Check date value of \"/security/users/siy\"",
          (new DateValue(2005, 3, 16)).equals(
                                directory.getNodeDate("/security/users/siy",
                                                      "pwsetdate", 0)), true);

      prc("Add date value to \"/security/users/siy\"",
          directory.addNodeDate("/security/users/siy", "pwsetdate",
                                new DateValue(2005, 3, 15)), false);

      prc("Remove date value from \"/security/users/siy\"",
          directory.deleteNodeAttributeValue("/security/users/siy",
                                             "pwsetdate", 0), true);

      prc("Add date as array to \"/security/users/siy\"",
          directory.setNodeDates("/security/users/siy", "pwsetdate",
                                 new DateValue[]
                                 { new DateValue(2005, 3, 19) }), true);

      DateValue[] d = directory.getNodeDates("/security/users/siy",
                                             "pwsetdate");

      prc("Check array of dates", 
          d.length == 1 && d[0].equals(new DateValue(2005, 3, 19)), 
          true);

      //--------------------------------------------------------------
      prc("Add time value to \"/security/users/siy\"",
          directory.addNodeTime("/security/users/siy", "pwsettime",
                                new TimeValue(10, 10, 10)), true);

      prc("Check time value of \"/security/users/siy\"",
          (new TimeValue(10, 10, 10)).equals(
                                 directory.getNodeTime("/security/users/siy",
                                                       "pwsettime", 0)), true);

      prc("Change time value of \"/security/users/siy\"",
          directory.setNodeTime("/security/users/siy", "pwsettime", 0,
                                new TimeValue(10, 10, 11)), true);

      prc("Check time value of \"/security/users/siy\"",
          (new TimeValue(10, 10, 11)).equals(
                                 directory.getNodeTime("/security/users/siy",
                                                       "pwsettime", 0)), true);

      prc("Add time value to \"/security/users/siy\"",
          directory.addNodeTime("/security/users/siy", "pwsettime",
                                new TimeValue(10, 10, 12)), false);

      prc("Remove time value from \"/security/users/siy\"",
          directory.deleteNodeAttributeValue("/security/users/siy",
                                             "pwsettime", 0), true);

      prc("Add time as array to \"/security/users/siy\"",
          directory.setNodeTimes("/security/users/siy", "pwsettime",
                                 new TimeValue[]
                                 { new TimeValue(12, 11, 12) }), true);

      TimeValue[] t = directory.getNodeTimes("/security/users/siy",
                                             "pwsettime");

      prc("Check array of times", t.length == 1
         && t[0].equals(new TimeValue(12, 11, 12)), true);

      //--------------------------------------------------------------
      //
      ///application/data/directoryRights
      BitField bitField0 = new BitField(7);
      BitField bitField1 = new BitField(7);

      bitField1.set(2);

      prc("Check bitfield value of \"/application/data/directoryRights\"",
          bitField0.equals(
                directory.getNodeBitField("/application/data/directoryRights",
                                          "permissions", 0)), true);

      prc("Change bitfield value of \"/application/data/directoryRights\" #1",
          directory.setNodeBitField("/application/data/directoryRights",
                                    "permissions", 0, bitField1), true);

      prc("Check bitfield value of \"/application/data/directoryRights\" #1",
          bitField1.equals(
                directory.getNodeBitField("/application/data/directoryRights",
                                          "permissions", 0)), true);

      prc("Change bitfield value of \"/application/data/directoryRights\" #2",
          directory.setNodeBitFields("/application/data/directoryRights",
                                     "permissions", new BitField[]
                                     { bitField0 }), true);

      prc("Check bitfield value of \"/application/data/directoryRights\" #2",
          bitField0.equals(
                directory.getNodeBitField("/application/data/directoryRights",
                                          "permissions", 0)), true);

      prc("Add bitfield value to \"/application/data/directoryRights\"",
          directory.addNodeBitField("/application/data/directoryRights",
                                    "permissions", bitField0), false);

      prc("Remove bitfield value from \"/application/data/directoryRights\"",
          directory.deleteNodeAttributeValue(
                                          "/application/data/directoryRights",
                                          "permissions", 0), false);

      prc("Set bitfield as array to \"/application/data/directoryRights\"",
          directory.setNodeBitFields("/application/data/directoryRights",
                                     "permissions", new BitField[]
                                     { bitField0 }), true);

      BitField[] f;
      f = directory.getNodeBitFields("/application/data/directoryRights",
                                     "permissions");

      prc("Check array of bitfields",
          f.length == 1 && f[0].equals(bitField0), true);

      //--------------------------------------------------------------
      //wrong parameters check
      prc("Add value to class stringOption (wrong attribute)",
          directory.addNodeString("/application/data/stringOption", "value",
                                  "value #0"), false);

      prc("Remove value from the class stringOption (wrong attribute)",
          directory.deleteNodeAttributeValue(
                                             "/application/data/stringOption",
                                             "value", 0), false);

      //--------------------------------------------------------------
      // Remaining methods

      boolean rc = false;

      rc = directory.deleteNodeAttribute("/application/data/strings",
                                         "values");

      prc("Checking deleteNodeAttribute() for non-mandatory attribute", rc,
          true);

      String[] a = directory.getNodeStrings("/application/data/strings",
                                            "values");

      //printArray(a);

      prc("Checking getNodeStrings() from empty attribute", a == null, true);

      Attribute[] attrStrings =
      { new Attribute(directory.getClassNodeAttribute("strings", "values"),
                      new Object[]
                      { new String("value 1"), new String("value 2") }), };

      prc("Checking setNodeAttributes()",
          directory.setNodeAttributes("/application/data/strings",
                                      attrStrings), true);

      a = directory.getNodeStrings("/application/data/strings", "values");

      rc = (a != null && a[0].equals("value 1") && a[1].equals("value 2"));

      prc("Checking getNodeStrings() from non-empty attribute", rc, true);

      Attribute[] aa;
      aa = directory.getNodeAttributes("/security/users/siy");

      //printArray(aa);

      prc("Checking getNodeAttributes() for \"/security/users/siy\"",
          aa != null && 
          aa.length == 5 && 
          aa[0].isValid() && 
          aa[1].isValid() && 
          aa[2].isValid() && 
          aa[3].isValid() && 
          aa[4].isValid(), true);

      prc("Checking getNodeClass() for \"/security/users/siy\"",
          "/meta/class/user".equals(directory.getNodeClass("/security/users/siy")),
          true);

      NodeAttribute[] na;
      na = directory.enumerateNodeAttributes("/security/users/siy");

      //printArray(na);
      prc("Checking enumerateNodeAttributes() for \"/security/users/siy\"",
          na != null && na.length == 5, true);

      //System.out.println(directory.getClassNodeAttribute("user",
      // "person"));
      prc("Checking getClassNodeAttribute() for class user",
          directory.getClassNodeAttribute("user", "person") != null, true);

      prc("Comitting chnages", directory.closeBatch(true), true);
      
      //--------------------------------------------------------------
      prc("Unbinding", directory.unbind(), true);

      printReport();
   }
}