0N/A/*
2362N/A * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jndi.ldap;
0N/A
0N/Aimport javax.naming.*;
0N/Aimport javax.naming.directory.*;
0N/Aimport java.util.Hashtable;
0N/Aimport com.sun.jndi.toolkit.dir.HierMemDirCtx;
0N/A
0N/A/**
0N/A * This is the class used to implement LDAP's GetSchema call.
0N/A *
0N/A * It subclasses HierMemDirContext for most of the functionality. It
0N/A * overrides functions that cause the schema definitions to change.
0N/A * In such a case, it write the schema to the LdapServer and (assuming
0N/A * there are no errors), calls it's superclass's equivalent function.
0N/A * Thus, the schema tree and the LDAP server's schema attributes are
0N/A * always in sync.
0N/A */
0N/A
0N/Afinal class LdapSchemaCtx extends HierMemDirCtx {
0N/A
0N/A static private final boolean debug = false;
0N/A
0N/A private static final int LEAF = 0; // schema object (e.g. attribute type defn)
0N/A private static final int SCHEMA_ROOT = 1; // schema tree root
0N/A static final int OBJECTCLASS_ROOT = 2; // root of object class subtree
0N/A static final int ATTRIBUTE_ROOT = 3; // root of attribute type subtree
0N/A static final int SYNTAX_ROOT = 4; // root of syntax subtree
0N/A static final int MATCHRULE_ROOT = 5; // root of matching rule subtree
0N/A static final int OBJECTCLASS = 6; // an object class definition
0N/A static final int ATTRIBUTE = 7; // an attribute type definition
0N/A static final int SYNTAX = 8; // a syntax definition
0N/A static final int MATCHRULE = 9; // a matching rule definition
0N/A
0N/A private SchemaInfo info= null;
0N/A private boolean setupMode = true;
0N/A
0N/A private int objectType;
0N/A
0N/A static DirContext createSchemaTree(Hashtable env, String subschemasubentry,
0N/A LdapCtx schemaEntry, Attributes schemaAttrs, boolean netscapeBug)
0N/A throws NamingException {
0N/A try {
0N/A LdapSchemaParser parser = new LdapSchemaParser(netscapeBug);
0N/A
0N/A SchemaInfo allinfo = new SchemaInfo(subschemasubentry,
0N/A schemaEntry, parser);
0N/A
0N/A LdapSchemaCtx root = new LdapSchemaCtx(SCHEMA_ROOT, env, allinfo);
0N/A parser.LDAP2JNDISchema(schemaAttrs, root);
0N/A return root;
0N/A } catch (NamingException e) {
0N/A schemaEntry.close(); // cleanup
0N/A throw e;
0N/A }
0N/A }
0N/A
0N/A // Called by createNewCtx
0N/A private LdapSchemaCtx(int objectType, Hashtable environment, SchemaInfo info) {
0N/A super(environment, LdapClient.caseIgnore);
0N/A
0N/A this.objectType = objectType;
0N/A this.info = info;
0N/A }
0N/A
0N/A // override HierMemDirCtx.close to prevent premature GC of shared data
0N/A public void close() throws NamingException {
0N/A info.close();
0N/A }
0N/A
0N/A // override to ignore obj and use attrs
0N/A // treat same as createSubcontext
0N/A final public void bind(Name name, Object obj, Attributes attrs)
0N/A throws NamingException {
0N/A if (!setupMode) {
0N/A if (obj != null) {
0N/A throw new IllegalArgumentException("obj must be null");
0N/A }
0N/A
0N/A // Update server
0N/A addServerSchema(attrs);
0N/A }
0N/A
0N/A // Update in-memory copy
0N/A LdapSchemaCtx newEntry =
0N/A (LdapSchemaCtx)super.doCreateSubcontext(name, attrs);
0N/A }
0N/A
0N/A final protected void doBind(Name name, Object obj, Attributes attrs,
0N/A boolean useFactory) throws NamingException {
0N/A if (!setupMode) {
0N/A throw new SchemaViolationException(
0N/A "Cannot bind arbitrary object; use createSubcontext()");
0N/A } else {
0N/A super.doBind(name, obj, attrs, false); // always ignore factories
0N/A }
0N/A }
0N/A
0N/A // override to use bind() instead
0N/A final public void rebind(Name name, Object obj, Attributes attrs)
0N/A throws NamingException {
0N/A try {
0N/A doLookup(name, false);
0N/A throw new SchemaViolationException(
0N/A "Cannot replace existing schema object");
0N/A } catch (NameNotFoundException e) {
0N/A bind(name, obj, attrs);
0N/A }
0N/A }
0N/A
0N/A final protected void doRebind(Name name, Object obj, Attributes attrs,
0N/A boolean useFactory) throws NamingException {
0N/A if (!setupMode) {
0N/A throw new SchemaViolationException(
0N/A "Cannot bind arbitrary object; use createSubcontext()");
0N/A } else {
0N/A super.doRebind(name, obj, attrs, false); // always ignore factories
0N/A }
0N/A }
0N/A
0N/A final protected void doUnbind(Name name) throws NamingException {
0N/A if (!setupMode) {
0N/A // Update server
0N/A try {
0N/A // Lookup entry from memory
0N/A LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);
0N/A
0N/A deleteServerSchema(target.attrs);
0N/A } catch (NameNotFoundException e) {
0N/A return;
0N/A }
0N/A }
0N/A // Update in-memory copy
0N/A super.doUnbind(name);
0N/A }
0N/A
0N/A final protected void doRename(Name oldname, Name newname)
0N/A throws NamingException {
0N/A if (!setupMode) {
0N/A throw new SchemaViolationException("Cannot rename a schema object");
0N/A } else {
0N/A super.doRename(oldname, newname);
0N/A }
0N/A }
0N/A
0N/A final protected void doDestroySubcontext(Name name) throws NamingException {
0N/A if (!setupMode) {
0N/A // Update server
0N/A try {
0N/A // Lookup entry from memory
0N/A LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);
0N/A
0N/A deleteServerSchema(target.attrs);
0N/A } catch (NameNotFoundException e) {
0N/A return;
0N/A }
0N/A }
0N/A
0N/A // Update in-memory copy
0N/A super.doDestroySubcontext(name);
0N/A }
0N/A
0N/A // Called to create oc, attr, syntax or matching rule roots and leaf entries
0N/A final LdapSchemaCtx setup(int objectType, String name, Attributes attrs)
0N/A throws NamingException{
0N/A try {
0N/A setupMode = true;
0N/A LdapSchemaCtx answer =
0N/A (LdapSchemaCtx) super.doCreateSubcontext(
0N/A new CompositeName(name), attrs);
0N/A
0N/A answer.objectType = objectType;
0N/A answer.setupMode = false;
0N/A return answer;
0N/A } finally {
0N/A setupMode = false;
0N/A }
0N/A }
0N/A
0N/A final protected DirContext doCreateSubcontext(Name name, Attributes attrs)
0N/A throws NamingException {
0N/A
0N/A if (attrs == null || attrs.size() == 0) {
0N/A throw new SchemaViolationException(
0N/A "Must supply attributes describing schema");
0N/A }
0N/A
0N/A if (!setupMode) {
0N/A // Update server
0N/A addServerSchema(attrs);
0N/A }
0N/A
0N/A // Update in-memory copy
0N/A LdapSchemaCtx newEntry =
0N/A (LdapSchemaCtx) super.doCreateSubcontext(name, attrs);
0N/A return newEntry;
0N/A }
0N/A
0N/A final private static Attributes deepClone(Attributes orig)
0N/A throws NamingException {
0N/A BasicAttributes copy = new BasicAttributes(true);
0N/A NamingEnumeration attrs = orig.getAll();
0N/A while (attrs.hasMore()) {
0N/A copy.put((Attribute)((Attribute)attrs.next()).clone());
0N/A }
0N/A return copy;
0N/A }
0N/A
0N/A final protected void doModifyAttributes(ModificationItem[] mods)
0N/A throws NamingException {
0N/A if (setupMode) {
0N/A super.doModifyAttributes(mods);
0N/A } else {
0N/A Attributes copy = deepClone(attrs);
0N/A
0N/A // Apply modifications to copy
0N/A applyMods(mods, copy);
0N/A
0N/A // Update server copy
0N/A modifyServerSchema(attrs, copy);
0N/A
0N/A // Update in-memory copy
0N/A attrs = copy;
0N/A }
0N/A }
0N/A
0N/A // we override this so the superclass creates the right kind of contexts
0N/A // Default is to create LEAF objects; caller will change after creation
0N/A // if necessary
0N/A final protected HierMemDirCtx createNewCtx() {
0N/A LdapSchemaCtx ctx = new LdapSchemaCtx(LEAF, myEnv, info);
0N/A return ctx;
0N/A }
0N/A
0N/A
0N/A final private void addServerSchema(Attributes attrs)
0N/A throws NamingException {
0N/A Attribute schemaAttr;
0N/A
0N/A switch (objectType) {
0N/A case OBJECTCLASS_ROOT:
0N/A schemaAttr = info.parser.stringifyObjDesc(attrs);
0N/A break;
0N/A
0N/A case ATTRIBUTE_ROOT:
0N/A schemaAttr = info.parser.stringifyAttrDesc(attrs);
0N/A break;
0N/A
0N/A case SYNTAX_ROOT:
0N/A schemaAttr = info.parser.stringifySyntaxDesc(attrs);
0N/A break;
0N/A
0N/A case MATCHRULE_ROOT:
0N/A schemaAttr = info.parser.stringifyMatchRuleDesc(attrs);
0N/A break;
0N/A
0N/A case SCHEMA_ROOT:
0N/A throw new SchemaViolationException(
0N/A "Cannot create new entry under schema root");
0N/A
0N/A default:
0N/A throw new SchemaViolationException(
0N/A "Cannot create child of schema object");
0N/A }
0N/A
0N/A Attributes holder = new BasicAttributes(true);
0N/A holder.put(schemaAttr);
0N/A //System.err.println((String)schemaAttr.get());
0N/A
0N/A info.modifyAttributes(myEnv, DirContext.ADD_ATTRIBUTE, holder);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * When we delete an entry, we use the original to make sure that
0N/A * any formatting inconsistencies are eliminated.
0N/A * This is because we're just deleting a value from an attribute
0N/A * on the server and there might not be any checks for extra spaces
0N/A * or parens.
0N/A */
0N/A final private void deleteServerSchema(Attributes origAttrs)
0N/A throws NamingException {
0N/A
0N/A Attribute origAttrVal;
0N/A
0N/A switch (objectType) {
0N/A case OBJECTCLASS_ROOT:
0N/A origAttrVal = info.parser.stringifyObjDesc(origAttrs);
0N/A break;
0N/A
0N/A case ATTRIBUTE_ROOT:
0N/A origAttrVal = info.parser.stringifyAttrDesc(origAttrs);
0N/A break;
0N/A
0N/A case SYNTAX_ROOT:
0N/A origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);
0N/A break;
0N/A
0N/A case MATCHRULE_ROOT:
0N/A origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);
0N/A break;
0N/A
0N/A case SCHEMA_ROOT:
0N/A throw new SchemaViolationException(
0N/A "Cannot delete schema root");
0N/A
0N/A default:
0N/A throw new SchemaViolationException(
0N/A "Cannot delete child of schema object");
0N/A }
0N/A
0N/A ModificationItem[] mods = new ModificationItem[1];
0N/A mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);
0N/A
0N/A info.modifyAttributes(myEnv, mods);
0N/A }
0N/A
0N/A /**
0N/A * When we modify an entry, we use the original attribute value
0N/A * in the schema to make sure that any formatting inconsistencies
0N/A * are eliminated. A modification is done by deleting the original
0N/A * value and adding a new value with the modification.
0N/A */
0N/A final private void modifyServerSchema(Attributes origAttrs,
0N/A Attributes newAttrs) throws NamingException {
0N/A
0N/A Attribute newAttrVal;
0N/A Attribute origAttrVal;
0N/A
0N/A switch (objectType) {
0N/A case OBJECTCLASS:
0N/A origAttrVal = info.parser.stringifyObjDesc(origAttrs);
0N/A newAttrVal = info.parser.stringifyObjDesc(newAttrs);
0N/A break;
0N/A
0N/A case ATTRIBUTE:
0N/A origAttrVal = info.parser.stringifyAttrDesc(origAttrs);
0N/A newAttrVal = info.parser.stringifyAttrDesc(newAttrs);
0N/A break;
0N/A
0N/A case SYNTAX:
0N/A origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);
0N/A newAttrVal = info.parser.stringifySyntaxDesc(newAttrs);
0N/A break;
0N/A
0N/A case MATCHRULE:
0N/A origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);
0N/A newAttrVal = info.parser.stringifyMatchRuleDesc(newAttrs);
0N/A break;
0N/A
0N/A default:
0N/A throw new SchemaViolationException(
0N/A "Cannot modify schema root");
0N/A }
0N/A
0N/A ModificationItem[] mods = new ModificationItem[2];
0N/A mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);
0N/A mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, newAttrVal);
0N/A
0N/A info.modifyAttributes(myEnv, mods);
0N/A }
0N/A
0N/A final static private class SchemaInfo {
0N/A private LdapCtx schemaEntry;
0N/A private String schemaEntryName;
0N/A LdapSchemaParser parser;
0N/A private String host;
0N/A private int port;
0N/A private boolean hasLdapsScheme;
0N/A
0N/A SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,
0N/A LdapSchemaParser parser) {
0N/A this.schemaEntryName = schemaEntryName;
0N/A this.schemaEntry = schemaEntry;
0N/A this.parser = parser;
0N/A this.port = schemaEntry.port_number;
0N/A this.host = schemaEntry.hostname;
0N/A this.hasLdapsScheme = schemaEntry.hasLdapsScheme;
0N/A }
0N/A
0N/A synchronized void close() throws NamingException {
0N/A if (schemaEntry != null) {
0N/A schemaEntry.close();
0N/A schemaEntry = null;
0N/A }
0N/A }
0N/A
0N/A private LdapCtx reopenEntry(Hashtable env) throws NamingException {
0N/A // Use subschemasubentry name as DN
0N/A return new LdapCtx(schemaEntryName, host, port,
0N/A env, hasLdapsScheme);
0N/A }
0N/A
0N/A synchronized void modifyAttributes(Hashtable env, ModificationItem[] mods)
0N/A throws NamingException {
0N/A if (schemaEntry == null) {
0N/A schemaEntry = reopenEntry(env);
0N/A }
0N/A schemaEntry.modifyAttributes("", mods);
0N/A }
0N/A
0N/A synchronized void modifyAttributes(Hashtable env, int mod,
0N/A Attributes attrs) throws NamingException {
0N/A if (schemaEntry == null) {
0N/A schemaEntry = reopenEntry(env);
0N/A }
0N/A schemaEntry.modifyAttributes("", mod, attrs);
0N/A }
0N/A }
0N/A}