blob: 102faeedd270d037ddf788d9855a606f140fd6b0 [file] [log] [blame]
/*
* 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;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
import org.apache.directory.server.core.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.interceptor.context.BindOperationContext;
import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
import org.apache.directory.server.core.interceptor.context.ListOperationContext;
import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
import org.apache.directory.server.core.partition.impl.btree.Index;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
import org.apache.directory.server.core.partition.tree.BranchNode;
import org.apache.directory.server.core.partition.tree.LeafNode;
import org.apache.directory.server.core.partition.tree.Node;
import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
import org.apache.directory.server.schema.registries.OidRegistry;
import org.apache.directory.shared.ldap.MultiException;
import org.apache.directory.shared.ldap.NotImplementedException;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.constants.SupportedSASLMechanisms;
import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeIdentifierException;
import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
import org.apache.directory.shared.ldap.filter.ExprNode;
import org.apache.directory.shared.ldap.filter.PresenceNode;
import org.apache.directory.shared.ldap.message.AttributeImpl;
import org.apache.directory.shared.ldap.message.AttributesImpl;
import org.apache.directory.shared.ldap.message.CascadeControl;
import org.apache.directory.shared.ldap.message.EntryChangeControl;
import org.apache.directory.shared.ldap.message.ManageDsaITControl;
import org.apache.directory.shared.ldap.message.PersistentSearchControl;
import org.apache.directory.shared.ldap.message.ServerSearchResult;
import org.apache.directory.shared.ldap.message.SubentriesControl;
import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.schema.AttributeType;
import org.apache.directory.shared.ldap.schema.Normalizer;
import org.apache.directory.shared.ldap.schema.UsageEnum;
import org.apache.directory.shared.ldap.util.DateUtils;
import org.apache.directory.shared.ldap.util.NamespaceTools;
import org.apache.directory.shared.ldap.util.SingletonEnumeration;
import org.apache.directory.shared.ldap.util.StringTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.ConfigurationException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* A nexus for partitions dedicated for storing entries specific to a naming
* context.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class DefaultPartitionNexus extends PartitionNexus
{
private static final Logger LOG = LoggerFactory.getLogger( DefaultPartitionNexus.class );
/** Speedup for logs */
private static final boolean IS_DEBUG = LOG.isDebugEnabled();
/** the vendorName string proudly set to: Apache Software Foundation*/
private static final String ASF = "Apache Software Foundation";
/** the vendorName DSE operational attribute */
private static final String VENDORNAME_ATTR = "vendorName";
/** the vendorVersion DSE operational attribute */
private static final String VENDORVERSION_ATTR = "vendorVersion";
/** the namingContexts DSE operational attribute */
private static final String NAMINGCTXS_ATTR = "namingContexts";
/** the closed state of this partition */
private boolean initialized;
private DirectoryService directoryService;
/** the system partition */
private Partition system;
/** the partitions keyed by normalized suffix strings */
private Map<String, Partition> partitions = new HashMap<String, Partition>();
/** A structure to hold all the partitions */
private BranchNode partitionLookupTree = new BranchNode();
/** the read only rootDSE attributes */
private final Attributes rootDSE;
private AttributeTypeRegistry attrRegistry;
private OidRegistry oidRegistry;
private Object partitionLookupTreeLock = new Object();
/**
* Creates the root nexus singleton of the entire system. The root DSE has
* several attributes that are injected into it besides those that may
* already exist. As partitions are added to the system more namingContexts
* attributes are added to the rootDSE.
*
* @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
* @param rootDSE the root entry for the DSA
*/
public DefaultPartitionNexus( Attributes rootDSE )
{
// setup that root DSE
this.rootDSE = rootDSE;
Attribute attr = new AttributeImpl( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
attr.add( ServerDNConstants.SCHEMA_DN );
rootDSE.put( attr );
attr = new AttributeImpl( "supportedLDAPVersion" );
rootDSE.put( attr );
attr.add( "3" );
attr = new AttributeImpl( "supportedFeatures" );
rootDSE.put( attr );
attr.add( "1.3.6.1.4.1.4203.1.5.1" );
attr = new AttributeImpl( "supportedExtension" );
rootDSE.put( attr );
attr.add( NoticeOfDisconnect.EXTENSION_OID );
// Add the supportedSASLMechanisms attribute to rootDSE
attr = new AttributeImpl( SupportedSASLMechanisms.ATTRIBUTE );
rootDSE.put( attr );
attr.add( SupportedSASLMechanisms.GSSAPI );
attr.add( SupportedSASLMechanisms.DIGEST_MD5 );
attr.add( SupportedSASLMechanisms.CRAM_MD5 );
attr = new AttributeImpl( "supportedControl" );
rootDSE.put( attr );
attr.add( PersistentSearchControl.CONTROL_OID );
attr.add( EntryChangeControl.CONTROL_OID );
attr.add( SubentriesControl.CONTROL_OID );
attr.add( ManageDsaITControl.CONTROL_OID );
attr.add( CascadeControl.CONTROL_OID );
attr = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
rootDSE.put( attr );
attr.add( SchemaConstants.TOP_OC );
attr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
attr = new AttributeImpl( NAMINGCTXS_ATTR );
rootDSE.put( attr );
attr = new AttributeImpl( VENDORNAME_ATTR );
attr.add( ASF );
rootDSE.put( attr );
Properties props = new Properties();
try
{
props.load( getClass().getResourceAsStream( "version.properties" ) );
}
catch ( IOException e )
{
LOG.error( "failed to LOG version properties" );
}
attr = new AttributeImpl( VENDORVERSION_ATTR );
attr.add( props.getProperty( "apacheds.version", "UNKNOWN" ) );
rootDSE.put( attr );
}
/**
* Always returns the string "NEXUS".
*
* @return the string "NEXUS"
*/
public String getId()
{
return "NEXUS";
}
// -----------------------------------------------------------------------
// C O N F I G U R A T I O N M E T H O D S
// -----------------------------------------------------------------------
/**
* Not supported!
*
* @throws UnsupportedOperationException everytime
*/
public void setId( String id )
{
throw new UnsupportedOperationException( "The id cannot be set for the partition nexus." );
}
/**
* Returns root the rootDSE.
*
* @return the root entry for the DSA
*/
public Attributes getContextEntry()
{
return rootDSE;
}
/**
* Sets root entry for this BTreePartition.
*
* @throws UnsupportedOperationException everytime
*/
public void setContextEntry( Attributes rootEntry )
{
throw new UnsupportedOperationException( "Setting the RootDSE is not allowed." );
}
/**
* Always returns the empty String "".
* @return the empty String ""
*/
public String getSuffix()
{
return "";
}
/**
* Unsupported operation on the Nexus.
* @throws UnsupportedOperationException everytime
*/
public void setSuffix( String suffix )
{
throw new UnsupportedOperationException();
}
/**
* Not support!
*/
public void setCacheSize( int cacheSize )
{
throw new UnsupportedOperationException( "You cannot set the cache size of the nexus" );
}
/**
* Not supported!
*
* @throws UnsupportedOperationException always
*/
public int getCacheSize()
{
throw new UnsupportedOperationException( "There is no cache size associated with the nexus" );
}
public void init( DirectoryService directoryService )
throws NamingException
{
// NOTE: We ignore ContextPartitionConfiguration parameter here.
if ( initialized )
{
return;
}
this.directoryService = directoryService;
this.attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
this.oidRegistry = directoryService.getRegistries().getOidRegistry();
initializeSystemPartition();
List<Partition> initializedPartitions = new ArrayList<Partition>();
initializedPartitions.add( 0, this.system );
//noinspection unchecked
Iterator<? extends Partition> partitions = ( Iterator<? extends Partition> ) directoryService.getPartitions().iterator();
try
{
while ( partitions.hasNext() )
{
Partition p = partitions.next();
AddContextPartitionOperationContext opCtx = new AddContextPartitionOperationContext( p );
addContextPartition( opCtx );
initializedPartitions.add( opCtx.getPartition() );
}
initialized = true;
}
finally
{
if ( !initialized )
{
Iterator<Partition> i = initializedPartitions.iterator();
while ( i.hasNext() )
{
Partition partition = i.next();
i.remove();
try
{
partition.destroy();
}
catch ( Exception e )
{
LOG.warn( "Failed to destroy a partition: " + partition.getSuffixDn(), e );
}
finally
{
unregister( partition );
}
}
}
}
}
private Partition initializeSystemPartition() throws NamingException
{
// initialize system partition first
Partition override = directoryService.getSystemPartition();
if ( override != null )
{
Attributes systemEntry = override.getContextEntry();
Attribute objectClassAttr = systemEntry.get( SchemaConstants.OBJECT_CLASS_AT );
if ( objectClassAttr == null )
{
objectClassAttr = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
systemEntry.put( objectClassAttr );
}
objectClassAttr.add( SchemaConstants.TOP_OC );
objectClassAttr.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
objectClassAttr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ),
NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) );
override.setContextEntry( systemEntry );
// ---------------------------------------------------------------
// check a few things to make sure users configured it properly
// ---------------------------------------------------------------
if ( ! override.getId().equals( "system" ) )
{
throw new ConfigurationException( "System partition has wrong name: should be 'system' not '"
+ override.getId() + "'." );
}
// add all attribute oids of index configs to a hashset
if ( override instanceof JdbmPartition )
{
Set<Index> indices = ( ( JdbmPartition ) override ).getIndexedAttributes();
Set<String> indexOids = new HashSet<String>();
OidRegistry registry = directoryService.getRegistries().getOidRegistry();
for ( Index index : indices )
{
indexOids.add( registry.getOid( index.getAttributeId() ) );
}
if ( ! indexOids.contains( registry.getOid( SchemaConstants.OBJECT_CLASS_AT ) ) )
{
LOG.warn( "CAUTION: You have not included objectClass as an indexed attribute" +
"in the system partition configuration. This will lead to poor " +
"performance. The server is automatically adding this index for you." );
JdbmIndex index = new JdbmIndex();
index.setAttributeId( SchemaConstants.OBJECT_CLASS_AT );
indices.add( index );
}
( ( JdbmPartition ) override ).setIndexedAttributes( indices );
system = override;
}
}
else
{
system = new JdbmPartition();
system.setId( "system" );
system.setCacheSize( 500 );
system.setSuffix( ServerDNConstants.SYSTEM_DN );
// Add objectClass attribute for the system partition
Set<Index> indexedAttrs = new HashSet<Index>();
indexedAttrs.add( new JdbmIndex( SchemaConstants.OBJECT_CLASS_AT ) );
( ( JdbmPartition ) system ).setIndexedAttributes( indexedAttrs );
// Add context entry for system partition
Attributes systemEntry = new AttributesImpl();
Attribute objectClassAttr = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
objectClassAttr.add( SchemaConstants.TOP_OC );
objectClassAttr.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
objectClassAttr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
systemEntry.put( objectClassAttr );
systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ),
NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) );
system.setContextEntry( systemEntry );
}
system.init( directoryService );
String key = system.getSuffixDn().toString();
if ( partitions.containsKey( key ) )
{
throw new ConfigurationException( "Duplicate partition suffix: " + key );
}
synchronized ( partitionLookupTree )
{
partitions.put( key, system );
partitionLookupTree.recursivelyAddPartition( partitionLookupTree, system.getSuffixDn(), 0, system );
Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
namingContexts.add( system.getUpSuffixDn().getUpName() );
}
return system;
}
public boolean isInitialized()
{
return initialized;
}
public synchronized void destroy()
{
if ( !initialized )
{
return;
}
// make sure this loop is not fail fast so all backing stores can
// have an attempt at closing down and synching their cached entries
for ( String suffix : new HashSet<String>( this.partitions.keySet() ) )
{
try
{
removeContextPartition( new RemoveContextPartitionOperationContext( new LdapDN( suffix ) ) );
}
catch ( NamingException e )
{
LOG.warn( "Failed to destroy a partition: " + suffix, e );
}
}
initialized = false;
}
/**
* @see Partition#sync()
*/
public void sync() throws NamingException
{
MultiException error = null;
for ( Partition partition : this.partitions.values() )
{
try
{
partition.sync();
}
catch ( NamingException e )
{
LOG.warn( "Failed to flush partition data out.", e );
if ( error == null )
{
//noinspection ThrowableInstanceNeverThrown
error = new MultiException( "Grouping many exceptions on root nexus sync()" );
}
// @todo really need to send this info to a monitor
error.addThrowable( e );
}
}
if ( error != null )
{
String msg = "Encountered failures while performing a sync() operation on backing stores";
//noinspection ThrowableInstanceNeverThrown
NamingException total = new NamingException( msg );
total.setRootCause( error );
}
}
// ------------------------------------------------------------------------
// ContextPartitionNexus Method Implementations
// ------------------------------------------------------------------------
public boolean compare( CompareOperationContext compareContext ) throws NamingException
{
Partition partition = getPartition( compareContext.getDn() );
AttributeTypeRegistry registry = directoryService.getRegistries().getAttributeTypeRegistry();
// complain if we do not recognize the attribute being compared
if ( !registry.hasAttributeType( compareContext.getOid() ) )
{
throw new LdapInvalidAttributeIdentifierException( compareContext.getOid() + " not found within the attributeType registry" );
}
AttributeType attrType = registry.lookup( compareContext.getOid() );
Attribute attr = partition.lookup( new LookupOperationContext( compareContext.getDn() ) ).get( attrType.getName() );
// complain if the attribute being compared does not exist in the entry
if ( attr == null )
{
throw new LdapNoSuchAttributeException();
}
// see first if simple match without normalization succeeds
// TODO Fix DIRSERVER-832
if ( attr.contains( compareContext.getValue() ) )
{
return true;
}
// now must apply normalization to all values (attr and in request) to compare
/*
* Get ahold of the normalizer for the attribute and normalize the request
* assertion value for comparisons with normalized attribute values. Loop
* through all values looking for a match.
*/
Normalizer normalizer = attrType.getEquality().getNormalizer();
Object reqVal = normalizer.normalize( compareContext.getValue() );
for ( int ii = 0; ii < attr.size(); ii++ )
{
Object attrValObj = normalizer.normalize( attr.get( ii ) );
if ( attrValObj instanceof String )
{
String attrVal = ( String ) attrValObj;
if ( ( reqVal instanceof String ) && attrVal.equals( reqVal ) )
{
return true;
}
}
else
{
byte[] attrVal = ( byte[] ) attrValObj;
if ( reqVal instanceof byte[] )
{
return Arrays.equals( attrVal, ( byte[] ) reqVal );
}
else if ( reqVal instanceof String )
{
return Arrays.equals( attrVal, StringTools.getBytesUtf8( ( String ) reqVal ) );
}
}
}
return false;
}
public synchronized void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException
{
Partition partition = opContext.getPartition();
// Turn on default indices
String key = partition.getSuffix();
if ( partitions.containsKey( key ) )
{
throw new ConfigurationException( "Duplicate partition suffix: " + key );
}
if ( ! partition.isInitialized() )
{
partition.init( directoryService );
}
synchronized ( partitionLookupTree )
{
LdapDN partitionSuffix = partition.getSuffixDn();
if ( partitionSuffix == null )
{
throw new ConfigurationException( "The current partition does not have any suffix: " + partition.getId() );
}
partitions.put( partitionSuffix.toString(), partition );
partitionLookupTree.recursivelyAddPartition( partitionLookupTree, partition.getSuffixDn(), 0, partition );
Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
LdapDN partitionUpSuffix = partition.getUpSuffixDn();
if ( partitionUpSuffix == null )
{
throw new ConfigurationException( "The current partition does not have any user provided suffix: " + partition.getId() );
}
namingContexts.add( partitionUpSuffix.getUpName() );
}
}
public synchronized void removeContextPartition( RemoveContextPartitionOperationContext removeContextPartition ) throws NamingException
{
String key = removeContextPartition.getDn().getNormName();
Partition partition = partitions.get( key );
if ( partition == null )
{
throw new NameNotFoundException( "No partition with suffix: " + key );
}
Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
namingContexts.remove( partition.getUpSuffixDn().getUpName() );
// Create a new partition list.
// This is easier to create a new structure from scratch than to reorganize
// the current structure. As this strcuture is not modified often
// this is an acceptable solution.
synchronized ( partitionLookupTree )
{
partitions.remove( key );
partitionLookupTree = new BranchNode();
for ( Partition part : partitions.values() )
{
partitionLookupTree.recursivelyAddPartition( partitionLookupTree, part.getSuffixDn(), 0, partition );
}
partition.sync();
partition.destroy();
}
}
public Partition getSystemPartition()
{
return system;
}
/**
* @see PartitionNexus#getLdapContext()
*/
public LdapContext getLdapContext()
{
throw new NotImplementedException();
}
/**
* @see PartitionNexus#getMatchedName( GetMatchedNameOperationContext )
*/
public LdapDN getMatchedName ( GetMatchedNameOperationContext getMatchedNameContext ) throws NamingException
{
LdapDN dn = ( LdapDN ) getMatchedNameContext.getDn().clone();
while ( dn.size() > 0 )
{
if ( hasEntry( new EntryOperationContext( dn ) ) )
{
return dn;
}
dn.remove( dn.size() - 1 );
}
return dn;
}
public LdapDN getSuffixDn()
{
return LdapDN.EMPTY_LDAPDN;
}
public LdapDN getUpSuffixDn()
{
return LdapDN.EMPTY_LDAPDN;
}
/**
* @see PartitionNexus#getSuffix( GetSuffixOperationContext )
*/
public LdapDN getSuffix ( GetSuffixOperationContext getSuffixContext ) throws NamingException
{
Partition backend = getPartition( getSuffixContext.getDn() );
return backend.getSuffixDn();
}
/**
* @see PartitionNexus#listSuffixes( ListSuffixOperationContext )
*/
public Iterator<String> listSuffixes ( ListSuffixOperationContext emptyContext ) throws NamingException
{
return Collections.unmodifiableSet( partitions.keySet() ).iterator();
}
public Attributes getRootDSE( GetRootDSEOperationContext getRootDSEContext )
{
return rootDSE;
}
/**
* Unregisters an ContextPartition with this BackendManager. Called for each
* registered Backend right befor it is to be stopped. This prevents
* protocol server requests from reaching the Backend and effectively puts
* the ContextPartition's naming context offline.
*
* Operations against the naming context should result in an LDAP BUSY
* result code in the returnValue if the naming context is not online.
*
* @param partition ContextPartition component to unregister with this
* BackendNexus.
* @throws NamingException if there are problems unregistering the partition
*/
private void unregister( Partition partition ) throws NamingException
{
Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
namingContexts.remove( partition.getSuffixDn().getUpName() );
partitions.remove( partition.getSuffixDn().toString() );
}
// ------------------------------------------------------------------------
// DirectoryPartition Interface Method Implementations
// ------------------------------------------------------------------------
public void bind( BindOperationContext bindContext ) throws NamingException
{
Partition partition = getPartition( bindContext.getDn() );
partition.bind( bindContext );
}
public void unbind( UnbindOperationContext unbindContext ) throws NamingException
{
Partition partition = getPartition( unbindContext.getDn() );
partition.unbind( unbindContext );
}
/**
* @see Partition#delete(DeleteOperationContext)
*/
public void delete( DeleteOperationContext deleteContext ) throws NamingException
{
Partition backend = getPartition( deleteContext.getDn() );
backend.delete( deleteContext );
}
/**
* Looks up the backend corresponding to the entry first, then checks to
* see if the entry already exists. If so an exception is thrown. If not
* the add operation against the backend proceeds. This check is performed
* here so backend implementors do not have to worry about performing these
* kinds of checks.
*
* @see Partition#add( AddOperationContext )
*/
public void add( AddOperationContext addContext ) throws NamingException
{
Partition backend = getPartition( addContext.getDn() );
backend.add( addContext );
}
public void modify( ModifyOperationContext modifyContext ) throws NamingException
{
Partition backend = getPartition( modifyContext.getDn() );
backend.modify( modifyContext );
}
/**
* @see Partition#list(ListOperationContext)
*/
public NamingEnumeration<SearchResult> list( ListOperationContext opContext ) throws NamingException
{
Partition backend = getPartition( opContext.getDn() );
return backend.list( opContext );
}
public NamingEnumeration<SearchResult> search( SearchOperationContext opContext )
throws NamingException
{
LdapDN base = opContext.getDn();
SearchControls searchCtls = opContext.getSearchControls();
ExprNode filter = opContext.getFilter();
if ( base.size() == 0 )
{
boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
// test for (objectClass=*)
boolean isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID );
/*
* if basedn is "", filter is "(objectclass=*)" and scope is object
* then we have a request for the rootDSE
*/
if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
{
String[] ids = searchCtls.getReturningAttributes();
// -----------------------------------------------------------
// If nothing is asked for then we just return the entry asis.
// We let other mechanisms filter out operational attributes.
// -----------------------------------------------------------
if ( ids == null || ids.length == 0 )
{
SearchResult result = new ServerSearchResult( "", null, ( Attributes ) getRootDSE( null ).clone(), false );
return new SingletonEnumeration<SearchResult>( result );
}
// -----------------------------------------------------------
// Collect all the real attributes besides 1.1, +, and * and
// note if we've seen these special attributes as well.
// -----------------------------------------------------------
Set<String> realIds = new HashSet<String>();
boolean containsAsterisk = false;
boolean containsPlus = false;
boolean containsOneDotOne = false;
for ( String id:ids )
{
String idTrimmed = id.trim();
if ( idTrimmed.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) )
{
containsAsterisk = true;
}
else if ( idTrimmed.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
{
containsPlus = true;
}
else if ( idTrimmed.equals( SchemaConstants.NO_ATTRIBUTE ) )
{
containsOneDotOne = true;
}
else
{
try
{
realIds.add( oidRegistry.getOid( idTrimmed ) );
}
catch ( NamingException e )
{
realIds.add( idTrimmed );
}
}
}
// return nothing
if ( containsOneDotOne )
{
SearchResult result = new ServerSearchResult( "", null, new AttributesImpl(), false );
return new SingletonEnumeration<SearchResult>( result );
}
// return everything
if ( containsAsterisk && containsPlus )
{
SearchResult result = new ServerSearchResult( "", null, ( Attributes ) getRootDSE( null ).clone(), false );
return new SingletonEnumeration<SearchResult>( result );
}
Attributes attrs = new AttributesImpl();
if ( containsAsterisk )
{
for ( NamingEnumeration<? extends Attribute> ii = getRootDSE( null ).getAll(); ii.hasMore(); /**/ )
{
// add all user attribute
Attribute attr = ii.next();
AttributeType type = attrRegistry.lookup( attr.getID() );
if ( type.getUsage() == UsageEnum.USER_APPLICATIONS )
{
attrs.put( attr );
}
// add attributes specifically asked for
else if ( realIds.contains( type.getOid() ) )
{
attrs.put( attr );
}
}
}
else if ( containsPlus )
{
for ( NamingEnumeration<? extends Attribute> ii = getRootDSE( null ).getAll(); ii.hasMore(); /**/ )
{
// add all operational attributes
Attribute attr = ii.next();
AttributeType type = attrRegistry.lookup( attr.getID() );
if ( type.getUsage() != UsageEnum.USER_APPLICATIONS )
{
attrs.put( attr );
}
// add user attributes specifically asked for
else if ( realIds.contains( type.getOid() ) )
{
attrs.put( attr );
}
}
}
else
{
for ( NamingEnumeration<? extends Attribute> ii = getRootDSE( null ).getAll(); ii.hasMore(); /**/ )
{
// add user attributes specifically asked for
Attribute attr = ii.next();
AttributeType type = attrRegistry.lookup( attr.getID() );
if ( realIds.contains( type.getOid() ) )
{
attrs.put( attr );
}
}
}
SearchResult result = new ServerSearchResult( "", null, attrs, false );
return new SingletonEnumeration<SearchResult>( result );
}
throw new LdapNameNotFoundException();
}
Partition backend = getPartition( base );
return backend.search( opContext );
}
public Attributes lookup( LookupOperationContext opContext ) throws NamingException
{
LdapDN dn = opContext.getDn();
if ( dn.size() == 0 )
{
Attributes retval = new AttributesImpl();
NamingEnumeration<String> list = rootDSE.getIDs();
if ( opContext.getAttrsId() != null )
{
while ( list.hasMore() )
{
String id = list.next();
if ( opContext.getAttrsId().contains( id ) )
{
Attribute attr = rootDSE.get( id );
retval.put( ( Attribute ) attr.clone() );
}
}
}
else
{
while ( list.hasMore() )
{
String id = list.next();
Attribute attr = rootDSE.get( id );
retval.put( ( Attribute ) attr.clone() );
}
}
return retval;
}
Partition backend = getPartition( dn );
return backend.lookup( opContext );
}
/**
* @see Partition#hasEntry(EntryOperationContext)
*/
public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
{
LdapDN dn = opContext.getDn();
if ( IS_DEBUG )
{
LOG.debug( "Check if DN '" + dn + "' exists." );
}
if ( dn.size() == 0 )
{
return true;
}
Partition backend = getPartition( dn );
return backend.hasEntry( opContext );
}
/**
* @see Partition#rename(RenameOperationContext)
*/
public void rename( RenameOperationContext opContext ) throws NamingException
{
Partition backend = getPartition( opContext.getDn() );
backend.rename( opContext );
}
/**
* @see Partition#move(MoveOperationContext)
*/
public void move( MoveOperationContext opContext ) throws NamingException
{
Partition backend = getPartition( opContext.getDn() );
backend.move( opContext );
}
public void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException
{
Partition backend = getPartition( opContext.getDn() );
backend.moveAndRename( opContext );
}
/**
* Gets the partition associated with a normalized dn.
*
* @param dn the normalized distinguished name to resolve to a partition
* @return the backend partition associated with the normalized dn
* @throws NamingException if the name cannot be resolved to a partition
*/
public Partition getPartition( LdapDN dn ) throws NamingException
{
Enumeration<String> rdns = dn.getAll();
Node currentNode = partitionLookupTree;
// This is synchronized so that we can't read the
// partitionList when it is modified.
synchronized ( partitionLookupTreeLock )
{
// Iterate through all the RDN until we find the associated partition
while ( rdns.hasMoreElements() )
{
String rdn = rdns.nextElement();
if ( currentNode == null )
{
break;
}
if ( currentNode instanceof LeafNode )
{
return ( ( LeafNode ) currentNode ).getPartition();
}
BranchNode currentBranch = ( BranchNode ) currentNode;
if ( currentBranch.contains( rdn ) )
{
currentNode = currentBranch.getChild( rdn );
if ( currentNode instanceof LeafNode )
{
return ( ( LeafNode ) currentNode ).getPartition();
}
}
}
}
throw new LdapNameNotFoundException( dn.getUpName() );
}
// ------------------------------------------------------------------------
// Private Methods
// ------------------------------------------------------------------------
public void registerSupportedExtensions( Set<String> extensionOids )
{
Attribute supportedExtension = rootDSE.get( "supportedExtension" );
if ( supportedExtension == null )
{
supportedExtension = new AttributeImpl( "supportedExtension" );
rootDSE.put( supportedExtension );
}
for ( String extensionOid : extensionOids )
{
supportedExtension.add( extensionOid );
}
}
}