From commits-return-32537-apmail-directory-commits-archive=directory.apache.org@directory.apache.org Fri Oct 14 22:37:14 2011 Return-Path: X-Original-To: apmail-directory-commits-archive@www.apache.org Delivered-To: apmail-directory-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 61A137C3D for ; Fri, 14 Oct 2011 22:37:13 +0000 (UTC) Received: (qmail 14332 invoked by uid 500); 14 Oct 2011 22:37:13 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 14246 invoked by uid 500); 14 Oct 2011 22:37:13 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 14231 invoked by uid 99); 14 Oct 2011 22:37:13 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 14 Oct 2011 22:37:13 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 14 Oct 2011 22:37:06 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 51C7B2388BA2 for ; Fri, 14 Oct 2011 22:36:20 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1183537 [11/11] - in /directory/apacheds/trunk/interceptors: admin/ admin/.settings/ authn/ authn/.settings/ authz/.settings/ changelog/ changelog/src/ changelog/src/main/ changelog/src/main/java/ changelog/src/main/java/org/ changelog/src... Date: Fri, 14 Oct 2011 22:36:15 -0000 To: commits@directory.apache.org From: elecharny@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20111014223620.51C7B2388BA2@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java?rev=1183537&view=auto ============================================================================== --- directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java (added) +++ directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java Fri Oct 14 22:36:08 2011 @@ -0,0 +1,615 @@ +/* + * 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.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.api.entry.ClonedServerEntry; +import org.apache.directory.server.core.api.interceptor.BaseInterceptor; +import org.apache.directory.server.core.api.interceptor.InterceptorChain; +import org.apache.directory.server.core.api.interceptor.NextInterceptor; +import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; +import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext; +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.partition.ByPassConstants; +import org.apache.directory.server.core.shared.sp.StoredProcEngine; +import org.apache.directory.server.core.shared.sp.StoredProcEngineConfig; +import org.apache.directory.server.core.shared.sp.StoredProcExecutionManager; +import org.apache.directory.server.core.shared.sp.java.JavaStoredProcEngineConfig; +import org.apache.directory.server.core.subtree.SubentryInterceptor; +import org.apache.directory.server.i18n.I18n; +import org.apache.directory.shared.ldap.model.constants.SchemaConstants; +import org.apache.directory.shared.ldap.model.entry.Attribute; +import org.apache.directory.shared.ldap.model.entry.Entry; +import org.apache.directory.shared.ldap.model.entry.Value; +import org.apache.directory.shared.ldap.model.exception.LdapException; +import org.apache.directory.shared.ldap.model.exception.LdapOperationErrorException; +import org.apache.directory.shared.ldap.model.exception.LdapOtherException; +import org.apache.directory.shared.ldap.model.name.Dn; +import org.apache.directory.shared.ldap.model.name.Rdn; +import org.apache.directory.shared.ldap.model.schema.NormalizerMappingResolver; +import org.apache.directory.shared.ldap.model.schema.normalizers.OidNormalizer; +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.TriggerSpecification.SPSpec; +import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The Trigger Service based on the Trigger Specification. + * + * @author Apache Directory Project + */ +public class TriggerInterceptor extends BaseInterceptor +{ + /** the logger for this class */ + private static final Logger LOG = LoggerFactory.getLogger( TriggerInterceptor.class ); + + /** the entry trigger attribute string: entryTrigger */ + private static final String ENTRY_TRIGGER_ATTR = "entryTriggerSpecification"; + + /** a triggerSpecCache that responds to add, delete, and modify attempts */ + private TriggerSpecCache triggerSpecCache; + + /** a normalizing Trigger Specification parser */ + private TriggerSpecificationParser triggerParser; + + /** */ + private InterceptorChain chain; + + /** whether or not this interceptor is activated */ + private boolean enabled = true; + + /** a Trigger Execution Authorizer */ + private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer(); + + private StoredProcExecutionManager manager; + + /** + * 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 Exception if there are problems accessing attribute values + * @param proxy the partition nexus proxy + */ + private void addPrescriptiveTriggerSpecs( OperationContext opContext, List triggerSpecs, + Dn dn, Entry entry ) throws LdapException + { + + /* + * 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.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) + { + Dn parentDn = dn.getParent(); + + entry = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); + } + + Attribute subentries = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); + + if ( subentries == null ) + { + return; + } + + for ( Value value : subentries ) + { + String subentryDn = value.getString(); + 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 Exception if there are problems accessing attribute values + */ + private void addEntryTriggerSpecs( List triggerSpecs, Entry entry ) throws LdapException + { + Attribute entryTrigger = entry.get( ENTRY_TRIGGER_ATTR ); + + if ( entryTrigger == null ) + { + return; + } + + for ( Value value : entryTrigger ) + { + String triggerString = value.getString(); + TriggerSpecification item; + + try + { + item = triggerParser.parse( triggerString ); + } + catch ( ParseException e ) + { + String msg = I18n.err( I18n.ERR_72, triggerString ); + LOG.error( msg, e ); + throw new LdapOperationErrorException( msg ); + } + + 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 the trigger specifications + * @param ldapOperation the ldap operation being performed + * @return the set of trigger specs for a trigger action + */ + public Map> getActionTimeMappedTriggerSpecsForOperation( + List triggerSpecs, LdapOperation ldapOperation ) + { + List afterTriggerSpecs = new ArrayList(); + Map> triggerSpecMap = new HashMap>(); + + for ( TriggerSpecification triggerSpec : triggerSpecs ) + { + 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( DirectoryService directoryService ) throws LdapException + { + super.init( directoryService ); + + triggerSpecCache = new TriggerSpecCache( directoryService ); + + triggerParser = new TriggerSpecificationParser( new NormalizerMappingResolver() + { + public Map getNormalizerMapping() throws Exception + { + return schemaManager.getNormalizerMapping(); + } + } ); + + chain = directoryService.getInterceptorChain(); + + //StoredProcEngineConfig javaxScriptSPEngineConfig = new JavaxStoredProcEngineConfig(); + StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig(); + List spEngineConfigs = new ArrayList(); + //spEngineConfigs.add( javaxScriptSPEngineConfig ); + spEngineConfigs.add( javaSPEngineConfig ); + String spContainer = "ou=Stored Procedures,ou=system"; + manager = new StoredProcExecutionManager( spContainer, spEngineConfigs ); + + this.enabled = true; // TODO: Get this from the configuration if needed. + } + + + public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException + { + Dn name = addContext.getDn(); + Entry entry = addContext.getEntry(); + + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.add( addContext ); + return; + } + + // Gather supplementary data. + StoredProcedureParameterInjector injector = new AddStoredProcedureParameterInjector( addContext, name, entry ); + + // Gather Trigger Specifications which apply to the entry being added. + List triggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( addContext, triggerSpecs, name, entry ); + + /** + * NOTE: We do not handle entryTriggerSpecs for ADD operation. + */ + + Map> triggerMap = getActionTimeMappedTriggerSpecsForOperation( + triggerSpecs, LdapOperation.ADD ); + + next.add( addContext ); + triggerSpecCache.subentryAdded( name, entry ); + + // Fire AFTER Triggers. + List afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); + executeTriggers( addContext, afterTriggerSpecs, injector ); + } + + + public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws LdapException + { + Dn name = deleteContext.getDn(); + + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.delete( deleteContext ); + return; + } + + // Gather supplementary data. + Entry deletedEntry = deleteContext.getEntry(); + + StoredProcedureParameterInjector injector = new DeleteStoredProcedureParameterInjector( deleteContext, name ); + + // Gather Trigger Specifications which apply to the entry being deleted. + List triggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( deleteContext, triggerSpecs, name, deletedEntry ); + addEntryTriggerSpecs( triggerSpecs, deletedEntry ); + + Map> triggerMap = getActionTimeMappedTriggerSpecsForOperation( + triggerSpecs, LdapOperation.DELETE ); + + next.delete( deleteContext ); + + triggerSpecCache.subentryDeleted( name, deletedEntry ); + + // Fire AFTER Triggers. + List afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); + executeTriggers( deleteContext, afterTriggerSpecs, injector ); + } + + + public void modify( NextInterceptor next, ModifyOperationContext modifyContext ) throws LdapException + { + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.modify( modifyContext ); + return; + } + + Dn normName = modifyContext.getDn(); + + // Gather supplementary data. + Entry originalEntry = modifyContext.getEntry(); + + StoredProcedureParameterInjector injector = new ModifyStoredProcedureParameterInjector( modifyContext ); + + // Gather Trigger Specifications which apply to the entry being modified. + List triggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( modifyContext, triggerSpecs, normName, originalEntry ); + addEntryTriggerSpecs( triggerSpecs, originalEntry ); + + Map> triggerMap = getActionTimeMappedTriggerSpecsForOperation( + triggerSpecs, LdapOperation.MODIFY ); + + next.modify( modifyContext ); + + triggerSpecCache.subentryModified( modifyContext, originalEntry ); + + // Fire AFTER Triggers. + List afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); + executeTriggers( modifyContext, afterTriggerSpecs, injector ); + } + + + public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws LdapException + { + Dn name = renameContext.getDn(); + Rdn newRdn = renameContext.getNewRdn(); + boolean deleteOldRn = renameContext.getDeleteOldRdn(); + + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.rename( renameContext ); + return; + } + + // Gather supplementary data. + Entry renamedEntry = ((ClonedServerEntry)renameContext.getEntry()).getClonedEntry(); + + // @TODO : To be completely reviewed !!! + Rdn oldRdn = name.getRdn(); + Dn oldSuperiorDn = name.getParent(); + Dn newSuperiorDn = oldSuperiorDn; + Dn oldDn = name; + Dn newDn = name; + newDn = newDn.add( newRdn ); + + StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( renameContext, + deleteOldRn, oldRdn, newRdn, oldSuperiorDn, newSuperiorDn, oldDn, newDn); + + // Gather Trigger Specifications which apply to the entry being renamed. + List triggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( renameContext, triggerSpecs, name, renamedEntry ); + addEntryTriggerSpecs( triggerSpecs, renamedEntry ); + + Map> triggerMap = getActionTimeMappedTriggerSpecsForOperation( + triggerSpecs, LdapOperation.MODIFYDN_RENAME ); + + next.rename( renameContext ); + triggerSpecCache.subentryRenamed( name, newDn); + + // Fire AFTER Triggers. + List afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); + executeTriggers( renameContext, afterTriggerSpecs, injector ); + } + + + public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException + { + Dn oldDn = moveAndRenameContext.getDn(); + Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn(); + Rdn newRdn = moveAndRenameContext.getNewRdn(); + boolean deleteOldRn = moveAndRenameContext.getDeleteOldRdn(); + + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.moveAndRename( moveAndRenameContext ); + return; + } + + // Gather supplementary data. + Entry movedEntry = moveAndRenameContext.getOriginalEntry(); + + Rdn oldRdn = oldDn.getRdn(); + Dn oldSuperiorDn = oldDn.getParent(); + Dn oldDN = oldDn; + Dn newDn = moveAndRenameContext.getNewDn(); + + StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( moveAndRenameContext, + deleteOldRn, oldRdn, newRdn, oldSuperiorDn, newSuperiorDn, oldDN, newDn); + + // Gather Trigger Specifications which apply to the entry being exported. + List exportTriggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( moveAndRenameContext, exportTriggerSpecs, oldDn, 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 SubentryInterceptor, + // but after this service. + Entry importedEntry = moveAndRenameContext.lookup( oldDn, ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); + + // 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. + SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class + .getSimpleName() ); + Entry fakeImportedEntry = subentryInterceptor.getSubentryAttributes(newDn, importedEntry ); + + for ( Attribute attribute : importedEntry ) + { + fakeImportedEntry.put( attribute ); + } + + // Gather Trigger Specifications which apply to the entry being imported. + // Note: Entry Trigger Specifications are not valid for Import. + List importTriggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( moveAndRenameContext, importTriggerSpecs, newDn, fakeImportedEntry ); + + Map> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( + exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT ); + + Map> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( + importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT ); + + next.moveAndRename( moveAndRenameContext ); + triggerSpecCache.subentryRenamed( oldDN, newDn); + + // Fire AFTER Triggers. + List afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER ); + List afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER ); + executeTriggers( moveAndRenameContext, afterExportTriggerSpecs, injector ); + executeTriggers( moveAndRenameContext, afterImportTriggerSpecs, injector ); + } + + + /** + * {@inheritDoc} + */ + public void move( NextInterceptor next, MoveOperationContext moveContext ) throws LdapException + { + // Bypass trigger handling if the service is disabled. + if ( !enabled ) + { + next.move( moveContext ); + return; + } + + Rdn rdn = moveContext.getRdn(); + Dn dn = moveContext.getDn(); + Dn newDn = moveContext.getNewDn(); + Dn oldSuperior = moveContext.getOldSuperior(); + Dn newSuperior = moveContext.getNewSuperior(); + + // Gather supplementary data. + Entry movedEntry = moveContext.getOriginalEntry(); + + //Rdn newRDN = dn.getRdn(); + + StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( moveContext, false, + rdn, rdn, oldSuperior, newSuperior, dn, newDn ); + + // Gather Trigger Specifications which apply to the entry being exported. + List exportTriggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( moveContext, exportTriggerSpecs, dn, 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 SubentryInterceptor, + // but after this service. + Entry importedEntry = moveContext.lookup( dn, ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); + + // 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. + SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class + .getSimpleName() ); + Entry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDn, importedEntry ); + + for ( Attribute attribute : importedEntry ) + { + fakeImportedEntry.put( attribute ); + } + + // Gather Trigger Specifications which apply to the entry being imported. + // Note: Entry Trigger Specifications are not valid for Import. + List importTriggerSpecs = new ArrayList(); + addPrescriptiveTriggerSpecs( moveContext, importTriggerSpecs, newDn, fakeImportedEntry ); + + Map> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( + exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT ); + + Map> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( + importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT ); + + next.move( moveContext ); + triggerSpecCache.subentryRenamed( dn, newDn ); + + // Fire AFTER Triggers. + List afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER ); + List afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER ); + executeTriggers( moveContext, afterExportTriggerSpecs, injector ); + executeTriggers( moveContext, afterImportTriggerSpecs, injector ); + } + + + //////////////////////////////////////////////////////////////////////////// + // Utility Methods + //////////////////////////////////////////////////////////////////////////// + + private Object executeTriggers( OperationContext opContext, List triggerSpecs, + StoredProcedureParameterInjector injector ) throws LdapException + { + Object result = null; + + for ( TriggerSpecification triggerSpec : triggerSpecs ) + { + // TODO: Replace the Authorization Code with a REAL one. + if ( triggerExecutionAuthorizer.hasPermission( opContext ) ) + { + /** + * If there is only one Trigger to be executed, this assignment + * will make sense (as in INSTEADOF search Triggers). + */ + result = executeTrigger( opContext, triggerSpec, injector ); + } + } + + /** + * If only one Trigger has been executed, returning its result + * can make sense (as in INSTEADOF Search Triggers). + */ + return result; + } + + + private Object executeTrigger( OperationContext opContext, TriggerSpecification tsec, + StoredProcedureParameterInjector injector ) throws LdapException + { + List returnValues = new ArrayList(); + List spSpecs = tsec.getSPSpecs(); + + for ( SPSpec spSpec : spSpecs ) + { + List arguments = new ArrayList(); + arguments.addAll( injector.getArgumentsToInject( opContext, spSpec.getParameters() ) ); + Object[] values = arguments.toArray(); + Object returnValue = executeProcedure( opContext, spSpec.getName(), values ); + returnValues.add( returnValue ); + } + + return returnValues; + } + + + private Object executeProcedure( OperationContext opContext, String procedure, Object[] values ) throws LdapException + { + try + { + Entry spUnit = manager.findStoredProcUnit( opContext.getSession(), procedure ); + StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit ); + + return engine.invokeProcedure( opContext.getSession(), procedure, values ); + } + catch ( Exception e ) + { + LdapOtherException lne = new LdapOtherException( e.getMessage(), e ); + lne.initCause( e ); + throw lne; + } + } +} Added: directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java?rev=1183537&view=auto ============================================================================== --- directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java (added) +++ directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java Fri Oct 14 22:36:08 2011 @@ -0,0 +1,262 @@ +/* + * 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.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.naming.directory.SearchControls; + +import org.apache.directory.server.constants.ApacheSchemaConstants; +import org.apache.directory.server.constants.ServerDNConstants; +import org.apache.directory.server.core.shared.DefaultCoreSession; +import org.apache.directory.server.core.api.CoreSession; +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.api.LdapPrincipal; +import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; +import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; +import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; +import org.apache.directory.server.core.api.partition.PartitionNexus; +import org.apache.directory.server.i18n.I18n; +import org.apache.directory.shared.ldap.model.constants.AuthenticationLevel; +import org.apache.directory.shared.ldap.model.constants.SchemaConstants; +import org.apache.directory.shared.ldap.model.entry.Attribute; +import org.apache.directory.shared.ldap.model.entry.Entry; +import org.apache.directory.shared.ldap.model.entry.Modification; +import org.apache.directory.shared.ldap.model.entry.StringValue; +import org.apache.directory.shared.ldap.model.entry.Value; +import org.apache.directory.shared.ldap.model.exception.LdapException; +import org.apache.directory.shared.ldap.model.exception.LdapOperationException; +import org.apache.directory.shared.ldap.model.filter.EqualityNode; +import org.apache.directory.shared.ldap.model.filter.ExprNode; +import org.apache.directory.shared.ldap.model.message.AliasDerefMode; +import org.apache.directory.shared.ldap.model.name.Dn; +import org.apache.directory.shared.ldap.model.schema.AttributeType; +import org.apache.directory.shared.ldap.model.schema.NormalizerMappingResolver; +import org.apache.directory.shared.ldap.model.schema.SchemaManager; +import org.apache.directory.shared.ldap.model.schema.normalizers.OidNormalizer; +import org.apache.directory.shared.ldap.trigger.TriggerSpecification; +import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A cache for Trigger Specifications which responds to specific events to + * perform cache house keeping as trigger subentries are added, deleted + * and modified. + * + * @author Apache Directory Project + */ +public class TriggerSpecCache +{ + /** the attribute id for prescriptive trigger: prescriptiveTrigger */ + private static final String PRESCRIPTIVE_TRIGGER_ATTR = "prescriptiveTriggerSpecification"; + + /** the logger for this class */ + private static final Logger LOG = LoggerFactory.getLogger( TriggerSpecCache.class ); + + /** a map of strings to TriggerSpecification collections */ + private final Map> triggerSpecs = new HashMap>(); + /** a handle on the partition nexus */ + private final PartitionNexus nexus; + /** a normalizing TriggerSpecification parser */ + private final TriggerSpecificationParser triggerSpecParser; + + + /** + * Creates a TriggerSpecification cache. + * + * @param directoryService the directory service core + * @throws LdapException with problems initializing cache + */ + public TriggerSpecCache( DirectoryService directoryService ) throws LdapException + { + this.nexus = directoryService.getPartitionNexus(); + final SchemaManager schemaManager = directoryService.getSchemaManager(); + + triggerSpecParser = new TriggerSpecificationParser( new NormalizerMappingResolver() + { + public Map getNormalizerMapping() throws Exception + { + return schemaManager.getNormalizerMapping(); + } + }); + + initialize( directoryService ); + } + + + private void initialize( DirectoryService directoryService ) throws LdapException + { + // search all naming contexts for trigger subentenries + // generate TriggerSpecification arrays for each subentry + // add that subentry to the hash + Set suffixes = nexus.listSuffixes(); + + AttributeType objectClassAt = directoryService.getSchemaManager(). + getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); + + for ( String suffix:suffixes ) + { + Dn baseDn = directoryService.getDnFactory().create( suffix ); + ExprNode filter = new EqualityNode( objectClassAt, + new StringValue( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ); + SearchControls ctls = new SearchControls(); + ctls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + + Dn adminDn = directoryService.getDnFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); + CoreSession adminSession = new DefaultCoreSession( + new LdapPrincipal( directoryService.getSchemaManager(), adminDn, AuthenticationLevel.STRONG ), directoryService ); + + SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, baseDn, + filter, ctls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS ); + + EntryFilteringCursor results = nexus.search( searchOperationContext ); + + try + { + while ( results.next() ) + { + Entry resultEntry = results.get(); + Dn subentryDn = resultEntry.getDn(); + Attribute triggerSpec = resultEntry.get( PRESCRIPTIVE_TRIGGER_ATTR ); + + if ( triggerSpec == null ) + { + LOG.warn( "Found triggerExecutionSubentry '" + subentryDn + "' without any " + PRESCRIPTIVE_TRIGGER_ATTR ); + continue; + } + + Dn normSubentryName = subentryDn.apply( directoryService.getSchemaManager() ); + subentryAdded( normSubentryName, resultEntry ); + } + + results.close(); + } + catch ( Exception e ) + { + throw new LdapOperationException( e.getMessage(), e ); + } + } + } + + + private boolean hasPrescriptiveTrigger( Entry entry ) throws LdapException + { + // only do something if the entry contains prescriptiveTrigger + Attribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); + + return triggerSpec != null; + } + + + public void subentryAdded( Dn normName, Entry entry ) throws LdapException + { + // only do something if the entry contains prescriptiveTrigger + Attribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR ); + + if ( triggerSpec == null ) + { + return; + } + + List subentryTriggerSpecs = new ArrayList(); + + for ( Value value:triggerSpec ) + { + TriggerSpecification item = null; + + try + { + item = triggerSpecParser.parse( value.getString() ); + subentryTriggerSpecs.add( item ); + } + catch ( ParseException e ) + { + String msg = I18n.err( I18n.ERR_73, item ); + LOG.error( msg, e ); + } + + } + + triggerSpecs.put( normName.getNormName(), subentryTriggerSpecs ); + } + + + public void subentryDeleted( Dn normName, Entry entry ) throws LdapException + { + if ( !hasPrescriptiveTrigger( entry ) ) + { + return; + } + + triggerSpecs.remove( normName.toString() ); + } + + + public void subentryModified( ModifyOperationContext opContext, Entry entry ) throws LdapException + { + if ( !hasPrescriptiveTrigger( entry ) ) + { + return; + } + + Dn normName = opContext.getDn(); + List mods = opContext.getModItems(); + + boolean isTriggerSpecModified = false; + + for ( Modification mod : mods ) + { + isTriggerSpecModified |= mod.getAttribute().contains( PRESCRIPTIVE_TRIGGER_ATTR ); + } + + if ( isTriggerSpecModified ) + { + subentryDeleted( normName, entry ); + subentryAdded( normName, entry ); + } + } + + + public List getSubentryTriggerSpecs( String subentryDn ) + { + List subentryTriggerSpecs = triggerSpecs.get( subentryDn ); + if ( subentryTriggerSpecs == null ) + { + return Collections.emptyList(); + } + return Collections.unmodifiableList( subentryTriggerSpecs ); + } + + + public void subentryRenamed( Dn oldName, Dn newName ) + { + triggerSpecs.put( newName.getNormName(), triggerSpecs.remove( oldName.getNormName() ) ); + } +}