JNDI LDAP Java

/*
 * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 2006.  Sun Microsystems. All rights reserved.
 * 
 * Creates the schema for storing CORBA object references according
 * to RFC 2714. After running this program, you should verify that
 * the schema has been updated correctly by using the directory server's
 * administration tool. If the schema has not been properly updated,
 * use the administration tool to correct it.
 *
 * You should first turn off schema-checking at the directory server 
 * before running this program.
 *
 * usage:
 * java [-Djava.naming.provider.url=] \
 *     CreateCorbaSchema [-h|-l|-s[n|n41|ad]] [-n] [-p] [-a
 *      
 * -h       Print the usage message
 *
 * -l       List the CORBA schema in the directory
 *
 * -s[n|n41|ad] Update schema:
 *                -sn   means use a workaround for schema bugs in
 *                      pre-4.1 releases of Netscape Directory Server;
 *
 *        -sn41 means use a workaround for schema bugs in
 *                      Netscape Directory Server version 4.1;
 *
 *        -sad  means use a workaround for schema bugs in
 *                      Microsoft Windows 2000 Active Directory
 *
 * -n   Use  as the distinguished name for authentication
 *
 * -p   Use  as the password for authentication
 *
 * -a Use  as the authentication mechanism. Default is "simple".
 *
 *
 * If neither -s, -l, nor -h has been specified, the default is "-l".
 *
 * The following example inserts the CORBA schema from RFC 2714 in a
 * Netscape Directory (using the workaround for 4.1 schema bugs),
 * logging in as "cn=directory manager" with the password "secret".
 *
 *     java CreateCorbaSchema -sn41 "-ncn=directory manager" -psecret
 *
 * @author Rosanna Lee
 */
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
public class CreateCorbaSchema extends CreateJavaSchema {
  private static String[] allAttrs = { "corbaIor", "corbaRepositoryId" };
  private static String[] allOCs = { "corbaObject", "corbaObjectReference",
      "corbaContainer" };
  public static void main(String[] args) {
    new CreateCorbaSchema().run(args, allAttrs, allOCs);
  }
  CreateCorbaSchema() {
  }
  /**
   * Add new attributes: corbaIor corbaRepositoryId
   */
  protected void updateAttributes(DirContext attrRoot, String[] attrIDs)
      throws NamingException {
    /* Get rid of old attr IDs */
    for (int i = 0; i < attrIDs.length; i++) {
      attrRoot.destroySubcontext(attrIDs[i]);
    }
    /* Add new and updated attr definitions */
    // corbaIor
    Attributes attrs = new BasicAttributes(true); // ignore case
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.14");
    attrs.put("NAME", "corbaIor");
    attrs.put("DESC",
        "Stringified interoperable object reference of a CORBA object");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.26");
    attrs.put("EQUALITY", "caseIgnoreIA5Match");
    attrs.put("SINGLE-VALUE", "true");
    attrRoot.createSubcontext("corbaIor", attrs);
    System.out.println("Created corbaIor attribute");
    // corbaRepositoryId
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.15");
    attrs.put("NAME", "corbaRepositoryId");
    attrs.put("DESC",
        "Repository ids of interfaces implemented by a CORBA object");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.15");
    attrs.put("EQUALITY", "caseExactMatch");
    attrRoot.createSubcontext("corbaRepositoryId", attrs);
    System.out.println("Created corbaRepositoryId attribute");
  }
  // Object Classes
  protected void updateObjectClasses(DirContext ocRoot, String[] ocIDs)
      throws NamingException {
    /* Get rid of old OCs - reverse order */
    for (int i = ocIDs.length - 1; i >= 0; i--) {
      ocRoot.destroySubcontext(ocIDs[i]);
    }
    // corbaObject
    Attributes attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.9");
    attrs.put("NAME", "corbaObject");
    attrs.put("DESC", "CORBA object representation");
    attrs.put("SUP", "top");
    attrs.put("ABSTRACT", "true");
    Attribute optional = new BasicAttribute("MAY", "corbaRepositoryId");
    optional.add("description");
    attrs.put(optional);
    ocRoot.createSubcontext("corbaObject", attrs);
    System.out.println("Created corbaObject object class");
    // corbaObjectReference
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.11");
    attrs.put("NAME", "corbaObjectReference");
    attrs.put("DESC", "CORBA interoperable object reference");
    attrs.put("SUP", "corbaObject");
    attrs.put("AUXILIARY", "true");
    Attribute corMust = new BasicAttribute("MUST", "corbaIor");
    if (netscape41bug) {
      corMust.add("objectclass");
    }
    if (netscapebug) {
      // Netscape ignores 'SUP' so we must add explicitly
      attrs.put(optional);
    }
    attrs.put(corMust);
    ocRoot.createSubcontext("corbaObjectReference", attrs);
    System.out.println("Created corbaObjectReference object class");
    // corbaContainer
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.10");
    attrs.put("NAME", "corbaContainer");
    attrs.put("DESC", "Container for a CORBA object");
    attrs.put("SUP", "top");
    attrs.put("STRUCTURAL", "true");
    Attribute ccMust = new BasicAttribute("MUST", "cn");
    if (netscape41bug) {
      ccMust.add("objectclass");
    }
    attrs.put(ccMust);
    ocRoot.createSubcontext("corbaContainer", attrs);
    System.out.println("Created corbaContainer object class");
  }
  /**
   * Inserts attribute definitions from RFC 2714 into the schema.
   * 
   * This method maps the LDAP schema definitions in RFC 2714 onto the
   * proprietary attributes required by the Active Directory schema.
   * 
   * The resulting attribute definitions are identical to those of RFC 2714.
   */
  protected void insertADAttributes(DirContext rootCtx, DirContext schemaCtx)
      throws NamingException {
    System.out.println("  [inserting new attribute definitions ...]");
    String dn = schemaCtx.getNameInNamespace();
    String attrID;
    attrID = new String("corbaIor");
    Attributes attrs1 = new BasicAttributes();
    attrs1.put(new BasicAttribute("adminDescription", attrID));
    attrs1.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.14"));
    attrs1.put(new BasicAttribute("attributeSyntax", "2.5.5.5"));
    attrs1.put(new BasicAttribute("cn", attrID));
    attrs1.put(new BasicAttribute("description",
        "Stringified interoperable object reference of a CORBA object"));
    attrs1.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs1.put(new BasicAttribute("isSingleValued", "TRUE"));
    attrs1.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs1.put(new BasicAttribute("name", attrID));
    attrs1
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs1.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs1.put(new BasicAttribute("oMSyntax", "22"));
    attrs1.put(new BasicAttribute("searchFlags", "0"));
    attrs1.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs1);
    System.out.println("    [" + attrID + "]");
    attrID = new String("corbaRepositoryId");
    Attributes attrs2 = new BasicAttributes();
    attrs2.put(new BasicAttribute("adminDescription", attrID));
    attrs2.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.15"));
    attrs2.put(new BasicAttribute("attributeSyntax", "2.5.5.12"));
    attrs2.put(new BasicAttribute("cn", attrID));
    attrs2.put(new BasicAttribute("description",
        "Repository ids of interfaces implemented by a CORBA object"));
    attrs2.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs2.put(new BasicAttribute("isSingleValued", "FALSE"));
    attrs2.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs2.put(new BasicAttribute("name", attrID));
    attrs2
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs2.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs2.put(new BasicAttribute("oMSyntax", "64"));
    attrs2.put(new BasicAttribute("searchFlags", "0"));
    attrs2.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs2);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // finally
  }
  /**
   * Inserts object class definitions from RFC 2714 into the schema.
   * 
   * This method maps the LDAP schema definitions in RFC 2714 onto the
   * proprietary attributes required by the Active Directory schema.
   * 
   * The resulting object class definitions differ from those of RFC 2714 in the
   * following ways:
   *  - Abstract and auxiliary classes are now defined as structural. - The
   * corbaObject class now inherits from corbaContainer. - The
   * corbaObjectReference class now inherits from corbaObject.
   * 
   * The effect of these differences is that CORBA object references cannot be
   * mixed-in with other directory entries, they may only be stored as
   * stand-alone entries.
   * 
   * The reason for these differences is due to the way auxiliary classes are
   * supported in Active Directory. Only the names of structural classes (not
   * auxiliary) may appear in the object class attribute of an entry. Therefore,
   * the abstract and auxiliary classes in the CORBA schema definition is
   * re-defined as structural.
   */
  protected void insertADObjectClasses(DirContext rootCtx, DirContext schemaCtx)
      throws NamingException {
    System.out.println("  [inserting new object class definitions ...]");
    String dn = schemaCtx.getNameInNamespace();
    String attrID;
    attrID = new String("corbaContainer");
    Attributes attrs1 = new BasicAttributes();
    attrs1.put(new BasicAttribute("cn", attrID));
    attrs1.put(new BasicAttribute("objectClass", "classSchema"));
    attrs1.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs1.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.10"));
    attrs1.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs1.put(new BasicAttribute("mustContain", "cn"));
    attrs1.put(new BasicAttribute("objectClassCategory", "1"));
    attrs1.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs1.put(new BasicAttribute("subclassOf", "top"));
    attrs1.put(new BasicAttribute("possSuperiors", "top")); // any superior
    attrs1
        .put(new BasicAttribute("description", "Container for a CORBA object"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs1);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // corbaObject relys on corbaContainer
    attrID = new String("corbaObject");
    Attributes attrs2 = new BasicAttributes();
    attrs2.put(new BasicAttribute("cn", attrID));
    attrs2.put(new BasicAttribute("objectClass", "classSchema"));
    attrs2.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs2.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.9"));
    attrs2.put(new BasicAttribute("lDAPDisplayName", attrID));
    Attribute coMay = new BasicAttribute("mayContain");
    coMay.add("corbaRepositoryId");
    coMay.add("description");
    attrs2.put(coMay);
    attrs2.put(new BasicAttribute("objectClassCategory", "1"));
    attrs2.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs2.put(new BasicAttribute("subclassOf", "corbaContainer"));
    attrs2
        .put(new BasicAttribute("description", "CORBA object representation"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs2);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // corbaObjectReference relys on corbaObject
    attrID = new String("corbaObjectReference");
    Attributes attrs3 = new BasicAttributes();
    attrs3.put(new BasicAttribute("cn", attrID));
    attrs3.put(new BasicAttribute("objectClass", "classSchema"));
    attrs3.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs3.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.11"));
    attrs3.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs3.put(new BasicAttribute("mustContain", "corbaIor"));
    attrs3.put(new BasicAttribute("objectClassCategory", "1"));
    attrs3.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs3.put(new BasicAttribute("subclassOf", "corbaObject"));
    attrs3.put(new BasicAttribute("description",
        "CORBA interoperable object reference"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs3);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // finally
  }
  protected void printUsage(String msg) {
    printUsageAux(msg, "Corba");
  }
}
class CreateJavaSchema {
  protected static String dn, passwd, auth;
  protected static boolean netscapebug;
  // NS 4.1 has problems parsing an object class definition which contains
  // a MUST clause without parentheses. The workaround is to add a
  // superfluous value (objectClass) to each MUST clause.
  // 
  // It also doesn't like the Octet String syntax (use Binary instead)
  //
  protected static boolean netscape41bug = false;
  // AD supports auxiliary classes in a peculiar way.
  protected static boolean activeDirectorySchemaBug = false;
  protected static boolean traceLdap = false;
  protected static final int LIST = 0;
  protected static final int UPDATE = 1;
  private static String[] allAttrs = { "javaSerializedObject",
      "javaFactoryLocation", "javaReferenceAddress", "javaFactory",
      "javaClassName", "javaClassNames", "javaDoc", "javaSerializedData",
      "javaCodebase", "javaFactory", "javaReferenceAddress" };
  private static String[] allOCs = { "javaObject", "javaNamingReference",
      "javaSerializedObject", "javaRemoteObject", "javaMarshalledObject",
      "javaContainer" };
  public static void main(String[] args) {
    new CreateJavaSchema().run(args, allAttrs, allOCs);
  }
  CreateJavaSchema() {
  }
  protected void run(String[] args, String[] attrIDs, String[] ocIDs) {
    int cmd = processCommandLine(args);
    try {
      DirContext ctx = signOn();
      switch (cmd) {
      case UPDATE:
        updateSchema(ctx, attrIDs, ocIDs);
        break;
      default:
        showSchema(ctx, attrIDs, ocIDs);
      }
    } catch (NamingException e) {
      e.printStackTrace();
    }
  }
  /**
   * Signs on to directory server using parameters supplied to program.
   * 
   * @return The initial context to the server.
   */
  private DirContext signOn() throws NamingException {
    if (dn != null && auth == null) {
      auth = "simple"; // use simple for Netscape
    }
    Hashtable env = new Hashtable();
    env
        .put(Context.INITIAL_CONTEXT_FACTORY,
            "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.REFERRAL, "follow");
    if (auth != null) {
      env.put(Context.SECURITY_AUTHENTICATION, auth);
      env.put(Context.SECURITY_PRINCIPAL, dn);
      env.put(Context.SECURITY_CREDENTIALS, passwd);
    }
    // Workaround for Netscape schema bugs
    if (netscapebug) {
      env.put("com.sun.naming.netscape.schemaBugs", "true");
    }
    // LDAP protocol tracing
    if (traceLdap) {
      env.put("com.sun.jndi.ldap.trace.ber", System.err);
    }
    return new InitialDirContext(env);
  }
  void showSchema(DirContext ctx, String[] attrs, String[] ocs)
      throws NamingException {
    DirContext attrRoot = (DirContext) ctx.getSchema("").lookup(
        "AttributeDefinition");
    printSchema(attrRoot, attrs);
    DirContext ocRoot = (DirContext) ctx.getSchema("")
        .lookup("ClassDefinition");
    printSchema(ocRoot, ocs);
  }
  private void printSchema(DirContext ctx, String[] ids) {
    for (int i = 0; i < ids.length; i++) {
      try {
        System.out.print(ids[i] + ": ");
        System.out.print(ctx.getAttributes(ids[i]));
      } catch (NamingException e) {
      } finally {
        System.out.println();
      }
    }
  }
  /**
   * Updates the schema:
   * 
   * Delete obsolete attributes: javaSerializedObject javaFactoryLocation
   * javaReferenceAddress javaFactory javaClassName + all the new ones that
   * we're going to add Add new and updated attributes: javaSerializedData
   * javaCodebase javaClassName javaClassNames javaFactory javaReferenceAddress
   * javaDoc
   * 
   * Delete obsolete object classes: javaNamingReference javaObject + all the
   * new ones that we're going to add Add new and updated object classes:
   * javaObject javaSerializedObject javaMarshalledObject javaNamingReference
   */
  private void updateSchema(DirContext ctx, String[] attrIDs, String[] ocIDs)
      throws NamingException {
    if (activeDirectorySchemaBug) {
      updateADSchema(ctx);
    } else {
      updateAttributes((DirContext) ctx.getSchema("").lookup(
          "AttributeDefinition"), attrIDs);
      updateObjectClasses((DirContext) ctx.getSchema("").lookup(
          "ClassDefinition"), ocIDs);
    }
    System.out
        .println("Please use your directory server's administration tool to verify");
    System.out.println("the correctness of the schema.");
  }
  /* Add new and updated attr definitions */
  protected void updateAttributes(DirContext attrRoot, String[] attrIDs)
      throws NamingException {
    /* Get rid of old attr IDs */
    for (int i = 0; i < attrIDs.length; i++) {
      attrRoot.destroySubcontext(attrIDs[i]);
    }
    // javaSerializedData
    Attributes attrs = new BasicAttributes(true); // ignore case
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.8");
    attrs.put("NAME", "javaSerializedData");
    attrs.put("DESC", "Serialized form of a Java object");
    if (netscape41bug) {
      // DS 4.1 doesn't like Octet String
      attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.5");
    } else {
      attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.40");
    }
    attrs.put("SINGLE-VALUE", "true");
    attrRoot.createSubcontext("javaSerializedData", attrs);
    System.out.println("Created javaSerializedData attribute");
    // javaCodebase
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.7");
    attrs.put("NAME", "javaCodebase");
    attrs.put("DESC", "URL(s) specifying the location of class definition");
    attrs.put("EQUALITY", "caseExactIA5Match");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.26");
    attrRoot.createSubcontext("javaCodebase", attrs);
    System.out.println("Created javaCodebase attribute");
    // javaClassName
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.6");
    attrs.put("NAME", "javaClassName");
    attrs.put("DESC",
        "Fully qualified name of distinguished class or interface");
    attrs.put("EQUALITY", "caseExactMatch");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.15");
    attrs.put("SINGLE-VALUE", "true");
    attrRoot.createSubcontext("javaClassName", attrs);
    System.out.println("Created javaClassName attribute");
    // javaClassNames
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.13");
    attrs.put("NAME", "javaClassNames");
    attrs.put("DESC", "Fully qualified Java class or interface name");
    attrs.put("EQUALITY", "caseExactMatch");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.15");
    attrRoot.createSubcontext("javaClassNames", attrs);
    System.out.println("Created javaClassNames attribute");
    // javaFactory
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.10");
    attrs.put("NAME", "javaFactory");
    attrs.put("DESC",
        "Fully qualified Java class name of a JNDI object factory");
    attrs.put("EQUALITY", "caseExactMatch");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.15");
    attrs.put("SINGLE-VALUE", "true");
    attrRoot.createSubcontext("javaFactory", attrs);
    System.out.println("Created javaFactory attribute");
    // javaReferenceAddress
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.11");
    attrs.put("NAME", "javaReferenceAddress");
    attrs.put("DESC", "Addresses associated with a JNDI Reference");
    attrs.put("EQUALITY", "caseExactMatch");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.15");
    attrRoot.createSubcontext("javaReferenceAddress", attrs);
    System.out.println("Created javaReferenceAddress attribute");
    // javaDoc
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.1.12");
    attrs.put("NAME", "javaDoc");
    attrs.put("DESC", "The Java documentation for the class");
    attrs.put("EQUALITY", "caseExactIA5Match");
    attrs.put("SYNTAX", "1.3.6.1.4.1.1466.115.121.1.26");
    attrRoot.createSubcontext("javaDoc", attrs);
    System.out.println("Created javaDoc attribute");
  }
  // Object Classes
  protected void updateObjectClasses(DirContext ocRoot, String[] ocIDs)
      throws NamingException {
    /* Get rid of old OCs - reverse order */
    for (int i = ocIDs.length - 1; i >= 0; i--) {
      ocRoot.destroySubcontext(ocIDs[i]);
    }
    // javaContainer
    Attributes attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.1");
    attrs.put("NAME", "javaContainer");
    attrs.put("DESC", "Container for a Java object");
    attrs.put("SUP", "top");
    attrs.put("STRUCTURAL", "true");
    Attribute jcMust = new BasicAttribute("MUST", "cn");
    if (netscape41bug) {
      jcMust.add("objectClass");
    }
    attrs.put(jcMust);
    ocRoot.createSubcontext("javaContainer", attrs);
    System.out.println("Created javaContainer object class");
    // javaObject
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.4");
    attrs.put("NAME", "javaObject");
    attrs.put("DESC", "Java object representation");
    attrs.put("SUP", "top");
    attrs.put("ABSTRACT", "true");
    Attribute joMust = new BasicAttribute("MUST", "javaClassName");
    if (netscape41bug) {
      joMust.add("objectClass");
    }
    attrs.put(joMust);
    Attribute optional = new BasicAttribute("MAY", "javaCodebase");
    optional.add("javaClassNames");
    optional.add("javaDoc");
    optional.add("description");
    attrs.put(optional);
    ocRoot.createSubcontext("javaObject", attrs);
    System.out.println("Created javaObject object class");
    // javaSerializedObject
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.5");
    attrs.put("NAME", "javaSerializedObject");
    attrs.put("DESC", "Java serialized object");
    attrs.put("SUP", "javaObject");
    attrs.put("AUXILIARY", "true");
    Attribute jsoMust = new BasicAttribute("MUST", "javaSerializedData");
    if (netscape41bug) {
      jsoMust.add("objectClass");
    }
    if (netscapebug) {
      // Netscape ignores 'SUP' so we must add explicitly
      attrs.put(optional);
      jsoMust.add("javaClassName");
    }
    attrs.put(jsoMust);
    ocRoot.createSubcontext("javaSerializedObject", attrs);
    System.out.println("Created javaSerializedObject object class");
    // javaMarshalledObject
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.8");
    attrs.put("NAME", "javaMarshalledObject");
    attrs.put("DESC", "Java marshalled object");
    attrs.put("SUP", "javaObject");
    attrs.put("AUXILIARY", "true");
    if (netscapebug) {
      // Netscape ignores 'SUP' so we must add explicitly
      attrs.put(optional);
    }
    attrs.put(jsoMust); // re-use the MUST from javaSerializedObject
    ocRoot.createSubcontext("javaMarshalledObject", attrs);
    System.out.println("Created javaMarshalledObject object class");
    // javaNamingReference
    attrs = new BasicAttributes(true);
    attrs.put("NUMERICOID", "1.3.6.1.4.1.42.2.27.4.2.7");
    attrs.put("NAME", "javaNamingReference");
    attrs.put("DESC", "JNDI reference");
    attrs.put("SUP", "javaObject");
    attrs.put("AUXILIARY", "true");
    if (netscapebug) {
      // Netscape ignores 'SUP' so we must add explicitly
      attrs.put("MUST", "javaClassName");
    } else {
      optional = new BasicAttribute("MAY");
    }
    optional.add("javaReferenceAddress");
    optional.add("javaFactory");
    attrs.put(optional);
    ocRoot.createSubcontext("javaNamingReference", attrs);
    System.out.println("Created javaNamingReference object class");
  }
  /**
   * Updates the Active Directory schema.
   * 
   * Modification of the (RFC 2252) schema descriptions is not supported in
   * Active Directory. Instead, the Active Directory (internal) schema must be
   * modified.
   */
  private void updateADSchema(DirContext rootCtx) throws NamingException {
    System.out.println("[updating Active Directory schema ...]");
    // acquire schema context
    DirContext schemaCtx = getADSchema(rootCtx);
    // insert attribute definitions
    insertADAttributes(rootCtx, schemaCtx);
    // insert object class definitions
    insertADObjectClasses(rootCtx, schemaCtx);
    System.out.println("[update completed]\n");
  }
  /**
   * Locates the Active Directory schema.
   * 
   * @return A context for the root of the Active Directory schema.
   */
  private DirContext getADSchema(DirContext rootCtx) throws NamingException {
    System.out.println("  [locating the schema]");
    String snc = "schemaNamingContext"; // DSE attribute
    Attributes attrs = rootCtx.getAttributes("", new String[] { snc });
    return (DirContext) rootCtx.lookup((String) attrs.get(snc).get());
  }
  /**
   * Inserts attribute definitions from RFC 2713 into the schema.
   * 
   * This method maps the LDAP schema definitions in RFC 2713 onto the
   * proprietary attributes required by the Active Directory schema.
   * 
   * The resulting attribute definitions are identical to those of RFC 2713.
   */
  protected void insertADAttributes(DirContext rootCtx, DirContext schemaCtx)
      throws NamingException {
    System.out.println("  [inserting new attribute definitions ...]");
    String dn = schemaCtx.getNameInNamespace();
    String attrID;
    attrID = new String("javaClassName");
    Attributes attrs1 = new BasicAttributes();
    attrs1.put(new BasicAttribute("adminDescription", attrID));
    attrs1.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.6"));
    attrs1.put(new BasicAttribute("attributeSyntax", "2.5.5.12"));
    attrs1.put(new BasicAttribute("cn", attrID));
    attrs1.put(new BasicAttribute("description",
        "Fully qualified name of distinguished Java class or interface"));
    attrs1.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs1.put(new BasicAttribute("isSingleValued", "TRUE"));
    attrs1.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs1.put(new BasicAttribute("name", attrID));
    attrs1
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs1.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs1.put(new BasicAttribute("oMSyntax", "64"));
    attrs1.put(new BasicAttribute("searchFlags", "0"));
    attrs1.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs1);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaCodeBase");
    Attributes attrs2 = new BasicAttributes();
    attrs2.put(new BasicAttribute("adminDescription", attrID));
    attrs2.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.7"));
    attrs2.put(new BasicAttribute("attributeSyntax", "2.5.5.5"));
    attrs2.put(new BasicAttribute("cn", attrID));
    attrs2.put(new BasicAttribute("description",
        "URL(s) specifying the location of class definition"));
    attrs2.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs2.put(new BasicAttribute("isSingleValued", "FALSE"));
    attrs2.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs2.put(new BasicAttribute("name", attrID));
    attrs2
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs2.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs2.put(new BasicAttribute("oMSyntax", "22"));
    attrs2.put(new BasicAttribute("searchFlags", "0"));
    attrs2.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs2);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaSerializedData");
    Attributes attrs3 = new BasicAttributes();
    attrs3.put(new BasicAttribute("adminDescription", attrID));
    attrs3.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.8"));
    attrs3.put(new BasicAttribute("attributeSyntax", "2.5.5.10"));
    attrs3.put(new BasicAttribute("cn", attrID));
    attrs3.put(new BasicAttribute("description",
        "Serialized form of a Java object"));
    attrs3.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs3.put(new BasicAttribute("isSingleValued", "TRUE"));
    attrs3.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs3.put(new BasicAttribute("name", attrID));
    attrs3
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs3.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs3.put(new BasicAttribute("oMSyntax", "4"));
    attrs3.put(new BasicAttribute("searchFlags", "0"));
    attrs3.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs3);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaFactory");
    Attributes attrs4 = new BasicAttributes();
    attrs4.put(new BasicAttribute("adminDescription", attrID));
    attrs4.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.10"));
    attrs4.put(new BasicAttribute("attributeSyntax", "2.5.5.12"));
    attrs4.put(new BasicAttribute("cn", attrID));
    attrs4.put(new BasicAttribute("description",
        "Fully qualified Java class name of a JNDI object factory"));
    attrs4.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs4.put(new BasicAttribute("isSingleValued", "TRUE"));
    attrs4.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs4.put(new BasicAttribute("name", attrID));
    attrs4
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs4.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs4.put(new BasicAttribute("oMSyntax", "64"));
    attrs4.put(new BasicAttribute("searchFlags", "0"));
    attrs4.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs4);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaReferenceAddress");
    Attributes attrs5 = new BasicAttributes();
    attrs5.put(new BasicAttribute("adminDescription", attrID));
    attrs5.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.11"));
    attrs5.put(new BasicAttribute("attributeSyntax", "2.5.5.12"));
    attrs5.put(new BasicAttribute("cn", attrID));
    attrs5.put(new BasicAttribute("description",
        "Addresses associated with a JNDI Reference"));
    attrs5.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs5.put(new BasicAttribute("isSingleValued", "FALSE"));
    attrs5.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs5.put(new BasicAttribute("name", attrID));
    attrs5
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs5.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs5.put(new BasicAttribute("oMSyntax", "64"));
    attrs5.put(new BasicAttribute("searchFlags", "0"));
    attrs5.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs5);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaDoc");
    Attributes attrs6 = new BasicAttributes();
    attrs6.put(new BasicAttribute("adminDescription", attrID));
    attrs6.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.12"));
    attrs6.put(new BasicAttribute("attributeSyntax", "2.5.5.5"));
    attrs6.put(new BasicAttribute("cn", attrID));
    attrs6.put(new BasicAttribute("description",
        "The Java documentation for the class"));
    attrs6.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs6.put(new BasicAttribute("isSingleValued", "FALSE"));
    attrs6.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs6.put(new BasicAttribute("name", attrID));
    attrs6
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs6.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs6.put(new BasicAttribute("oMSyntax", "22"));
    attrs6.put(new BasicAttribute("searchFlags", "0"));
    attrs6.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs6);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaClassNames");
    Attributes attrs7 = new BasicAttributes();
    attrs7.put(new BasicAttribute("adminDescription", attrID));
    attrs7.put(new BasicAttribute("attributeID", "1.3.6.1.4.1.42.2.27.4.1.13"));
    attrs7.put(new BasicAttribute("attributeSyntax", "2.5.5.12"));
    attrs7.put(new BasicAttribute("cn", attrID));
    attrs7.put(new BasicAttribute("description",
        "Fully qualified Java class or interface name"));
    attrs7.put(new BasicAttribute("distinguishedName", "CN=" + attrID + ","
        + dn));
    attrs7.put(new BasicAttribute("isSingleValued", "FALSE"));
    attrs7.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs7.put(new BasicAttribute("name", attrID));
    attrs7
        .put(new BasicAttribute("objectCategory", "CN=Attribute-Schema," + dn));
    attrs7.put(new BasicAttribute("objectClass", "attributeSchema"));
    attrs7.put(new BasicAttribute("oMSyntax", "64"));
    attrs7.put(new BasicAttribute("searchFlags", "0"));
    attrs7.put(new BasicAttribute("systemOnly", "FALSE"));
    schemaCtx.createSubcontext("cn=" + attrID, attrs7);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // finally
  }
  /**
   * Inserts object class definitions from RFC 2713 into the schema.
   * 
   * This method maps the LDAP schema definitions in RFC 2713 onto the
   * proprietary attributes required by the Active Directory schema.
   * 
   * The resulting object class definitions differ from those of RFC 2713 in the
   * following ways: - Abstract and auxiliary classes are now defined as
   * structural. - The javaObject class now inherits from javaContainer. - The
   * javaNamingReference, javaSerializedObject and javaMarshalledObject now
   * inherit from javaObject.
   * 
   * The effect of these differences is that Java objects cannot be mixed-in
   * with other directory entries, they may only be stored as stand-alone
   * entries.
   * 
   * The reason for these differences is due to the way auxiliary classes are
   * supported the Active Directory. Only the names of structural classes (not
   * auxiliary) may appear in the object class attribute of an entry. Therefore,
   * the abstract and auxiliary classes in the Java schema definition are
   * re-defined as structural.
   */
  protected void insertADObjectClasses(DirContext rootCtx, DirContext schemaCtx)
      throws NamingException {
    System.out.println("  [inserting new object class definitions ...]");
    String dn = schemaCtx.getNameInNamespace();
    String attrID;
    attrID = new String("javaContainer");
    Attributes attrs1 = new BasicAttributes();
    attrs1.put(new BasicAttribute("objectClass", "classSchema"));
    attrs1.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs1.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.1"));
    attrs1.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs1.put(new BasicAttribute("mustContain", "cn"));
    attrs1.put(new BasicAttribute("objectClassCategory", "1"));
    attrs1.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs1.put(new BasicAttribute("subclassOf", "top"));
    attrs1.put(new BasicAttribute("possSuperiors", "top")); // any superior
    attrs1
        .put(new BasicAttribute("description", "Container for a Java object"));
    schemaCtx.createSubcontext("CN=" + attrID, attrs1);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // because javaObject relys on javaContainer
    attrID = new String("javaObject");
    Attributes attrs2 = new BasicAttributes();
    attrs2.put(new BasicAttribute("objectClass", "classSchema"));
    attrs2.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs2.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.4"));
    attrs2.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs2.put(new BasicAttribute("mustContain", "javaClassName"));
    Attribute joMay = new BasicAttribute("mayContain");
    joMay.add("javaClassNames");
    joMay.add("javaCodeBase");
    joMay.add("javaDoc");
    joMay.add("description");
    attrs2.put(joMay);
    attrs2.put(new BasicAttribute("objectClassCategory", "1"));
    attrs2.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs2.put(new BasicAttribute("subclassOf", "javaContainer"));
    attrs2.put(new BasicAttribute("description", "Java object representation"));
    schemaCtx.createSubcontext("CN=" + attrID, attrs2);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // because next 3 rely on javaObject
    attrID = new String("javaSerializedObject");
    Attributes attrs3 = new BasicAttributes();
    attrs3.put(new BasicAttribute("objectClass", "classSchema"));
    attrs3.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs3.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.5"));
    attrs3.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs3.put(new BasicAttribute("mustContain", "javaSerializedData"));
    attrs3.put(new BasicAttribute("objectClassCategory", "1"));
    attrs3.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs3.put(new BasicAttribute("subclassOf", "javaObject"));
    attrs3.put(new BasicAttribute("description", "Java serialized object"));
    schemaCtx.createSubcontext("CN=" + attrID, attrs3);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaNamingReference");
    Attributes attrs4 = new BasicAttributes();
    attrs4.put(new BasicAttribute("objectClass", "classSchema"));
    attrs4.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs4.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.7"));
    attrs4.put(new BasicAttribute("lDAPDisplayName", attrID));
    Attribute jnrMay = new BasicAttribute("mayContain");
    jnrMay.add("javaReferenceAddress");
    jnrMay.add("javaFactory");
    attrs4.put(jnrMay);
    attrs4.put(new BasicAttribute("objectClassCategory", "1"));
    attrs4.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs4.put(new BasicAttribute("subclassOf", "javaObject"));
    attrs4.put(new BasicAttribute("description", "JNDI reference"));
    schemaCtx.createSubcontext("CN=" + attrID, attrs4);
    System.out.println("    [" + attrID + "]");
    attrID = new String("javaMarshalledObject");
    Attributes attrs5 = new BasicAttributes();
    attrs5.put(new BasicAttribute("objectClass", "classSchema"));
    attrs5.put(new BasicAttribute("defaultHidingValue", "FALSE"));
    attrs5.put(new BasicAttribute("governsID", "1.3.6.1.4.1.42.2.27.4.2.8"));
    attrs5.put(new BasicAttribute("lDAPDisplayName", attrID));
    attrs5.put(new BasicAttribute("mustContain", "javaSerializedData"));
    attrs5.put(new BasicAttribute("objectClassCategory", "1"));
    attrs5.put(new BasicAttribute("systemOnly", "FALSE"));
    attrs5.put(new BasicAttribute("subclassOf", "javaObject"));
    attrs5.put(new BasicAttribute("description", "Java marshalled object"));
    schemaCtx.createSubcontext("CN=" + attrID, attrs5);
    System.out.println("    [" + attrID + "]");
    flushADSchemaMods(rootCtx); // finally
  }
  /**
   * Writes schema modifications to the Active Directory schema immediately.
   */
  protected void flushADSchemaMods(DirContext rootCtx) throws NamingException {
    rootCtx
        .modifyAttributes("", new ModificationItem[] { new ModificationItem(
            DirContext.ADD_ATTRIBUTE,
            new BasicAttribute("schemaUpdateNow", "1")) });
  }
  private int processCommandLine(String[] args) {
    String option;
    boolean schema = false;
    boolean list = false;
    for (int i = 0; i < args.length; i++) {
      option = args[i];
      if (option.startsWith("-h")) {
        printUsage(null);
      }
      if (option.startsWith("-s")) {
        schema = true;
        netscapebug = option.equals("-sn");
        netscape41bug = option.equals("-sn41");
        activeDirectorySchemaBug = option.equals("-sad");
      } else if (option.startsWith("-l")) {
        list = true;
      } else if (option.startsWith("-a")) {
        auth = option.substring(2);
      } else if (option.startsWith("-n")) {
        dn = option.substring(2);
      } else if (option.startsWith("-p")) {
        passwd = option.substring(2);
      } else if (option.startsWith("-trace")) {
        traceLdap = true;
      } else {
        // invalid option
        printUsage("Invalid option");
      }
    }
    if (!schema) {
      return LIST;
    } else {
      return UPDATE;
    }
  }
  protected void printUsage(String msg) {
    printUsageAux(msg, "Java");
  }
  protected void printUsageAux(String msg, String key) {
    if (msg != null) {
      System.out.println(msg);
    }
    System.out.print("Usage: ");
    System.out
        .println("java [-Djava.naming.provider.url=] \\");
    System.out.println("  Create" + key
        + "Schema [-h|-l|-s[n|n41|ad]] [-n] [-p] [-a]");
    System.out.println();
    System.out.println("  -h\t\tPrint the usage message");
    System.out.println("  -l\t\tList the " + key + " schema in the directory");
    System.out.println("  -s[n|n41|ad]\tUpdate schema:");
    System.out
        .println("\t\t -sn   use workaround for Netscape Directory pre-4.1 schema bug");
    System.out
        .println("\t\t -sn41 use workaround for Netscape Directory 4.1 schema bug");
    System.out
        .println("\t\t -sad  use workaround for Active Directory schema bug");
    System.out
        .println("  -n\tUse  as the distinguished name for authentication");
    System.out
        .println("  -p\tUse  as the password for authentication");
    System.out
        .println("  -a\tUse  as the authentication mechanism");
    System.out
        .println("\t\t Default is 'simple' if dn specified; otherwise 'none'");
    System.exit(-1);
  }
}