/*
* Copyright 2017 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.services.task.assignment;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.assertj.core.util.Arrays;
import org.jbpm.services.task.HumanTaskServiceFactory;
import org.jbpm.services.task.assignment.impl.strategy.LoadBalanceAssignmentStrategy;
import org.jbpm.services.task.audit.JPATaskLifeCycleEventListener;
import org.jbpm.services.task.lifecycle.listeners.BAMTaskEventListener;
import org.jbpm.services.task.utils.TaskFluent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.kie.api.task.model.Task;
import org.kie.internal.task.api.InternalTaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bitronix.tm.resource.jdbc.PoolingDataSource;
public class TotalCompletionTimeAssignmentStrategyTest extends AbstractAssignmentTest {
private PoolingDataSource pds;
private EntityManagerFactory emf;
private Long taskIds[];
private static final Logger logger = LoggerFactory.getLogger(TotalCompletionTimeAssignmentStrategyTest.class);
private static final String DARTH_VADER = "Darth Vader";
private static final String BOBBA_FET = "Bobba Fet";
private static final String LUKE_CAGE = "Luke Cage";
private static final String TONY_STARK = "Tony Stark";
private static final String DEPLOYMENT_ID = "org.jbpm:jbpm-human-task:7.1.0";
private static final String PROCESS_ID = "testing tasks";
private static final Map<String,Object> data = new HashMap<>();
/**
* Creates tasks and completes them so that there is
* BAMTaskSummary data
*/
private void forceBAMEntries() {
for (int count = 0; count < 10; count++) {
TaskFluent task1 = new TaskFluent()
.setName("MultiUserLoadBalanceTask1")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setAdminUser("Administrator")
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID);
long taskId = createTaskWithoutAssert(task1);
int waitTime = 100;
if (count%3 == 0) {
waitTime = 230;
} else if (count%2 == 0) {
waitTime = 180;
}
completeTask(taskId,waitTime);
}
for (int count = 0; count < 10; count++) {
TaskFluent task2 = new TaskFluent()
.setName("MultiUserLoadBalanceTask2")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setAdminUser("Administrator")
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID);
long taskId = createTaskWithoutAssert(task2);
int waitTime = 1000;
if (count%3 == 0) {
waitTime = 2300;
} else if (count%2 == 0) {
waitTime = 1800;
}
completeTask(taskId,waitTime);
}
}
@Before
public void setUp() throws Exception {
System.setProperty("org.jbpm.task.assignment.enabled", "true");
System.setProperty("org.jbpm.task.assignment.strategy", "LoadBalance");
System.setProperty("org.jbpm.task.assignment.loadbalance.calculator","org.jbpm.services.task.assignment.impl.TotalCompletionTimeLoadCalculator");
System.setProperty("org.jbpm.services.task.assignment.taskduration.timetolive", "1000");
pds = setupPoolingDataSource();
emf = Persistence.createEntityManagerFactory( "org.jbpm.services.task" );
AssignmentServiceProvider.override(new LoadBalanceAssignmentStrategy());
this.taskService = (InternalTaskService) HumanTaskServiceFactory.newTaskServiceConfigurator()
.entityManagerFactory(emf)
.listener(new JPATaskLifeCycleEventListener(true))
.listener(new BAMTaskEventListener(true))
.getTaskService();
taskIds = new Long[100]; // giving ourselves lots of room
forceBAMEntries();
}
@After
public void clean() throws Exception {
System.clearProperty("org.jbpm.task.assignment.enabled");
System.clearProperty("org.jbpm.task.assignment.strategy");
System.clearProperty("org.jbpm.task.assignment.loadbalance.calculator");
AssignmentServiceProvider.clear();
if (emf != null) {
emf.close();
}
if (pds != null) {
pds.close();
}
}
@Test
public void testMultipleUser() {
String expectedOwners[] = Arrays.array(BOBBA_FET,DARTH_VADER,LUKE_CAGE);
// Everyone gets two of these tasks to start
for (int x = 0; x < 6; x++) {
TaskFluent task1 = new TaskFluent()
.setName("MultiUserLoadBalanceTask1")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task1,expectedOwners[x%3]);
}
// Complete Darth's first task
completeTask(taskIds[1],500);
// Darth moves to the front since
// he has less active tasks
TaskFluent task2 = new TaskFluent()
.setName("MultiUserLoadBalanceTask2")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
createAndAssertTask(task2,DARTH_VADER);
logger.info("testMultipleUser completed");
}
@Test
public void testMultipleUserWithGroup() {
String expectedOwners[] = Arrays.array(BOBBA_FET,LUKE_CAGE,TONY_STARK);
// While the Crusaders (the group containing Tony Stark)
// is added before Luke Cage, Luke will show up as the
// second assignee. This is because group members are always
// added to the end of the potential owners list
for (int x = 0; x < 3; x++) {
TaskFluent task1 = new TaskFluent()
.setName("MultiUserLoadBalanceTask1")
.addPotentialUser(BOBBA_FET)
.addPotentialGroup("Crusaders")
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task1,expectedOwners[x]);
}
// Complete Luke's task
completeTask(taskIds[1],1000);
// Luke jumps to the front of the expected owners
// because he has no active tasks
expectedOwners = Arrays.array(LUKE_CAGE,BOBBA_FET,TONY_STARK);
for (int x = 3; x < 6; x++) {
TaskFluent task2 = new TaskFluent()
.setName("MultiUserLoadBalanceTask2")
.addPotentialUser(BOBBA_FET)
.addPotentialGroup("Crusaders")
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task2,expectedOwners[x-3]);
}
logger.info("testMultipleUserWithGroup completed");
}
@Test
public void testMultipleUserWithAdd() {
String expectedOwners[] = Arrays.array(BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK);
for (int x = 0; x < 3; x++) {
TaskFluent task1 = new TaskFluent()
.setName("MultiUserLoadBalanceTask1")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task1,expectedOwners[x]);
}
// Complete Darth Vader's task
completeTask(taskIds[1],1000);
// Create a new task and see if
// Darth gets assigned
TaskFluent task2 = new TaskFluent()
.setName("MultiUserLoadBalanceTask2")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
createAndAssertTask(task2,DARTH_VADER);
// Add Tony Stark to the list of potential
// owners and see him assigned to the new task
TaskFluent task3 = new TaskFluent()
.setName("MultiUserLoadBalanceTask3")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.addPotentialUser(TONY_STARK)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
createAndAssertTask(task3,TONY_STARK);
logger.info("testMultipleUserWithAdd completed");
}
@Test
public void testMultipleUsersWithRemove() {
// First a set of tasks with our entire
// crew of villains and heroes
String expectedOwners[] = Arrays.array(BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK);
for (int x = 0; x < 4; x++) {
TaskFluent task1 = new TaskFluent()
.setName("MultiUserLoadBalanceTask1")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(DARTH_VADER)
.addPotentialUser(LUKE_CAGE)
.addPotentialUser(TONY_STARK)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task1,expectedOwners[x]);
}
// Next we will complete Darth Vader's task
completeTask(taskIds[1],1500);
// Now do a series of tasks without Darth Vader
expectedOwners = Arrays.array(BOBBA_FET,LUKE_CAGE,TONY_STARK);
for (int x = 4; x < 7; x++) {
TaskFluent task2 = new TaskFluent()
.setName("MultiUserLoadBalanceTask2")
.addPotentialUser(BOBBA_FET)
.addPotentialUser(LUKE_CAGE)
.addPotentialUser(TONY_STARK)
.setDeploymentID(DEPLOYMENT_ID)
.setProcessId(PROCESS_ID)
.setAdminUser("Administrator");
taskIds[x] = createAndAssertTask(task2,expectedOwners[x-4]);
}
logger.info("testMultipleUsersWithRemove completed");
}
private long createTaskWithoutAssert(TaskFluent tf) {
Task task = tf.getTask();
taskService.addTask(task, data);
return task.getId();
}
private long createAndAssertTask(TaskFluent tf, String expectedOwner) {
Task task = tf.getTask();
taskService.addTask(task, data);
long taskId = task.getId();
assertEquals("Owner mismatch",expectedOwner,taskService.getTaskById(taskId).getTaskData().getActualOwner().getId());
return taskId;
}
private void completeTask(long taskId, long delay) {
Task task = taskService.getTaskById(taskId);
String owner = task.getTaskData().getActualOwner().getId();
logger.debug("Starting task {} with user {}",taskId,owner);
taskService.start(taskId, owner);
if (delay > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// swallowing this exception
}
}
taskService.complete(taskId, owner, data);
}
}