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