How to achieve Task reassignment from one group to another in BPM Suite 6 ?

Solution Verified - Updated -

Environment

  • Red Hat JBoss BPM Suite 6.x

Issue

  • Is there no way to cover the functionality of Task Reassignment through Task API methods like delegate or forward or nominate from one group to another? Apparently, the three possible operations- delegate, forward and nominate do not look like cover this functionality. For example, if users have many tasks assigned to a group, say Office1 , and users need to reassign (independent of the task state) those tasks to another group called Office2, how can that be achieved using jBPM6 API? Does the remote Java API or REST or JMS API features provide any such functionality to achieve the above objective ?
  • We are not able to get task reassignment to work with multiple groups. We need to be able to reassign the task to multiple groups when a user has it claimed for an X amount of time and has not started it.

Resolution

Disclaimer: Please note that the sample code within this article is provided for customer benefit, but, is not supported by Red Hat. Customers can use it "as it is" or by customizing it appropriately. Red Hat does not support custom code. Please refer to the Scope of coverage (What Red Hat supports) for further details.

It is not possible to reassign a User Task from one group to another by using any of the operations like delegate, forward or nominate in BPMS 6.x. It is also not possible through remote Java API, REST or JMS API. It will be possible to forward a task for a group or set of groups in RHPAM 7 according to feature request raised.

As a workaround to accomplish it the small change users need to make is to use the methods like ((InternalPeopleAssignments) task.getPeopleAssignments()).setPotentialOwners(potentialOwners) to manually update the people assignments of the User Task. Please find a sample demo (jBPM6Ex90_forwardToGroup.zip) project attached. Note that it will not work with remote Java API, REST or JMS APIs.

Root Cause

The current implementation of Remote and JMS APIs do not provide the feature to all of the TaskService implemented methods except for the ones shown in the Remote API documentation. As it can be seen from the following code of AbstractRemoteCommandObject which contains the logic to check the commands (e.g. Tasks) to whether it is implemented or not. It can be seen from here that AbstractRemoteCommandObject checks internally with AcceptedCommands shown just below it to check the accepted Commands and there it does not list the Command for performing ((InternalPeopleAssignments) task.getPeopleAssignments()).setPotentialOwners(potentialOwners) which could be used to manually change the potential owners for forwarding the Task to another group.

org.kie.services.client.api.command.AbstractRemoteCommandObject.java

...
package org.kie.services.client.api.command;
...
/**
 * This class contains the logic to interact with the REST or JMS api's. It is the basis for all of the remote interface instances.
 */
public abstract class AbstractRemoteCommandObject {

    protected static final Logger logger = LoggerFactory.getLogger(AbstractRemoteCommandObject.class);

    protected final RemoteConfiguration config;

    // Do not modify this: this is automatically incremented by the maven replacer plugin
    private final String ARTIFACT_VERSION = "6.0.3.1";

    AbstractRemoteCommandObject(RemoteConfiguration config) {
        this.config = config;
    }

    protected boolean isTaskService = false;

    // Compatibility methods -----------------------------------------------------------------------------------------------------

    public void readExternal(ObjectInput arg0) throws IOException, ClassNotFoundException {
        String methodName = (new Throwable()).getStackTrace()[0].getMethodName();
        throw new UnsupportedOperationException(methodName + " is not supported on the JAXB " + Task.class.getSimpleName()
                + " implementation.");
    }
...

   // Execute methods -----------------------------------------------------------------------------------------------------

    public <T> T execute(Command<T> cmd) {
        if( ! AcceptedCommands.getSet().contains(cmd.getClass()) ) {
            StackTraceElement [] st = Thread.currentThread().getStackTrace();
            String methodName = st[2].getMethodName();
            throw new UnsupportedOperationException( "The ." + methodName + "(..) method is not supported on the remote api." );
        }

        if( AcceptedCommands.SEND_OBJECT_PARAMETER_COMMANDS.contains(cmd.getClass()) ) {
            List<Object> extraClassInstanceList = new ArrayList<Object>();
            preprocessCommand(cmd, extraClassInstanceList);

            if( ! extraClassInstanceList.isEmpty() ) { 
                Set<Class<?>> extraJaxbClasses = new HashSet<Class<?>>();
                for( Object jaxbObject : extraClassInstanceList ) { 
                    Class<?> jaxbClass = jaxbObject.getClass();
                    if( jaxbClass.isLocalClass() || jaxbClass.isAnonymousClass() ) { 
                        throw new SerializationException("Only proper classes are allowed as parameters for the remote API: neither local nor anonymous classes are accepted: " + jaxbClass.getName());
                    }
                    extraJaxbClasses.add(jaxbClass);
                }
                config.addJaxbClasses(extraJaxbClasses);
            }
        }

        if (config.isRest()) {
            return executeRestCommand(cmd);
        } else {
            return executeJmsCommand(cmd);
        }
    }
...

org.kie.services.shared.AcceptedCommands

package org.kie.services.shared;
...
@SuppressWarnings("rawtypes")
public class AcceptedCommands {

    private AcceptedCommands() { 
        // static fields only
    }

    private static Set<Class<? extends Command>> acceptedCommands = new HashSet<Class<? extends Command>>();
    static {
        acceptedCommands.add(AbortWorkItemCommand.class);
        acceptedCommands.add(CompleteWorkItemCommand.class);
        acceptedCommands.add(GetWorkItemCommand.class);

        acceptedCommands.add(AbortProcessInstanceCommand.class);
        acceptedCommands.add(GetProcessIdsCommand.class);
        acceptedCommands.add(GetProcessInstanceByCorrelationKeyCommand.class);
        acceptedCommands.add(GetProcessInstanceCommand.class);
        acceptedCommands.add(GetProcessInstancesCommand.class);
        acceptedCommands.add(SetProcessInstanceVariablesCommand.class);
        acceptedCommands.add(SignalEventCommand.class);
        acceptedCommands.add(StartCorrelatedProcessCommand.class);
        acceptedCommands.add(StartProcessCommand.class);

        acceptedCommands.add(GetVariableCommand.class);
        acceptedCommands.add(GetFactCountCommand.class);
        acceptedCommands.add(GetGlobalCommand.class);
        acceptedCommands.add(GetIdCommand.class);
        acceptedCommands.add(SetGlobalCommand.class);

        acceptedCommands.add(DeleteCommand.class);
        acceptedCommands.add(FireAllRulesCommand.class);
        acceptedCommands.add(InsertObjectCommand.class);
        acceptedCommands.add(UpdateCommand.class);

        // Task commands
        acceptedCommands.add(ActivateTaskCommand.class);
        acceptedCommands.add(AddTaskCommand.class);
        acceptedCommands.add(ClaimNextAvailableTaskCommand.class);
        acceptedCommands.add(ClaimTaskCommand.class);
        acceptedCommands.add(CompleteTaskCommand.class);
        acceptedCommands.add(DelegateTaskCommand.class);
        acceptedCommands.add(ExitTaskCommand.class);
        acceptedCommands.add(FailTaskCommand.class);
        acceptedCommands.add(ForwardTaskCommand.class);
        acceptedCommands.add(GetAttachmentCommand.class);

        acceptedCommands.add(GetContentCommand.class);
        acceptedCommands.add(GetTaskContentCommand.class);

        acceptedCommands.add(GetTaskAssignedAsBusinessAdminCommand.class);
        acceptedCommands.add(GetTaskAssignedAsPotentialOwnerCommand.class);
        acceptedCommands.add(GetTaskByWorkItemIdCommand.class);
        acceptedCommands.add(GetTaskCommand.class);
        acceptedCommands.add(GetTasksByProcessInstanceIdCommand.class);
        acceptedCommands.add(GetTasksByStatusByProcessInstanceIdCommand.class);
        acceptedCommands.add(GetTasksByVariousFieldsCommand.class);
        acceptedCommands.add(GetTasksOwnedCommand.class);
        acceptedCommands.add(NominateTaskCommand.class);
        acceptedCommands.add(ReleaseTaskCommand.class);
        acceptedCommands.add(ResumeTaskCommand.class);
        acceptedCommands.add(SkipTaskCommand.class);
        acceptedCommands.add(StartTaskCommand.class);
        acceptedCommands.add(StopTaskCommand.class);
        acceptedCommands.add(SuspendTaskCommand.class);
        acceptedCommands.add(CompositeCommand.class);
        acceptedCommands.add(ProcessSubTaskCommand.class);
        acceptedCommands.add(ExecuteTaskRulesCommand.class);
        acceptedCommands.add(CancelDeadlineCommand.class);

        // audit commands
        acceptedCommands.add(ClearHistoryLogsCommand.class);
        acceptedCommands.add(FindActiveProcessInstancesCommand.class);
        acceptedCommands.add(FindNodeInstancesCommand.class);
        acceptedCommands.add(FindProcessInstanceCommand.class);
        acceptedCommands.add(FindProcessInstancesCommand.class);
        acceptedCommands.add(FindSubProcessInstancesCommand.class);
        acceptedCommands.add(FindSubProcessInstancesCommand.class);
        acceptedCommands.add(FindVariableInstancesCommand.class);
        acceptedCommands.add(FindVariableInstancesByNameCommand.class);

        acceptedCommands = Collections.unmodifiableSet(acceptedCommands);
    }
...

Attachments

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.