/* * 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 java.util.Collection; import java.util.Optional; import java.util.function.Consumer; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.jbpm.services.task.HumanTaskServiceFactory; import org.jbpm.services.task.assignment.impl.strategy.LoadBalanceAssignmentStrategy; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.kie.api.task.model.Status; import org.kie.api.task.model.Task; import org.kie.internal.task.api.InternalTaskService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import bitronix.tm.resource.jdbc.PoolingDataSource; public class LoadBalanceAssignmentStrategyTest extends AbstractAssignmentTest { private PoolingDataSource pds; private EntityManagerFactory emf; private ListMultimap<String, Task> tasks; private static final Logger logger = LoggerFactory.getLogger(LoadBalanceAssignmentStrategyTest.class); private static final String BASE_TASK_INFO = "with (new Task()) { priority = 55, taskData = (with (new TaskData()) { } ), "; private static final String MULTI_ACTOR_ASSIGNMENTS = "" + "peopleAssignments = (with (new PeopleAssignments()) { potentialOwners = [new User('Bobba Fet'), new User('Darth Vader'), new User('Luke Cage')]," + " businessAdministrators = [new User('Administrator')], } ),"; private static final String MULTI_ACTOR_WITH_GROUP_ASSIGNMENTS = "" + "peopleAssignments = (with (new PeopleAssignments()) { potentialOwners = [new User('Bobba Fet'), new Group('Crusaders'), new User('Luke Cage')]," + " businessAdministrators = [new User('Administrator')], } ),"; private static final String ADD_ACTOR_ASSIGNMENTS = "" + "peopleAssignments = (with (new PeopleAssignments()) { potentialOwners = [new User('Bobba Fet'), new User('Darth Vader'), new User('Luke Cage'), new User('Tony Stark')]," + " businessAdministrators = [new User('Administrator')], } ),"; private static final String REMOVE_ACTOR_ASSIGNMENTS = "" + "peopleAssignments = (with (new PeopleAssignments()) { potentialOwners = [new User('Bobba Fet'), new User('Luke Cage'), new User('Tony Stark')]," + " businessAdministrators = [new User('Administrator')], } ),"; 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"; @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.TaskCountLoadCalculator"); System.setProperty("org.jbpm.task.assignment.loadbalance.entry.timetolive", "10"); // this has to be low in order that we update the load balances pds = setupPoolingDataSource(); emf = Persistence.createEntityManagerFactory( "org.jbpm.services.task" ); AssignmentServiceProvider.override(new LoadBalanceAssignmentStrategy()); this.taskService = (InternalTaskService) HumanTaskServiceFactory.newTaskServiceConfigurator() .entityManagerFactory(emf) .getTaskService(); this.tasks = ArrayListMultimap.create(); } @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() { final String taskString = "(" + BASE_TASK_INFO + MULTI_ACTOR_ASSIGNMENTS + "name = 'MultiUserLoadBalanceTask'})"; tasks.put(BOBBA_FET,createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); assertNumberOfNonCompletedTasks(BOBBA_FET, 1); assertNumberOfNonCompletedTasks(LUKE_CAGE, 1); tasks.put(BOBBA_FET,createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 2); assertNumberOfNonCompletedTasks(BOBBA_FET, 2); assertNumberOfNonCompletedTasks(LUKE_CAGE, 2); // Check that if we complete a task and then create a new one // that it gets assigned to the proper user getTaskToComplete(DARTH_VADER).ifPresent(complete); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 2); // Make sure that we aren't trying to complete a previously completed task getTaskToComplete(DARTH_VADER).ifPresent(complete); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); logger.info("testMultipleUser completed"); } @Test public void testMultipleUserWithGroup() { final String taskString = "(" + BASE_TASK_INFO + MULTI_ACTOR_WITH_GROUP_ASSIGNMENTS + "name = 'MultiUserWithGroupLoadBalanceTask'})"; tasks.put(BOBBA_FET, createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); tasks.put(LUKE_CAGE, createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); tasks.put(TONY_STARK, createForCompletionTask(taskString, TONY_STARK, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); assertNumberOfNonCompletedTasks(BOBBA_FET, 1); assertNumberOfNonCompletedTasks(LUKE_CAGE, 1); assertNumberOfNonCompletedTasks(TONY_STARK, 1); tasks.put(BOBBA_FET, createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); tasks.put(LUKE_CAGE, createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); tasks.put(TONY_STARK, createForCompletionTask(taskString, TONY_STARK, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); assertNumberOfNonCompletedTasks(BOBBA_FET, 2); assertNumberOfNonCompletedTasks(LUKE_CAGE, 2); assertNumberOfNonCompletedTasks(TONY_STARK, 2); getTaskToComplete(LUKE_CAGE).ifPresent(complete); assertNumberOfNonCompletedTasks(LUKE_CAGE, 1); tasks.put(LUKE_CAGE, createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,"Crusaders",LUKE_CAGE)); assertNumberOfNonCompletedTasks(LUKE_CAGE, 2); logger.info("testMultipleUserWithGroup completed"); } @Test public void testMultipleUserWithAdd() { final String taskString = "(" + BASE_TASK_INFO + MULTI_ACTOR_ASSIGNMENTS + "name = 'MultiUserWithAddLoadBalanceTask'})"; final String taskString2 = "(" + BASE_TASK_INFO + ADD_ACTOR_ASSIGNMENTS + "name = 'MultiUserWithAddLoadBalanceTask2'})"; tasks.put(BOBBA_FET,createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); assertNumberOfNonCompletedTasks(BOBBA_FET, 1); assertNumberOfNonCompletedTasks(LUKE_CAGE, 1); tasks.put(BOBBA_FET,createForCompletionTask(taskString, BOBBA_FET, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString, LUKE_CAGE, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 2); assertNumberOfNonCompletedTasks(BOBBA_FET, 2); assertNumberOfNonCompletedTasks(LUKE_CAGE, 2); // Check that if we complete a task and then create a new one // that it gets assigned to the proper user getTaskToComplete(DARTH_VADER).ifPresent(complete); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 3, BOBBA_FET,DARTH_VADER,LUKE_CAGE)); assertNumberOfNonCompletedTasks(DARTH_VADER, 2); // Now add a user with no tasks and make sure that // the assignment goes to the new user tasks.put(TONY_STARK, createForCompletionTask(taskString2,TONY_STARK,4,BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK)); assertNumberOfNonCompletedTasks(TONY_STARK,1); } @Test public void testMultipleUsersWithRemove() { final String taskString = "(" + BASE_TASK_INFO + ADD_ACTOR_ASSIGNMENTS + "name = 'MultiUserWithRemoveLoadBalanceTask'})"; final String taskString2 = "(" + BASE_TASK_INFO + REMOVE_ACTOR_ASSIGNMENTS + "name = 'MultiUserWithRemoveLoadBalanceTask2'})"; tasks.put(BOBBA_FET,createForCompletionTask(taskString, BOBBA_FET, 4, BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK)); tasks.put(DARTH_VADER,createForCompletionTask(taskString, DARTH_VADER, 4, BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString, LUKE_CAGE, 4, BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK)); tasks.put(TONY_STARK,createForCompletionTask(taskString, TONY_STARK, 4, BOBBA_FET,DARTH_VADER,LUKE_CAGE,TONY_STARK)); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); assertNumberOfNonCompletedTasks(BOBBA_FET, 1); assertNumberOfNonCompletedTasks(LUKE_CAGE, 1); assertNumberOfNonCompletedTasks(TONY_STARK, 1); tasks.put(BOBBA_FET,createForCompletionTask(taskString2, BOBBA_FET, 3, BOBBA_FET,LUKE_CAGE,TONY_STARK)); tasks.put(LUKE_CAGE,createForCompletionTask(taskString2, LUKE_CAGE, 3, BOBBA_FET,LUKE_CAGE,TONY_STARK)); tasks.put(TONY_STARK,createForCompletionTask(taskString2, TONY_STARK, 3, BOBBA_FET,LUKE_CAGE,TONY_STARK)); assertNumberOfNonCompletedTasks(DARTH_VADER, 1); assertNumberOfNonCompletedTasks(BOBBA_FET, 2); assertNumberOfNonCompletedTasks(LUKE_CAGE, 2); assertNumberOfNonCompletedTasks(TONY_STARK, 2); } private Consumer<Task> complete = (task) -> { completeTask(task); this.tasks.get(task.getTaskData().getActualOwner().getId()).remove(task); }; private Optional<Task> getTaskToComplete(String user) { Optional<Task> task = Optional.empty(); if (tasks.containsKey(user)) { Collection<Task> taskCollection = tasks.get(user); if (!taskCollection.isEmpty()) { task = taskCollection.stream().filter(t -> !t.getTaskData().getStatus().equals(Status.Completed)).findFirst(); } } if (!task.isPresent()) { logger.warn("No task to complete found for {}",user); } return task; } }