/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jbpm.test.listener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.drools.persistence.api.TransactionManager; import org.drools.persistence.api.TransactionManagerFactory; import org.drools.persistence.api.TransactionSynchronization; import org.kie.api.event.process.DefaultProcessEventListener; import org.kie.api.event.process.ProcessCompletedEvent; import org.kie.api.event.process.ProcessNodeLeftEvent; import org.kie.api.event.process.ProcessNodeTriggeredEvent; import org.kie.api.event.process.ProcessStartedEvent; import org.kie.api.event.process.ProcessVariableChangedEvent; import org.kie.api.runtime.process.ProcessInstance; public class TrackingProcessEventListener extends DefaultProcessEventListener { private final int numberOfCountDownsNeeded; private boolean transactional = true; public TrackingProcessEventListener(int involvedThreads) { this.numberOfCountDownsNeeded = involvedThreads; this.processesAbortedLatch = new CountDownLatch(involvedThreads); this.processesStartedLatch = new CountDownLatch(involvedThreads); this.processesCompletedLatch = new CountDownLatch(involvedThreads); } public TrackingProcessEventListener() { this(1); } public TrackingProcessEventListener(boolean transactional) { this(1); this.transactional = transactional; } private final List<String> processesStarted = new ArrayList<String>(); private CountDownLatch processesStartedLatch; private final List<String> processesCompleted = new ArrayList<String>(); private CountDownLatch processesCompletedLatch; private final List<String> processesAborted = new ArrayList<String>(); private CountDownLatch processesAbortedLatch; private final List<String> nodesTriggered = new ArrayList<String>(); private final ConcurrentHashMap<String, CountDownLatch> nodeTriggeredLatchMap = new ConcurrentHashMap<String, CountDownLatch>(); private final List<String> nodesLeft = new ArrayList<String>(); private final ConcurrentHashMap<String, CountDownLatch> nodeLeftLatchMap = new ConcurrentHashMap<String, CountDownLatch>(); private final List<String> variablesChanged = new ArrayList<String>(); @Override public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) { String nodeName = event.getNodeInstance().getNodeName(); CountDownLatch nodeLatch = getNodeTriggeredLatch(nodeName); nodesTriggered.add(nodeName); countDown(nodeLatch); } @Override public void beforeNodeLeft(ProcessNodeLeftEvent event) { String nodeName = event.getNodeInstance().getNodeName(); CountDownLatch nodeLatch = getNodeLeftLatch(nodeName); nodesLeft.add(nodeName); countDown(nodeLatch); } @Override public void beforeProcessStarted(ProcessStartedEvent event) { if( processesStartedLatch.getCount() == 0 ) { processesStartedLatch = new CountDownLatch(numberOfCountDownsNeeded); } processesStarted.add(event.getProcessInstance().getProcessId()); countDown(processesStartedLatch); } @Override public void beforeProcessCompleted(ProcessCompletedEvent event) { if (event.getProcessInstance().getState() == ProcessInstance.STATE_ABORTED) { processesAborted.add(event.getProcessInstance().getProcessId()); if( processesAbortedLatch.getCount() == 0 ) { processesAbortedLatch = new CountDownLatch(numberOfCountDownsNeeded); } countDown(processesAbortedLatch); } else { processesCompleted.add(event.getProcessInstance().getProcessId()); if( processesCompletedLatch.getCount() == 0 ) { processesCompletedLatch = new CountDownLatch(numberOfCountDownsNeeded); } countDown(processesCompletedLatch); } } @Override public void beforeVariableChanged(ProcessVariableChangedEvent event) { variablesChanged.add(event.getVariableId()); } public List<String> getNodesTriggered() { return Collections.unmodifiableList(nodesTriggered); } public List<String> getNodesLeft() { return Collections.unmodifiableList(nodesLeft); } public List<String> getProcessesStarted() { return Collections.unmodifiableList(processesStarted); } public List<String> getProcessesCompleted() { return Collections.unmodifiableList(processesCompleted); } public List<String> getProcessesAborted() { return Collections.unmodifiableList(processesAborted); } public List<String> getVariablesChanged() { return Collections.unmodifiableList(variablesChanged); } public boolean wasNodeTriggered(String nodeName) { return nodesTriggered.contains(nodeName); } public boolean wasNodeLeft(String nodeName) { return nodesLeft.contains(nodeName); } public boolean wasProcessStarted(String processName) { return processesStarted.contains(processName); } public boolean wasProcessCompleted(String processName) { return processesCompleted.contains(processName); } public boolean wasProcessAborted(String processName) { return processesAborted.contains(processName); } public boolean wasVariableChanged(String variableId) { return variablesChanged.contains(variableId); } public boolean waitForProcessToStart(long milliseconds) throws Exception { return processesStartedLatch.await(milliseconds, TimeUnit.MILLISECONDS); } public boolean waitForProcessToComplete(long milliseconds) throws Exception { return processesCompletedLatch.await(milliseconds, TimeUnit.MILLISECONDS); } public boolean waitForProcessToAbort(long milliseconds) throws Exception { return processesAbortedLatch.await(milliseconds, TimeUnit.MILLISECONDS); } public boolean waitForNodeTobeTriggered(String nodeName, long milliseconds) throws Exception { CountDownLatch nodeLatch = getNodeTriggeredLatch(nodeName); return nodeLatch.await(milliseconds, TimeUnit.MILLISECONDS); } public boolean waitForNodeToBeLeft(String nodeName, long milliseconds) throws Exception { CountDownLatch nodeLatch = getNodeLeftLatch(nodeName); return nodeLatch.await(milliseconds, TimeUnit.MILLISECONDS); } private CountDownLatch getNodeTriggeredLatch(String nodeName) { return getNodeLatch(nodeTriggeredLatchMap, nodeName); } private CountDownLatch getNodeLeftLatch(String nodeName) { return getNodeLatch(nodeLeftLatchMap, nodeName); } private CountDownLatch getNodeLatch(ConcurrentHashMap<String, CountDownLatch> nodeLatchMap, String nodeName) { synchronized(nodeLatchMap) { CountDownLatch nodeLatch = new CountDownLatch(numberOfCountDownsNeeded); CountDownLatch previousLatch = nodeLatchMap.putIfAbsent(nodeName,nodeLatch); if( previousLatch != null ) { return previousLatch; } return nodeLatch; } } public void clear() { nodesTriggered.clear(); nodesLeft.clear(); processesStarted.clear(); processesCompleted.clear(); processesAborted.clear(); variablesChanged.clear(); processesStartedLatch = new CountDownLatch(numberOfCountDownsNeeded); processesAbortedLatch = new CountDownLatch(numberOfCountDownsNeeded); processesCompletedLatch = new CountDownLatch(numberOfCountDownsNeeded); nodeTriggeredLatchMap.clear(); nodeLeftLatchMap.clear(); } protected void countDown(final CountDownLatch latch) { try { TransactionManager tm = TransactionManagerFactory.get().newTransactionManager(); if (transactional && tm != null && tm.getStatus() != TransactionManager.STATUS_NO_TRANSACTION && tm.getStatus() != TransactionManager.STATUS_ROLLEDBACK && tm.getStatus() != TransactionManager.STATUS_COMMITTED) { tm.registerTransactionSynchronization(new TransactionSynchronization() { @Override public void beforeCompletion() { } @Override public void afterCompletion(int status) { latch.countDown(); } }); } else { latch.countDown(); } } catch (Exception e) { latch.countDown(); } } }