Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 2491 invoked from network); 25 Sep 2009 01:22:27 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 25 Sep 2009 01:22:27 -0000 Received: (qmail 19014 invoked by uid 500); 25 Sep 2009 01:22:27 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 18950 invoked by uid 500); 25 Sep 2009 01:22:27 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 18941 invoked by uid 99); 25 Sep 2009 01:22:27 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 25 Sep 2009 01:22:27 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 25 Sep 2009 01:22:23 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 71C5E238890A; Fri, 25 Sep 2009 01:22:02 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r818687 - in /directory: apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/reg... Date: Fri, 25 Sep 2009 01:22:02 -0000 To: commits@directory.apache.org From: elecharny@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090925012202.71C5E238890A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: elecharny Date: Fri Sep 25 01:22:01 2009 New Revision: 818687 URL: http://svn.apache.org/viewvc?rev=818687&view=rev Log: Handling ObjectClass superiors when updating the schema. Modified: directory/apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ObjectClassSynchronizer.java directory/shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/registries/ObjectClassRegistry.java Modified: directory/apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ObjectClassSynchronizer.java URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ObjectClassSynchronizer.java?rev=818687&r1=818686&r2=818687&view=diff ============================================================================== --- directory/apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ObjectClassSynchronizer.java (original) +++ directory/apacheds/branches/apacheds-schema/core-api/src/main/java/org/apache/directory/server/core/schema/registries/synchronizers/ObjectClassSynchronizer.java Fri Sep 25 01:22:01 2009 @@ -26,6 +26,7 @@ import org.apache.directory.shared.ldap.constants.MetaSchemaConstants; import org.apache.directory.shared.ldap.constants.SchemaConstants; import org.apache.directory.shared.ldap.exception.LdapInvalidNameException; +import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException; import org.apache.directory.shared.ldap.message.ResultCodeEnum; import org.apache.directory.shared.ldap.name.LdapDN; import org.apache.directory.shared.ldap.name.Rdn; @@ -105,6 +106,15 @@ if ( ( schema != null ) && schema.isEnabled() ) { + // Check that the entry has no descendant + if ( objectClassRegistry.hasDescendants( oc.getOid() ) ) + { + String msg = "Cannot delete " + entry.getDn().getUpName() + ", as there are some " + + " dependant ObjectClasses"; + + throw new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM ); + } + objectClassRegistry.unregister( oc.getOid() ); } @@ -135,6 +145,12 @@ ServerEntry targetEntry = ( ServerEntry ) entry.clone(); String newOid = ( String ) newRdn.getValue(); targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid ); + + // Inject the new DN + LdapDN newDn = new LdapDN( targetEntry.getDn() ); + newDn.remove( newDn.size() - 1 ); + newDn.add( newRdn ); + checkOidIsUnique( newOid ); ObjectClass oc = factory.getObjectClass( targetEntry, registries, schemaName ); @@ -142,6 +158,15 @@ if ( ( schema != null ) && schema.isEnabled() ) { + // Check that the entry has no descendant + if ( objectClassRegistry.hasDescendants( oldOc.getOid() ) ) + { + String msg = "Cannot rename " + entry.getDn().getUpName() + " to " + newDn + + " as the later has descendants' ObjectClasses"; + + throw new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM ); + } + objectClassRegistry.unregister( oldOc.getOid() ); objectClassRegistry.register( oc ); } Modified: directory/shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/registries/ObjectClassRegistry.java URL: http://svn.apache.org/viewvc/directory/shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/registries/ObjectClassRegistry.java?rev=818687&r1=818686&r2=818687&view=diff ============================================================================== --- directory/shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/registries/ObjectClassRegistry.java (original) +++ directory/shared/branches/shared-schema/ldap/src/main/java/org/apache/directory/shared/ldap/schema/registries/ObjectClassRegistry.java Fri Sep 25 01:22:01 2009 @@ -20,8 +20,21 @@ package org.apache.directory.shared.ldap.schema.registries; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.naming.NamingException; +import javax.naming.directory.NoSuchAttributeException; + import org.apache.directory.shared.ldap.schema.ObjectClass; import org.apache.directory.shared.ldap.schema.SchemaObjectType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -32,6 +45,15 @@ */ public class ObjectClassRegistry extends SchemaObjectRegistry { + /** static class logger */ + private static final Logger LOG = LoggerFactory.getLogger( ObjectClassRegistry.class ); + + /** Speedup for DEBUG mode */ + private static final boolean IS_DEBUG = LOG.isDebugEnabled(); + + /** maps OIDs to a Set of descendants for that OID */ + private final Map> oidToDescendantSet; + /** * Creates a new default ObjectClassRegistry instance. * @@ -40,5 +62,209 @@ public ObjectClassRegistry( OidRegistry oidRegistry ) { super( SchemaObjectType.OBJECT_CLASS, oidRegistry ); + oidToDescendantSet= new ConcurrentHashMap>(); + } + + + /** + * Quick lookup to see if an objectClass has descendants. + * + * @param ancestorId the name alias or OID for an ObjectClass + * @return an Iterator over the ObjectClasses which have the ancestor + * within their superior chain to the top + * @throws NamingException if the ancestor ObjectClass cannot be + * discerned from the ancestorId supplied + */ + public boolean hasDescendants( String ancestorId ) throws NamingException + { + try + { + String oid = getOidByName( ancestorId ); + Set descendants = oidToDescendantSet.get( oid ); + return (descendants != null) && !descendants.isEmpty(); + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } + } + + + /** + * Get's an iterator over the set of descendant ObjectClasses for + * some ancestor's name alias or their OID. + * + * @param ancestorId the name alias or OID for an ObjectClass + * @return an Iterator over the ObjectClasses which have the ancestor + * within their superior chain to the top + * @throws NamingException if the ancestor ObjectClass cannot be + * discerned from the ancestorId supplied + */ + @SuppressWarnings("unchecked") + public Iterator descendants( String ancestorId ) throws NamingException + { + try + { + String oid = getOidByName( ancestorId ); + Set descendants = oidToDescendantSet.get( oid ); + + if ( descendants == null ) + { + return Collections.EMPTY_SET.iterator(); + } + + return descendants.iterator(); + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } + } + + + /** + * Store the ObjectClass into a map associating an ObjectClass to its + * descendants. + * + * @param attributeType The ObjectClass to register + * @throws NamingException If something went wrong + */ + public void registerDescendants( ObjectClass objectClass, List ancestors ) + throws NamingException + { + // add this attribute to descendant list of other attributes in superior chain + if ( ( ancestors == null ) || ( ancestors.size() == 0 ) ) + { + return; + } + + for ( ObjectClass ancestor : ancestors ) + { + // Get the ancestor's descendant, if any + Set descendants = oidToDescendantSet.get( ancestor.getOid() ); + + // Initialize the descendant Set to store the descendants for the attributeType + if ( descendants == null ) + { + descendants = new HashSet( 1 ); + oidToDescendantSet.put( ancestor.getOid(), descendants ); + } + + // Add the current ObjectClass as a descendant + descendants.add( objectClass ); + + try + { + // And recurse until we reach the top of the hierarchy + registerDescendants( objectClass, ancestor.getSuperiors() ); + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } + } + } + + + /** + * Remove the ObjectClass from the map associating an ObjectClass to its + * descendants. + * + * @param attributeType The ObjectClass to unregister + * @param ancestor its ancestor + * @throws NamingException If something went wrong + */ + public void unregisterDescendants( ObjectClass attributeType, List ancestors ) + throws NamingException + { + // add this attribute to descendant list of other attributes in superior chain + if ( ( ancestors == null ) || ( ancestors.size() == 0 ) ) + { + return; + } + + for ( ObjectClass ancestor : ancestors ) + { + // Get the ancestor's descendant, if any + Set descendants = oidToDescendantSet.get( ancestor.getOid() ); + + if ( descendants != null ) + { + descendants.remove( attributeType ); + + if ( descendants.size() == 0 ) + { + oidToDescendantSet.remove( descendants ); + } + } + + try + { + // And recurse until we reach the top of the hierarchy + unregisterDescendants( attributeType, ancestor.getSuperiors() ); + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } + } + } + + + /** + * Registers a new ObjectClass with this registry. + * + * @param objectClass the ObjectClass to register + * @throws NamingException if the ObjectClass is already registered or + * the registration operation is not supported + */ + public void register( ObjectClass objectClass ) throws NamingException + { + try + { + super.register( objectClass ); + + // Register this ObjectClass into the Descendant map + registerDescendants( objectClass, objectClass.getSuperiors() ); + + // Internally associate the OID to the registered AttributeType + if ( IS_DEBUG ) + { + LOG.debug( "registred objectClass: {}", objectClass ); + } + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } + } + + + /** + * Removes the ObjectClass registered with this registry. + * + * @param numericOid the numeric identifier + * @throws NamingException if the numeric identifier is invalid + */ + public ObjectClass unregister( String numericOid ) throws NamingException + { + try + { + ObjectClass removed = super.unregister( numericOid ); + + // Deleting an ObjectClass which might be used as a superior means we have + // to recursively update the descendant map. We also have to remove + // the at.oid -> descendant relation + oidToDescendantSet.remove( numericOid ); + + // Now recurse if needed + unregisterDescendants( removed, removed.getSuperiors() ); + + return removed; + } + catch ( NamingException ne ) + { + throw new NoSuchAttributeException( ne.getMessage() ); + } } }