blob: 10e8500ad3dca66cba7702e5216f10eef7cc52a5 [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.trigger;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import org.apache.directory.server.core.DirectoryServiceConfiguration;
import org.apache.directory.server.core.configuration.InterceptorConfiguration;
import org.apache.directory.server.core.interceptor.BaseInterceptor;
import org.apache.directory.server.core.interceptor.InterceptorChain;
import org.apache.directory.server.core.interceptor.NextInterceptor;
import org.apache.directory.server.core.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
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.OperationContext;
import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
import org.apache.directory.server.core.invocation.Invocation;
import org.apache.directory.server.core.invocation.InvocationStack;
import org.apache.directory.server.core.jndi.ServerLdapContext;
import org.apache.directory.server.core.partition.PartitionNexusProxy;
import org.apache.directory.server.core.sp.LdapClassLoader;
import org.apache.directory.server.core.subtree.SubentryService;
import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.exception.LdapNamingException;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
import org.apache.directory.shared.ldap.trigger.ActionTime;
import org.apache.directory.shared.ldap.trigger.LdapOperation;
import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
import org.apache.directory.shared.ldap.trigger.TriggerSpecification.SPSpec;
import org.apache.directory.shared.ldap.util.DirectoryClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Trigger Service based on the Trigger Specification.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev:$
*/
public class TriggerService extends BaseInterceptor
{
/** the logger for this class */
private static final Logger log = LoggerFactory.getLogger( TriggerService.class );
/** The service name */
public static final String NAME = "triggerService";
/** the entry trigger attribute string: entryTrigger */
private static final String ENTRY_TRIGGER_ATTR = "entryTriggerSpecification";
/**
* the multivalued operational attribute used to track the prescriptive
* trigger subentries that apply to an entry
*/
private static final String TRIGGER_SUBENTRIES_ATTR = "triggerExecutionSubentries";
/** a triggerSpecCache that responds to add, delete, and modify attempts */
private TriggerSpecCache triggerSpecCache;
/** a normalizing Trigger Specification parser */
private TriggerSpecificationParser triggerParser;
/** the attribute type registry */
private AttributeTypeRegistry attrRegistry;
/** */
private InterceptorChain chain;
/** whether or not this interceptor is activated */
private boolean enabled = true;
/** a Trigger Execution Authorizer */
private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer();
/**
* Adds prescriptiveTrigger TriggerSpecificaitons to a collection of
* TriggerSpeficaitions by accessing the triggerSpecCache. The trigger
* specification cache is accessed for each trigger subentry associated
* with the entry.
* Note that subentries are handled differently: their parent, the administrative
* entry is accessed to determine the perscriptiveTriggers effecting the AP
* and hence the subentry which is considered to be in the same context.
*
* @param triggerSpecs the collection of trigger specifications to add to
* @param dn the normalized distinguished name of the entry
* @param entry the target entry that is considered as the trigger source
* @throws NamingException if there are problems accessing attribute values
*/
private void addPrescriptiveTriggerSpecs( List<TriggerSpecification> triggerSpecs, PartitionNexusProxy proxy,
LdapDN dn, Attributes entry ) throws NamingException
{
/*
* If the protected entry is a subentry, then the entry being evaluated
* for perscriptiveTriggerss is in fact the administrative entry. By
* substituting the administrative entry for the actual subentry the
* code below this "if" statement correctly evaluates the effects of
* perscriptiveTrigger on the subentry. Basically subentries are considered
* to be in the same naming context as their access point so the subentries
* effecting their parent entry applies to them as well.
*/
if ( entry.get( SchemaConstants.OBJECT_CLASS_AT ).contains( SchemaConstants.SUBENTRY_OC ) )
{
LdapDN parentDn = ( LdapDN ) dn.clone();
parentDn.remove( dn.size() - 1 );
entry = proxy.lookup( new LookupOperationContext( parentDn ), PartitionNexusProxy.LOOKUP_BYPASS );
}
Attribute subentries = entry.get( TRIGGER_SUBENTRIES_ATTR );
if ( subentries == null )
{
return;
}
for ( int ii = 0; ii < subentries.size(); ii++ )
{
String subentryDn = ( String ) subentries.get( ii );
triggerSpecs.addAll( triggerSpecCache.getSubentryTriggerSpecs( subentryDn ) );
}
}
/**
* Adds the set of entryTriggers to a collection of trigger specifications.
* The entryTrigger is parsed and tuples are generated on they fly then
* added to the collection.
*
* @param triggerSpecs the collection of trigger specifications to add to
* @param entry the target entry that is considered as the trigger source
* @throws NamingException if there are problems accessing attribute values
*/
private void addEntryTriggerSpecs( List<TriggerSpecification> triggerSpecs, Attributes entry ) throws NamingException
{
Attribute entryTrigger = entry.get( ENTRY_TRIGGER_ATTR );
if ( entryTrigger == null )
{
return;
}
for ( int ii = 0; ii < entryTrigger.size(); ii++ )
{
String triggerString = ( String ) entryTrigger.get( ii );
TriggerSpecification item;
try
{
item = triggerParser.parse( triggerString );
}
catch ( ParseException e )
{
String msg = "failed to parse entryTrigger: " + triggerString;
log.error( msg, e );
throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
}
triggerSpecs.add( item );
}
}
/**
* Return a selection of trigger specifications for a certain type of trigger action time.
*
* @NOTE: This method serves as an extion point for new Action Time types.
*
* @param triggerSpecs
* @param ldapOperation
*/
public Map<ActionTime, List<TriggerSpecification>> getActionTimeMappedTriggerSpecsForOperation( List<TriggerSpecification> triggerSpecs, LdapOperation ldapOperation )
{
List<TriggerSpecification> afterTriggerSpecs = new ArrayList<TriggerSpecification>();
Map<ActionTime, List<TriggerSpecification>> triggerSpecMap = new HashMap<ActionTime, List<TriggerSpecification>>();
Iterator<TriggerSpecification> it = triggerSpecs.iterator();
while ( it.hasNext() )
{
TriggerSpecification triggerSpec = it.next();
if ( triggerSpec.getLdapOperation().equals( ldapOperation ) )
{
if ( triggerSpec.getActionTime().equals( ActionTime.AFTER ) )
{
afterTriggerSpecs.add( triggerSpec );
}
else
{
}
}
}
triggerSpecMap.put( ActionTime.AFTER, afterTriggerSpecs );
return triggerSpecMap;
}
////////////////////////////////////////////////////////////////////////////
// Interceptor Overrides
////////////////////////////////////////////////////////////////////////////
public void init( DirectoryServiceConfiguration dirServCfg, InterceptorConfiguration intCfg ) throws NamingException
{
super.init( dirServCfg, intCfg );
triggerSpecCache = new TriggerSpecCache( dirServCfg );
attrRegistry = dirServCfg.getRegistries().getAttributeTypeRegistry();
triggerParser = new TriggerSpecificationParser
( new NormalizerMappingResolver()
{
public Map getNormalizerMapping() throws NamingException
{
return attrRegistry.getNormalizerMapping();
}
}
);
chain = dirServCfg.getInterceptorChain();
this.enabled = true; // TODO: Get this from the configuration if needed.
}
public void add( NextInterceptor next, OperationContext addContext ) throws NamingException
{
LdapDN name = addContext.getDn();
Attributes entry = ((AddOperationContext)addContext).getEntry();
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.add( addContext );
return;
}
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
StoredProcedureParameterInjector injector = new AddStoredProcedureParameterInjector( invocation, name, entry );
// Gather Trigger Specifications which apply to the entry being deleted.
List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, entry );
/**
* NOTE: We do not handle entryTriggerSpecs for ADD operation.
*/
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.ADD.
Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.ADD );
next.add( addContext );
triggerSpecCache.subentryAdded( name, entry );
// Fire AFTER Triggers.
List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
}
public void delete( NextInterceptor next, OperationContext deleteContext ) throws NamingException
{
LdapDN name = deleteContext.getDn();
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.delete( deleteContext );
return;
}
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
Attributes deletedEntry = proxy.lookup( new LookupOperationContext( name ), PartitionNexusProxy.LOOKUP_BYPASS );
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
StoredProcedureParameterInjector injector = new DeleteStoredProcedureParameterInjector( invocation, name );
// Gather Trigger Specifications which apply to the entry being deleted.
List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, deletedEntry );
addEntryTriggerSpecs( triggerSpecs, deletedEntry );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.DELETE.
Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.DELETE );
next.delete( deleteContext );
triggerSpecCache.subentryDeleted( name, deletedEntry );
// Fire AFTER Triggers.
List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
}
public void modify( NextInterceptor next, OperationContext opContext ) throws NamingException
{
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.modify( opContext );
return;
}
LdapDN normName = opContext.getDn();
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
Attributes modifiedEntry = proxy.lookup( new LookupOperationContext( normName ), PartitionNexusProxy.LOOKUP_BYPASS );
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
StoredProcedureParameterInjector injector = new ModifyStoredProcedureParameterInjector( invocation, opContext );
// Gather Trigger Specifications which apply to the entry being modified.
List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( triggerSpecs, proxy, normName, modifiedEntry );
addEntryTriggerSpecs( triggerSpecs, modifiedEntry );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFY.
Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFY );
next.modify( opContext );
triggerSpecCache.subentryModified( opContext, modifiedEntry );
// Fire AFTER Triggers.
List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
}
public void rename( NextInterceptor next, OperationContext renameContext ) throws NamingException
{
LdapDN name = renameContext.getDn();
String newRdn = ((RenameOperationContext)renameContext).getNewRdn();
boolean deleteOldRn = ((RenameOperationContext)renameContext).getDelOldDn();
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.rename( renameContext );
return;
}
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
Attributes renamedEntry = proxy.lookup( new LookupOperationContext( name ), PartitionNexusProxy.LOOKUP_BYPASS );
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
LdapDN oldRDN = new LdapDN( name.getRdn().getUpName() );
LdapDN newRDN = new LdapDN( newRdn );
LdapDN oldSuperiorDN = ( LdapDN ) name.clone();
oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
LdapDN newSuperiorDN = ( LdapDN ) oldSuperiorDN.clone();
LdapDN oldDN = ( LdapDN ) name.clone();
LdapDN newDN = ( LdapDN ) name.clone();
newDN.add( newRdn );
StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
invocation, deleteOldRn, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
// Gather Trigger Specifications which apply to the entry being renamed.
List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, renamedEntry );
addEntryTriggerSpecs( triggerSpecs, renamedEntry );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFYDN_RENAME.
Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFYDN_RENAME );
next.rename( renameContext );
triggerSpecCache.subentryRenamed( name, newDN );
// Fire AFTER Triggers.
List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
}
public void moveAndRename( NextInterceptor next, OperationContext moveAndRenameContext ) throws NamingException
{
LdapDN oriChildName = moveAndRenameContext.getDn();
LdapDN parent = ((MoveAndRenameOperationContext)moveAndRenameContext).getParent();
String newRn = ((MoveAndRenameOperationContext)moveAndRenameContext).getNewRdn();
boolean deleteOldRn = ((MoveAndRenameOperationContext)moveAndRenameContext).getDelOldDn();
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.moveAndRename( moveAndRenameContext );
return;
}
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
Attributes movedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
LdapDN newRDN = new LdapDN( newRn );
LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
LdapDN newSuperiorDN = ( LdapDN ) parent.clone();
LdapDN oldDN = ( LdapDN ) oriChildName.clone();
LdapDN newDN = ( LdapDN ) parent.clone();
newDN.add( newRn );
StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
invocation, deleteOldRn, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
// Gather Trigger Specifications which apply to the entry being exported.
List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( exportTriggerSpecs, proxy, oriChildName, movedEntry );
addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
// Get the entry again without operational attributes
// because access control subentry operational attributes
// will not be valid at the new location.
// This will certainly be fixed by the SubentryService,
// but after this service.
Attributes importedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
// As the target entry does not exist yet and so
// its subentry operational attributes are not there,
// we need to construct an entry to represent it
// at least with minimal requirements which are object class
// and access control subentry operational attributes.
SubentryService subentryService = ( SubentryService ) chain.get( SubentryService.NAME );
Attributes fakeImportedEntry = subentryService.getSubentryAttributes( newDN, importedEntry );
NamingEnumeration attrList = importedEntry.getAll();
while ( attrList.hasMore() )
{
fakeImportedEntry.put( ( Attribute ) attrList.next() );
}
// Gather Trigger Specifications which apply to the entry being imported.
// Note: Entry Trigger Specifications are not valid for Import.
List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( importTriggerSpecs, proxy, newDN, fakeImportedEntry );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFYDN_EXPORT.
Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFYDN_IMPORT.
Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
next.moveAndRename( moveAndRenameContext );
triggerSpecCache.subentryRenamed( oldDN, newDN );
// Fire AFTER Triggers.
List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
executeTriggers( afterExportTriggerSpecs, injector, callerRootCtx );
executeTriggers( afterImportTriggerSpecs, injector, callerRootCtx );
}
public void move( NextInterceptor next, OperationContext moveContext ) throws NamingException
{
// Bypass trigger handling if the service is disabled.
if ( !enabled )
{
next.move( moveContext );
return;
}
LdapDN oriChildName = moveContext.getDn();
LdapDN newParentName = ((MoveOperationContext)moveContext).getParent();
// Gather supplementary data.
Invocation invocation = InvocationStack.getInstance().peek();
PartitionNexusProxy proxy = invocation.getProxy();
Attributes movedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
LdapDN newRDN = new LdapDN( oriChildName.getRdn().getUpName() );
LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
LdapDN newSuperiorDN = ( LdapDN ) newParentName.clone();
LdapDN oldDN = ( LdapDN ) oriChildName.clone();
LdapDN newDN = ( LdapDN ) newParentName.clone();
newDN.add( newRDN.getUpName() );
StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
invocation, false, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
// Gather Trigger Specifications which apply to the entry being exported.
List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( exportTriggerSpecs, proxy, oriChildName, movedEntry );
addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
// Get the entry again without operational attributes
// because access control subentry operational attributes
// will not be valid at the new location.
// This will certainly be fixed by the SubentryService,
// but after this service.
Attributes importedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
// As the target entry does not exist yet and so
// its subentry operational attributes are not there,
// we need to construct an entry to represent it
// at least with minimal requirements which are object class
// and access control subentry operational attributes.
SubentryService subentryService = ( SubentryService ) chain.get( SubentryService.NAME );
Attributes fakeImportedEntry = subentryService.getSubentryAttributes( newDN, importedEntry );
NamingEnumeration attrList = importedEntry.getAll();
while ( attrList.hasMore() )
{
fakeImportedEntry.put( ( Attribute ) attrList.next() );
}
// Gather Trigger Specifications which apply to the entry being imported.
// Note: Entry Trigger Specifications are not valid for Import.
List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
addPrescriptiveTriggerSpecs( importTriggerSpecs, proxy, newDN, fakeImportedEntry );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFYDN_EXPORT.
Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
// Gather a Map<ActionTime,TriggerSpecification> where TriggerSpecification.ldapOperation = LdapOperation.MODIFYDN_IMPORT.
Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
next.move( moveContext );
triggerSpecCache.subentryRenamed( oldDN, newDN );
// Fire AFTER Triggers.
List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
executeTriggers( afterExportTriggerSpecs, injector, callerRootCtx );
executeTriggers( afterImportTriggerSpecs, injector, callerRootCtx );
}
////////////////////////////////////////////////////////////////////////////
// Utility Methods
////////////////////////////////////////////////////////////////////////////
private Object executeTriggers( List<TriggerSpecification> triggerSpecs, StoredProcedureParameterInjector injector, ServerLdapContext callerRootCtx ) throws NamingException
{
Object result = null;
Iterator<TriggerSpecification> it = triggerSpecs.iterator();
while( it.hasNext() )
{
TriggerSpecification tsec = it.next();
// TODO: Replace the Authorization Code with a REAL one.
if ( triggerExecutionAuthorizer.hasPermission() )
{
/**
* If there is only one Trigger to be executed, this assignment
* will make sense (as in INSTEADOF search Triggers).
*/
result = executeTrigger( tsec, injector, callerRootCtx );
}
}
/**
* If only one Trigger has been executed, returning its result
* will make sense (as in INSTEADOF search Triggers).
*/
return result;
}
private Object executeTrigger( TriggerSpecification tsec, StoredProcedureParameterInjector injector, ServerLdapContext callerRootCtx ) throws NamingException
{
List<Object> returnValues = new ArrayList<Object>();
List<SPSpec> spSpecs = tsec.getSPSpecs();
for ( SPSpec spSpec : spSpecs )
{
List<Object> arguments = new ArrayList<Object>();
arguments.addAll( injector.getArgumentsToInject( spSpec.getParameters() ) );
List<Class> typeList = new ArrayList<Class>();
typeList.addAll( getTypesFromValues( arguments ) );
Class[] types = getTypesFromValues( arguments ).toArray( EMPTY_CLASS_ARRAY );
Object[] values = arguments.toArray();
Object returnValue = executeProcedure( callerRootCtx, spSpec.getName(), types, values );
returnValues.add(returnValue);
}
return returnValues;
}
private static Class[] EMPTY_CLASS_ARRAY = new Class[ 0 ];
private List<Class> getTypesFromValues( List objects )
{
List<Class> types = new ArrayList<Class>();
Iterator it = objects.iterator();
while( it.hasNext() )
{
types.add( it.next().getClass() );
}
return types;
}
private Object executeProcedure( ServerLdapContext ctx, String procedure, Class[] types, Object[] values ) throws NamingException
{
int lastDot = procedure.lastIndexOf( '.' );
String className = procedure.substring( 0, lastDot );
String methodName = procedure.substring( lastDot + 1 );
LdapClassLoader loader = new LdapClassLoader( ctx );
try
{
Class clazz = loader.loadClass( className );
Method proc = DirectoryClassUtils.getAssignmentCompatibleMethod( clazz, methodName, types );
return proc.invoke( null, values );
}
catch ( Exception e )
{
log.debug( "Exception occured during executing stored procedure:\n" +
e.getMessage() + "\n" + e.getStackTrace() );
LdapNamingException lne = new LdapNamingException( ResultCodeEnum.OTHER );
lne.setRootCause( e );
throw lne;
}
}
}