001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021 package org.apache.directory.server.core.trigger; 022 023 024 import java.text.ParseException; 025 import java.util.ArrayList; 026 import java.util.HashMap; 027 import java.util.List; 028 import java.util.Map; 029 030 import org.apache.directory.server.core.DirectoryService; 031 import org.apache.directory.server.core.entry.ClonedServerEntry; 032 import org.apache.directory.server.core.interceptor.BaseInterceptor; 033 import org.apache.directory.server.core.interceptor.InterceptorChain; 034 import org.apache.directory.server.core.interceptor.NextInterceptor; 035 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 036 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; 037 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 038 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext; 039 import org.apache.directory.server.core.interceptor.context.MoveOperationContext; 040 import org.apache.directory.server.core.interceptor.context.OperationContext; 041 import org.apache.directory.server.core.interceptor.context.RenameOperationContext; 042 import org.apache.directory.server.core.partition.ByPassConstants; 043 import org.apache.directory.server.core.sp.StoredProcEngine; 044 import org.apache.directory.server.core.sp.StoredProcEngineConfig; 045 import org.apache.directory.server.core.sp.StoredProcExecutionManager; 046 import org.apache.directory.server.core.sp.java.JavaStoredProcEngineConfig; 047 import org.apache.directory.server.core.subtree.SubentryInterceptor; 048 import org.apache.directory.server.i18n.I18n; 049 import org.apache.directory.shared.ldap.constants.SchemaConstants; 050 import org.apache.directory.shared.ldap.entry.EntryAttribute; 051 import org.apache.directory.shared.ldap.entry.ServerEntry; 052 import org.apache.directory.shared.ldap.entry.Value; 053 import org.apache.directory.shared.ldap.exception.LdapOperationErrorException; 054 import org.apache.directory.shared.ldap.exception.LdapOtherException; 055 import org.apache.directory.shared.ldap.name.DN; 056 import org.apache.directory.shared.ldap.name.RDN; 057 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver; 058 import org.apache.directory.shared.ldap.schema.SchemaManager; 059 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; 060 import org.apache.directory.shared.ldap.trigger.ActionTime; 061 import org.apache.directory.shared.ldap.trigger.LdapOperation; 062 import org.apache.directory.shared.ldap.trigger.TriggerSpecification; 063 import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser; 064 import org.apache.directory.shared.ldap.trigger.TriggerSpecification.SPSpec; 065 import org.slf4j.Logger; 066 import org.slf4j.LoggerFactory; 067 068 069 /** 070 * The Trigger Service based on the Trigger Specification. 071 * 072 * @org.apache.xbean.XBean 073 * 074 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 075 * @version $Rev:$ 076 */ 077 public class TriggerInterceptor extends BaseInterceptor 078 { 079 /** the logger for this class */ 080 private static final Logger LOG = LoggerFactory.getLogger( TriggerInterceptor.class ); 081 082 /** the entry trigger attribute string: entryTrigger */ 083 private static final String ENTRY_TRIGGER_ATTR = "entryTriggerSpecification"; 084 085 /** a triggerSpecCache that responds to add, delete, and modify attempts */ 086 private TriggerSpecCache triggerSpecCache; 087 088 /** a normalizing Trigger Specification parser */ 089 private TriggerSpecificationParser triggerParser; 090 091 /** */ 092 private InterceptorChain chain; 093 094 /** whether or not this interceptor is activated */ 095 private boolean enabled = true; 096 097 /** a Trigger Execution Authorizer */ 098 private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer(); 099 100 private StoredProcExecutionManager manager; 101 102 /** 103 * Adds prescriptiveTrigger TriggerSpecificaitons to a collection of 104 * TriggerSpeficaitions by accessing the triggerSpecCache. The trigger 105 * specification cache is accessed for each trigger subentry associated 106 * with the entry. 107 * Note that subentries are handled differently: their parent, the administrative 108 * entry is accessed to determine the perscriptiveTriggers effecting the AP 109 * and hence the subentry which is considered to be in the same context. 110 * 111 * @param triggerSpecs the collection of trigger specifications to add to 112 * @param dn the normalized distinguished name of the entry 113 * @param entry the target entry that is considered as the trigger source 114 * @throws Exception if there are problems accessing attribute values 115 * @param proxy the partition nexus proxy 116 */ 117 private void addPrescriptiveTriggerSpecs( OperationContext opContext, List<TriggerSpecification> triggerSpecs, 118 DN dn, ServerEntry entry ) throws Exception 119 { 120 121 /* 122 * If the protected entry is a subentry, then the entry being evaluated 123 * for perscriptiveTriggerss is in fact the administrative entry. By 124 * substituting the administrative entry for the actual subentry the 125 * code below this "if" statement correctly evaluates the effects of 126 * perscriptiveTrigger on the subentry. Basically subentries are considered 127 * to be in the same naming context as their access point so the subentries 128 * effecting their parent entry applies to them as well. 129 */ 130 if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) 131 { 132 DN parentDn = ( DN ) dn.clone(); 133 parentDn.remove( dn.size() - 1 ); 134 135 entry = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS ); 136 } 137 138 EntryAttribute subentries = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); 139 140 if ( subentries == null ) 141 { 142 return; 143 } 144 145 for ( Value<?> value:subentries ) 146 { 147 String subentryDn = value.getString(); 148 triggerSpecs.addAll( triggerSpecCache.getSubentryTriggerSpecs( subentryDn ) ); 149 } 150 } 151 152 /** 153 * Adds the set of entryTriggers to a collection of trigger specifications. 154 * The entryTrigger is parsed and tuples are generated on they fly then 155 * added to the collection. 156 * 157 * @param triggerSpecs the collection of trigger specifications to add to 158 * @param entry the target entry that is considered as the trigger source 159 * @throws Exception if there are problems accessing attribute values 160 */ 161 private void addEntryTriggerSpecs( List<TriggerSpecification> triggerSpecs, ServerEntry entry ) throws Exception 162 { 163 EntryAttribute entryTrigger = entry.get( ENTRY_TRIGGER_ATTR ); 164 165 if ( entryTrigger == null ) 166 { 167 return; 168 } 169 170 for ( Value<?> value:entryTrigger ) 171 { 172 String triggerString = value.getString(); 173 TriggerSpecification item; 174 175 try 176 { 177 item = triggerParser.parse( triggerString ); 178 } 179 catch ( ParseException e ) 180 { 181 String msg = I18n.err( I18n.ERR_72, triggerString ); 182 LOG.error( msg, e ); 183 throw new LdapOperationErrorException( msg ); 184 } 185 186 triggerSpecs.add( item ); 187 } 188 } 189 190 /** 191 * Return a selection of trigger specifications for a certain type of trigger action time. 192 * 193 * @note This method serves as an extion point for new Action Time types. 194 * 195 * @param triggerSpecs the trigger specifications 196 * @param ldapOperation the ldap operation being performed 197 * @return the set of trigger specs for a trigger action 198 */ 199 public Map<ActionTime, List<TriggerSpecification>> getActionTimeMappedTriggerSpecsForOperation( List<TriggerSpecification> triggerSpecs, LdapOperation ldapOperation ) 200 { 201 List<TriggerSpecification> afterTriggerSpecs = new ArrayList<TriggerSpecification>(); 202 Map<ActionTime, List<TriggerSpecification>> triggerSpecMap = new HashMap<ActionTime, List<TriggerSpecification>>(); 203 204 for ( TriggerSpecification triggerSpec : triggerSpecs ) 205 { 206 if ( triggerSpec.getLdapOperation().equals( ldapOperation ) ) 207 { 208 if ( triggerSpec.getActionTime().equals( ActionTime.AFTER ) ) 209 { 210 afterTriggerSpecs.add( triggerSpec ); 211 } 212 else 213 { 214 215 } 216 } 217 } 218 219 triggerSpecMap.put( ActionTime.AFTER, afterTriggerSpecs ); 220 221 return triggerSpecMap; 222 } 223 224 //////////////////////////////////////////////////////////////////////////// 225 // Interceptor Overrides 226 //////////////////////////////////////////////////////////////////////////// 227 228 public void init( DirectoryService directoryService ) throws Exception 229 { 230 super.init( directoryService ); 231 232 triggerSpecCache = new TriggerSpecCache( directoryService ); 233 final SchemaManager schemaManager = directoryService.getSchemaManager(); 234 235 triggerParser = new TriggerSpecificationParser 236 ( new NormalizerMappingResolver() 237 { 238 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception 239 { 240 return schemaManager.getNormalizerMapping(); 241 } 242 } 243 ); 244 chain = directoryService.getInterceptorChain(); 245 246 //StoredProcEngineConfig javaxScriptSPEngineConfig = new JavaxStoredProcEngineConfig(); 247 StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig(); 248 List<StoredProcEngineConfig> spEngineConfigs = new ArrayList<StoredProcEngineConfig>(); 249 //spEngineConfigs.add( javaxScriptSPEngineConfig ); 250 spEngineConfigs.add( javaSPEngineConfig ); 251 String spContainer = "ou=Stored Procedures,ou=system"; 252 manager = new StoredProcExecutionManager( spContainer, spEngineConfigs ); 253 254 this.enabled = true; // TODO: Get this from the configuration if needed. 255 } 256 257 258 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception 259 { 260 DN name = addContext.getDn(); 261 ServerEntry entry = addContext.getEntry(); 262 263 // Bypass trigger handling if the service is disabled. 264 if ( !enabled ) 265 { 266 next.add( addContext ); 267 return; 268 } 269 270 // Gather supplementary data. 271 StoredProcedureParameterInjector injector = new AddStoredProcedureParameterInjector( addContext, name, entry ); 272 273 // Gather Trigger Specifications which apply to the entry being added. 274 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>(); 275 addPrescriptiveTriggerSpecs( addContext, triggerSpecs, name, entry ); 276 277 /** 278 * NOTE: We do not handle entryTriggerSpecs for ADD operation. 279 */ 280 281 Map<ActionTime, List<TriggerSpecification>> triggerMap 282 = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.ADD ); 283 284 next.add( addContext ); 285 triggerSpecCache.subentryAdded( name, entry ); 286 287 // Fire AFTER Triggers. 288 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); 289 executeTriggers( addContext, afterTriggerSpecs, injector ); 290 } 291 292 293 public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws Exception 294 { 295 DN name = deleteContext.getDn(); 296 297 // Bypass trigger handling if the service is disabled. 298 if ( !enabled ) 299 { 300 next.delete( deleteContext ); 301 return; 302 } 303 304 // Gather supplementary data. 305 ClonedServerEntry deletedEntry = deleteContext.lookup( name , ByPassConstants.LOOKUP_BYPASS ); 306 307 StoredProcedureParameterInjector injector = new DeleteStoredProcedureParameterInjector( deleteContext, name ); 308 309 // Gather Trigger Specifications which apply to the entry being deleted. 310 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>(); 311 addPrescriptiveTriggerSpecs( deleteContext, triggerSpecs, name, deletedEntry ); 312 addEntryTriggerSpecs( triggerSpecs, deletedEntry ); 313 314 Map<ActionTime, List<TriggerSpecification>> triggerMap = 315 getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.DELETE ); 316 317 next.delete( deleteContext ); 318 triggerSpecCache.subentryDeleted( name, deletedEntry ); 319 320 // Fire AFTER Triggers. 321 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); 322 executeTriggers( deleteContext, afterTriggerSpecs, injector ); 323 } 324 325 326 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception 327 { 328 // Bypass trigger handling if the service is disabled. 329 if ( !enabled ) 330 { 331 next.modify( opContext ); 332 return; 333 } 334 335 DN normName = opContext.getDn(); 336 337 // Gather supplementary data. 338 ClonedServerEntry modifiedEntry = opContext.lookup( normName, ByPassConstants.LOOKUP_BYPASS ); 339 340 StoredProcedureParameterInjector injector = new ModifyStoredProcedureParameterInjector( opContext ); 341 342 // Gather Trigger Specifications which apply to the entry being modified. 343 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>(); 344 addPrescriptiveTriggerSpecs( opContext, triggerSpecs, normName, modifiedEntry ); 345 addEntryTriggerSpecs( triggerSpecs, modifiedEntry ); 346 347 Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFY ); 348 349 next.modify( opContext ); 350 triggerSpecCache.subentryModified( opContext, modifiedEntry ); 351 352 // Fire AFTER Triggers. 353 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); 354 executeTriggers( opContext, afterTriggerSpecs, injector ); 355 } 356 357 358 public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws Exception 359 { 360 DN name = renameContext.getDn(); 361 RDN newRdn = renameContext.getNewRdn(); 362 boolean deleteOldRn = renameContext.getDelOldDn(); 363 364 // Bypass trigger handling if the service is disabled. 365 if ( !enabled ) 366 { 367 next.rename( renameContext ); 368 return; 369 } 370 371 // Gather supplementary data. 372 ServerEntry renamedEntry = (ServerEntry)renameContext.getEntry().getClonedEntry(); 373 374 // @TODO : To be completely reviewed !!! 375 DN oldRDN = new DN( name.getRdn().getName() ); 376 DN oldSuperiorDN = ( DN ) name.clone(); 377 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 ); 378 DN newSuperiorDN = ( DN ) oldSuperiorDN.clone(); 379 DN oldDN = ( DN ) name.clone(); 380 DN newDN = ( DN ) name.clone(); 381 newDN.add( newRdn ); 382 383 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( 384 renameContext, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN ); 385 386 // Gather Trigger Specifications which apply to the entry being renamed. 387 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>(); 388 addPrescriptiveTriggerSpecs( renameContext, triggerSpecs, name, renamedEntry ); 389 addEntryTriggerSpecs( triggerSpecs, renamedEntry ); 390 391 Map<ActionTime, List<TriggerSpecification>> triggerMap = 392 getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFYDN_RENAME ); 393 394 next.rename( renameContext ); 395 triggerSpecCache.subentryRenamed( name, newDN ); 396 397 // Fire AFTER Triggers. 398 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER ); 399 executeTriggers( renameContext, afterTriggerSpecs, injector ); 400 } 401 402 403 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) 404 throws Exception 405 { 406 DN oriChildName = opContext.getDn(); 407 DN parent = opContext.getParent(); 408 RDN newRdn = opContext.getNewRdn(); 409 boolean deleteOldRn = opContext.getDelOldDn(); 410 411 // Bypass trigger handling if the service is disabled. 412 if ( !enabled ) 413 { 414 next.moveAndRename( opContext ); 415 return; 416 } 417 418 // Gather supplementary data. 419 ClonedServerEntry movedEntry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS ); 420 421 DN oldRDN = new DN( oriChildName.getRdn().getName() ); 422 DN oldSuperiorDN = ( DN ) oriChildName.clone(); 423 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 ); 424 DN newSuperiorDN = ( DN ) parent.clone(); 425 DN oldDN = ( DN ) oriChildName.clone(); 426 DN newDN = ( DN ) parent.clone(); 427 newDN.add( newRdn.getName() ); 428 429 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( 430 opContext, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN ); 431 432 // Gather Trigger Specifications which apply to the entry being exported. 433 List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>(); 434 addPrescriptiveTriggerSpecs( opContext, exportTriggerSpecs, oriChildName, movedEntry ); 435 addEntryTriggerSpecs( exportTriggerSpecs, movedEntry ); 436 437 // Get the entry again without operational attributes 438 // because access control subentry operational attributes 439 // will not be valid at the new location. 440 // This will certainly be fixed by the SubentryInterceptor, 441 // but after this service. 442 ClonedServerEntry importedEntry = opContext.lookup( oriChildName, 443 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS ); 444 445 // As the target entry does not exist yet and so 446 // its subentry operational attributes are not there, 447 // we need to construct an entry to represent it 448 // at least with minimal requirements which are object class 449 // and access control subentry operational attributes. 450 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() ); 451 ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry ); 452 453 for ( EntryAttribute attribute:importedEntry ) 454 { 455 fakeImportedEntry.put( attribute ); 456 } 457 458 // Gather Trigger Specifications which apply to the entry being imported. 459 // Note: Entry Trigger Specifications are not valid for Import. 460 List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>(); 461 addPrescriptiveTriggerSpecs( opContext, importTriggerSpecs, newDN, fakeImportedEntry ); 462 463 Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = 464 getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT ); 465 466 Map<ActionTime, List<TriggerSpecification>> importTriggerMap = 467 getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT ); 468 469 next.moveAndRename( opContext ); 470 triggerSpecCache.subentryRenamed( oldDN, newDN ); 471 472 // Fire AFTER Triggers. 473 List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER ); 474 List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER ); 475 executeTriggers( opContext, afterExportTriggerSpecs, injector ); 476 executeTriggers( opContext, afterImportTriggerSpecs, injector ); 477 } 478 479 480 public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception 481 { 482 // Bypass trigger handling if the service is disabled. 483 if ( !enabled ) 484 { 485 next.move( opContext ); 486 return; 487 } 488 489 DN oriChildName = opContext.getDn(); 490 DN newParentName = opContext.getParent(); 491 492 // Gather supplementary data. 493 ClonedServerEntry movedEntry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS ); 494 495 DN oldRDN = new DN( oriChildName.getRdn().getName() ); 496 RDN newRDN = new RDN( oriChildName.getRdn().getName() ); 497 DN oldSuperiorDN = ( DN ) oriChildName.clone(); 498 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 ); 499 DN newSuperiorDN = ( DN ) newParentName.clone(); 500 DN oldDN = ( DN ) oriChildName.clone(); 501 DN newDN = ( DN ) newParentName.clone(); 502 newDN.add( newRDN.getName() ); 503 504 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector( 505 opContext, false, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN ); 506 507 // Gather Trigger Specifications which apply to the entry being exported. 508 List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>(); 509 addPrescriptiveTriggerSpecs( opContext, exportTriggerSpecs, oriChildName, movedEntry ); 510 addEntryTriggerSpecs( exportTriggerSpecs, movedEntry ); 511 512 // Get the entry again without operational attributes 513 // because access control subentry operational attributes 514 // will not be valid at the new location. 515 // This will certainly be fixed by the SubentryInterceptor, 516 // but after this service. 517 ClonedServerEntry importedEntry = opContext.lookup( oriChildName, 518 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS ); 519 520 // As the target entry does not exist yet and so 521 // its subentry operational attributes are not there, 522 // we need to construct an entry to represent it 523 // at least with minimal requirements which are object class 524 // and access control subentry operational attributes. 525 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() ); 526 ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry ); 527 528 for ( EntryAttribute attribute:importedEntry ) 529 { 530 fakeImportedEntry.put( attribute ); 531 } 532 533 // Gather Trigger Specifications which apply to the entry being imported. 534 // Note: Entry Trigger Specifications are not valid for Import. 535 List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>(); 536 addPrescriptiveTriggerSpecs( opContext, importTriggerSpecs, newDN, fakeImportedEntry ); 537 538 Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT ); 539 540 Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT ); 541 542 next.move( opContext ); 543 triggerSpecCache.subentryRenamed( oldDN, newDN ); 544 545 // Fire AFTER Triggers. 546 List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER ); 547 List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER ); 548 executeTriggers( opContext, afterExportTriggerSpecs, injector ); 549 executeTriggers( opContext, afterImportTriggerSpecs, injector ); 550 } 551 552 //////////////////////////////////////////////////////////////////////////// 553 // Utility Methods 554 //////////////////////////////////////////////////////////////////////////// 555 556 557 private Object executeTriggers( OperationContext opContext, List<TriggerSpecification> triggerSpecs, 558 StoredProcedureParameterInjector injector ) throws Exception 559 { 560 Object result = null; 561 562 for ( TriggerSpecification triggerSpec : triggerSpecs ) 563 { 564 // TODO: Replace the Authorization Code with a REAL one. 565 if ( triggerExecutionAuthorizer.hasPermission( opContext ) ) 566 { 567 /** 568 * If there is only one Trigger to be executed, this assignment 569 * will make sense (as in INSTEADOF search Triggers). 570 */ 571 result = executeTrigger( opContext, triggerSpec, injector ); 572 } 573 } 574 575 /** 576 * If only one Trigger has been executed, returning its result 577 * can make sense (as in INSTEADOF Search Triggers). 578 */ 579 return result; 580 } 581 582 private Object executeTrigger( OperationContext opContext, TriggerSpecification tsec, 583 StoredProcedureParameterInjector injector ) throws Exception 584 { 585 List<Object> returnValues = new ArrayList<Object>(); 586 List<SPSpec> spSpecs = tsec.getSPSpecs(); 587 for ( SPSpec spSpec : spSpecs ) 588 { 589 List<Object> arguments = new ArrayList<Object>(); 590 arguments.addAll( injector.getArgumentsToInject( opContext, spSpec.getParameters() ) ); 591 Object[] values = arguments.toArray(); 592 Object returnValue = executeProcedure( opContext, spSpec.getName(), values ); 593 returnValues.add( returnValue ); 594 } 595 596 return returnValues; 597 } 598 599 600 private Object executeProcedure( OperationContext opContext, String procedure, Object[] values ) throws Exception 601 { 602 603 try 604 { 605 ClonedServerEntry spUnit = manager.findStoredProcUnit( opContext.getSession(), procedure ); 606 StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit ); 607 return engine.invokeProcedure( opContext.getSession(), procedure, values ); 608 } 609 catch ( Exception e ) 610 { 611 LdapOtherException lne = new LdapOtherException( e.getMessage() ); 612 lne.initCause( e ); 613 throw lne; 614 } 615 } 616 617 }