434 lines · 14.5 KB · Java
RawDownload
/* * Copyright (c) 1999, 2003, 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. */ 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 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);                parser.LDAP2JNDISchema(schemaAttrs, root);                return root;            } catch (NamingException e) {                schemaEntry.close(); // cleanup                throw e;            }    }     // Called by createNewCtx    private LdapSchemaCtx(int objectType, Hashtable 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 attrs = orig.getAll();        while (attrs.hasMore()) {            copy.put((Attribute)((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.      */    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.      */    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);        }    }}