Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSchemaCtx.java
41161 views
/*1* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import javax.naming.*;28import javax.naming.directory.*;29import java.util.Hashtable;30import com.sun.jndi.toolkit.dir.HierMemDirCtx;3132/**33* This is the class used to implement LDAP's GetSchema call.34*35* It subclasses HierMemDirContext for most of the functionality. It36* overrides functions that cause the schema definitions to change.37* In such a case, it write the schema to the LdapServer and (assuming38* there are no errors), calls it's superclass's equivalent function.39* Thus, the schema tree and the LDAP server's schema attributes are40* always in sync.41*/4243final class LdapSchemaCtx extends HierMemDirCtx {4445private static final boolean debug = false;4647private static final int LEAF = 0; // schema object (e.g. attribute type defn)48private static final int SCHEMA_ROOT = 1; // schema tree root49static final int OBJECTCLASS_ROOT = 2; // root of object class subtree50static final int ATTRIBUTE_ROOT = 3; // root of attribute type subtree51static final int SYNTAX_ROOT = 4; // root of syntax subtree52static final int MATCHRULE_ROOT = 5; // root of matching rule subtree53static final int OBJECTCLASS = 6; // an object class definition54static final int ATTRIBUTE = 7; // an attribute type definition55static final int SYNTAX = 8; // a syntax definition56static final int MATCHRULE = 9; // a matching rule definition5758private SchemaInfo info= null;59private boolean setupMode = true;6061private int objectType;6263static DirContext createSchemaTree(Hashtable<String,Object> env,64String subschemasubentry, LdapCtx schemaEntry,65Attributes schemaAttrs, boolean netscapeBug)66throws NamingException {67try {68LdapSchemaParser parser = new LdapSchemaParser(netscapeBug);6970SchemaInfo allinfo = new SchemaInfo(subschemasubentry,71schemaEntry, parser);7273LdapSchemaCtx root = new LdapSchemaCtx(SCHEMA_ROOT, env, allinfo);74LdapSchemaParser.LDAP2JNDISchema(schemaAttrs, root);75return root;76} catch (NamingException e) {77schemaEntry.close(); // cleanup78throw e;79}80}8182// Called by createNewCtx83private LdapSchemaCtx(int objectType, Hashtable<String,Object> environment,84SchemaInfo info) {85super(environment, LdapClient.caseIgnore);8687this.objectType = objectType;88this.info = info;89}9091// override HierMemDirCtx.close to prevent premature GC of shared data92public void close() throws NamingException {93info.close();94}9596// override to ignore obj and use attrs97// treat same as createSubcontext98public final void bind(Name name, Object obj, Attributes attrs)99throws NamingException {100if (!setupMode) {101if (obj != null) {102throw new IllegalArgumentException("obj must be null");103}104105// Update server106addServerSchema(attrs);107}108109// Update in-memory copy110LdapSchemaCtx newEntry =111(LdapSchemaCtx)super.doCreateSubcontext(name, attrs);112}113114protected final void doBind(Name name, Object obj, Attributes attrs,115boolean useFactory) throws NamingException {116if (!setupMode) {117throw new SchemaViolationException(118"Cannot bind arbitrary object; use createSubcontext()");119} else {120super.doBind(name, obj, attrs, false); // always ignore factories121}122}123124// override to use bind() instead125public final void rebind(Name name, Object obj, Attributes attrs)126throws NamingException {127try {128doLookup(name, false);129throw new SchemaViolationException(130"Cannot replace existing schema object");131} catch (NameNotFoundException e) {132bind(name, obj, attrs);133}134}135136protected final void doRebind(Name name, Object obj, Attributes attrs,137boolean useFactory) throws NamingException {138if (!setupMode) {139throw new SchemaViolationException(140"Cannot bind arbitrary object; use createSubcontext()");141} else {142super.doRebind(name, obj, attrs, false); // always ignore factories143}144}145146protected final void doUnbind(Name name) throws NamingException {147if (!setupMode) {148// Update server149try {150// Lookup entry from memory151LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);152153deleteServerSchema(target.attrs);154} catch (NameNotFoundException e) {155return;156}157}158// Update in-memory copy159super.doUnbind(name);160}161162protected final void doRename(Name oldname, Name newname)163throws NamingException {164if (!setupMode) {165throw new SchemaViolationException("Cannot rename a schema object");166} else {167super.doRename(oldname, newname);168}169}170171protected final void doDestroySubcontext(Name name) throws NamingException {172if (!setupMode) {173// Update server174try {175// Lookup entry from memory176LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);177178deleteServerSchema(target.attrs);179} catch (NameNotFoundException e) {180return;181}182}183184// Update in-memory copy185super.doDestroySubcontext(name);186}187188// Called to create oc, attr, syntax or matching rule roots and leaf entries189final LdapSchemaCtx setup(int objectType, String name, Attributes attrs)190throws NamingException{191try {192setupMode = true;193LdapSchemaCtx answer =194(LdapSchemaCtx) super.doCreateSubcontext(195new CompositeName(name), attrs);196197answer.objectType = objectType;198answer.setupMode = false;199return answer;200} finally {201setupMode = false;202}203}204205protected final DirContext doCreateSubcontext(Name name, Attributes attrs)206throws NamingException {207208if (attrs == null || attrs.size() == 0) {209throw new SchemaViolationException(210"Must supply attributes describing schema");211}212213if (!setupMode) {214// Update server215addServerSchema(attrs);216}217218// Update in-memory copy219LdapSchemaCtx newEntry =220(LdapSchemaCtx) super.doCreateSubcontext(name, attrs);221return newEntry;222}223224private static final Attributes deepClone(Attributes orig)225throws NamingException {226BasicAttributes copy = new BasicAttributes(true);227NamingEnumeration<? extends Attribute> attrs = orig.getAll();228while (attrs.hasMore()) {229copy.put((Attribute)attrs.next().clone());230}231return copy;232}233234protected final void doModifyAttributes(ModificationItem[] mods)235throws NamingException {236if (setupMode) {237super.doModifyAttributes(mods);238} else {239Attributes copy = deepClone(attrs);240241// Apply modifications to copy242applyMods(mods, copy);243244// Update server copy245modifyServerSchema(attrs, copy);246247// Update in-memory copy248attrs = copy;249}250}251252// we override this so the superclass creates the right kind of contexts253// Default is to create LEAF objects; caller will change after creation254// if necessary255protected final HierMemDirCtx createNewCtx() {256LdapSchemaCtx ctx = new LdapSchemaCtx(LEAF, myEnv, info);257return ctx;258}259260261private final void addServerSchema(Attributes attrs)262throws NamingException {263Attribute schemaAttr;264265switch (objectType) {266case OBJECTCLASS_ROOT:267schemaAttr = info.parser.stringifyObjDesc(attrs);268break;269270case ATTRIBUTE_ROOT:271schemaAttr = info.parser.stringifyAttrDesc(attrs);272break;273274case SYNTAX_ROOT:275schemaAttr = info.parser.stringifySyntaxDesc(attrs);276break;277278case MATCHRULE_ROOT:279schemaAttr = info.parser.stringifyMatchRuleDesc(attrs);280break;281282case SCHEMA_ROOT:283throw new SchemaViolationException(284"Cannot create new entry under schema root");285286default:287throw new SchemaViolationException(288"Cannot create child of schema object");289}290291Attributes holder = new BasicAttributes(true);292holder.put(schemaAttr);293//System.err.println((String)schemaAttr.get());294295info.modifyAttributes(myEnv, DirContext.ADD_ATTRIBUTE, holder);296297}298299/**300* When we delete an entry, we use the original to make sure that301* any formatting inconsistencies are eliminated.302* This is because we're just deleting a value from an attribute303* on the server and there might not be any checks for extra spaces304* or parens.305*/306private final void deleteServerSchema(Attributes origAttrs)307throws NamingException {308309Attribute origAttrVal;310311switch (objectType) {312case OBJECTCLASS_ROOT:313origAttrVal = info.parser.stringifyObjDesc(origAttrs);314break;315316case ATTRIBUTE_ROOT:317origAttrVal = info.parser.stringifyAttrDesc(origAttrs);318break;319320case SYNTAX_ROOT:321origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);322break;323324case MATCHRULE_ROOT:325origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);326break;327328case SCHEMA_ROOT:329throw new SchemaViolationException(330"Cannot delete schema root");331332default:333throw new SchemaViolationException(334"Cannot delete child of schema object");335}336337ModificationItem[] mods = new ModificationItem[1];338mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);339340info.modifyAttributes(myEnv, mods);341}342343/**344* When we modify an entry, we use the original attribute value345* in the schema to make sure that any formatting inconsistencies346* are eliminated. A modification is done by deleting the original347* value and adding a new value with the modification.348*/349private final void modifyServerSchema(Attributes origAttrs,350Attributes newAttrs) throws NamingException {351352Attribute newAttrVal;353Attribute origAttrVal;354355switch (objectType) {356case OBJECTCLASS:357origAttrVal = info.parser.stringifyObjDesc(origAttrs);358newAttrVal = info.parser.stringifyObjDesc(newAttrs);359break;360361case ATTRIBUTE:362origAttrVal = info.parser.stringifyAttrDesc(origAttrs);363newAttrVal = info.parser.stringifyAttrDesc(newAttrs);364break;365366case SYNTAX:367origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);368newAttrVal = info.parser.stringifySyntaxDesc(newAttrs);369break;370371case MATCHRULE:372origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);373newAttrVal = info.parser.stringifyMatchRuleDesc(newAttrs);374break;375376default:377throw new SchemaViolationException(378"Cannot modify schema root");379}380381ModificationItem[] mods = new ModificationItem[2];382mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);383mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, newAttrVal);384385info.modifyAttributes(myEnv, mods);386}387388private static final class SchemaInfo {389private LdapCtx schemaEntry;390private String schemaEntryName;391LdapSchemaParser parser;392private String host;393private int port;394private boolean hasLdapsScheme;395396SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,397LdapSchemaParser parser) {398this.schemaEntryName = schemaEntryName;399this.schemaEntry = schemaEntry;400this.parser = parser;401this.port = schemaEntry.port_number;402this.host = schemaEntry.hostname;403this.hasLdapsScheme = schemaEntry.hasLdapsScheme;404}405406synchronized void close() throws NamingException {407if (schemaEntry != null) {408schemaEntry.close();409schemaEntry = null;410}411}412413private LdapCtx reopenEntry(Hashtable<?,?> env) throws NamingException {414// Use subschemasubentry name as DN415return new LdapCtx(schemaEntryName, host, port,416env, hasLdapsScheme);417}418419synchronized void modifyAttributes(Hashtable<?,?> env,420ModificationItem[] mods)421throws NamingException {422if (schemaEntry == null) {423schemaEntry = reopenEntry(env);424}425schemaEntry.modifyAttributes("", mods);426}427428synchronized void modifyAttributes(Hashtable<?,?> env, int mod,429Attributes attrs) throws NamingException {430if (schemaEntry == null) {431schemaEntry = reopenEntry(env);432}433schemaEntry.modifyAttributes("", mod, attrs);434}435}436}437438439