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 }