org.jboss.processFlow.knowledgeService.BaseKnowledgeSessionBean.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.processFlow.knowledgeService.BaseKnowledgeSessionBean.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file 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.processFlow.knowledgeService;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PreDestroy;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.transaction.UserTransaction;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.SessionConfiguration;
import org.drools.SystemEventListener;
import org.drools.SystemEventListenerFactory;
import org.drools.agent.KnowledgeAgent;
import org.drools.agent.KnowledgeAgentConfiguration;
import org.drools.agent.KnowledgeAgentFactory;
import org.drools.agent.impl.PrintStreamSystemEventListener;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.command.SingleSessionCommandService;
import org.drools.common.InternalKnowledgeRuntime;
import org.drools.compiler.PackageBuilder;
import org.drools.core.util.DelegatingSystemEventListener;
import org.drools.definition.KnowledgePackage;
import org.drools.definition.process.Node;
import org.drools.definition.process.Process;
import org.drools.definition.process.WorkflowProcess;
import org.drools.definitions.impl.KnowledgePackageImp;
import org.drools.event.process.ProcessEventListener;
import org.drools.event.rule.AgendaEventListener;
import org.drools.event.rule.DefaultAgendaEventListener;
import org.drools.event.rule.RuleFlowGroupActivatedEvent;
import org.drools.event.rule.WorkingMemoryEventListener;
import org.drools.io.Resource;
import org.drools.io.ResourceChangeScannerConfiguration;
import org.drools.io.ResourceFactory;
import org.drools.io.impl.InputStreamResource;
import org.drools.management.DroolsManagementAgent;
import org.drools.marshalling.ObjectMarshallingStrategy;
import org.drools.marshalling.impl.ClassObjectMarshallingStrategyAcceptor;
import org.drools.marshalling.impl.SerializablePlaceholderResolverStrategy;
import org.drools.persistence.jpa.JPAKnowledgeService;
import org.drools.persistence.jpa.JpaJDKTimerService;
import org.drools.persistence.jpa.marshaller.JPAPlaceholderResolverStrategy;
import org.drools.persistence.jpa.processinstance.JPAWorkItemManagerFactory;
import org.drools.runtime.Environment;
import org.drools.runtime.EnvironmentName;
import org.drools.runtime.KnowledgeRuntime;
import org.drools.runtime.KnowledgeSessionConfiguration;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.process.WorkItemHandler;
import org.jboss.processFlow.tasks.ITaskService;
import org.jboss.processFlow.util.LogSystemEventListener;
import org.jboss.processFlow.workItem.WorkItemHandlerLifecycle;
import org.jbpm.compiler.ProcessBuilderImpl;
import org.jbpm.integration.console.shared.GuvnorConnectionUtils;
import org.jbpm.persistence.processinstance.ProcessInstanceInfo;
import org.jbpm.task.service.TaskService;
import org.jbpm.workflow.core.NodeContainer;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;

/**
 *<pre>
 *architecture
 * Drools knowledgeBase management
 *  - this implementation instantiates a single instance of org.drools.KnowledgeBase
 *  - this KnowledgeBase is kept current by interacting with a remote BRMS guvnor service
 *  - note: this KnowledgeBase instance is instantiated the first time any IKnowledgeSession operation is invoked
 *  - the KnowledgeBase is not instantiated in a start() method because the BRMS guvnor may be co-located on the same jvm
 *      as this KnowledgeSessionService and may not yet be available (depending on boot-loader order)
 *      
 *
 * WorkItemHandler Management
 *  - Creating & configuring custom work item handlers in PFP is almost identical to creating custom work item handlers in stock BRMS
 *     - Background Documentation :       12.1.3  Registering your own service handlers
 *      - The following are a few processFlowProvision additions :
 *
 *       1)  programmatically registered work item handlers
 *         -- every StatefulKnowledgeSession managed by the processFlowProvision knowledgeSessionService is automatically registered with
 *
 *          the following workItemHandlers :
 *           1)  "Human Task"    :   org.jboss.processFlow.tasks.handlers.PFPAddHumanTaskHandler
 *           2)  "Skip Task"     :   org.jboss.processFlow.tasks.handlers.PFPSkipTaskHandler
 *           3)  "Fail Task"     :   org.jboss.processFlow.tasks.handlers.PFPFailTaskHandler
 *           4)  "Email"         :   org.jboss.processFlow.tasks.handlers.PFPEmailWorkItemHandler
 *
 *      2)  defining configurable work item handlers
 *        -- jbpm5 allows for more than one META-INF/drools.session.conf in the runtime classpath
 *          -- subsequently, there is the potential for mulitple locations that define custom work item handlers
 *         -- the ability to have multiple META-INF/drools.session.conf files on the runtime classpath most likely will lead to
 *               increased difficulty isolating problems encountered with defining and registering custom work item handlers
 *        -- processFlowProvision/build.properties includes the following property:  space.delimited.workItemHandler.configs
 *         -- rather than allowing for multiple locations to define custom work item handlers,
 *               use of the 'space.delimited.workItemHandler.configs' property centralalizes where to define additional custom workItemHandlers
 *         -- please see documentation provided for that property in the build.properties
 *
 * processEventListeners
 *      - ProcessEventListeners get registered with the knowledgeSession/processEngine
 *      - when any of the corresponding events occurs in the lifecycle of a process instance, those processevent listeners get invoked
 *      - a configurable list of process event listeners can be registered with the process engine via the following system prroperty:
 *          IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS
 *
 *      - in processFlowProvision, we have two classes that implement org.drools.event.process.ProcessEventListener :
 *          1)  the 'busySessionsListener' inner class constructed in this knowledgeSessionService    
 *              -- used to help maintain our ksessionid state
 *              -- a new instance is automatically registered with a ksession with new ksession creation or ksession re-load
 *          2)  org.jboss.processFlow.bam.AsyncBAMProducer
 *              -- sends BAM events to a hornetq queue
 *              -- registered by including it in IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS system property
 *
 *
 * BAM audit logging
 *  - this implementation leverages a pool of JMS producers to send BAM events to a JMS provider
 *  - a corresponding BAM consumer receives those BAM events and persists to the BRMS BAM database
 *  - it is possible to disable the production of BAM events by NOT including 'org.jboss.processFlow.bam.AsyncBAMProducer' as a value
 *    in the IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS property
 *  - note:  if 'org.jboss.processFlow.bam.AsyncBAMProducer' is not included, then any clients that query the BRMS BAM database will be affected
 *  - an example is the BRMS gwt-console-server
 *      the gwt-console-server queries the BRMS BAM database for listing of active process instances
 *      
 *     
 *  22 Jan 2013:  various performance optimizations and general cleanup contributed by Michal Valach.  thank you!
 */
public class BaseKnowledgeSessionBean {

    public static final String EMF_NAME = "org.jbpm.persistence.jpa";
    public static final String DROOLS_SESSION_CONF_PATH = "/META-INF/drools.session.conf";
    public static final String DROOLS_SESSION_TEMPLATE_PATH = "drools.session.template.path";
    public static final String DROOLS_WORK_ITEM_HANDLERS = "drools.workItemHandlers";
    public static final String USE_IN_MEMORY_KNOWLEDGE_SESSION = "use.in.memory.knowledge.session";

    protected Logger log = Logger.getLogger(BaseKnowledgeSessionBean.class);
    protected String droolsResourceScannerInterval = "30";
    protected boolean enableLog = false;
    protected boolean enableKnowledgeRuntimeLogger = true;
    protected Map<String, Class<?>> programmaticallyLoadedWorkItemHandlers = new HashMap<String, Class<?>>();

    protected KnowledgeBase kbase = null;
    protected KnowledgeAgent kagent = null;
    protected boolean kAgentMonitor = true;
    protected long lastKAgentRefresh;
    protected int kAgentRefreshHours = 12;
    protected SystemEventListener originalSystemEventListener = null;
    protected DroolsManagementAgent kmanagement = null;
    protected GuvnorConnectionUtils guvnorUtils = null;
    protected Properties ksconfigProperties;
    protected String[] processEventListeners;
    protected String guvnorChangeSet;

    protected Properties guvnorProps;
    protected String taskCleanUpImpl;
    protected String templateString;
    private int sessionTemplateInstantiationAttempts = 0;
    private Object templateStringLockObj = new Object();
    private Object sessionTemplateInstantiationLock = new Object();
    protected boolean useInMemoryKnowledgeSession = Boolean
            .parseBoolean(System.getProperty(USE_IN_MEMORY_KNOWLEDGE_SESSION, "FALSE"));

    /* static variable because :
     *   1)  TaskService is a thread-safe object
     *   2)  TaskService is needed for both :
     *     - PFP HumanTaskService           :   functions using a jta enable entity manager for human task functionality
     *     - PFP KnowledgeSessionService    :   needed to instantiate TasksAdmin object and register with knowledgeSession
     */
    protected static TaskService jtaTaskService;

    protected @PersistenceUnit(unitName = EMF_NAME) EntityManagerFactory jbpmCoreEMF;
    protected @javax.annotation.Resource UserTransaction uTrnx;
    private boolean useJPAPlaceholderResolverStrategy;

    protected void start() throws Exception {
        /*  - set KnowledgeBase properties
         *  - the alternative to this programmatic approach is a 'META-INF/drools.session.conf' on the classpath
         */
        ksconfigProperties = new Properties();
        ksconfigProperties.put("drools.commandService", SingleSessionCommandService.class.getName());
        ksconfigProperties.put("drools.processInstanceManagerFactory",
                "org.jbpm.persistence.processinstance.JPAProcessInstanceManagerFactory");
        ksconfigProperties.setProperty("drools.workItemManagerFactory", JPAWorkItemManagerFactory.class.getName());
        ksconfigProperties.put("drools.processSignalManagerFactory",
                "org.jbpm.persistence.processinstance.JPASignalManagerFactory");

        if (System.getProperty("org.jboss.processFlow.drools.resource.scanner.interval") != null)
            droolsResourceScannerInterval = System
                    .getProperty("org.jboss.processFlow.drools.resource.scanner.interval");

        taskCleanUpImpl = System.getProperty(IKnowledgeSession.TASK_CLEAN_UP_PROCESS_EVENT_LISTENER_IMPL);
        useJPAPlaceholderResolverStrategy = Boolean
                .parseBoolean(System.getProperty(IKnowledgeSession.USE_JPA_PLACEHOLDER_RESOLVER_STRATEGY, "FALSE"));

        String timerService = System.getProperty("drools.timerService", JpaJDKTimerService.class.getName());
        ksconfigProperties.setProperty("drools.timerService", timerService);

        guvnorUtils = new GuvnorConnectionUtils();
        enableLog = Boolean.parseBoolean(System.getProperty("org.jboss.enableLog", "TRUE"));

        if (System.getProperty(IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS) != null)
            processEventListeners = System.getProperty(IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS)
                    .split("\\s");

        kAgentRefreshHours = Integer.parseInt(System.getProperty("org.jboss.processFlow.kAgentRefreshHours", "12"));
        kAgentMonitor = Boolean.parseBoolean(System.getProperty("org.jboss.processFlow.kAgentMonitor", "TRUE"));

        enableKnowledgeRuntimeLogger = Boolean.parseBoolean(
                System.getProperty("org.jboss.processFlow.statefulKnowledge.enableKnowledgeRuntimeLogger", "TRUE"));

        // 2) set the Drools system event listener to our implementation ...
        originalSystemEventListener = SystemEventListenerFactory.getSystemEventListener();
        if (originalSystemEventListener == null
                || originalSystemEventListener instanceof DelegatingSystemEventListener) {
            // We need to check for DelegatingSystemEventListener so we don't get a
            // StackOverflowError when we set it back.  If it is a DelegatingSystemEventListener,
            // we instead use what drools wraps by default, which is PrintStreamSystemEventListener.
            // Refer to org.drools.impl.SystemEventListenerServiceImpl for more information.
            originalSystemEventListener = new PrintStreamSystemEventListener();
        }
        SystemEventListenerFactory.setSystemEventListener(new LogSystemEventListener());

        programmaticallyLoadedWorkItemHandlers.put(ITaskService.HUMAN_TASK,
                Class.forName("org.jboss.processFlow.tasks.handlers.PFPAddHumanTaskHandler"));
        programmaticallyLoadedWorkItemHandlers.put(ITaskService.SKIP_TASK,
                Class.forName("org.jboss.processFlow.tasks.handlers.PFPSkipTaskHandler"));
        programmaticallyLoadedWorkItemHandlers.put(ITaskService.FAIL_TASK,
                Class.forName("org.jboss.processFlow.tasks.handlers.PFPFailTaskHandler"));
        programmaticallyLoadedWorkItemHandlers.put(IKnowledgeSession.EMAIL,
                Class.forName("org.jboss.processFlow.email.PFPEmailWorkItemHandler"));

        StringBuilder logBuilder = new StringBuilder();
        logBuilder.append("start() ksession props as follows :\n\tdrools guvnor scanner interval = ");
        logBuilder.append(droolsResourceScannerInterval);
        logBuilder.append("\n\ttimerService = ");
        logBuilder.append(timerService);
        logBuilder.append("\n\ttaskCleanUpImpl = ");
        logBuilder.append(taskCleanUpImpl);
        logBuilder.append("\n\tenableLog = ");
        logBuilder.append(enableLog);
        logBuilder.append("\n\tkAgentMonitor = ");
        logBuilder.append(kAgentMonitor);
        logBuilder.append("\n\tkAgentRefreshHours = ");
        logBuilder.append(kAgentRefreshHours);
        logBuilder.append("\n\tuseJPAPlaceholderResolverStrategy = ");
        logBuilder.append(useJPAPlaceholderResolverStrategy);
        logBuilder.append("\n\tuseInMemoryKnowledgeSession = ");
        logBuilder.append(useInMemoryKnowledgeSession);
        log.info(logBuilder.toString());

    }

    @PreDestroy
    public void destroy() throws Exception {
        if (kagent != null) {
            kagent.dispose();
            kagent = null;
        }
    }

    /******************************************************************************
     * *************        Drools KnowledgeBase Management               *********/

    // critical that each StatefulKnowledgeSession have its own JPA 'Environment'
    protected Environment createKnowledgeSessionEnvironment() {
        Environment env = KnowledgeBaseFactory.newEnvironment();
        env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, jbpmCoreEMF);
        if (useJPAPlaceholderResolverStrategy) {
            env.set(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES, new ObjectMarshallingStrategy[] {
                    new JPAPlaceholderResolverStrategy(env),
                    new SerializablePlaceholderResolverStrategy(ClassObjectMarshallingStrategyAcceptor.DEFAULT) });
        }
        return env;
    }

    protected synchronized void checkKAgentAndBaseHealth() {
        long kAgentRefreshLapsedTime = System.currentTimeMillis() - lastKAgentRefresh;
        long kAgentRefreshMillis = 1000 * 60 * kAgentRefreshHours;
        if (kbase == null || (kAgentMonitor && (kAgentRefreshLapsedTime > kAgentRefreshMillis))) {
            log.info("checkKAgentAndBaseHealth() will now refresh kbase and kagent.  lapsed time "
                    + kAgentRefreshLapsedTime + " is greater than " + kAgentRefreshMillis);
            //ddoyle: Setting the kbase to null seems to cause issues in
            //kbase = null;
            createKnowledgeBaseViaKnowledgeAgentOrBuilder();
        }
    }

    public synchronized void createKnowledgeBaseViaKnowledgeAgentOrBuilder() {
        try {
            this.createKnowledgeBaseViaKnowledgeAgent();
        } catch (ConnectException x) {
            log.warn(
                    "createKnowledgeBaseViaKnowledgeAgentOrBuilder() can not create a kbase via a kagent due to a connection problem with guvnor ... will now create kbase via knowledgeBuilder");
            rebuildKnowledgeBaseViaKnowledgeBuilder();
        }
    }

    public synchronized void createOrRebuildKnowledgeBaseViaKnowledgeAgentOrBuilder() {
        try {
            this.createKnowledgeBaseViaKnowledgeAgent(true);
        } catch (ConnectException x) {
            log.warn(
                    "createOrRebuildKnowledgeBaseViaKnowledgeAgentOrBuilder() can not create a kbase via a kagent due to a connection problem with guvnor ... will now create kbase via knowledgeBuilder");
            rebuildKnowledgeBaseViaKnowledgeBuilder();
        }
    }

    public synchronized void rebuildKnowledgeBaseViaKnowledgeAgent() throws ConnectException {
        this.createKnowledgeBaseViaKnowledgeAgent(true);
    }

    protected synchronized void createKnowledgeBaseViaKnowledgeAgent() throws ConnectException {
        this.createKnowledgeBaseViaKnowledgeAgent(false);
    }

    // only one knowledgeBase object is needed and is shared amongst all StatefulKnowledgeSessions
    // needs to be invoked AFTER guvnor is available (obviously)
    // setting 'force' parameter to true re-creates an existing kbase
    protected synchronized void createKnowledgeBaseViaKnowledgeAgent(boolean forceRefresh) throws ConnectException {
        log.info("createKnowledgeBaseViaKnowledgeAgent() forceRefresh = " + forceRefresh);
        if (kbase != null && !forceRefresh)
            return;

        if (kagent != null) {
            kagent.dispose();
            kagent = null;
        }

        // investigate:  List<String> guvnorPackages = guvnorUtils.getBuiltPackageNames();
        // http://ratwateribm:8080/jboss-brms/org.drools.guvnor.Guvnor/package/org.jboss.processFlow/test-pfp-snapshot

        if (!guvnorUtils.guvnorExists()) {
            StringBuilder sBuilder = new StringBuilder();
            sBuilder.append(guvnorUtils.getGuvnorProtocol());
            sBuilder.append("://");
            sBuilder.append(guvnorUtils.getGuvnorHost());
            sBuilder.append("/");
            sBuilder.append(guvnorUtils.getGuvnorSubdomain());
            sBuilder.append("/rest/packages/");
            throw new ConnectException(
                    "createKnowledgeBase() cannot connect to guvnor at URL : " + sBuilder.toString());
        }

        // for polling of guvnor to occur, the polling and notifier services must be started
        ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService()
                .newResourceChangeScannerConfiguration();

        // Do not start change set notifications
        Integer droolsResourceScannerIntervalValue = -1;
        try {
            droolsResourceScannerIntervalValue = Integer.valueOf(droolsResourceScannerInterval);
        } catch (NumberFormatException nfe) {
            log.error("DroolsResourceScannerInterval is not an integer: " + droolsResourceScannerInterval, nfe);
        }
        if (droolsResourceScannerIntervalValue > 0) {
            sconf.setProperty("drools.resource.scanner.interval", droolsResourceScannerInterval);
            ResourceFactory.getResourceChangeScannerService().configure(sconf);
            ResourceFactory.getResourceChangeScannerService().start();
            ResourceFactory.getResourceChangeNotifierService().start();
        }

        KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration(); // implementation = org.drools.agent.impl.KnowledgeAgentConfigurationImpl

        /*  - incremental change set processing enabled
        - will create a single KnowledgeBase and always refresh that same instance
        */
        aconf.setProperty("drools.agent.newInstance", "false");
        if (droolsResourceScannerIntervalValue < 0) {
            aconf.setProperty("drools.agent.scanResources", Boolean.FALSE.toString());
            aconf.setProperty("drools.agent.scanDirectories", Boolean.FALSE.toString());
            aconf.setProperty("drools.agent.monitorChangeSetEvents", Boolean.FALSE.toString());
        }

        /*  -- Knowledge Agent provides automatic loading, caching and re-loading of resources
        -- the knowledge agent can update or rebuild this knowledge base as the resources it uses are changed
        */
        kagent = KnowledgeAgentFactory.newKnowledgeAgent("Guvnor default", aconf);
        StringReader sReader = guvnorUtils.createChangeSet();
        try {
            guvnorChangeSet = IOUtils.toString(sReader);
            sReader.close();
        } catch (Exception x) {
            x.printStackTrace();
        }

        kagent.applyChangeSet(ResourceFactory.newByteArrayResource(guvnorChangeSet.getBytes()));

        /*  - set KnowledgeBase as instance variable to this mbean for use throughout all functionality of this service
        - a knowledge base is a collection of compiled definitions, such as rules and processes, which are compiled using the KnowledgeBuilder
        - the knowledge base itself does not contain instance data, known as facts
        - instead, sessions are created from the knowledge base into which data can be inserted and where process instances may be started
        - creating the knowledge base can be heavy, whereas session creation is very light :  http://blog.athico.com/2011/09/small-efforts-big-improvements.html
        - a knowledge base is also serializable, allowing for it to be stored
        */
        kbase = kagent.getKnowledgeBase();
        lastKAgentRefresh = System.currentTimeMillis();
        log.info("createKnowledgeBaseViaKnowledgeAgent() just refreshed kBase via knowledgeAgent");
    }

    /*
     * intention of this function is to create a knowledgeBase without a strict dependency on guvnor
     * will still query guvnor for packages but will continue on even if problems communicating with guvnor exists
     * this function could be of use in those scenarios where guvnor is not accessible
     * knowledgeBase can subsequently be populated via one of the addProcessToKnowledgeBase(....) functions
     * in all cases, the knowledgeBase created by this function will NOT be registered with a knowledgeAgent that receives updates from guvnor
     */
    public synchronized void rebuildKnowledgeBaseViaKnowledgeBuilder() {
        guvnorProps = new Properties();
        try {
            KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            if (guvnorUtils.guvnorExists()) {
                guvnorProps.load(BaseKnowledgeSessionBean.class.getResourceAsStream("/jbpm.console.properties"));
                StringBuilder guvnorSBuilder = new StringBuilder();
                guvnorSBuilder.append(guvnorProps.getProperty(GuvnorConnectionUtils.GUVNOR_PROTOCOL_KEY));
                guvnorSBuilder.append("://");
                guvnorSBuilder.append(guvnorProps.getProperty(GuvnorConnectionUtils.GUVNOR_HOST_KEY));
                guvnorSBuilder.append("/");
                guvnorSBuilder.append(guvnorProps.getProperty(GuvnorConnectionUtils.GUVNOR_SUBDOMAIN_KEY));
                String guvnorURI = guvnorSBuilder.toString();
                List<String> packages = guvnorUtils.getPackageNames();
                if (packages.size() > 0) {
                    for (String pkg : packages) {
                        GuvnorRestApi guvnorRestApi = new GuvnorRestApi(guvnorURI);
                        try {
                            InputStream binaryPackage = guvnorRestApi.getBinaryPackage(pkg);
                            kbuilder.add(new InputStreamResource(binaryPackage), ResourceType.PKG);
                            guvnorRestApi.close();
                        } catch (java.io.IOException y) {
                            log.error(
                                    "rebuildKnowledgeBaseViaKnowledgeBuilder() returned following exception when querying package = "
                                            + pkg + " : " + y);
                        }
                    }
                } else {
                    log.warn("rebuildKnowledgeBaseViaKnowledgeBuilder() no packages returned from Guvnor");
                }
            } else if (StringUtils.isNotEmpty(System.getProperty(IKnowledgeSession.CHANGE_SET_URLS))) {
                processEventListeners = System
                        .getProperty(IKnowledgeSession.SPACE_DELIMITED_PROCESS_EVENT_LISTENERS).split("\\s");
                String[] changeSetUrls = System.getProperty(IKnowledgeSession.CHANGE_SET_URLS).split("\\s");
                for (String changeSetUrl : changeSetUrls) {
                    InputStream iStream = null;
                    try {
                        iStream = new FileInputStream(changeSetUrl);
                        Resource rObj = new InputStreamResource(iStream);
                        kbuilder.add(rObj, ResourceType.PKG);
                    } finally {
                        if (iStream != null)
                            iStream.close();
                    }
                }
            } else {
                log.warn(
                        "rebuildKnowledgeBaseViaKnowledgeBuilder() guvnor does not exist and the following property is null: "
                                + IKnowledgeSession.CHANGE_SET_URLS);
            }
            kbase = kbuilder.newKnowledgeBase();
            log.info("rebuildKnowledgeBaseViaKnowledgeBuilder() just created kbase via KnowledgeBase");
        } catch (RuntimeException x) {
            throw x;
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    // compile a process into a package and add it to the knowledge base 
    public void addProcessToKnowledgeBase(Process processObj, Resource resourceObj) {
        checkKAgentAndBaseHealth();

        PackageBuilder packageBuilder = new PackageBuilder();
        ProcessBuilderImpl processBuilder = new ProcessBuilderImpl(packageBuilder);
        processBuilder.buildProcess(processObj, resourceObj);

        List<KnowledgePackage> kpackages = new ArrayList<KnowledgePackage>();
        kpackages.add(new KnowledgePackageImp(packageBuilder.getPackage()));
        kbase.addKnowledgePackages(kpackages);
        log.info("addProcessToKnowledgeBase() just added process obj to the kbase with Id : " + processObj.getId());
    }

    public void addProcessToKnowledgeBase(File bpmnFile) {
        checkKAgentAndBaseHealth();

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newFileResource(bpmnFile), ResourceType.BPMN2);
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
        if (!kbuilder.hasErrors()) {
            log.info("addProcessToKnowledgeBase() just added the following bpmn2 process definition to the kbase: "
                    + bpmnFile.getName());
        } else {
            log.error(
                    "addProcessToKnowledgeBase() following errors occurred when adding bpmn2 process definition : "
                            + bpmnFile.getName() + "\n\t" + kbuilder.getErrors().toString());
        }
    }

    public String getAllProcessesInPackage(String pkgName) throws ConnectException {
        if (!guvnorUtils.guvnorExists()) {
            StringBuilder sBuilder = new StringBuilder();
            sBuilder.append(guvnorUtils.getGuvnorProtocol());
            sBuilder.append("://");
            sBuilder.append(guvnorUtils.getGuvnorHost());
            sBuilder.append("/");
            sBuilder.append(guvnorUtils.getGuvnorSubdomain());
            sBuilder.append("/rest/packages/");
            throw new ConnectException(
                    "createKnowledgeBase() cannot connect to guvnor at URL : " + sBuilder.toString());
        }
        List<String> processes = guvnorUtils.getAllProcessesInPackage(pkgName);
        StringBuilder sBuilder = new StringBuilder("getAllProcessesInPackage() pkgName = " + pkgName);
        if (processes.isEmpty()) {
            sBuilder.append("\n\n\t :  not processes found");
            return sBuilder.toString();
        }
        for (String pDef : processes) {
            sBuilder.append("\n\t");
            sBuilder.append(pDef);
        }
        return sBuilder.toString();
    }

    public String printKnowledgeBaseContent() {
        checkKAgentAndBaseHealth();

        StringBuilder sBuilder = new StringBuilder();
        sBuilder.append("guvnor changesets:\n\t");

        if (guvnorChangeSet != null)
            sBuilder.append(guvnorChangeSet);
        else
            sBuilder.append("not yet created by knowledgeAgent");

        Collection<KnowledgePackage> kPackages = kbase.getKnowledgePackages();
        if (kPackages != null && kPackages.size() > 0) {
            sBuilder.append("\nprintKnowledgeBaseContent()\n\t");
            for (KnowledgePackage kPackage : kPackages) {
                Collection<Process> processes = kPackage.getProcesses();
                if (processes.size() == 0) {
                    sBuilder.append("\n\tpackage = " + kPackage.getName() + " : no process definitions found ");
                } else {
                    for (Process process : processes) {
                        sBuilder.append("\n\tpackage = " + kPackage.getName() + " : pDef= " + process.getId()
                                + " : pDefVersion= " + process.getVersion());
                    }
                }
            }
        } else {
            sBuilder.append("\n\nNo Packages found in kbase");
        }
        sBuilder.append("\n");
        return sBuilder.toString();
    }

    protected SessionTemplate newSessionTempate() {
        return newSessionTemplate(null);
    }

    protected SessionTemplate newSessionTemplate(StatefulKnowledgeSession ksession) {
        //looking for session.templte on local file system
        if (templateString == null) {
            synchronized (templateStringLockObj) {
                if (templateString == null) {
                    String droolsSessionTemplatePath = System.getProperty(DROOLS_SESSION_TEMPLATE_PATH);
                    if (StringUtils.isNotEmpty(droolsSessionTemplatePath)) {
                        File droolsSessionTemplate = new File(droolsSessionTemplatePath);
                        if (!droolsSessionTemplate.exists()) {
                            log.error("newSessionTemplate() drools session template not found at : "
                                    + droolsSessionTemplatePath);
                            sessionTemplateInstantiationAttempts = -1;
                        } else {
                            FileInputStream fStream = null;
                            try {
                                fStream = new FileInputStream(droolsSessionTemplate);
                                templateString = IOUtils.toString(fStream);

                            } catch (IOException x) {
                                x.printStackTrace();
                            } finally {
                                if (fStream != null) {
                                    try {
                                        fStream.close();
                                    } catch (Exception x) {
                                        x.printStackTrace();
                                    }
                                }
                            }
                        }
                    } else {
                        throw new RuntimeException("newSessionTemplate() following property must be defined : "
                                + DROOLS_SESSION_TEMPLATE_PATH);
                    }
                }
            }
        }
        if (sessionTemplateInstantiationAttempts == -1)
            return null;
        if (sessionTemplateInstantiationAttempts == 0) {
            synchronized (sessionTemplateInstantiationLock) {
                if (sessionTemplateInstantiationAttempts == -1)
                    return null;
                return parseSessionTemplateString(ksession);
            }
        }
        return parseSessionTemplateString(ksession);
    }

    // TO-DO:  somehow add ksession via mvel to workItemhandlers defined in session template
    private SessionTemplate parseSessionTemplateString(StatefulKnowledgeSession ksession) {
        ParserConfiguration pconf = new ParserConfiguration();
        pconf.addImport("SessionTemplate", SessionTemplate.class);
        ParserContext context = new ParserContext(pconf);
        Serializable s = MVEL.compileExpression(templateString.trim(), context);
        try {
            Map vars = new HashMap();
            vars.put(IKnowledgeSession.KSESSION, ksession);
            SessionTemplate sTemplate = (SessionTemplate) MVEL.executeExpression(s, vars);
            sessionTemplateInstantiationAttempts = 1;
            return sTemplate;
        } catch (Throwable x) {
            sessionTemplateInstantiationAttempts = -1;
            x.printStackTrace();
            log.error("newSessionTemplate() following exception thrown \n\t" + x.getLocalizedMessage()
                    + "\n : with session template string = \n\n" + templateString);
            return null;
        }
    }

    /******************************************************************************
     * *************            WorkItemHandler Management               *********/

    public String printWorkItemHandlers() {
        StringBuilder sBuilder = new StringBuilder("Programmatically Loaded Work Item Handlers :");
        for (String name : programmaticallyLoadedWorkItemHandlers.keySet()) {
            sBuilder.append("\n\t");
            sBuilder.append(name);
            sBuilder.append(" : ");
            sBuilder.append(programmaticallyLoadedWorkItemHandlers.get(name));
        }
        sBuilder.append("\nWork Item Handlers loaded from drools session template:");
        SessionTemplate sTemplate = newSessionTemplate(null);
        if (sTemplate != null && (sTemplate.getWorkItemHandlers() != null)) {
            for (Map.Entry<?, ?> entry : sTemplate.getWorkItemHandlers().entrySet()) {
                Class wiClass = entry.getValue().getClass();
                sBuilder.append("\n\t");
                sBuilder.append(entry.getKey());
                sBuilder.append(" : ");
                sBuilder.append(wiClass.getClass());
            }
        } else {
            sBuilder.append("\n\tsessionTemplate not instantiated or is empty... check previous exceptions");
        }
        sBuilder.append("\nConfiguration Loaded Work Item Handlers :");
        SessionConfiguration ksConfig = (SessionConfiguration) KnowledgeBaseFactory
                .newKnowledgeSessionConfiguration(ksconfigProperties);
        try {
            Map<String, WorkItemHandler> wiHandlers = ksConfig.getWorkItemHandlers();
            if (wiHandlers.size() == 0) {
                sBuilder.append("\n\t no work item handlers defined");
                Properties badProps = createPropsFromDroolsSessionConf();
                if (badProps == null)
                    sBuilder.append("\n\tunable to locate " + DROOLS_SESSION_CONF_PATH);
                else
                    sBuilder.append("\n\tlocated" + DROOLS_SESSION_CONF_PATH);
            } else {
                for (String name : wiHandlers.keySet()) {
                    sBuilder.append("\n\t");
                    sBuilder.append(name);
                    sBuilder.append(" : ");
                    Class wiClass = wiHandlers.get(name).getClass();
                    sBuilder.append(wiClass);
                }
            }
        } catch (NullPointerException x) {
            sBuilder.append(
                    "\n\tError intializing at least one of the configured work item handlers via drools.session.conf.\n\tEnsure all space delimited work item handlers listed in drools.session.conf exist on the classpath");
            Properties badProps = createPropsFromDroolsSessionConf();
            if (badProps == null) {
                sBuilder.append("\n\tunable to locate " + DROOLS_SESSION_CONF_PATH);
            } else {
                try {
                    Enumeration badEnums = badProps.propertyNames();
                    while (badEnums.hasMoreElements()) {
                        String handlerConfName = (String) badEnums.nextElement();
                        if (DROOLS_WORK_ITEM_HANDLERS.equals(handlerConfName)) {
                            String[] badHandlerNames = ((String) badProps.get(handlerConfName)).split("\\s");
                            for (String badHandlerName : badHandlerNames) {
                                sBuilder.append("\n\t\t");
                                sBuilder.append(badHandlerName);
                                InputStream iStream = this.getClass()
                                        .getResourceAsStream("/META-INF/" + badHandlerName);
                                if (iStream != null) {
                                    sBuilder.append("\t : found on classpath");
                                    iStream.close();
                                } else {
                                    sBuilder.append("\t : NOT FOUND on classpath !!!!!  ");
                                }
                            }
                        }
                    }
                } catch (Exception y) {
                    y.printStackTrace();
                }
            }
        } catch (org.mvel2.CompileException x) {
            sBuilder.append("\n\t located " + DROOLS_SESSION_CONF_PATH);
            sBuilder.append(
                    "\n\t however, following ClassNotFoundException encountered when instantiating defined work item handlers : \n\t\t");
            sBuilder.append(x.getLocalizedMessage());
        }
        sBuilder.append("\n");
        return sBuilder.toString();
    }

    private Properties createPropsFromDroolsSessionConf() {
        Properties badProps = null;
        InputStream iStream = null;
        try {
            iStream = this.getClass().getResourceAsStream(DROOLS_SESSION_CONF_PATH);
            if (iStream != null) {
                badProps = new Properties();
                badProps.load(iStream);
                iStream.close();
            }
        } catch (Exception x) {
            x.printStackTrace();
        }
        return badProps;
    }

    protected void registerWorkItemHandler(StatefulKnowledgeSession ksession, String serviceTaskName,
            WorkItemHandlerLifecycle handler) {
        try {
            ksession.getWorkItemManager().registerWorkItemHandler(serviceTaskName, handler);
        } catch (NullPointerException x) {
            StringBuilder sBuilder = new StringBuilder();
            sBuilder.append(
                    "registerHumanTaskWorkItemHandler() ********* NullPointerException when attempting to programmatically register workItemHander of type: "
                            + serviceTaskName);
            sBuilder.append("\nthe following is a report of your work item situation: \n\n");
            sBuilder.append(printWorkItemHandlers());
            sBuilder.append("\n");
            log.error(sBuilder);
            throw x;
        }
    }

    protected void registerAddHumanTaskWorkItemHandler(StatefulKnowledgeSession ksession) {
        try {
            // 1.  instantiate an object and register with this session workItemManager 
            Class workItemHandlerClass = programmaticallyLoadedWorkItemHandlers.get(ITaskService.HUMAN_TASK);
            WorkItemHandlerLifecycle handler = (WorkItemHandlerLifecycle) workItemHandlerClass.newInstance();

            // 2.  register workItemHandler with workItemManager
            registerWorkItemHandler(ksession, ITaskService.HUMAN_TASK, handler);

            // 3).  call init() on newly instantiated WorkItemHandlerLifecycle
            handler.init(ksession);
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    protected void registerSkipHumanTaskWorkItemHandler(StatefulKnowledgeSession ksession) {
        try {
            Class workItemHandlerClass = programmaticallyLoadedWorkItemHandlers.get(ITaskService.SKIP_TASK);
            WorkItemHandlerLifecycle handler = (WorkItemHandlerLifecycle) workItemHandlerClass.newInstance();
            registerWorkItemHandler(ksession, ITaskService.SKIP_TASK, handler);
            handler.init(ksession);
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    protected void registerFailHumanTaskWorkItemHandler(StatefulKnowledgeSession ksession) {
        try {
            Class workItemHandlerClass = programmaticallyLoadedWorkItemHandlers.get(ITaskService.FAIL_TASK);
            WorkItemHandlerLifecycle handler = (WorkItemHandlerLifecycle) workItemHandlerClass.newInstance();
            registerWorkItemHandler(ksession, ITaskService.FAIL_TASK, handler);
            handler.init(ksession);
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    protected void registerEmailWorkItemHandler(StatefulKnowledgeSession ksession) {
        String address = System.getProperty("org.jbpm.workItemHandler.mail.address");
        String port = System.getProperty("org.jbpm.workItemHandler.mail.port");
        String userId = System.getProperty("org.jbpm.workItemHandler.mail.userId");
        String password = System.getProperty("org.jbpm.workItemHandler.mail.password");
        WorkItemHandlerLifecycle handler = null;
        try {
            Class workItemHandlerClass = programmaticallyLoadedWorkItemHandlers.get(IKnowledgeSession.EMAIL);
            Class[] classParams = new Class[] { String.class, String.class, String.class, String.class };
            Object[] objParams = new Object[] { address, port, userId, password };
            Constructor cObj = workItemHandlerClass.getConstructor(classParams);
            handler = (WorkItemHandlerLifecycle) cObj.newInstance(objParams);
            registerWorkItemHandler(ksession, IKnowledgeSession.EMAIL, handler);
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    /******************************************************************************
     * *************    ProcessEventListener Management                  *********/

    // listens for agenda changes like rules being activated, fired, cancelled, etc
    protected void addAgendaEventListener(StatefulKnowledgeSession ksession) {
        final AgendaEventListener agendaEventListener = new DefaultAgendaEventListener() {

            @Override
            public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
                KnowledgeRuntime kRuntime = event.getKnowledgeRuntime();
                if (kRuntime instanceof StatefulKnowledgeSession) {
                    ((StatefulKnowledgeSession) kRuntime).fireAllRules();
                }
            }
        };
        ksession.addEventListener(agendaEventListener);
    }

    /******************************************************************************
     *************        StatefulKnowledgeSession Management               *********/

    protected StatefulKnowledgeSession makeStatefulKnowledgeSession() {
        // 1) instantiate a KnowledgeBase via query to guvnor or kbuilder
        createKnowledgeBaseViaKnowledgeAgentOrBuilder();

        // 2) very important that a unique 'Environment' is created per StatefulKnowledgeSession
        Environment ksEnv = createKnowledgeSessionEnvironment();

        // Nick: always instantiate new ksconfig to make it threadlocal to bypass the ConcurrentModificationExcepotion
        KnowledgeSessionConfiguration ksConfig = KnowledgeBaseFactory
                .newKnowledgeSessionConfiguration(ksconfigProperties);

        // 3) instantiate StatefulKnowledgeSession
        //    make synchronize because under heavy load, appears that underlying SessionInfo.update() breaks with a NPE
        StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession(kbase, ksConfig, ksEnv);
        return ksession;
    }

    protected void addExtrasCommon(StatefulKnowledgeSession ksession) {

        // 1) register a configurable WorkItemHandlers with StatefulKnowledgeSession
        this.registerAddHumanTaskWorkItemHandler(ksession);
        this.registerSkipHumanTaskWorkItemHandler(ksession);
        this.registerFailHumanTaskWorkItemHandler(ksession);
        this.registerEmailWorkItemHandler(ksession);

        //1.5 register any addition workItemHandlers and eventListeners defined in drools.session.template
        SessionTemplate sTemplate = newSessionTemplate(ksession);
        if (sTemplate != null) {
            if (sTemplate.getWorkItemHandlers() != null) {
                for (Map.Entry<String, ?> entry : sTemplate.getWorkItemHandlers().entrySet()) {
                    try {
                        WorkItemHandler wHandler = (WorkItemHandler) entry.getValue();
                        ksession.getWorkItemManager().registerWorkItemHandler(entry.getKey(), wHandler);
                    } catch (Exception x) {
                        throw new RuntimeException(
                                "addExtrasToStatefulKnowledgeSession() following exception occurred when registering workItemId = "
                                        + entry.getKey() + " : " + x.getLocalizedMessage());
                    }
                }
            }
            if (sTemplate.getEventListeners() != null) {
                List eListeners = sTemplate.getEventListeners();
                for (Object eListener : eListeners) {
                    Object eObj = MVEL.eval((String) eListener);
                    if (eObj instanceof AgendaEventListener) {
                        ksession.addEventListener((AgendaEventListener) eObj);
                    } else if (eObj instanceof WorkingMemoryEventListener) {
                        ksession.addEventListener((WorkingMemoryEventListener) eObj);
                    } else if (eObj instanceof ProcessEventListener) {
                        ksession.addEventListener((ProcessEventListener) eObj);
                    } else {
                        log.error("addExtrasToStatefulKnowledgeSession() invalid eventListener : " + eListener);
                    }
                }
            }
        }

        // 2)  add agendaEventListener to knowledge session to notify knowledge session of various rules events
        addAgendaEventListener(ksession);
    }

    /******************************************************************************
    *************              Process Definition Management              *********/
    public List<SerializableProcessMetaData> retrieveProcesses() throws Exception {
        checkKAgentAndBaseHealth();
        List<SerializableProcessMetaData> result = new ArrayList<SerializableProcessMetaData>();
        for (KnowledgePackage kpackage : kbase.getKnowledgePackages()) {
            for (Process processObj : kpackage.getProcesses()) {
                result.add(getProcess(processObj.getId()));
            }
        }
        log.info("getProcesses() # of processes = " + result.size());
        return result;
    }

    public SerializableProcessMetaData getProcess(String processId) {
        checkKAgentAndBaseHealth();
        Process processObj = kbase.getProcess(processId);
        Long pVersion = 0L;
        if (!StringUtils.isEmpty(processObj.getVersion())) {
            try {
                pVersion = Long.parseLong(processObj.getVersion());
            } catch (NumberFormatException x) {
                log.error("getProcess() processId = " + processId
                        + " : process versions must be of type long. the following is invalid: "
                        + processObj.getVersion());
            }
        }
        SerializableProcessMetaData spObj = new SerializableProcessMetaData(processObj.getId(),
                processObj.getName(), pVersion, processObj.getPackageName());
        if (processObj instanceof org.drools.definition.process.WorkflowProcess) {
            Node[] nodes = ((WorkflowProcess) processObj).getNodes();
            addNodesInfo(spObj.getNodes(), nodes, "id=");
        }
        return spObj;
    }

    private void addNodesInfo(List<SerializableNodeMetaData> snList, Node[] nodes, String prefix) {
        for (Node nodeObj : nodes) {
            // JA Bride:  AsyncBAMProducer has been modified from stock jbpm5 to persist the "uniqueNodeId" in the jbpm_bam database
            //  (as opposed to persisting just the simplistic nodeId)
            //  will need to invoke same functionality here to calculate 'uniqueNodeId' 
            String uniqueId = org.jbpm.bpmn2.xml.XmlBPMNProcessDumper.getUniqueNodeId(nodeObj);
            SerializableNodeMetaData snObj = new SerializableNodeMetaData(
                    (Integer) nodeObj.getMetaData().get(SerializableNodeMetaData.X),
                    (Integer) nodeObj.getMetaData().get(SerializableNodeMetaData.Y),
                    (Integer) nodeObj.getMetaData().get(SerializableNodeMetaData.HEIGHT),
                    (Integer) nodeObj.getMetaData().get(SerializableNodeMetaData.WIDTH), uniqueId);
            snList.add(snObj);
            if (nodeObj instanceof NodeContainer) {
                addNodesInfo(snObj.getNodes(), ((NodeContainer) nodeObj).getNodes(),
                        prefix + nodeObj.getId() + ":");
            }
        }
    }

    public void removeProcess(String processId) {
        throw new UnsupportedOperationException();
    }

    public List<ProcessInstanceInfo> getActiveProcessInstances(Map<String, Object> queryCriteria) {
        EntityManager psqlEm = null;
        List<ProcessInstanceInfo> results = null;
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("FROM ProcessInstanceInfo p ");
        if (queryCriteria != null && queryCriteria.size() > 0) {
            sqlBuilder.append("WHERE ");
            if (queryCriteria.containsKey(IKnowledgeSession.PROCESS_ID)) {
                sqlBuilder.append("p.processid = :processId");
            }
        }
        try {
            psqlEm = jbpmCoreEMF.createEntityManager();
            Query processInstanceQuery = psqlEm.createQuery(sqlBuilder.toString());
            if (queryCriteria != null && queryCriteria.size() > 0) {
                if (queryCriteria.containsKey(IKnowledgeSession.PROCESS_ID)) {
                    processInstanceQuery = processInstanceQuery.setParameter(IKnowledgeSession.PROCESS_ID,
                            queryCriteria.get(IKnowledgeSession.PROCESS_ID));
                }
            }
            results = processInstanceQuery.getResultList();
            return results;
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    public String printActiveProcessInstances(Map<String, Object> queryCriteria) {
        List<ProcessInstanceInfo> pInstances = getActiveProcessInstances(queryCriteria);
        StringBuffer sBuffer = new StringBuffer();
        if (pInstances != null) {
            sBuffer.append("\npInstanceId\tprocessId");
            for (ProcessInstanceInfo pInstance : pInstances) {
                sBuffer.append("\n" + pInstance.getId() + "\t" + pInstance.getProcessId());
            }
            sBuffer.append("\n");
        } else {
            sBuffer.append("\nno active process instances found\n");
        }
        return sBuffer.toString();
    }

    private static final List<Integer> NON_ROLLBACK_TX = Arrays.asList(new Integer[] {
            javax.transaction.Status.STATUS_NO_TRANSACTION, javax.transaction.Status.STATUS_ROLLEDBACK });

    protected void rollbackTrnx() {
        try {
            if (!NON_ROLLBACK_TX.contains(uTrnx.getStatus())) {
                uTrnx.rollback();
            }
        } catch (Exception e) {
            log.error(e.getMessage() + " - at: " + (e.getStackTrace() == null ? null : e.getStackTrace()[0]));
        }
    }
}