/*
 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.jndi.ldap;

import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
import com.sun.jndi.toolkit.dir.HierMemDirCtx;

This is the class used to implement LDAP's GetSchema call. It subclasses HierMemDirContext for most of the functionality. It overrides functions that cause the schema definitions to change. In such a case, it write the schema to the LdapServer and (assuming there are no errors), calls it's superclass's equivalent function. Thus, the schema tree and the LDAP server's schema attributes are always in sync.
/** * This is the class used to implement LDAP's GetSchema call. * * It subclasses HierMemDirContext for most of the functionality. It * overrides functions that cause the schema definitions to change. * In such a case, it write the schema to the LdapServer and (assuming * there are no errors), calls it's superclass's equivalent function. * Thus, the schema tree and the LDAP server's schema attributes are * always in sync. */
final class LdapSchemaCtx extends HierMemDirCtx { static private final boolean debug = false; private static final int LEAF = 0; // schema object (e.g. attribute type defn) private static final int SCHEMA_ROOT = 1; // schema tree root static final int OBJECTCLASS_ROOT = 2; // root of object class subtree static final int ATTRIBUTE_ROOT = 3; // root of attribute type subtree static final int SYNTAX_ROOT = 4; // root of syntax subtree static final int MATCHRULE_ROOT = 5; // root of matching rule subtree static final int OBJECTCLASS = 6; // an object class definition static final int ATTRIBUTE = 7; // an attribute type definition static final int SYNTAX = 8; // a syntax definition static final int MATCHRULE = 9; // a matching rule definition private SchemaInfo info= null; private boolean setupMode = true; private int objectType; static DirContext createSchemaTree(Hashtable<String,Object> env, String subschemasubentry, LdapCtx schemaEntry, Attributes schemaAttrs, boolean netscapeBug) throws NamingException { try { LdapSchemaParser parser = new LdapSchemaParser(netscapeBug); SchemaInfo allinfo = new SchemaInfo(subschemasubentry, schemaEntry, parser); LdapSchemaCtx root = new LdapSchemaCtx(SCHEMA_ROOT, env, allinfo); LdapSchemaParser.LDAP2JNDISchema(schemaAttrs, root); return root; } catch (NamingException e) { schemaEntry.close(); // cleanup throw e; } } // Called by createNewCtx private LdapSchemaCtx(int objectType, Hashtable<String,Object> environment, SchemaInfo info) { super(environment, LdapClient.caseIgnore); this.objectType = objectType; this.info = info; } // override HierMemDirCtx.close to prevent premature GC of shared data public void close() throws NamingException { info.close(); } // override to ignore obj and use attrs // treat same as createSubcontext final public void bind(Name name, Object obj, Attributes attrs) throws NamingException { if (!setupMode) { if (obj != null) { throw new IllegalArgumentException("obj must be null"); } // Update server addServerSchema(attrs); } // Update in-memory copy LdapSchemaCtx newEntry = (LdapSchemaCtx)super.doCreateSubcontext(name, attrs); } final protected void doBind(Name name, Object obj, Attributes attrs, boolean useFactory) throws NamingException { if (!setupMode) { throw new SchemaViolationException( "Cannot bind arbitrary object; use createSubcontext()"); } else { super.doBind(name, obj, attrs, false); // always ignore factories } } // override to use bind() instead final public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { try { doLookup(name, false); throw new SchemaViolationException( "Cannot replace existing schema object"); } catch (NameNotFoundException e) { bind(name, obj, attrs); } } final protected void doRebind(Name name, Object obj, Attributes attrs, boolean useFactory) throws NamingException { if (!setupMode) { throw new SchemaViolationException( "Cannot bind arbitrary object; use createSubcontext()"); } else { super.doRebind(name, obj, attrs, false); // always ignore factories } } final protected void doUnbind(Name name) throws NamingException { if (!setupMode) { // Update server try { // Lookup entry from memory LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false); deleteServerSchema(target.attrs); } catch (NameNotFoundException e) { return; } } // Update in-memory copy super.doUnbind(name); } final protected void doRename(Name oldname, Name newname) throws NamingException { if (!setupMode) { throw new SchemaViolationException("Cannot rename a schema object"); } else { super.doRename(oldname, newname); } } final protected void doDestroySubcontext(Name name) throws NamingException { if (!setupMode) { // Update server try { // Lookup entry from memory LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false); deleteServerSchema(target.attrs); } catch (NameNotFoundException e) { return; } } // Update in-memory copy super.doDestroySubcontext(name); } // Called to create oc, attr, syntax or matching rule roots and leaf entries final LdapSchemaCtx setup(int objectType, String name, Attributes attrs) throws NamingException{ try { setupMode = true; LdapSchemaCtx answer = (LdapSchemaCtx) super.doCreateSubcontext( new CompositeName(name), attrs); answer.objectType = objectType; answer.setupMode = false; return answer; } finally { setupMode = false; } } final protected DirContext doCreateSubcontext(Name name, Attributes attrs) throws NamingException { if (attrs == null || attrs.size() == 0) { throw new SchemaViolationException( "Must supply attributes describing schema"); } if (!setupMode) { // Update server addServerSchema(attrs); } // Update in-memory copy LdapSchemaCtx newEntry = (LdapSchemaCtx) super.doCreateSubcontext(name, attrs); return newEntry; } final private static Attributes deepClone(Attributes orig) throws NamingException { BasicAttributes copy = new BasicAttributes(true); NamingEnumeration<? extends Attribute> attrs = orig.getAll(); while (attrs.hasMore()) { copy.put((Attribute)attrs.next().clone()); } return copy; } final protected void doModifyAttributes(ModificationItem[] mods) throws NamingException { if (setupMode) { super.doModifyAttributes(mods); } else { Attributes copy = deepClone(attrs); // Apply modifications to copy applyMods(mods, copy); // Update server copy modifyServerSchema(attrs, copy); // Update in-memory copy attrs = copy; } } // we override this so the superclass creates the right kind of contexts // Default is to create LEAF objects; caller will change after creation // if necessary final protected HierMemDirCtx createNewCtx() { LdapSchemaCtx ctx = new LdapSchemaCtx(LEAF, myEnv, info); return ctx; } final private void addServerSchema(Attributes attrs) throws NamingException { Attribute schemaAttr; switch (objectType) { case OBJECTCLASS_ROOT: schemaAttr = info.parser.stringifyObjDesc(attrs); break; case ATTRIBUTE_ROOT: schemaAttr = info.parser.stringifyAttrDesc(attrs); break; case SYNTAX_ROOT: schemaAttr = info.parser.stringifySyntaxDesc(attrs); break; case MATCHRULE_ROOT: schemaAttr = info.parser.stringifyMatchRuleDesc(attrs); break; case SCHEMA_ROOT: throw new SchemaViolationException( "Cannot create new entry under schema root"); default: throw new SchemaViolationException( "Cannot create child of schema object"); } Attributes holder = new BasicAttributes(true); holder.put(schemaAttr); //System.err.println((String)schemaAttr.get()); info.modifyAttributes(myEnv, DirContext.ADD_ATTRIBUTE, holder); }
When we delete an entry, we use the original to make sure that any formatting inconsistencies are eliminated. This is because we're just deleting a value from an attribute on the server and there might not be any checks for extra spaces or parens.
/** * When we delete an entry, we use the original to make sure that * any formatting inconsistencies are eliminated. * This is because we're just deleting a value from an attribute * on the server and there might not be any checks for extra spaces * or parens. */
final private void deleteServerSchema(Attributes origAttrs) throws NamingException { Attribute origAttrVal; switch (objectType) { case OBJECTCLASS_ROOT: origAttrVal = info.parser.stringifyObjDesc(origAttrs); break; case ATTRIBUTE_ROOT: origAttrVal = info.parser.stringifyAttrDesc(origAttrs); break; case SYNTAX_ROOT: origAttrVal = info.parser.stringifySyntaxDesc(origAttrs); break; case MATCHRULE_ROOT: origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs); break; case SCHEMA_ROOT: throw new SchemaViolationException( "Cannot delete schema root"); default: throw new SchemaViolationException( "Cannot delete child of schema object"); } ModificationItem[] mods = new ModificationItem[1]; mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal); info.modifyAttributes(myEnv, mods); }
When we modify an entry, we use the original attribute value in the schema to make sure that any formatting inconsistencies are eliminated. A modification is done by deleting the original value and adding a new value with the modification.
/** * When we modify an entry, we use the original attribute value * in the schema to make sure that any formatting inconsistencies * are eliminated. A modification is done by deleting the original * value and adding a new value with the modification. */
final private void modifyServerSchema(Attributes origAttrs, Attributes newAttrs) throws NamingException { Attribute newAttrVal; Attribute origAttrVal; switch (objectType) { case OBJECTCLASS: origAttrVal = info.parser.stringifyObjDesc(origAttrs); newAttrVal = info.parser.stringifyObjDesc(newAttrs); break; case ATTRIBUTE: origAttrVal = info.parser.stringifyAttrDesc(origAttrs); newAttrVal = info.parser.stringifyAttrDesc(newAttrs); break; case SYNTAX: origAttrVal = info.parser.stringifySyntaxDesc(origAttrs); newAttrVal = info.parser.stringifySyntaxDesc(newAttrs); break; case MATCHRULE: origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs); newAttrVal = info.parser.stringifyMatchRuleDesc(newAttrs); break; default: throw new SchemaViolationException( "Cannot modify schema root"); } ModificationItem[] mods = new ModificationItem[2]; mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal); mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, newAttrVal); info.modifyAttributes(myEnv, mods); } final static private class SchemaInfo { private LdapCtx schemaEntry; private String schemaEntryName; LdapSchemaParser parser; private String host; private int port; private boolean hasLdapsScheme; SchemaInfo(String schemaEntryName, LdapCtx schemaEntry, LdapSchemaParser parser) { this.schemaEntryName = schemaEntryName; this.schemaEntry = schemaEntry; this.parser = parser; this.port = schemaEntry.port_number; this.host = schemaEntry.hostname; this.hasLdapsScheme = schemaEntry.hasLdapsScheme; } synchronized void close() throws NamingException { if (schemaEntry != null) { schemaEntry.close(); schemaEntry = null; } } private LdapCtx reopenEntry(Hashtable<?,?> env) throws NamingException { // Use subschemasubentry name as DN return new LdapCtx(schemaEntryName, host, port, env, hasLdapsScheme); } synchronized void modifyAttributes(Hashtable<?,?> env, ModificationItem[] mods) throws NamingException { if (schemaEntry == null) { schemaEntry = reopenEntry(env); } schemaEntry.modifyAttributes("", mods); } synchronized void modifyAttributes(Hashtable<?,?> env, int mod, Attributes attrs) throws NamingException { if (schemaEntry == null) { schemaEntry = reopenEntry(env); } schemaEntry.modifyAttributes("", mod, attrs); } } }