/*
* 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.impl.strategy;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jbpm.services.task.assignment.LoadCalculator;
import org.jbpm.services.task.assignment.UserTaskLoad;
import org.kie.api.runtime.EnvironmentName;
import org.kie.api.task.TaskContext;
import org.kie.api.task.model.Group;
import org.kie.api.task.model.OrganizationalEntity;
import org.kie.api.task.model.Task;
import org.kie.api.task.model.User;
import org.kie.internal.task.api.UserInfo;
import org.kie.internal.task.api.assignment.Assignment;
import org.kie.internal.task.api.assignment.AssignmentStrategy;
import org.kie.internal.task.api.model.InternalPeopleAssignments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Assignment strategy that uses a plug-able approach for calculating
* the load that potential task owners have. It then assigns the task
* to the user with the lightest load.
*/
public class LoadBalanceAssignmentStrategy implements AssignmentStrategy {
private static final Logger logger = LoggerFactory.getLogger(LoadBalanceAssignmentStrategy.class);
private static final String IDENTIFIER = "LoadBalance";
private LoadCalculator calculator;
public LoadBalanceAssignmentStrategy() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
String calculatorClass = System.getProperty("org.jbpm.task.assignment.loadbalance.calculator","org.jbpm.services.task.assignment.impl.TaskCountLoadCalculator");
this.calculator = (LoadCalculator)Class.forName(calculatorClass).newInstance();
}
private Function<OrganizationalEntity, User> entityToUser = (oe) -> {
return (User)oe;
};
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public Assignment apply(Task task, TaskContext taskContext, String excludedUser) {
List<OrganizationalEntity> excluded = ((InternalPeopleAssignments)task.getPeopleAssignments()).getExcludedOwners();
UserInfo userInfo = (UserInfo) ((org.jbpm.services.task.commands.TaskContext)taskContext).get(EnvironmentName.TASK_USER_INFO);
// Get the the users from the task's the potential owners, making sure that excluded users are not included
List<OrganizationalEntity> potentialOwners = task.getPeopleAssignments().getPotentialOwners().stream()
.filter(oe -> oe instanceof User && !excluded.contains(oe) && !oe.getId().equals(excludedUser))
.collect(Collectors.toList());
// Get the users belonging to groups that are potential owners
task.getPeopleAssignments().getPotentialOwners().stream().filter(oe -> oe instanceof Group)
.forEach(oe -> {
Iterator<OrganizationalEntity> groupUsers = userInfo.getMembersForGroup((Group)oe);
if (groupUsers != null) {
groupUsers.forEachRemaining(user -> {
if (user != null && !excluded.contains(user) && !potentialOwners.contains(user) && !user.getId().equals(excludedUser)) {
potentialOwners.add(user);
}
});
}
});
logger.debug("Asking the load calculator [{}] for task loads for the users {}",calculator.getIdentifier(),potentialOwners);
List<User> users = potentialOwners.stream().map(entityToUser).collect(Collectors.toList());
Collection<UserTaskLoad> loads = calculator.getUserTaskLoads(users, taskContext);
UserTaskLoad lightestLoad = loads.stream().min(UserTaskLoad::compareTo).orElse(null);
return lightestLoad != null ? new Assignment(lightestLoad.getUser().getId()):null;
}
}