blob: 0e091a0798920825d689e91f9fd41a33e3b40cb9 [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;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.directory.api.ldap.extras.controls.ad.TreeDelete;
import org.apache.directory.api.ldap.model.constants.Loggers;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.exception.LdapAffectMultipleDsaException;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
import org.apache.directory.api.ldap.model.exception.LdapOtherException;
import org.apache.directory.api.ldap.model.exception.LdapPartialResultException;
import org.apache.directory.api.ldap.model.exception.LdapReferralException;
import org.apache.directory.api.ldap.model.exception.LdapServiceUnavailableException;
import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
import org.apache.directory.api.ldap.model.filter.PresenceNode;
import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.name.Rdn;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.ldap.model.url.LdapUrl;
import org.apache.directory.server.constants.ApacheSchemaConstants;
import org.apache.directory.server.core.api.CoreSession;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.OperationManager;
import org.apache.directory.server.core.api.ReferralManager;
import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
import org.apache.directory.server.core.api.interceptor.Interceptor;
import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
import org.apache.directory.server.core.api.interceptor.context.GetRootDseOperationContext;
import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
import org.apache.directory.server.core.api.interceptor.context.OperationContext;
import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.api.partition.PartitionTxn;
import org.apache.directory.server.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The default implementation of an OperationManager.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class DefaultOperationManager implements OperationManager
{
/** A logger specifically for operations */
private static final Logger OPERATION_LOG = LoggerFactory.getLogger( Loggers.OPERATION_LOG.getName() );
/** A logger specifically for operations time */
private static final Logger OPERATION_TIME = LoggerFactory.getLogger( Loggers.OPERATION_TIME.getName() );
/** A logger specifically for operations statistics */
private static final Logger OPERATION_STAT = LoggerFactory.getLogger( Loggers.OPERATION_STAT.getName() );
/** Speedup for logs */
private static final boolean IS_DEBUG = OPERATION_LOG.isDebugEnabled();
private static final boolean IS_TIME = OPERATION_TIME.isDebugEnabled();
private static final boolean IS_STAT = OPERATION_STAT.isDebugEnabled();
/** The directory service instance */
private final DirectoryService directoryService;
/** A lock used to protect against concurrent operations */
private ReadWriteLock rwLock = new ReentrantReadWriteLock( true );
/** A reference to the ObjectClass AT */
protected AttributeType objectClassAT;
/** The nbChildren count attributeType */
protected AttributeType nbChildrenAT;
public DefaultOperationManager( DirectoryService directoryService )
{
this.directoryService = directoryService;
}
/**
* {@inheritDoc}
*/
public ReadWriteLock getRWLock()
{
return rwLock;
}
/**
* Acquires a ReadLock
*/
public void lockRead()
{
rwLock.readLock().lock();
}
/**
* Acquires a WriteLock
*/
public void lockWrite()
{
rwLock.writeLock().lock();
}
/**
* Releases a WriteLock
*/
public void unlockWrite()
{
rwLock.writeLock().unlock();
}
/**
* Releases a ReadLock
*/
public void unlockRead()
{
rwLock.readLock().unlock();
}
/**
* Eagerly populates fields of operation contexts so multiple Interceptors
* in the processing pathway can reuse this value without performing a
* redundant lookup operation.
*
* @param opContext the operation context to populate with cached fields
*/
private void eagerlyPopulateFields( OperationContext opContext ) throws LdapException
{
// If the entry field is not set for ops other than add for example
// then we set the entry but don't freak if we fail to do so since it
// may not exist in the first place
if ( opContext.getEntry() == null )
{
// We have to use the admin session here, otherwise we may have
// trouble reading the entry due to insufficient access rights
CoreSession adminSession = opContext.getSession().getDirectoryService().getAdminSession();
LookupOperationContext lookupContext = new LookupOperationContext( adminSession, opContext.getDn(),
SchemaConstants.ALL_ATTRIBUTES_ARRAY );
lookupContext.setPartition( opContext.getPartition() );
lookupContext.setTransaction( opContext.getTransaction() );
Entry foundEntry = opContext.getSession().getDirectoryService().getPartitionNexus().lookup( lookupContext );
if ( foundEntry != null )
{
opContext.setEntry( foundEntry );
}
else
{
// This is an error : we *must* have an entry if we want to be able to rename.
throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_256_NO_SUCH_OBJECT, opContext.getDn() ) );
}
}
}
private Entry getOriginalEntry( OperationContext opContext ) throws LdapException
{
// We have to use the admin session here, otherwise we may have
// trouble reading the entry due to insufficient access rights
CoreSession adminSession = opContext.getSession().getDirectoryService().getAdminSession();
Entry foundEntry = adminSession.lookup( opContext.getDn(), SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES,
SchemaConstants.ALL_USER_ATTRIBUTES );
if ( foundEntry != null )
{
return foundEntry;
}
else
{
// This is an error : we *must* have an entry if we want to be able to rename.
throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_256_NO_SUCH_OBJECT,
opContext.getDn() ) );
}
}
private LdapReferralException buildReferralException( Entry parentEntry, Dn childDn ) throws LdapException
{
// Get the Ref attributeType
Attribute refs = parentEntry.get( SchemaConstants.REF_AT );
List<String> urls = new ArrayList<>();
try
{
// manage each Referral, building the correct URL for each of them
for ( Value url : refs )
{
// we have to replace the parent by the referral
LdapUrl ldapUrl = new LdapUrl( url.getString() );
// We have a problem with the Dn : we can't use the UpName,
// as we may have some spaces around the ',' and '+'.
// So we have to take the Rdn one by one, and create a
// new Dn with the type and value UP form
Dn urlDn = ldapUrl.getDn().add( childDn );
ldapUrl.setDn( urlDn );
urls.add( ldapUrl.toString() );
}
}
catch ( LdapURLEncodingException luee )
{
throw new LdapOperationErrorException( luee.getMessage(), luee );
}
// Return with an exception
LdapReferralException lre = new LdapReferralException( urls );
lre.setRemainingDn( childDn );
lre.setResolvedDn( parentEntry.getDn() );
lre.setResolvedObject( parentEntry );
return lre;
}
private LdapReferralException buildReferralExceptionForSearch( Entry parentEntry, Dn childDn, SearchScope scope )
throws LdapException
{
// Get the Ref attributeType
Attribute refs = parentEntry.get( SchemaConstants.REF_AT );
List<String> urls = new ArrayList<>();
// manage each Referral, building the correct URL for each of them
for ( Value url : refs )
{
// we have to replace the parent by the referral
try
{
LdapUrl ldapUrl = new LdapUrl( url.getString() );
StringBuilder urlString = new StringBuilder();
if ( ( ldapUrl.getDn() == null ) || ( ldapUrl.getDn() == Dn.ROOT_DSE ) )
{
ldapUrl.setDn( parentEntry.getDn() );
}
else
{
// We have a problem with the Dn : we can't use the UpName,
// as we may have some spaces around the ',' and '+'.
// So we have to take the Rdn one by one, and create a
// new Dn with the type and value UP form
Dn urlDn = ldapUrl.getDn().add( childDn );
ldapUrl.setDn( urlDn );
}
urlString.append( ldapUrl.toString() ).append( "??" );
switch ( scope )
{
case OBJECT:
urlString.append( "base" );
break;
case SUBTREE:
urlString.append( "sub" );
break;
case ONELEVEL:
urlString.append( "one" );
break;
default:
throw new IllegalArgumentException( "Unexpected scope " + scope );
}
urls.add( urlString.toString() );
}
catch ( LdapURLEncodingException luee )
{
// The URL is not correct, returns it as is
urls.add( url.getString() );
}
}
// Return with an exception
LdapReferralException lre = new LdapReferralException( urls );
lre.setRemainingDn( childDn );
lre.setResolvedDn( parentEntry.getDn() );
lre.setResolvedObject( parentEntry );
return lre;
}
private LdapPartialResultException buildLdapPartialResultException( Dn childDn )
{
LdapPartialResultException lpre = new LdapPartialResultException( I18n.err( I18n.ERR_315 ) );
lpre.setRemainingDn( childDn );
lpre.setResolvedDn( Dn.EMPTY_DN );
return lpre;
}
/**
* {@inheritDoc}
*/
public void add( AddOperationContext addContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> AddOperation : {}", addContext );
}
long addStart = 0L;
if ( IS_TIME )
{
addStart = System.nanoTime();
}
ensureStarted();
// Normalize the addContext Dn
Dn dn = addContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
addContext.setDn( dn );
}
// Find the working partition
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
addContext.setPartition( partition );
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( addContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
finally
{
// Unlock the referral manager
directoryService.getReferralManager().unlock();
}
// Call the Add method
Interceptor head = directoryService.getInterceptor( addContext.getNextInterceptor() );
lockWrite();
// Start a Write transaction right away
PartitionTxn transaction = addContext.getSession().getTransaction( partition );
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( addContext.getSession().hasSessionTransaction() )
{
addContext.getSession().addTransaction( partition, transaction );
}
}
addContext.setTransaction( transaction );
head.add( addContext );
if ( !addContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
transaction.abort();
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< AddOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Add operation took {} ns", ( System.nanoTime() - addStart ) );
}
}
/**
* {@inheritDoc}
*/
public void bind( BindOperationContext bindContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> BindOperation : {}", bindContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Call the Delete method
Interceptor head = directoryService.getInterceptor( bindContext.getNextInterceptor() );
// Normalize the addContext Dn
Dn dn = bindContext.getDn();
if ( ( dn != null ) && !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
bindContext.setDn( dn );
}
lockRead();
try
{
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
{
bindContext.setPartition( partition );
bindContext.setTransaction( partitionTxn );
head.bind( bindContext );
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
finally
{
unlockRead();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< BindOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Bind operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public boolean compare( CompareOperationContext compareContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> CompareOperation : {}", compareContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the compareContext Dn
Dn dn = compareContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
compareContext.setDn( dn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !compareContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralException( parentEntry, childDn );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( compareContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
}
finally
{
// Unlock the ReferralManager
directoryService.getReferralManager().unlock();
}
// populate the context with the old entry
compareContext.setOriginalEntry( getOriginalEntry( compareContext ) );
// Call the Compare method
Interceptor head = directoryService.getInterceptor( compareContext.getNextInterceptor() );
boolean result = false;
lockRead();
try
{
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
{
compareContext.setPartition( partition );
compareContext.setTransaction( partitionTxn );
result = head.compare( compareContext );
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
finally
{
unlockRead();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< CompareOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Compare operation took {} ns", ( System.nanoTime() - opStart ) );
}
return result;
}
private void deleteEntry( DeleteOperationContext deleteContext , Dn dn ) throws LdapException
{
DeleteOperationContext entryDeleteContext =
new DeleteOperationContext( deleteContext.getSession(), dn );
entryDeleteContext.setTransaction( deleteContext.getTransaction() );
eagerlyPopulateFields( entryDeleteContext );
// Call the Delete method
Interceptor head = directoryService.getInterceptor( deleteContext.getNextInterceptor() );
head.delete( entryDeleteContext );
}
private void processTreeDelete( DeleteOperationContext deleteContext, Dn dn ) throws LdapException, CursorException
{
objectClassAT = directoryService.getSchemaManager().getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
nbChildrenAT = directoryService.getSchemaManager().getAttributeType( ApacheSchemaConstants.NB_CHILDREN_OID );
// This is a depth first recursive operation
PresenceNode filter = new PresenceNode( objectClassAT );
SearchOperationContext searchContext = new SearchOperationContext(
deleteContext.getSession(),
dn,
SearchScope.ONELEVEL, filter,
ApacheSchemaConstants.NB_CHILDREN_OID );
searchContext.setTransaction( deleteContext.getTransaction() );
EntryFilteringCursor cursor = search( searchContext );
cursor.beforeFirst();
while ( cursor.next() )
{
Entry entry = cursor.get();
if ( Integer.parseInt( entry.get( nbChildrenAT ).getString() ) == 0 )
{
// We can delete the entry
deleteEntry( deleteContext, entry.getDn() );
}
else
{
// Recurse
processTreeDelete( deleteContext, entry.getDn() );
}
}
// Done with the children, we can delete the entry
// We can delete the entry
deleteEntry( deleteContext, dn );
}
/**
* {@inheritDoc}
*/
public void delete( DeleteOperationContext deleteContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> DeleteOperation : {}", deleteContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the deleteContext Dn
Dn dn = deleteContext.getDn();
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
deleteContext.setPartition( partition );
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
deleteContext.setDn( dn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !deleteContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralException( parentEntry, childDn );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// We can't delete an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( deleteContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
}
finally
{
// Unlock the ReferralManager
directoryService.getReferralManager().unlock();
}
// populate the context with the old entry
lockWrite();
// Start a Write transaction right away
PartitionTxn transaction = deleteContext.getSession().getTransaction( partition );
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( deleteContext.getSession().hasSessionTransaction() )
{
deleteContext.getSession().addTransaction( partition, transaction );
}
}
deleteContext.setTransaction( transaction );
// Check if the TreeDelete control is used
if ( deleteContext.hasRequestControl( TreeDelete.OID ) )
{
try
{
processTreeDelete( deleteContext, deleteContext.getDn() );
if ( !deleteContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( CursorException ce )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw new LdapOtherException( ce.getMessage(), ce );
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
}
else
{
eagerlyPopulateFields( deleteContext );
// Call the Delete method
Interceptor head = directoryService.getInterceptor( deleteContext.getNextInterceptor() );
head.delete( deleteContext );
if ( !deleteContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
transaction.abort();
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< DeleteOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Delete operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public Entry getRootDse( GetRootDseOperationContext getRootDseContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> GetRootDseOperation : {}", getRootDseContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
Interceptor head = directoryService.getInterceptor( getRootDseContext.getNextInterceptor() );
Entry root;
try
{
lockRead();
Partition partition = directoryService.getPartitionNexus().getPartition( Dn.ROOT_DSE );
try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
{
getRootDseContext.setPartition( partition );
getRootDseContext.setTransaction( partitionTxn );
root = head.getRootDse( getRootDseContext );
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
finally
{
unlockRead();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< getRootDseOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "GetRootDSE operation took {} ns", ( System.nanoTime() - opStart ) );
}
return root;
}
/**
* {@inheritDoc}
*/
public boolean hasEntry( HasEntryOperationContext hasEntryContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> hasEntryOperation : {}", hasEntryContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
Interceptor head = directoryService.getInterceptor( hasEntryContext.getNextInterceptor() );
boolean result = false;
lockRead();
// Normalize the addContext Dn
Dn dn = hasEntryContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
hasEntryContext.setDn( dn );
}
try
{
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
{
hasEntryContext.setPartition( partition );
hasEntryContext.setTransaction( partitionTxn );
result = head.hasEntry( hasEntryContext );
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
finally
{
unlockRead();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< HasEntryOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "HasEntry operation took {} ns", ( System.nanoTime() - opStart ) );
}
return result;
}
/**
* {@inheritDoc}
*/
public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> LookupOperation : {}", lookupContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
Interceptor head = directoryService.getInterceptor( lookupContext.getNextInterceptor() );
Entry entry = null;
// Normalize the modifyContext Dn
Dn dn = lookupContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
lookupContext.setDn( dn );
}
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
lookupContext.setPartition( partition );
// Start a read transaction right away
try ( PartitionTxn transaction = partition.beginReadTransaction() )
{
lookupContext.setTransaction( transaction );
lockRead();
try
{
entry = head.lookup( lookupContext );
}
finally
{
unlockRead();
}
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< LookupOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Lookup operation took {} ns", ( System.nanoTime() - opStart ) );
}
return entry;
}
/**
* {@inheritDoc}
*/
public void modify( ModifyOperationContext modifyContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> ModifyOperation : {}", modifyContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the modifyContext Dn
Dn dn = modifyContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
modifyContext.setDn( dn );
}
ReferralManager referralManager = directoryService.getReferralManager();
// We have to deal with the referral first
referralManager.lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = referralManager.getParentReferral( dn );
if ( parentEntry != null )
{
if ( referralManager.isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !modifyContext.isReferralIgnored() )
{
// Throw a Referral Exception
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
throw buildReferralException( parentEntry, childDn );
}
}
else if ( referralManager.hasParentReferral( dn ) )
{
// We can't delete an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( modifyContext.isReferralIgnored() )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
throw buildLdapPartialResultException( childDn );
}
else
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
throw buildReferralException( parentEntry, childDn );
}
}
}
}
finally
{
// Unlock the ReferralManager
referralManager.unlock();
}
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
modifyContext.setPartition( partition );
lockWrite();
// Start a Write transaction right away
PartitionTxn transaction = modifyContext.getSession().getTransaction( partition );
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( modifyContext.getSession().hasSessionTransaction() )
{
modifyContext.getSession().addTransaction( partition, transaction );
}
}
modifyContext.setTransaction( transaction );
// populate the context with the old entry
eagerlyPopulateFields( modifyContext );
// Call the Modify method
Interceptor head = directoryService.getInterceptor( modifyContext.getNextInterceptor() );
head.modify( modifyContext );
if ( !modifyContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
transaction.abort();
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< ModifyOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Modify operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public void move( MoveOperationContext moveContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> MoveOperation : {}", moveContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the moveContext Dn
Dn dn = moveContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
moveContext.setDn( dn );
}
// Normalize the moveContext superior Dn
Dn newSuperiorDn = moveContext.getNewSuperior();
if ( !newSuperiorDn.isSchemaAware() )
{
newSuperiorDn = new Dn( directoryService.getSchemaManager(), newSuperiorDn );
moveContext.setNewSuperior( newSuperiorDn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !moveContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralException( parentEntry, childDn );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// We can't delete an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( moveContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
// Now, check the destination
// If he parent Dn is a referral, or has a referral ancestor, we have to issue a AffectMultipleDsas result
// as stated by RFC 3296 Section 5.6.2
if ( directoryService.getReferralManager().isReferral( newSuperiorDn )
|| directoryService.getReferralManager().hasParentReferral( newSuperiorDn ) )
{
throw new LdapAffectMultipleDsaException();
}
}
finally
{
// Unlock the referral manager
directoryService.getReferralManager().unlock();
}
lockWrite();
// Find the working partition
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
moveContext.setPartition( partition );
// Start a Write transaction right away
PartitionTxn transaction = moveContext.getSession().getTransaction( partition );
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( moveContext.getSession().hasSessionTransaction() )
{
moveContext.getSession().addTransaction( partition, transaction );
}
}
moveContext.setTransaction( transaction );
Entry originalEntry = getOriginalEntry( moveContext );
moveContext.setOriginalEntry( originalEntry );
// Call the Move method
Interceptor head = directoryService.getInterceptor( moveContext.getNextInterceptor() );
head.move( moveContext );
if ( !moveContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< MoveOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Move operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> MoveAndRenameOperation : {}", moveAndRenameContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the moveAndRenameContext Dn
Dn dn = moveAndRenameContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
moveAndRenameContext.setDn( dn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !moveAndRenameContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralException( parentEntry, childDn );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// We can't delete an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( moveAndRenameContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
// Now, check the destination
// Normalize the moveAndRenameContext Dn
Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn();
if ( !newSuperiorDn.isSchemaAware() )
{
newSuperiorDn = new Dn( directoryService.getSchemaManager(), newSuperiorDn );
moveAndRenameContext.setNewSuperiorDn( newSuperiorDn );
}
// If he parent Dn is a referral, or has a referral ancestor, we have to issue a AffectMultipleDsas result
// as stated by RFC 3296 Section 5.6.2
if ( directoryService.getReferralManager().isReferral( newSuperiorDn )
|| directoryService.getReferralManager().hasParentReferral( newSuperiorDn ) )
{
// The parent Dn is a referral, we have to issue a AffectMultipleDsas result
// as stated by RFC 3296 Section 5.6.2
throw new LdapAffectMultipleDsaException();
}
}
finally
{
// Unlock the ReferralManager
directoryService.getReferralManager().unlock();
}
// Find the working partition
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
moveAndRenameContext.setPartition( partition );
lockWrite();
// Start a Write transaction right away
PartitionTxn transaction = moveAndRenameContext.getSession().getTransaction( partition );
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( moveAndRenameContext.getSession().hasSessionTransaction() )
{
moveAndRenameContext.getSession().addTransaction( partition, transaction );
}
}
moveAndRenameContext.setOriginalEntry( getOriginalEntry( moveAndRenameContext ) );
moveAndRenameContext.setModifiedEntry( moveAndRenameContext.getOriginalEntry().clone() );
moveAndRenameContext.setTransaction( transaction );
// Call the MoveAndRename method
Interceptor head = directoryService.getInterceptor( moveAndRenameContext.getNextInterceptor() );
head.moveAndRename( moveAndRenameContext );
if ( !moveAndRenameContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
transaction.abort();
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< MoveAndRenameOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "MoveAndRename operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public void rename( RenameOperationContext renameContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> RenameOperation : {}", renameContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the renameContext Dn
Dn dn = renameContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
renameContext.setDn( dn );
}
// Inject the newDn into the operation context
// Inject the new Dn into the context
if ( !dn.isEmpty() )
{
Dn newDn = dn.getParent();
Rdn newRdn = renameContext.getNewRdn();
if ( !newRdn.isSchemaAware() )
{
newRdn = new Rdn( directoryService.getSchemaManager(), newRdn );
renameContext.setNewRdn( newRdn );
}
newDn = newDn.add( renameContext.getNewRdn() );
renameContext.setNewDn( newDn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can delete it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !renameContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralException( parentEntry, childDn );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// We can't delete an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( renameContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralException( parentEntry, childDn );
}
}
}
}
finally
{
// Unlock the ReferralManager
directoryService.getReferralManager().unlock();
}
lockWrite();
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
// Start a Write transaction right away
PartitionTxn transaction = renameContext.getSession().getTransaction( partition );
// Call the rename method
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( renameContext.getSession().hasSessionTransaction() )
{
renameContext.getSession().addTransaction( partition, transaction );
}
}
renameContext.setPartition( partition );
// populate the context with the old entry
PartitionTxn partitionTxn = null;
try
{
partitionTxn = partition.beginReadTransaction();
renameContext.setTransaction( partitionTxn );
eagerlyPopulateFields( renameContext );
}
finally
{
try
{
// Nothing to do
if ( partitionTxn != null )
{
partitionTxn.close();
}
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
Entry originalEntry = getOriginalEntry( renameContext );
renameContext.setOriginalEntry( originalEntry );
renameContext.setModifiedEntry( originalEntry.clone() );
Interceptor head = directoryService.getInterceptor( renameContext.getNextInterceptor() );
// Start a Write transaction right away
transaction = renameContext.getSession().getTransaction( partition );
// Call the Rename method
try
{
if ( transaction == null )
{
transaction = partition.beginWriteTransaction();
if ( renameContext.getSession().hasSessionTransaction() )
{
renameContext.getSession().addTransaction( partition, transaction );
}
}
renameContext.setTransaction( transaction );
head.rename( renameContext );
if ( !renameContext.getSession().hasSessionTransaction() )
{
transaction.commit();
}
}
catch ( LdapException le )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw le;
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
}
catch ( IOException ioe )
{
try
{
if ( transaction != null )
{
transaction.abort();
}
throw new LdapOtherException( ioe.getMessage(), ioe );
}
catch ( IOException ioe2 )
{
throw new LdapOtherException( ioe2.getMessage(), ioe2 );
}
}
}
finally
{
unlockWrite();
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< RenameOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Rename operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
/**
* {@inheritDoc}
*/
public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> SearchOperation : {}", searchContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Normalize the searchContext Dn
Dn dn = searchContext.getDn();
if ( !dn.isSchemaAware() )
{
dn = new Dn( directoryService.getSchemaManager(), dn );
searchContext.setDn( dn );
}
// We have to deal with the referral first
directoryService.getReferralManager().lockRead();
try
{
// Check if we have an ancestor for this Dn
Entry parentEntry = directoryService.getReferralManager().getParentReferral( dn );
if ( parentEntry != null )
{
// We have found a parent referral for the current Dn
Dn childDn = dn.getDescendantOf( parentEntry.getDn() );
if ( directoryService.getReferralManager().isReferral( dn ) )
{
// This is a referral. We can return it if the ManageDsaIt flag is true
// Otherwise, we just throw a LdapReferralException
if ( !searchContext.isReferralIgnored() )
{
// Throw a Referral Exception
throw buildReferralExceptionForSearch( parentEntry, childDn, searchContext.getScope() );
}
}
else if ( directoryService.getReferralManager().hasParentReferral( dn ) )
{
// We can't search an entry which has an ancestor referral
// Depending on the Context.REFERRAL property value, we will throw
// a different exception.
if ( searchContext.isReferralIgnored() )
{
throw buildLdapPartialResultException( childDn );
}
else
{
throw buildReferralExceptionForSearch( parentEntry, childDn, searchContext.getScope() );
}
}
}
}
finally
{
// Unlock the ReferralManager
directoryService.getReferralManager().unlock();
}
// Call the Search method
Interceptor head = directoryService.getInterceptor( searchContext.getNextInterceptor() );
EntryFilteringCursor cursor = null;
Partition partition = directoryService.getPartitionNexus().getPartition( dn );
try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
{
searchContext.setPartition( partition );
searchContext.setTransaction( partitionTxn );
lockRead();
try
{
cursor = head.search( searchContext );
}
finally
{
unlockRead();
}
}
catch ( IOException ioe )
{
throw new LdapOtherException( ioe.getMessage(), ioe );
}
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< SearchOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Search operation took {} ns", ( System.nanoTime() - opStart ) );
}
return cursor;
}
/**
* {@inheritDoc}
*/
public void unbind( UnbindOperationContext unbindContext ) throws LdapException
{
if ( IS_DEBUG )
{
OPERATION_LOG.debug( ">> UnbindOperation : {}", unbindContext );
}
long opStart = 0L;
if ( IS_TIME )
{
opStart = System.nanoTime();
}
ensureStarted();
// Call the Unbind method
Interceptor head = directoryService.getInterceptor( unbindContext.getNextInterceptor() );
head.unbind( unbindContext );
if ( IS_DEBUG )
{
OPERATION_LOG.debug( "<< UnbindOperation successful" );
}
if ( IS_TIME )
{
OPERATION_TIME.debug( "Unbind operation took {} ns", ( System.nanoTime() - opStart ) );
}
}
private void ensureStarted() throws LdapServiceUnavailableException
{
if ( !directoryService.isStarted() )
{
throw new LdapServiceUnavailableException( ResultCodeEnum.UNAVAILABLE, I18n.err( I18n.ERR_316 ) );
}
}
}