package org.alfresco.repo.policy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.policy.Policy.Arg; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.util.GUID; /** * Transaction Behaviour Queue. * * Responsible for keeping a record of behaviours to execute at the end of a transaction. */ public class TransactionBehaviourQueue implements TransactionListener { /** Id used in equals and hash */ private String id = GUID.generate(); // Transaction Keys for Behaviour Execution state private static final String QUEUE_CONTEXT_KEY = TransactionBehaviourQueue.class.getName() + ".context"; /** * Queue a behaviour for end-of-transaction execution * * @param
P extends Policy * @param behaviour Behaviour * @param definition PolicyDefinition
* @param policyInterface P * @param method Method * @param args Object[] */ @SuppressWarnings("unchecked") public
void queue(Behaviour behaviour, PolicyDefinition
definition, P policyInterface, Method method, Object[] args) { // Construct queue context, if required QueueContext queueContext = (QueueContext)AlfrescoTransactionSupport.getResource(QUEUE_CONTEXT_KEY); if (queueContext == null) { queueContext = new QueueContext(); AlfrescoTransactionSupport.bindResource(QUEUE_CONTEXT_KEY, queueContext); AlfrescoTransactionSupport.bindListener(this); } // Determine if behaviour instance has already been queued // Identity of ExecutionContext is Behaviour + KEY argument(s) ExecutionInstanceKey key = new ExecutionInstanceKey(behaviour, definition.getArguments(), args); ExecutionContext executionContext = queueContext.index.get(key); if (executionContext == null) { // Context does not exist // Create execution context for behaviour executionContext = new ExecutionContext
(); executionContext.method = method; executionContext.args = args; executionContext.policyInterface = policyInterface; // Defer or execute now? if (!queueContext.committed) { // queue behaviour for deferred execution queueContext.queue.offer(executionContext); } else { // execute now execute(executionContext); } queueContext.index.put(key, executionContext); } else { // Context does already exist // Update behaviour instance execution context, in particular, argument state that is marked END_TRANSACTION Arg[] argDefs = definition.getArguments(); for (int i = 0; i < argDefs.length; i++) { if (argDefs[i].equals(Arg.END_VALUE)) { executionContext.args[i] = args[i]; } } } } /* (non-Javadoc) * @see org.alfresco.repo.transaction.TransactionListener#flush() */ public void flush() { } /* (non-Javadoc) * @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean) */ @SuppressWarnings("unchecked") public void beforeCommit(boolean readOnly) { QueueContext queueContext = (QueueContext)AlfrescoTransactionSupport.getResource(QUEUE_CONTEXT_KEY); ExecutionContext context = queueContext.queue.poll(); while (context != null) { execute(context); context = queueContext.queue.poll(); } queueContext.committed = true; } /* (non-Javadoc) * @see org.alfresco.repo.transaction.TransactionListener#beforeCompletion() */ public void beforeCompletion() { } /* (non-Javadoc) * @see org.alfresco.repo.transaction.TransactionListener#afterCommit() */ public void afterCommit() { } /* (non-Javadoc) * @see org.alfresco.repo.transaction.TransactionListener#afterRollback() */ public void afterRollback() { } /** * Execution Instance Key - to uniquely identify an ExecutionContext */ private class ExecutionInstanceKey { public ExecutionInstanceKey(Behaviour behaviour, Arg[] argDefs, Object[] args) { this.behaviour = behaviour; for (int i = 0; i < argDefs.length; i++) { if (argDefs[i].equals(Arg.KEY)) { keys.add(args[i]); } } } Behaviour behaviour; ArrayList