/*
* 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.
*
* 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.bpmn2.concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.jbpm.bpmn2.objects.Status;
import org.jbpm.bpmn2.objects.TestWorkItemHandler;
import org.jbpm.test.util.AbstractBaseTest;
import org.junit.Ignore;
import org.junit.Test;
import org.kie.api.event.process.ProcessCompletedEvent;
import org.kie.api.event.process.ProcessEventListener;
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.io.ResourceType;
import org.kie.api.runtime.process.WorkItem;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.runtime.StatefulKnowledgeSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This test costs time and resources, please only run locally for the time being.
*/
@Ignore
public class MultipleProcessesPerThreadTest extends AbstractBaseTest {
private static final int LOOPS = 1000;
private static final Logger logger = LoggerFactory.getLogger(MultipleProcessesPerThreadTest.class);
protected static StatefulKnowledgeSession createStatefulKnowledgeSession(KnowledgeBase kbase) {
return kbase.newStatefulKnowledgeSession();
}
@Test
public void doMultipleProcessesInMultipleThreads() {
HelloWorldProcessThread hello = new HelloWorldProcessThread();
UserTaskProcessThread user = new UserTaskProcessThread();
hello.start();
user.start();
try {
hello.join();
user.join();
} catch (Throwable t) {
t.printStackTrace();
}
assertTrue( "Hello World process thread did not complete successfully", hello.status == Status.SUCCESS );
assertTrue( "User Task process thread did not complete successfully", user.status == Status.SUCCESS );
}
private static class HelloWorldProcessThread implements Runnable {
private Thread thread;
volatile Status status;
private volatile CountDownLatch latch;
public void start() {
thread = new Thread(this);
thread.start();
}
public void run() {
this.status = Status.SUCCESS;
StatefulKnowledgeSession ksession = null;
try {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("BPMN2-MultiThreadServiceProcess-Timer.bpmn", getClass()), ResourceType.BPMN2);
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
ksession = createStatefulKnowledgeSession(kbase);
} catch(Exception e) {
e.printStackTrace();
logger.error("Unable to set up knowlede base or session.", e);
this.status = Status.FAIL;
}
for (int i = 1; i <= LOOPS; i++) {
logger.debug("Starting hello world process, loop {}/{}", i, LOOPS);
latch = new CountDownLatch(1);
CompleteProcessListener listener = new CompleteProcessListener(latch);
ksession.addEventListener(listener);
try {
ksession.startProcess("hello-world");
} catch (Throwable t) {
t.printStackTrace();
}
try {
latch.await();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
public synchronized void join() throws InterruptedException {
thread.join();
}
}
private static class UserTaskProcessThread implements Runnable {
private Thread thread;
volatile Status status;
private volatile CountDownLatch latch;
public void start() {
thread = new Thread(this);
thread.start();
}
public void run() {
this.status = Status.SUCCESS;
StatefulKnowledgeSession ksession = null;
try {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("BPMN2-MultiThreadServiceProcess-Task.bpmn", getClass()), ResourceType.BPMN2);
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
ksession = createStatefulKnowledgeSession(kbase);
} catch(Exception e) {
e.printStackTrace();
logger.error("Unable to set up knowlede base or session.", e);
this.status = Status.FAIL;
}
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
for (int i = 1; i <= LOOPS; i++) {
logger.debug("Starting user task process, loop {}/{}", i, LOOPS);
latch = new CountDownLatch(1);
CompleteProcessListener listener = new CompleteProcessListener(latch);
ksession.addEventListener(listener);
try {
ksession.startProcess("user-task");
} catch (Throwable t) {
t.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<WorkItem> items = new ArrayList<WorkItem>();
items = workItemHandler.getWorkItems();
for (WorkItem item : items) {
try {
ksession.getWorkItemManager().completeWorkItem(item.getId(), null);
} catch (Throwable t) {
t.printStackTrace();
}
}
try {
latch.await();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
public synchronized void join() throws InterruptedException {
thread.join();
}
}
private static class CompleteProcessListener implements ProcessEventListener {
private volatile CountDownLatch guard;
public CompleteProcessListener(CountDownLatch guard) {
this.guard = guard;
}
public void beforeProcessStarted(ProcessStartedEvent event) {
}
public void afterProcessStarted(ProcessStartedEvent event) {
}
public void beforeProcessCompleted(ProcessCompletedEvent event) {
}
public void afterProcessCompleted(ProcessCompletedEvent event) {
guard.countDown();
}
public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) {
}
public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {
}
public void beforeNodeLeft(ProcessNodeLeftEvent event) {
}
public void afterNodeLeft(ProcessNodeLeftEvent event) {
}
public void beforeVariableChanged(ProcessVariableChangedEvent event) {
}
public void afterVariableChanged(ProcessVariableChangedEvent event) {
}
}
}