/* * 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); } }}
/*
* 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);
}
}
}