Package org.jboss.soa.esb.services.jbpm.cmd

Source Code of org.jboss.soa.esb.services.jbpm.cmd.AsyncProcessSignal$AsyncSignalAction

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.soa.esb.services.jbpm.cmd;

import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.transaction.Synchronization;
import javax.transaction.Transaction;

import org.apache.log4j.Logger;
import org.jboss.soa.esb.common.TransactionStrategy;
import org.jboss.soa.esb.services.jbpm.Mapping;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.command.SignalCommand;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.def.Action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.instantiation.Delegation;
import org.jbpm.job.ExecuteActionJob;
import org.jbpm.job.executor.JobExecutor;
import org.jbpm.msg.MessageService;
import org.jbpm.svc.Services;

/**
* Handle the asynchronous signalling of a process instance task.
*
* @author <a href='kevin.conner@jboss.com'>Kevin Conner</a>
*/
class AsyncProcessSignal
{
    /**
     * The logger for this class.
     */
    private static Logger logger = Logger.getLogger(AsyncProcessSignal.class);
   
    /**
     * The name of the ESB asynchronous signal action.
     */
    private static final String ESB_ASYNC_SIGNAL_ACTION_NAME = "ESB_ASYNC_SIGNAL_ACTION" ;
    /**
     * The base name of the ESB asynchronous signal context variable.
     */
    private static final String ESB_ASYNC_SIGNAL_VARIABLE_NAME = "ESB_ASYNC_SIGNAL_VARIABLE_" ;
    /**
     * The name of the ESB asynchronous signal transition variable.
     */
    private static final String ESB_ASYNC_SIGNAL_TRANSITION_VARIABLE_NAME = ESB_ASYNC_SIGNAL_VARIABLE_NAME + "TRANSITION_" ;
    /**
     * The name of the ESB asynchronous signal actor variable.
     */
    private static final String ESB_ASYNC_SIGNAL_ACTOR_VARIABLE_NAME = ESB_ASYNC_SIGNAL_VARIABLE_NAME + "ACTOR_" ;
    /**
     * The name of the ESB asynchronous signal variable count.
     */
    private static final String ESB_ASYNC_SIGNAL_VARIABLE_COUNT = ESB_ASYNC_SIGNAL_VARIABLE_NAME + "COUNT_" ;
    /**
     * The name of the ESB asynchronous signal variable names.
     */
    private static final String ESB_ASYNC_SIGNAL_VARIABLE_NAMES = ESB_ASYNC_SIGNAL_VARIABLE_NAME + "NAME_" ;
    /**
     * Map of active synchronisations.
     */
    private static final ConcurrentHashMap<Transaction, Synchronization> SYNCHRONISATIONS = new ConcurrentHashMap<Transaction, Synchronization>() ;
   
    /**
     * Create an asynchronous signal job for the specified token and transition.
     * @param token The token to signal.
     * @param transitionName The transition to signal or null if the default transition is to be used.
     * @param actor The actor to use.
     * @param globalProcessScope The default process scope to use.
     * @param variables Any variables to update.
     */
    static void createSignalJob(final JbpmContext jbpmContext, final Token token, final String transitionName, final String actor,
        final boolean defaultProcessScope, final Map<Mapping, Object> variables)
    {
        final boolean isDebugEnabled = logger.isDebugEnabled() ;
        final long tokenId = token.getId() ;
        final ProcessInstance processInstance = token.getProcessInstance() ;
        final long processInstanceId = processInstance.getId() ;
        final ContextInstance contextInstance = processInstance.getContextInstance() ;
       
        if (isDebugEnabled)
        {
            logger.debug("Locking token id " + tokenId + " from process instance " + processInstanceId) ;
        }
        token.lock(ESB_ASYNC_SIGNAL_ACTION_NAME);
       
        final String transitionVariableName = ESB_ASYNC_SIGNAL_TRANSITION_VARIABLE_NAME ;
        setVariable(contextInstance, token, transitionVariableName, transitionName) ;
        final String actorVariableName = ESB_ASYNC_SIGNAL_ACTOR_VARIABLE_NAME ;
        setVariable(contextInstance, token, actorVariableName, actor) ;
       
        final int numVariables = (variables == null ? 0 : variables.size()) ;
        if (numVariables > 0)
        {
            int count = 0 ;
            final Iterator<Map.Entry<Mapping, Object>> variableEntryIter = variables.entrySet().iterator() ;
            do
            {
                final Map.Entry<Mapping, Object> variableEntry = variableEntryIter.next() ;
                final Mapping mapping = (Mapping)variableEntry.getKey() ;
                final String name = mapping.getBpm() ;
                setVariable(contextInstance, token, name, variableEntry.getValue()) ;
                final Boolean isProcessScope = mapping.getIsProcessScope() ;
                final boolean setScope = (isProcessScope == null ? defaultProcessScope : isProcessScope.booleanValue()) ;
                if (setScope)
                {
                  setVariable(contextInstance, token, ESB_ASYNC_SIGNAL_VARIABLE_NAMES + (count++), name) ;
                }
            } while (variableEntryIter.hasNext()) ;
            setVariable(contextInstance, token, ESB_ASYNC_SIGNAL_VARIABLE_COUNT, Integer.toString(count)) ;
        }
        else
        {
          setVariable(contextInstance, token, ESB_ASYNC_SIGNAL_VARIABLE_COUNT, String.valueOf(0)) ;
        }
       
        final ExecuteActionJob signalJob = new ExecuteActionJob(token) ;
        signalJob.setAction(getAsyncSignalAction(token)) ;
        signalJob.setDueDate(new Date()) ;
        signalJob.setSuspended(token.isSuspended()) ;
       
        if (isDebugEnabled)
        {
            logger.debug("Sending " + (token.isSuspended() ? "suspended " : "") +"signal task to message service for token id " + tokenId + " from process instance " + processInstanceId) ;
        }
        final MessageService messageService = (MessageService)Services.getCurrentService(Services.SERVICENAME_MESSAGE, true) ;
        messageService.send(signalJob) ;
        if (isDebugEnabled)
        {
            logger.debug("Sent signal task to message service for token id " + tokenId + " from process instance " + processInstanceId) ;
        }
       
        final JobExecutor jobExecutor = jbpmContext.getJbpmConfiguration().getJobExecutor() ;
        if (jobExecutor != null)
        {
            final TransactionStrategy transactionStrategy = TransactionStrategy.getTransactionStrategy(true) ;
            try
            {
                if (transactionStrategy.isActive())
                {
                    final Transaction transaction = (Transaction)transactionStrategy.getTransaction() ;
                    if ((transaction != null) && !SYNCHRONISATIONS.containsKey(transaction))
                    {
                        final Synchronization synch = new JobNotifierSynchronisation(transaction, jobExecutor) ;
                        transaction.registerSynchronization(synch) ;
                        SYNCHRONISATIONS.put(transaction, synch) ;
                    }
                }
            }
            catch (final Exception ex)
            {
                if (logger.isDebugEnabled())
                {
                    logger.debug("Failed to register synchronization", ex) ;
                }
            }
        }
    }
   
    /**
     * Set the context instance variable.
     * @param contextInstance The context instance.
     * @param token The current token.
     * @param name The variable name
     * @param value The variable value
     */
    private static void setVariable(final ContextInstance contextInstance, final Token token, final String name, final Object value)
    {
        if (value != null)
        {
            contextInstance.setVariableLocally(name, value, token) ;
        }
    }
   
    /**
     * Locate the asynchronous signal action associated with the process instance.
     * @param token The token to signal.
     * @return The action used for asynchronous signalling.
     * @throws JbpmException For errors creating or locating the action.
     */
    private static Action getAsyncSignalAction(final Token token)
        throws JbpmException
    {
        final ProcessInstance processInstance = token.getProcessInstance() ;
        final ProcessDefinition processDefinition = token.getProcessInstance().getProcessDefinition() ;
        if (processDefinition == null)
        {
            throw new JbpmException("Could not locate process definition for process instance: " + processInstance.getId()) ;
        }
        final Action currentAction = token.getProcessInstance().getProcessDefinition().getAction(ESB_ASYNC_SIGNAL_ACTION_NAME) ;
        if (currentAction!= null)
        {
            return currentAction ;
        }
       
        if (logger.isDebugEnabled())
        {
            logger.debug("Creating Callback action for process definition: " + processDefinition.getName() + ", id: " + processDefinition.getId()) ;
        }
        final Delegation delegation = new Delegation(AsyncSignalAction.class.getName()) ;
        delegation.setConfigType("constructor") ;
        final Action newAction = new Action(delegation) ;
        newAction.setName(ESB_ASYNC_SIGNAL_ACTION_NAME) ;
        processDefinition.addAction(newAction) ;
       
        return newAction ;
    }
   
    /**
     * The task for asynchronously signalling the token.
     * @author <a href='kevin.conner@jboss.com'>Kevin Conner</a>
     */
    private static class AsyncSignalAction implements ActionHandler
    {
        /**
         * Serial version UID for this action.
         */
        private static final long serialVersionUID = -1462271955979788905L;
       
        /**
         * Construct the signal job.
         * @param configuration The configuration.
         */
        AsyncSignalAction(final String configuration)
            throws JbpmException
        {
        }
       
        /**
         * Process the background signal task.
         * @param executionContext The current execution context.
         */
        public void execute(final ExecutionContext executionContext)
            throws Exception
        {
            final boolean isDebugEnabled = logger.isDebugEnabled() ;
            final Token token = executionContext.getToken() ;
            final long tokenId = token.getId() ;
            if (isDebugEnabled)
            {
                logger.debug("Unlocking token id " + tokenId + " from process instance " +
                    token.getProcessInstance().getId()) ;
            }
            token.unlock(ESB_ASYNC_SIGNAL_ACTION_NAME) ;
            if (isDebugEnabled)
            {
                logger.debug("Signaling task " + tokenId + " from process instance " +
                    token.getProcessInstance().getId()) ;
            }
            final ProcessInstance processInstance = token.getProcessInstance() ;
            final ContextInstance contextInstance = processInstance.getContextInstance() ;
            final JbpmContext jbpmContext = executionContext.getJbpmContext() ;
            final String transitionName = (String)removeVariableLocally(jbpmContext, contextInstance, ESB_ASYNC_SIGNAL_TRANSITION_VARIABLE_NAME, token) ;
            final String actor = (String)removeVariableLocally(jbpmContext, contextInstance, ESB_ASYNC_SIGNAL_ACTOR_VARIABLE_NAME, token) ;
            final String origActor = jbpmContext.getActorId() ;
           
            final int variableCount = Integer.parseInt((String)removeVariableLocally(jbpmContext, contextInstance, ESB_ASYNC_SIGNAL_VARIABLE_COUNT, token)) ;
            for(int count = 0 ; count < variableCount ; count++)
            {
                final String name = (String)removeVariableLocally(jbpmContext, contextInstance, ESB_ASYNC_SIGNAL_VARIABLE_NAMES + count, token) ;
                final Object value = removeVariableLocally(jbpmContext, contextInstance, name, token) ;
                contextInstance.setVariable(name, value) ;
            }
           
            try
            {
                if (actor != null)
                {
                    jbpmContext.setActorId(actor) ;
                }
                final SignalCommand signalCommand = new SignalCommand(tokenId, transitionName) ;
                signalCommand.execute(jbpmContext) ;
            }
            finally
            {
                jbpmContext.setActorId(origActor) ;
            }
            if (isDebugEnabled)
            {
                logger.debug("Signalled task " + tokenId + " from process instance " +
                    token.getProcessInstance().getId()) ;
            }
        }
       
        private Object removeVariableLocally(final JbpmContext jbpmContext, final ContextInstance contextInstance, final String name, final Token token)
        {
            final Object value = contextInstance.getVariableLocally(name, token) ;

            if (value != null)
            {
                contextInstance.deleteVariable(name, token) ;
            }
            return value ;
        }
    }
   
    /**
     * Synchronisation to notify the job executor.
     * @author kevin
     */
    private static final class JobNotifierSynchronisation implements Synchronization
    {
        /**
         * The associated transaction.
         */
        private Transaction transaction ;
        /**
         * The current job executor.
         */
        private final JobExecutor jobExecutor ;
       
        /**
         * Create the notifier synchronisation.
         * @param transaction The current transaction.
         * @param jobExecutor The current job executor.
         */
        public JobNotifierSynchronisation(final Transaction transaction, final JobExecutor jobExecutor)
        {
            this.transaction = transaction ;
            this.jobExecutor = jobExecutor ;
        }
       
        /**
         * The before completion notification.
         */
        public void beforeCompletion()
        {
        }
       
        /**
         * The after completion notification.
         * @param status The status of the transaction.
         */
        public void afterCompletion(final int status)
        {
            SYNCHRONISATIONS.remove(transaction) ;
            synchronized(jobExecutor)
            {
                jobExecutor.notify() ;
            }
        }
    }
}
TOP

Related Classes of org.jboss.soa.esb.services.jbpm.cmd.AsyncProcessSignal$AsyncSignalAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.