| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.server.core.partition.avl; |
| |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.directory.server.constants.ApacheSchemaConstants; |
| import org.apache.directory.server.core.cursor.Cursor; |
| import org.apache.directory.server.core.entry.ClonedServerEntry; |
| import org.apache.directory.server.core.entry.ServerAttribute; |
| import org.apache.directory.server.core.entry.ServerBinaryValue; |
| import org.apache.directory.server.core.entry.ServerEntry; |
| import org.apache.directory.server.core.entry.ServerStringValue; |
| import org.apache.directory.server.schema.registries.AttributeTypeRegistry; |
| import org.apache.directory.server.schema.registries.OidRegistry; |
| import org.apache.directory.server.schema.registries.Registries; |
| import org.apache.directory.server.xdbm.Index; |
| import org.apache.directory.server.xdbm.IndexCursor; |
| import org.apache.directory.server.xdbm.IndexEntry; |
| import org.apache.directory.server.xdbm.IndexNotFoundException; |
| import org.apache.directory.server.xdbm.LongComparator; |
| import org.apache.directory.server.xdbm.Store; |
| import org.apache.directory.shared.ldap.constants.SchemaConstants; |
| import org.apache.directory.shared.ldap.entry.EntryAttribute; |
| import org.apache.directory.shared.ldap.entry.Modification; |
| import org.apache.directory.shared.ldap.entry.ModificationOperation; |
| import org.apache.directory.shared.ldap.entry.Value; |
| import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException; |
| import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException; |
| import org.apache.directory.shared.ldap.message.ResultCodeEnum; |
| import org.apache.directory.shared.ldap.name.AttributeTypeAndValue; |
| import org.apache.directory.shared.ldap.name.LdapDN; |
| import org.apache.directory.shared.ldap.name.Rdn; |
| import org.apache.directory.shared.ldap.schema.AttributeType; |
| import org.apache.directory.shared.ldap.schema.MatchingRule; |
| import org.apache.directory.shared.ldap.util.NamespaceTools; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * A Store implementation backed by in memory AVL trees. |
| * |
| * TODO - this class is extremely like the JdbmStore implementation of the |
| * Store interface which tells us that it's best for us to have some kind |
| * of abstract class. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @version $Rev$, $Date$ |
| */ |
| public class AvlStore<E> implements Store<E> |
| { |
| /** static logger */ |
| private static final Logger LOG = LoggerFactory.getLogger( AvlStore.class ); |
| |
| private OidRegistry oidRegistry; |
| private AttributeTypeRegistry attributeTypeRegistry; |
| |
| /** Two static declaration to avoid lookup all over the code */ |
| private static AttributeType OBJECT_CLASS_AT; |
| private static AttributeType ALIASED_OBJECT_NAME_AT; |
| |
| /** the master table storing entries by primary key */ |
| private AvlMasterTable<ServerEntry> master; |
| |
| /** the normalized distinguished name index */ |
| private AvlIndex<String,E> ndnIdx; |
| /** the user provided distinguished name index */ |
| private AvlIndex<String,E> updnIdx; |
| /** the attribute existence index */ |
| private AvlIndex<String,E> existenceIdx; |
| /** a system index on aliasedObjectName attribute */ |
| private AvlIndex<String,E> aliasIdx; |
| /** a system index on the entries of descendants of root DN*/ |
| private AvlIndex<Long,E> subLevelIdx; |
| /** the parent child relationship index */ |
| private AvlIndex<Long,E> oneLevelIdx; |
| /** the one level scope alias index */ |
| private AvlIndex<Long,E> oneAliasIdx; |
| /** the subtree scope alias index */ |
| private AvlIndex<Long,E> subAliasIdx; |
| |
| /** a map of attributeType numeric ID to user userIndices */ |
| private Map<String, AvlIndex<?,E>> userIndices |
| = new HashMap<String, AvlIndex<?,E>>(); |
| |
| /** a map of attributeType numeric ID to system userIndices */ |
| private Map<String, AvlIndex<?,E>> systemIndices |
| = new HashMap<String, AvlIndex<?,E>>(); |
| |
| /** true if initialized */ |
| private boolean initialized; |
| |
| /** |
| * TODO we need to check out why we have so many suffix |
| * dn and string accessor/mutators on both Store and Partition |
| * interfaces. I think a lot of this comes from the fact |
| * that we implemented LdapDN to have both the up and norm |
| * names. |
| */ |
| private LdapDN suffixDn; |
| |
| private String name; |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @SuppressWarnings("unchecked") |
| public void add( LdapDN normName, ServerEntry entry ) throws Exception |
| { |
| if ( entry instanceof ClonedServerEntry ) |
| { |
| throw new Exception( "Cannot store a ClonedServerEntry" ); |
| } |
| |
| Long id; |
| Long parentId; |
| |
| id = master.getNextId(); |
| |
| // |
| // Suffix entry cannot have a parent since it is the root so it is |
| // capped off using the zero value which no entry can have since |
| // entry sequences start at 1. |
| // |
| |
| LdapDN parentDn = null; |
| |
| if ( normName.getNormName().equals( suffixDn.getNormName() ) ) |
| { |
| parentId = 0L; |
| } |
| else |
| { |
| parentDn = ( LdapDN ) normName.clone(); |
| parentDn.remove( parentDn.size() - 1 ); |
| parentId = getEntryId( parentDn.toString() ); |
| } |
| |
| // don't keep going if we cannot find the parent Id |
| if ( parentId == null ) |
| { |
| throw new LdapNameNotFoundException( "Id for parent '" + parentDn + "' not found!" ); |
| } |
| |
| EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT ); |
| |
| if ( objectClass == null ) |
| { |
| String msg = "Entry " + normName.getUpName() + " contains no objectClass attribute: " + entry; |
| throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION ); |
| } |
| |
| // Start adding the system userIndices |
| // Why bother doing a lookup if this is not an alias. |
| |
| if ( objectClass.contains( SchemaConstants.ALIAS_OC ) ) |
| { |
| EntryAttribute aliasAttr = entry.get( ALIASED_OBJECT_NAME_AT ); |
| addAliasIndices( id, normName, aliasAttr.getString() ); |
| } |
| |
| if ( !Character.isDigit( normName.toNormName().charAt( 0 ) ) ) |
| { |
| throw new IllegalStateException( "Not a normalized name: " + normName.toNormName() ); |
| } |
| |
| ndnIdx.add( normName.toNormName(), id ); |
| updnIdx.add( normName.getUpName(), id ); |
| oneLevelIdx.add( parentId, id ); |
| |
| Long tempId = parentId; |
| while( tempId != null && tempId != 0 && tempId != 1 ) |
| { |
| subLevelIdx.add( tempId, id ); |
| tempId = getParentId( tempId ); |
| } |
| subLevelIdx.add( id, id ); |
| |
| // Now work on the user defined userIndices |
| for ( EntryAttribute attribute : entry ) |
| { |
| String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid(); |
| |
| if ( hasUserIndexOn( attributeOid ) ) |
| { |
| Index<Object,E> idx = ( Index<Object,E> ) getUserIndex( attributeOid ); |
| |
| // here lookup by attributeId is OK since we got attributeId from |
| // the entry via the enumeration - it's in there as is for sure |
| |
| for ( Value<?> value : attribute ) |
| { |
| idx.add( value.get(), id ); |
| } |
| |
| // Adds only those attributes that are indexed |
| existenceIdx.add( attributeOid, id ); |
| } |
| } |
| |
| master.put( id, entry ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void addIndex( Index<?, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| userIndices.put( index.getAttributeId(), ( AvlIndex<?,E> ) index ); |
| } |
| else |
| { |
| userIndices.put( index.getAttributeId(), ( AvlIndex<?,E> ) convert( index ) ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public int count() throws Exception |
| { |
| return master.count(); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @SuppressWarnings("unchecked") |
| public void delete( Long id ) throws Exception |
| { |
| ServerEntry entry = lookup( id ); |
| Long parentId = getParentId( id ); |
| |
| EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT ); |
| |
| if ( objectClass.contains( SchemaConstants.ALIAS_OC ) ) |
| { |
| dropAliasIndices( id ); |
| } |
| |
| ndnIdx.drop( id ); |
| updnIdx.drop( id ); |
| oneLevelIdx.drop( id ); |
| |
| if( parentId != 1 )// should not use getParentId() to compare, onelevel index drops the 'id' |
| { |
| subLevelIdx.drop( id ); |
| } |
| |
| // Remove parent's reference to entry only if entry is not the upSuffix |
| if ( !parentId.equals( 0L ) ) |
| { |
| oneLevelIdx.drop( parentId, id ); |
| } |
| |
| for ( EntryAttribute attribute : entry ) |
| { |
| String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid(); |
| |
| if ( hasUserIndexOn( attributeOid ) ) |
| { |
| Index<?,E> index = getUserIndex( attributeOid ); |
| |
| // here lookup by attributeId is ok since we got attributeId from |
| // the entry via the enumeration - it's in there as is for sure |
| for ( Value<?> value : attribute ) |
| { |
| ( ( AvlIndex ) index ).drop( value.get(), id ); |
| } |
| |
| existenceIdx.drop( attributeOid, id ); |
| } |
| } |
| |
| master.delete( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void destroy() throws Exception |
| { |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<String, E> getAliasIndex() |
| { |
| return aliasIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public int getChildCount( Long id ) throws Exception |
| { |
| return oneLevelIdx.count( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getEntryDn( Long id ) throws Exception |
| { |
| return ndnIdx.reverseLookup( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Long getEntryId( String dn ) throws Exception |
| { |
| return ndnIdx.forwardLookup( dn ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getEntryUpdn( Long id ) throws Exception |
| { |
| return updnIdx.reverseLookup( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getEntryUpdn( String dn ) throws Exception |
| { |
| Long id = ndnIdx.forwardLookup( dn ); |
| return updnIdx.reverseLookup( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getName() |
| { |
| return name; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<String, E> getNdnIndex() |
| { |
| return ndnIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<Long, E> getOneAliasIndex() |
| { |
| return oneAliasIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<Long, E> getOneLevelIndex() |
| { |
| return oneLevelIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Long getParentId( String dn ) throws Exception |
| { |
| Long childId = ndnIdx.forwardLookup( dn ); |
| return oneLevelIdx.reverseLookup( childId ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Long getParentId( Long childId ) throws Exception |
| { |
| return oneLevelIdx.reverseLookup( childId ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<String, E> getPresenceIndex() |
| { |
| return existenceIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getProperty( String propertyName ) throws Exception |
| { |
| return master.getProperty( propertyName ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<Long, E> getSubAliasIndex() |
| { |
| return subAliasIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<Long, E> getSubLevelIndex() |
| { |
| return subLevelIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LdapDN getNormSuffixDn() |
| { |
| return suffixDn; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getUpSuffixString() |
| { |
| return suffixDn.getUpName(); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<?, E> getSystemIndex( String id ) throws IndexNotFoundException |
| { |
| return systemIndices.get( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LdapDN getUpSuffixDn() throws Exception |
| { |
| // TODO fix this |
| return new LdapDN( suffixDn.getUpName() ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<String, E> getUpdnIndex() |
| { |
| return updnIdx; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Index<?, E> getUserIndex( String id ) throws IndexNotFoundException |
| { |
| return userIndices.get( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Set<? extends Index<?, E>> getUserIndices() |
| { |
| return new HashSet<Index<?,E>>( userIndices.values() ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean hasSystemIndexOn( String id ) throws Exception |
| { |
| return systemIndices.containsKey( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean hasUserIndexOn( String id ) throws Exception |
| { |
| return userIndices.containsKey( id ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| * TODO why this and initRegistries on Store interface ??? |
| */ |
| public void initialize( Registries registries ) throws Exception |
| { |
| initRegistries( registries ); |
| |
| OBJECT_CLASS_AT = attributeTypeRegistry.lookup( SchemaConstants.OBJECT_CLASS_AT ); |
| ALIASED_OBJECT_NAME_AT = attributeTypeRegistry.lookup( SchemaConstants.ALIASED_OBJECT_NAME_AT ); |
| |
| |
| // Create the master table (the table containing all the entries) |
| master = new AvlMasterTable<ServerEntry>( name, new LongComparator(), null, false ); |
| |
| // ------------------------------------------------------------------- |
| // Initializes the user and system indices |
| // ------------------------------------------------------------------- |
| |
| setupSystemIndices(); |
| setupUserIndices(); |
| |
| // We are done ! |
| initialized = true; |
| } |
| |
| |
| private void setupSystemIndices() throws Exception |
| { |
| // let's check and make sure the supplied indices are OK |
| |
| if ( ndnIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_N_DN_OID ); |
| ndnIdx = new AvlIndex<String,E>(); |
| ndnIdx.setAttributeId( ApacheSchemaConstants.APACHE_N_DN_OID ); |
| ndnIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_N_DN_OID, ndnIdx ); |
| } |
| |
| if ( updnIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_UP_DN_OID ); |
| updnIdx = new AvlIndex<String,E>(); |
| updnIdx.setAttributeId( ApacheSchemaConstants.APACHE_UP_DN_OID ); |
| updnIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_UP_DN_OID, updnIdx ); |
| } |
| |
| if ( existenceIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_EXISTANCE_OID ); |
| existenceIdx = new AvlIndex<String,E>(); |
| existenceIdx.setAttributeId( ApacheSchemaConstants.APACHE_EXISTANCE_OID ); |
| existenceIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_EXISTANCE_OID, existenceIdx ); |
| } |
| |
| if ( oneLevelIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_ONE_LEVEL_OID ); |
| oneLevelIdx = new AvlIndex<Long,E>(); |
| oneLevelIdx.setAttributeId( ApacheSchemaConstants.APACHE_ONE_LEVEL_OID ); |
| oneLevelIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_ONE_LEVEL_OID, oneLevelIdx ); |
| } |
| |
| if ( oneAliasIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_ONE_ALIAS_OID ); |
| oneAliasIdx = new AvlIndex<Long,E>(); |
| oneAliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_ONE_ALIAS_OID ); |
| oneAliasIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_ONE_ALIAS_OID, oneAliasIdx ); |
| } |
| |
| if ( subAliasIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_SUB_ALIAS_OID ); |
| subAliasIdx = new AvlIndex<Long,E>(); |
| subAliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_SUB_ALIAS_OID ); |
| subAliasIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_SUB_ALIAS_OID, subAliasIdx ); |
| } |
| |
| if ( aliasIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( ApacheSchemaConstants.APACHE_ALIAS_OID ); |
| aliasIdx = new AvlIndex<String,E>(); |
| aliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_ALIAS_OID ); |
| aliasIdx.initialize( attributeType ); |
| systemIndices.put( ApacheSchemaConstants.APACHE_ALIAS_OID, aliasIdx ); |
| } |
| |
| if ( subLevelIdx == null ) |
| { |
| AttributeType attributeType = attributeTypeRegistry.lookup( SUBLEVEL ); |
| subLevelIdx = new AvlIndex<Long, E>(); |
| subLevelIdx.setAttributeId( SUBLEVEL ); |
| subLevelIdx.initialize( attributeType ); |
| systemIndices.put( SUBLEVEL, subLevelIdx ); |
| } |
| } |
| |
| |
| private void setupUserIndices() throws Exception |
| { |
| if ( userIndices != null && userIndices.size() > 0 ) |
| { |
| Map<String, AvlIndex<?,E>> tmp = new HashMap<String, AvlIndex<?,E>>(); |
| |
| for ( AvlIndex<?,E> index : userIndices.values() ) |
| { |
| String oid = oidRegistry.getOid( index.getAttributeId() ); |
| AttributeType attributeType = attributeTypeRegistry.lookup( oid ); |
| |
| // Check that the attributeType has an EQUALITY matchingRule |
| MatchingRule mr = attributeType.getEquality(); |
| |
| if ( mr != null ) |
| { |
| index.initialize( attributeTypeRegistry.lookup( oid ) ); |
| tmp.put( oid, index ); |
| } |
| else |
| { |
| LOG.error( "Cannot build an index for attribute '{}', no EQUALITY MatchingRule defined", |
| attributeType.getName() ); |
| } |
| } |
| |
| userIndices = tmp; |
| } |
| else |
| { |
| userIndices = new HashMap<String, AvlIndex<?,E>>(); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void initRegistries( Registries registries ) |
| { |
| this.attributeTypeRegistry = registries.getAttributeTypeRegistry(); |
| this.oidRegistry = registries.getOidRegistry(); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean isInitialized() |
| { |
| return initialized; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public IndexCursor<Long, E> list( Long id ) throws Exception |
| { |
| IndexCursor<Long,E> cursor = oneLevelIdx.forwardCursor( id ); |
| cursor.beforeValue( id, null ); |
| return cursor; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public ServerEntry lookup( Long id ) throws Exception |
| { |
| return master.get( id ); |
| } |
| |
| |
| /** |
| * Recursively modifies the distinguished name of an entry and the names of |
| * its descendants calling itself in the recursion. |
| * |
| * @param id the primary key of the entry |
| * @param updn User provided distinguished name to set as the new DN |
| * @param isMove whether or not the name change is due to a move operation |
| * which affects alias userIndices. |
| * @throws NamingException if something goes wrong |
| */ |
| private void modifyDn( Long id, LdapDN updn, boolean isMove ) throws Exception |
| { |
| String aliasTarget; |
| |
| // update normalized DN index |
| ndnIdx.drop( id ); |
| if ( !updn.isNormalized() ) |
| { |
| updn.normalize( attributeTypeRegistry.getNormalizerMapping() ); |
| } |
| ndnIdx.add( updn.toNormName(), id ); |
| |
| // update user provided DN index |
| updnIdx.drop( id ); |
| updnIdx.add( updn.getUpName(), id ); |
| |
| /* |
| * Read Alias Index Tuples |
| * |
| * If this is a name change due to a move operation then the one and |
| * subtree userIndices for aliases were purged before the aliases were |
| * moved. Now we must add them for each alias entry we have moved. |
| * |
| * aliasTarget is used as a marker to tell us if we're moving an |
| * alias. If it is null then the moved entry is not an alias. |
| */ |
| if ( isMove ) |
| { |
| aliasTarget = aliasIdx.reverseLookup( id ); |
| |
| if ( null != aliasTarget ) |
| { |
| addAliasIndices( id, new LdapDN( getEntryDn( id ) ), aliasTarget ); |
| } |
| } |
| |
| Cursor<IndexEntry<Long,E>> children = list( id ); |
| while ( children.next() ) |
| { |
| // Get the child and its id |
| IndexEntry<Long,E> rec = children.get(); |
| Long childId = rec.getId(); |
| |
| /* |
| * Calculate the DN for the child's new name by copying the parents |
| * new name and adding the child's old upRdn to new name as its Rdn |
| */ |
| LdapDN childUpdn = ( LdapDN ) updn.clone(); |
| LdapDN oldUpdn = new LdapDN( getEntryUpdn( childId ) ); |
| |
| String rdn = oldUpdn.get( oldUpdn.size() - 1 ); |
| LdapDN rdnDN = new LdapDN( rdn ); |
| rdnDN.normalize( attributeTypeRegistry.getNormalizerMapping() ); |
| childUpdn.add( rdnDN.getRdn() ); |
| |
| // Modify the child |
| ServerEntry entry = lookup( childId ); |
| entry.setDn( childUpdn ); |
| master.put( childId, entry ); |
| |
| // Recursively change the names of the children below |
| modifyDn( childId, childUpdn, isMove ); |
| } |
| |
| children.close(); |
| } |
| |
| |
| /** |
| * Adds a set of attribute values while affecting the appropriate userIndices. |
| * The entry is not persisted: it is only changed in anticipation for a put |
| * into the master table. |
| * |
| * @param id the primary key of the entry |
| * @param entry the entry to alter |
| * @param mods the attribute and values to add |
| * @throws Exception if index alteration or attribute addition fails |
| */ |
| @SuppressWarnings("unchecked") |
| private void add( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception |
| { |
| if ( entry instanceof ClonedServerEntry ) |
| { |
| throw new Exception( "Cannot store a ClonedServerEntry" ); |
| } |
| |
| String modsOid = oidRegistry.getOid( mods.getId() ); |
| |
| if ( hasUserIndexOn( modsOid ) ) |
| { |
| Index<?,E> index = getUserIndex( modsOid ); |
| |
| for ( Value<?> value : mods ) |
| { |
| ( ( AvlIndex ) index ).add( value.get(), id ); |
| } |
| |
| // If the attr didn't exist for this id add it to existence index |
| if ( !existenceIdx.forward( modsOid, id ) ) |
| { |
| existenceIdx.add( modsOid, id ); |
| } |
| } |
| |
| // add all the values in mods to the same attribute in the entry |
| AttributeType type = attributeTypeRegistry.lookup( modsOid ); |
| |
| for ( Value<?> value : mods ) |
| { |
| entry.add( type, value ); |
| } |
| |
| if ( modsOid.equals( oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT ) ) ) |
| { |
| String ndnStr = ndnIdx.reverseLookup( id ); |
| addAliasIndices( id, new LdapDN( ndnStr ), mods.getString() ); |
| } |
| } |
| |
| |
| /** |
| * Completely removes the set of values for an attribute having the values |
| * supplied while affecting the appropriate userIndices. The entry is not |
| * persisted: it is only changed in anticipation for a put into the master |
| * table. Note that an empty attribute w/o values will remove all the |
| * values within the entry where as an attribute w/ values will remove those |
| * attribute values it contains. |
| * |
| * @param id the primary key of the entry |
| * @param entry the entry to alter |
| * @param mods the attribute and its values to delete |
| * @throws Exception if index alteration or attribute modification fails. |
| */ |
| @SuppressWarnings("unchecked") |
| private void remove( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception |
| { |
| if ( entry instanceof ClonedServerEntry ) |
| { |
| throw new Exception( "Cannot store a ClonedServerEntry" ); |
| } |
| |
| String modsOid = oidRegistry.getOid( mods.getId() ); |
| |
| if ( hasUserIndexOn( modsOid ) ) |
| { |
| Index<?,E> index = getUserIndex( modsOid ); |
| |
| for ( Value<?> value : mods ) |
| { |
| ( ( AvlIndex ) index ).drop( value.get(), id ); |
| } |
| |
| /* |
| * If no attribute values exist for this entryId in the index then |
| * we remove the existance index entry for the removed attribute. |
| */ |
| if ( null == index.reverseLookup( id ) ) |
| { |
| existenceIdx.drop( modsOid, id ); |
| } |
| } |
| |
| AttributeType attrType = attributeTypeRegistry.lookup( modsOid ); |
| /* |
| * If there are no attribute values in the modifications then this |
| * implies the compelete removal of the attribute from the entry. Else |
| * we remove individual attribute values from the entry in mods one |
| * at a time. |
| */ |
| if ( mods.size() == 0 ) |
| { |
| entry.removeAttributes( attrType ); |
| } |
| else |
| { |
| EntryAttribute entryAttr = entry.get( attrType ); |
| |
| for ( Value<?> value : mods ) |
| { |
| if ( value instanceof ServerStringValue ) |
| { |
| entryAttr.remove( ( String ) value.get() ); |
| } |
| else |
| { |
| entryAttr.remove( ( byte[] ) value.get() ); |
| } |
| } |
| |
| // if nothing is left just remove empty attribute |
| if ( entryAttr.size() == 0 ) |
| { |
| entry.removeAttributes( entryAttr.getId() ); |
| } |
| } |
| |
| // Aliases->single valued comp/partial attr removal is not relevant here |
| if ( modsOid.equals( oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT ) ) ) |
| { |
| dropAliasIndices( id ); |
| } |
| } |
| |
| |
| /** |
| * Completely replaces the existing set of values for an attribute with the |
| * modified values supplied affecting the appropriate userIndices. The entry |
| * is not persisted: it is only changed in anticipation for a put into the |
| * master table. |
| * |
| * @param id the primary key of the entry |
| * @param entry the entry to alter |
| * @param mods the replacement attribute and values |
| * @throws Exception if index alteration or attribute modification |
| * fails. |
| */ |
| @SuppressWarnings("unchecked") |
| private void replace( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception |
| { |
| if ( entry instanceof ClonedServerEntry ) |
| { |
| throw new Exception( "Cannot store a ClonedServerEntry" ); |
| } |
| |
| String modsOid = oidRegistry.getOid( mods.getId() ); |
| |
| if ( hasUserIndexOn( modsOid ) ) |
| { |
| Index<?,E> index = getUserIndex( modsOid ); |
| |
| // if the id exists in the index drop all existing attribute value index entries and add new ones |
| if( index.reverse( id ) ) |
| { |
| ( ( AvlIndex<?,E> ) index ).drop( id ); |
| } |
| |
| for ( Value<?> value : mods ) |
| { |
| ( ( AvlIndex<Object,E> ) index ).add( value.get(), id ); |
| } |
| |
| /* |
| * If no attribute values exist for this entryId in the index then |
| * we remove the existance index entry for the removed attribute. |
| */ |
| if ( null == index.reverseLookup( id ) ) |
| { |
| existenceIdx.drop( modsOid, id ); |
| } |
| } |
| |
| String aliasAttributeOid = oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT ); |
| |
| if ( modsOid.equals( aliasAttributeOid ) ) |
| { |
| dropAliasIndices( id ); |
| } |
| |
| // replaces old attributes with new modified ones if they exist |
| if ( mods.size() > 0 ) |
| { |
| entry.put( mods ); |
| } |
| else |
| // removes old attributes if new replacements do not exist |
| { |
| entry.remove( mods ); |
| } |
| |
| if ( modsOid.equals( aliasAttributeOid ) && mods.size() > 0 ) |
| { |
| String ndnStr = ndnIdx.reverseLookup( id ); |
| addAliasIndices( id, new LdapDN( ndnStr ), mods.getString() ); |
| } |
| } |
| |
| |
| public void modify( LdapDN dn, ModificationOperation modOp, ServerEntry mods ) throws Exception |
| { |
| if ( mods instanceof ClonedServerEntry ) |
| { |
| throw new Exception( "Cannot store a ClonedServerEntry" ); |
| } |
| |
| Long id = getEntryId( dn.toString() ); |
| ServerEntry entry = ( ServerEntry ) master.get( id ); |
| |
| for ( AttributeType attributeType : mods.getAttributeTypes() ) |
| { |
| EntryAttribute attr = mods.get( attributeType ); |
| |
| switch ( modOp ) |
| { |
| case ADD_ATTRIBUTE: |
| add( id, entry, attr ); |
| break; |
| |
| case REMOVE_ATTRIBUTE: |
| remove( id, entry, attr ); |
| break; |
| |
| case REPLACE_ATTRIBUTE: |
| replace( id, entry, attr ); |
| |
| break; |
| |
| default: |
| throw new Exception( "Unidentified modification operation" ); |
| } |
| } |
| |
| master.put( id, entry ); |
| } |
| |
| |
| public void modify( LdapDN dn, List<Modification> mods ) throws Exception |
| { |
| Long id = getEntryId( dn.toString() ); |
| ServerEntry entry = ( ServerEntry ) master.get( id ); |
| |
| for ( Modification mod : mods ) |
| { |
| ServerAttribute attrMods = ( ServerAttribute ) mod.getAttribute(); |
| |
| switch ( mod.getOperation() ) |
| { |
| case ADD_ATTRIBUTE: |
| add( id, entry, attrMods ); |
| break; |
| |
| case REMOVE_ATTRIBUTE: |
| remove( id, entry, attrMods ); |
| break; |
| |
| case REPLACE_ATTRIBUTE: |
| replace( id, entry, attrMods ); |
| break; |
| |
| default: |
| throw new Exception( "Unidentified modification operation" ); |
| } |
| } |
| |
| master.put( id, entry ); |
| } |
| |
| |
| public void move( LdapDN oldChildDn, LdapDN newParentDn, Rdn newRdn, boolean deleteOldRdn ) throws Exception |
| { |
| Long childId = getEntryId( oldChildDn.toString() ); |
| rename( oldChildDn, newRdn, deleteOldRdn ); |
| LdapDN newUpdn = move( oldChildDn, childId, newParentDn ); |
| |
| // Update the current entry |
| ServerEntry entry = lookup( childId ); |
| entry.setDn( newUpdn ); |
| master.put( childId, entry ); |
| } |
| |
| |
| public void move( LdapDN oldChildDn, LdapDN newParentDn ) throws Exception |
| { |
| Long childId = getEntryId( oldChildDn.toString() ); |
| LdapDN newUpdn = move( oldChildDn, childId, newParentDn ); |
| |
| // Update the current entry |
| ServerEntry entry = lookup( childId ); |
| entry.setDn( newUpdn ); |
| master.put( childId, entry ); |
| } |
| |
| |
| /** |
| * Moves an entry under a new parent. The operation causes a shift in the |
| * parent child relationships between the old parent, new parent and the |
| * child moved. All other descendant entries under the child never change |
| * their direct parent child relationships. Hence after the parent child |
| * relationship changes are broken at the old parent and set at the new |
| * parent a modifyDn operation is conducted to handle name changes |
| * propagating down through the moved child and its descendants. |
| * |
| * @param oldChildDn the normalized dn of the child to be moved |
| * @param childId the id of the child being moved |
| * @param newParentDn the normalized dn of the new parent for the child |
| * @throws NamingException if something goes wrong |
| */ |
| private LdapDN move( LdapDN oldChildDn, Long childId, LdapDN newParentDn ) throws Exception |
| { |
| // Get the child and the new parent to be entries and Ids |
| Long newParentId = getEntryId( newParentDn.toString() ); |
| Long oldParentId = getParentId( childId ); |
| |
| /* |
| * All aliases including and below oldChildDn, will be affected by |
| * the move operation with respect to one and subtree userIndices since |
| * their relationship to ancestors above oldChildDn will be |
| * destroyed. For each alias below and including oldChildDn we will |
| * drop the index tuples mapping ancestor ids above oldChildDn to the |
| * respective target ids of the aliases. |
| */ |
| dropMovedAliasIndices( oldChildDn ); |
| |
| /* |
| * Drop the old parent child relationship and add the new one |
| * Set the new parent id for the child replacing the old parent id |
| */ |
| oneLevelIdx.drop( oldParentId, childId ); |
| oneLevelIdx.add( newParentId, childId ); |
| |
| updateSubLevelIndex( childId, oldParentId, newParentId ); |
| |
| /* |
| * Build the new user provided DN (updn) for the child using the child's |
| * user provided RDN & the new parent's UPDN. Basically add the child's |
| * UpRdn String to the tail of the new parent's Updn Name. |
| */ |
| LdapDN childUpdn = new LdapDN( getEntryUpdn( childId ) ); |
| String childRdn = childUpdn.get( childUpdn.size() - 1 ); |
| LdapDN newUpdn = new LdapDN( getEntryUpdn( newParentId ) ); |
| newUpdn.add( newUpdn.size(), childRdn ); |
| |
| // Call the modifyDn operation with the new updn |
| modifyDn( childId, newUpdn, true ); |
| |
| return newUpdn; |
| } |
| |
| |
| /** |
| * Changes the relative distinguished name of an entry specified by a |
| * distinguished name with the optional removal of the old Rdn attribute |
| * value from the entry. Name changes propagate down as dn changes to the |
| * descendants of the entry where the Rdn changed. |
| * |
| * An Rdn change operation does not change parent child relationships. It |
| * merely propagates a name change at a point in the DIT where the Rdn is |
| * changed. The change propagates down the subtree rooted at the |
| * distinguished name specified. |
| * |
| * @param dn the normalized distinguished name of the entry to alter |
| * @param newRdn the new Rdn to set |
| * @param deleteOldRdn whether or not to remove the old Rdn attr/val |
| * @throws Exception if there are any errors propagating the name changes |
| */ |
| @SuppressWarnings("unchecked") |
| public void rename( LdapDN dn, Rdn newRdn, boolean deleteOldRdn ) throws Exception |
| { |
| Long id = getEntryId( dn.getNormName() ); |
| ServerEntry entry = lookup( id ); |
| LdapDN updn = entry.getDn(); |
| |
| /* |
| * H A N D L E N E W R D N |
| * ==================================================================== |
| * Add the new Rdn attribute to the entry. If an index exists on the |
| * new Rdn attribute we add the index for this attribute value pair. |
| * Also we make sure that the existance index shows the existance of the |
| * new Rdn attribute within this entry. |
| */ |
| |
| for ( AttributeTypeAndValue newAtav : newRdn ) |
| { |
| String newNormType = newAtav.getNormType(); |
| String newNormValue = ( String ) newAtav.getNormValue(); |
| AttributeType newRdnAttrType = attributeTypeRegistry.lookup( newNormType ); |
| |
| Object unEscapedRdn = Rdn.unescapeValue( (String)newAtav.getUpValue() ); |
| |
| Value<?> value = null; |
| |
| if ( unEscapedRdn instanceof String ) |
| { |
| value = new ServerStringValue( newRdnAttrType, (String)unEscapedRdn ); |
| } |
| else |
| { |
| value = new ServerBinaryValue( newRdnAttrType, (byte[])unEscapedRdn ); |
| } |
| |
| value.normalize(); |
| |
| entry.add( newRdnAttrType, value ); |
| |
| if ( hasUserIndexOn( newNormType ) ) |
| { |
| Index<?, E> index = getUserIndex( newNormType ); |
| ( ( Index ) index ).add( newNormValue, id ); |
| |
| // Make sure the altered entry shows the existence of the new attrib |
| if ( !existenceIdx.forward( newNormType, id ) ) |
| { |
| existenceIdx.add( newNormType, id ); |
| } |
| } |
| } |
| |
| /* |
| * H A N D L E O L D R D N |
| * ==================================================================== |
| * If the old Rdn is to be removed we need to get the attribute and |
| * value for it. Keep in mind the old Rdn need not be based on the |
| * same attr as the new one. We remove the Rdn value from the entry |
| * and remove the value/id tuple from the index on the old Rdn attr |
| * if any. We also test if the delete of the old Rdn index tuple |
| * removed all the attribute values of the old Rdn using a reverse |
| * lookup. If so that means we blew away the last value of the old |
| * Rdn attribute. In this case we need to remove the attrName/id |
| * tuple from the existance index. |
| * |
| * We only remove an ATAV of the old Rdn if it is not included in the |
| * new Rdn. |
| */ |
| |
| if ( deleteOldRdn ) |
| { |
| Rdn oldRdn = updn.getRdn(); |
| for ( AttributeTypeAndValue oldAtav : oldRdn ) |
| { |
| // check if the new ATAV is part of the old Rdn |
| // if that is the case we do not remove the ATAV |
| boolean mustRemove = true; |
| for ( AttributeTypeAndValue newAtav : newRdn ) |
| { |
| if ( oldAtav.equals( newAtav ) ) |
| { |
| mustRemove = false; |
| break; |
| } |
| } |
| |
| if ( mustRemove ) |
| { |
| String oldNormType = oldAtav.getNormType(); |
| String oldNormValue = ( String ) oldAtav.getNormValue(); |
| AttributeType oldRdnAttrType = attributeTypeRegistry.lookup( oldNormType ); |
| entry.remove( oldRdnAttrType, oldNormValue ); |
| |
| if ( hasUserIndexOn( oldNormType ) ) |
| { |
| Index<?, E> index = getUserIndex( oldNormType ); |
| ( ( AvlIndex ) index ).drop( oldNormValue, id ); |
| |
| /* |
| * If there is no value for id in this index due to our |
| * drop above we remove the oldRdnAttr from the existance idx |
| */ |
| if ( null == index.reverseLookup( id ) ) |
| { |
| existenceIdx.drop( oldNormType, id ); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * H A N D L E D N C H A N G E |
| * ==================================================================== |
| * 1) Build the new user defined distinguished name |
| * - clone / copy old updn |
| * - remove old upRdn from copy |
| * - add the new upRdn to the copy |
| * 2) Make call to recursive modifyDn method to change the names of the |
| * entry and its descendants |
| */ |
| |
| LdapDN newUpdn = ( LdapDN ) updn.clone(); // copy da old updn |
| newUpdn.remove( newUpdn.size() - 1 ); // remove old upRdn |
| newUpdn.add( newRdn.getUpName() ); // add da new upRdn |
| |
| // gotta normalize cuz this thang is cloned and not normalized by default |
| newUpdn.normalize( attributeTypeRegistry.getNormalizerMapping() ); |
| |
| modifyDn( id, newUpdn, false ); // propagate dn changes |
| |
| // Update the current entry |
| entry.setDn( newUpdn ); |
| master.put( id, entry ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setAliasIndex( Index<String, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.aliasIdx = ( AvlIndex<String, E> ) index; |
| } |
| else |
| { |
| this.aliasIdx = ( AvlIndex<String, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setName( String name ) |
| { |
| this.name = name; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setNdnIndex( Index<String, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.ndnIdx = ( AvlIndex<String, E> ) index; |
| } |
| else |
| { |
| this.ndnIdx = ( AvlIndex<String, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setOneAliasIndex( Index<Long, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.oneAliasIdx = ( AvlIndex<Long, E> ) index; |
| } |
| else |
| { |
| this.oneAliasIdx = ( AvlIndex<Long, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setOneLevelIndex( Index<Long, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.oneLevelIdx = ( AvlIndex<Long, E> ) index; |
| } |
| else |
| { |
| this.oneLevelIdx = ( AvlIndex<Long, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setPresenceIndex( Index<String, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.existenceIdx = ( AvlIndex<String, E> ) index; |
| } |
| else |
| { |
| this.existenceIdx = ( AvlIndex<String, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setProperty( String propertyName, String propertyValue ) throws Exception |
| { |
| master.setProperty( propertyName, propertyValue ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setSubAliasIndex( Index<Long, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.subAliasIdx = ( AvlIndex<Long, E> ) index; |
| } |
| else |
| { |
| this.subAliasIdx = ( AvlIndex<Long, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setSubLevelIndex( Index<Long, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.subLevelIdx = ( AvlIndex<Long, E> ) index; |
| } |
| else |
| { |
| this.subLevelIdx = ( AvlIndex<Long, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setUpSuffixString( String suffixDn ) throws Exception |
| { |
| // TODO fix this |
| this.suffixDn = new LdapDN( suffixDn ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setUpdnIndex( Index<String, E> index ) throws Exception |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.updnIdx = ( AvlIndex<String, E> ) index; |
| } |
| else |
| { |
| this.updnIdx = ( AvlIndex<String, E> ) convert( index ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setUserIndices( Set<? extends Index<?, E>> userIndices ) throws Exception |
| { |
| protect( "setUserIndices" ); |
| |
| for ( Index<?, E> index : userIndices ) |
| { |
| if ( index instanceof AvlIndex ) |
| { |
| this.userIndices.put( index.getAttribute().getOid(), ( AvlIndex<?,E> ) index ); |
| continue; |
| } |
| |
| LOG.warn( "Supplied index {} is not a AvlIndex. " + |
| "Will create new AvlIndex using copied configuration parameters.", index ); |
| |
| AvlIndex<?,E> avlIndex = ( AvlIndex<?, E> ) convert( index ); |
| |
| this.userIndices.put( index.getAttributeId(), avlIndex ); |
| } |
| } |
| |
| |
| private <K> Index<K,E> convert( Index<K,E> index ) throws Exception |
| { |
| AvlIndex<K, E> avlIndex = new AvlIndex<K, E>(); |
| avlIndex.setAttributeId( index.getAttributeId() ); |
| return avlIndex; |
| } |
| |
| |
| private void protect( String method ) |
| { |
| if ( initialized ) |
| { |
| throw new IllegalStateException( "Cannot call store method: " + method ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Iterator<String> systemIndices() |
| { |
| return systemIndices.keySet().iterator(); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Iterator<String> userIndices() |
| { |
| return userIndices.keySet().iterator(); |
| } |
| |
| |
| /** |
| * Adds userIndices for an aliasEntry to be added to the database while checking |
| * for constrained alias constructs like alias cycles and chaining. |
| * |
| * @param aliasDn normalized distinguished name for the alias entry |
| * @param aliasTarget the user provided aliased entry dn as a string |
| * @param aliasId the id of alias entry to add |
| * @throws NamingException if index addition fails, and if the alias is |
| * not allowed due to chaining or cycle formation. |
| * @throws Exception if the wrappedCursor btrees cannot be altered |
| */ |
| private void addAliasIndices( Long aliasId, LdapDN aliasDn, String aliasTarget ) throws Exception |
| { |
| LdapDN normalizedAliasTargetDn; // Name value of aliasedObjectName |
| Long targetId; // Id of the aliasedObjectName |
| LdapDN ancestorDn; // Name of an alias entry relative |
| Long ancestorId; // Id of an alias entry relative |
| |
| // Access aliasedObjectName, normalize it and generate the Name |
| normalizedAliasTargetDn = new LdapDN( aliasTarget ); |
| normalizedAliasTargetDn.normalize( attributeTypeRegistry.getNormalizerMapping() ); |
| |
| /* |
| * Check For Cycles |
| * |
| * Before wasting time to lookup more values we check using the target |
| * dn to see if we have the possible formation of an alias cycle. This |
| * happens when the alias refers back to a target that is also a |
| * relative of the alias entry. For detection we test if the aliased |
| * entry Dn starts with the target Dn. If it does then we know the |
| * aliased target is a relative and we have a perspecitive cycle. |
| */ |
| if ( aliasDn.startsWith( normalizedAliasTargetDn ) ) |
| { |
| if ( aliasDn.equals( normalizedAliasTargetDn ) ) |
| { |
| throw new Exception( "[36] aliasDereferencingProblem - " + "attempt to create alias to itself." ); |
| } |
| |
| throw new Exception( "[36] aliasDereferencingProblem - " |
| + "attempt to create alias with cycle to relative " + aliasTarget |
| + " not allowed from descendent alias " + aliasDn ); |
| } |
| |
| /* |
| * Check For Aliases External To Naming Context |
| * |
| * id may be null but the alias may be to a valid entry in |
| * another namingContext. Such aliases are not allowed and we |
| * need to point it out to the user instead of saying the target |
| * does not exist when it potentially could outside of this upSuffix. |
| */ |
| if ( !normalizedAliasTargetDn.startsWith( suffixDn ) ) |
| { |
| // Complain specifically about aliases to outside naming contexts |
| throw new Exception( "[36] aliasDereferencingProblem -" |
| + " the alias points to an entry outside of the " + suffixDn.getUpName() |
| + " namingContext to an object whose existance cannot be" + " determined." ); |
| } |
| |
| // L O O K U P T A R G E T I D |
| targetId = ndnIdx.forwardLookup( normalizedAliasTargetDn.toNormName() ); |
| |
| /* |
| * Check For Target Existance |
| * |
| * We do not allow the creation of inconsistant aliases. Aliases should |
| * not be broken links. If the target does not exist we start screaming |
| */ |
| if ( null == targetId ) |
| { |
| // Complain about target not existing |
| throw new Exception( "[33] aliasProblem - " |
| + "the alias when dereferenced would not name a known object " |
| + "the aliasedObjectName must be set to a valid existing " + "entry." ); |
| } |
| |
| /* |
| * Detect Direct Alias Chain Creation |
| * |
| * Rather than resusitate the target to test if it is an alias and fail |
| * due to chaing creation we use the alias index to determine if the |
| * target is an alias. Hence if the alias we are about to create points |
| * to another alias as its target in the aliasedObjectName attribute, |
| * then we have a situation where an alias chain is being created. |
| * Alias chaining is not allowed so we throw and exception. |
| */ |
| if ( null != aliasIdx.reverseLookup( targetId ) ) |
| { |
| // Complain about illegal alias chain |
| throw new Exception( "[36] aliasDereferencingProblem -" |
| + " the alias points to another alias. Alias chaining is" + " not supported by this backend." ); |
| } |
| |
| // Add the alias to the simple alias index |
| aliasIdx.add( normalizedAliasTargetDn.getNormName(), aliasId ); |
| |
| /* |
| * Handle One Level Scope Alias Index |
| * |
| * The first relative is special with respect to the one level alias |
| * index. If the target is not a sibling of the alias then we add the |
| * index entry maping the parent's id to the aliased target id. |
| */ |
| ancestorDn = ( LdapDN ) aliasDn.clone(); |
| ancestorDn.remove( aliasDn.size() - 1 ); |
| ancestorId = getEntryId( ancestorDn.toNormName() ); |
| |
| // check if alias parent and aliased entry are the same |
| LdapDN normalizedAliasTargetParentDn = ( LdapDN ) normalizedAliasTargetDn.clone(); |
| normalizedAliasTargetParentDn.remove( normalizedAliasTargetDn.size() - 1 ); |
| if ( ! aliasDn.startsWith( normalizedAliasTargetParentDn ) ) |
| { |
| oneAliasIdx.add( ancestorId, targetId ); |
| } |
| |
| /* |
| * Handle Sub Level Scope Alias Index |
| * |
| * Walk the list of relatives from the parents up to the upSuffix, testing |
| * to see if the alias' target is a descendant of the relative. If the |
| * alias target is not a descentant of the relative it extends the scope |
| * and is added to the sub tree scope alias index. The upSuffix node is |
| * ignored since everything is under its scope. The first loop |
| * iteration shall handle the parents. |
| */ |
| while ( !ancestorDn.equals( suffixDn ) && null != ancestorId ) |
| { |
| if ( !NamespaceTools.isDescendant( ancestorDn, normalizedAliasTargetDn ) ) |
| { |
| subAliasIdx.add( ancestorId, targetId ); |
| } |
| |
| ancestorDn.remove( ancestorDn.size() - 1 ); |
| ancestorId = getEntryId( ancestorDn.toNormName() ); |
| } |
| } |
| |
| |
| /** |
| * Removes the index entries for an alias before the entry is deleted from |
| * the master table. |
| * |
| * @todo Optimize this by walking the hierarchy index instead of the name |
| * @param aliasId the id of the alias entry in the master table |
| * @throws NamingException if we cannot parse ldap names |
| * @throws Exception if we cannot delete index values in the database |
| */ |
| private void dropAliasIndices( Long aliasId ) throws Exception |
| { |
| String targetDn = aliasIdx.reverseLookup( aliasId ); |
| Long targetId = getEntryId( targetDn ); |
| String aliasDn = getEntryDn( aliasId ); |
| LdapDN ancestorDn = ( LdapDN ) new LdapDN( aliasDn ).getPrefix( 1 ); |
| Long ancestorId = getEntryId( ancestorDn.toString() ); |
| |
| /* |
| * We cannot just drop all tuples in the one level and subtree userIndices |
| * linking baseIds to the targetId. If more than one alias refers to |
| * the target then droping all tuples with a value of targetId would |
| * make all other aliases to the target inconsistent. |
| * |
| * We need to walk up the path of alias ancestors until we reach the |
| * upSuffix, deleting each ( ancestorId, targetId ) tuple in the |
| * subtree scope alias. We only need to do this for the direct parent |
| * of the alias on the one level subtree. |
| */ |
| oneAliasIdx.drop( ancestorId, targetId ); |
| subAliasIdx.drop( ancestorId, targetId ); |
| |
| while ( !ancestorDn.equals( suffixDn ) ) |
| { |
| ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 ); |
| ancestorId = getEntryId( ancestorDn.toString() ); |
| |
| subAliasIdx.drop( ancestorId, targetId ); |
| } |
| |
| // Drops all alias tuples pointing to the id of the alias to be deleted |
| aliasIdx.drop( aliasId ); |
| } |
| |
| |
| /** |
| * |
| * updates the SubLevel Index as part of a move operation. |
| * |
| * @param childId child id to be moved |
| * @param oldParentId old parent's id |
| * @param newParentId new parent's id |
| * @throws Exception |
| */ |
| private void updateSubLevelIndex( Long childId, Long oldParentId, Long newParentId ) throws Exception |
| { |
| Long tempId = oldParentId; |
| List<Long> parentIds = new ArrayList<Long>(); |
| |
| // find all the parents of the oldParentId |
| while( tempId != 0 && tempId != 1 && tempId != null ) |
| { |
| parentIds.add( tempId ); |
| tempId = getParentId( tempId ); |
| } |
| |
| // find all the children of the childId |
| Cursor<IndexEntry<Long,E>> cursor = subLevelIdx.forwardCursor( childId ); |
| |
| List<Long> childIds = new ArrayList<Long>(); |
| childIds.add( childId ); |
| |
| while( cursor.next() ) |
| { |
| childIds.add( cursor.get().getId() ); |
| } |
| |
| // detach the childId and all its children from oldParentId and all it parents excluding the root |
| for( Long pid : parentIds ) |
| { |
| for( Long cid: childIds ) |
| { |
| subLevelIdx.drop( pid, cid ); |
| } |
| } |
| |
| parentIds.clear(); |
| tempId = newParentId; |
| |
| // find all the parents of the newParentId |
| while( tempId != 0 && tempId != 1 && tempId != null ) |
| { |
| parentIds.add( tempId ); |
| tempId = getParentId( tempId ); |
| } |
| |
| // attach the childId and all its children to newParentId and all it parents excluding the root |
| for( Long id : parentIds ) |
| { |
| for( Long cid: childIds ) |
| { |
| subLevelIdx.add( id, cid ); |
| } |
| } |
| } |
| |
| |
| /** |
| * For all aliases including and under the moved base, this method removes |
| * one and subtree alias index tuples for old ancestors above the moved base |
| * that will no longer be ancestors after the move. |
| * |
| * @param movedBase the base at which the move occured - the moved node |
| * @throws NamingException if system userIndices fail |
| */ |
| private void dropMovedAliasIndices( final LdapDN movedBase ) throws Exception |
| { |
| // // Find all the aliases from movedBase down |
| // IndexAssertion<Object,E> isBaseDescendant = new IndexAssertion<Object,E>() |
| // { |
| // public boolean assertCandidate( IndexEntry<Object,E> rec ) throws Exception |
| // { |
| // String dn = getEntryDn( rec.getId() ); |
| // return dn.endsWith( movedBase.toString() ); |
| // } |
| // }; |
| |
| Long movedBaseId = getEntryId( movedBase.toString() ); |
| |
| if ( aliasIdx.reverseLookup( movedBaseId ) != null ) |
| { |
| dropAliasIndices( movedBaseId, movedBase ); |
| } |
| |
| // throw new NotImplementedException( "Fix the code below this line" ); |
| |
| // NamingEnumeration<ForwardIndexEntry> aliases = |
| // new IndexAssertionEnumeration( aliasIdx.listIndices( movedBase.toString(), true ), isBaseDescendant ); |
| // |
| // while ( aliases.hasMore() ) |
| // { |
| // ForwardIndexEntry entry = aliases.next(); |
| // dropAliasIndices( (Long)entry.getId(), movedBase ); |
| // } |
| } |
| |
| |
| /** |
| * For the alias id all ancestor one and subtree alias tuples are moved |
| * above the moved base. |
| * |
| * @param aliasId the id of the alias |
| * @param movedBase the base where the move occured |
| * @throws Exception if userIndices fail |
| */ |
| private void dropAliasIndices( Long aliasId, LdapDN movedBase ) throws Exception |
| { |
| String targetDn = aliasIdx.reverseLookup( aliasId ); |
| Long targetId = getEntryId( targetDn ); |
| String aliasDn = getEntryDn( aliasId ); |
| |
| /* |
| * Start droping index tuples with the first ancestor right above the |
| * moved base. This is the first ancestor effected by the move. |
| */ |
| LdapDN ancestorDn = ( LdapDN ) movedBase.getPrefix( 1 ); |
| Long ancestorId = getEntryId( ancestorDn.toString() ); |
| |
| /* |
| * We cannot just drop all tuples in the one level and subtree userIndices |
| * linking baseIds to the targetId. If more than one alias refers to |
| * the target then droping all tuples with a value of targetId would |
| * make all other aliases to the target inconsistent. |
| * |
| * We need to walk up the path of alias ancestors right above the moved |
| * base until we reach the upSuffix, deleting each ( ancestorId, |
| * targetId ) tuple in the subtree scope alias. We only need to do |
| * this for the direct parent of the alias on the one level subtree if |
| * the moved base is the alias. |
| */ |
| if ( aliasDn.equals( movedBase.toString() ) ) |
| { |
| oneAliasIdx.drop( ancestorId, targetId ); |
| } |
| |
| subAliasIdx.drop( ancestorId, targetId ); |
| |
| while ( !ancestorDn.equals( suffixDn ) ) |
| { |
| ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 ); |
| ancestorId = getEntryId( ancestorDn.toString() ); |
| |
| subAliasIdx.drop( ancestorId, targetId ); |
| } |
| } |
| |
| |
| /** |
| * @{inhertDoc} |
| */ |
| public void sync() throws Exception |
| { |
| } |
| } |