cancel
Showing results for 
Search instead for 
Did you mean: 

[Issue] TaskCandidateQuery uses different group id from TaskCandidateGroup

cfitch
Champ in-the-making
Champ in-the-making
Greetings, I'm having an issue with the Vacation Request example and taskCandidateUser/Group Queries.
Env:
Grails 2.2.1, JDK 1.6, Activiti Plugin 5.10, Shiro Plugin 1.14

Description
I installed the Activiti Plugin and followed the code for another plugin in order to integrate wth Shiro. I've installed the Vacation Request Sample app. Other than some quirks mostly due to my ignorance, everything has been working pretty well. However, there is one issue that I've run in to. Basically a task query using taskCandidateQuery returns no tasks for a newly created process instance while taskCandidateGroup queries do return the task. After adding some logging and enabling debug, I've found an inconsistency: when using taskCandidateUser, the query executed uses the group id. However, when using taskCandidateGroup, the group name is used. In the ACT_RU_IDENTITYLINK table, the GROUP_ID column contains the names of the groups and not the id's.

So my question is, how do I resolve this inconsistency? I'm guessing the issue is related to how the shiro user/role code works with activiti user/group code.

Here's the SQL executed for taskCandidateQuery and the parameters(formatted for easy viewing):

2013-05-02 10:25:44,042 [http-bio-8080-exec-3]
DEBUG TaskEntity.selectTaskByQueryCriteria  - ==>
    Preparing: select distinct T.*
               from ACT_RU_TASK T
               inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = T.ID_
               WHERE T.PROC_INST_ID_ = ?
               and T.ASSIGNEE_ is null
               and I.TYPE_ = 'candidate'
               and ( I.USER_ID_ = ? or I.GROUP_ID_ IN ( ? ) )
               LIMIT ? OFFSET ?

2013-05-02 10:25:44,042 [http-bio-8080-exec-3]
DEBUG TaskEntity.selectTaskByQueryCriteria  - ==>
Parameters: 1201(String),
            admin(String),
            1(String),
            2147483647(Integer),
            0(Integer)


Here's the SQL executed for taskCandidateQuery and the parameters(formatted for easy viewing):

2013-05-02 10:25:44,062 [http-bio-8080-exec-3]
DEBUG TaskEntity.selectTaskByQueryCriteria  - ==> 
   Preparing: select distinct T.*
            from ACT_RU_TASK T
            inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = T.ID_
            WHERE T.PROC_INST_ID_ = ?
            and T.ASSIGNEE_ is null
            and I.TYPE_ = 'candidate'
            and ( I.GROUP_ID_ IN ( ? ) )
            LIMIT ? OFFSET ?
           
2013-05-02 10:25:44,063 [http-bio-8080-exec-3]
DEBUG TaskEntity.selectTaskByQueryCriteria  - ==>
Parameters: 1201(String),
         Admin(String),
         2147483647(Integer),
         0(Integer)



The identity link table contains the following data:

SELECT * FROM ACT_RU_IDENTITYLINK;
ID_     REV_     GROUP_ID_     TYPE_     USER_ID_     TASK_ID_     PROC_DEF_ID_ 
1109   1   PMSupport   candidate   null   1108   null
1110   1   Admin   candidate   null   1108   null


Here are the two queries (first one returns nothing, second one works)

task = activitiService.taskService.createTaskQuery()
                                    .processInstanceId(pi.id)
                                    .taskCandidateUser(username)
                                    .singleResult();



task = activitiService.taskService.createTaskQuery()
                                    .processInstanceId(pi.id)
                                    .taskCandidateGroup(theUser.getUserRoleName())
                                    .singleResult();


Other info:
I modified the process definition slightly to use my role names instead of the defaults. They are: Admin, PMSupport.

I can provide additional info as requested.

Any help or insight would be greatly appreciated.

7 REPLIES 7

cfitch
Champ in-the-making
Champ in-the-making
Here's additional info:
The user class:
<code>
/**
* Base class for Users in the system
*/
class WMSUser implements org.activiti.engine.identity.User
{
    // Added for activiti
    String id;

    String username;
    String passwordHash;
    String emailAddress;
   
    String firstName;
    String lastName;
    String name;
   
    ContactInfo contactInfo;
   
    Date dateCreated;
    Date lastUpdated;
   
    static hasMany = [ roles: WMSRole, permissions: String ];
    static mapping =
    {
        tablePerHierarchy false;
    }
    static mappedBy = [  ];
   
    static transients = [ 'email','password' ];

    static constraints =
    {
        username(nullable: false, blank: false, unique: true);
        contactInfo(nullable: true);
        password(nullable: true);
        email(nullable: true);
        firstName(nullable: true);
        lastName(nullable: true);
        name(nullable: true);
    }
   
    String getFirstName()
    {
        return name;
    }
   
    String getLastName()
    {
        return name;
    }

    String getEmail()
    {
        return emailAddress;
    }
   
    void setEmail(String email)
    {
        emailAddress;
    }
   
    String getPassword()
    {
        return "";
    }
   
    void setPassword(String password)
    {
    }
   
    Set<WMSRole> getAuthorities()
    {
        log.debug("[getAuthorities()]:");
        return roles as Set;
    }
   
    String getUserRoleName()
    {
        if(roles)
        {
            return ((WMSRole[])roles)[0].name;
        }
        else
        {
            return null;
        }
    }
   
}
</code>

Here's the role:
<code>
/**
* Base class for Roles in the system
*/
class WMSRole implements org.activiti.engine.identity.Group
{
    // added for activiti
    String id;
    String type;
   
    String name;
    String description;
    Date dateCreated;
    Date lastUpdated;
   

    static hasMany = [ users: WMSUser, permissions: String ];
    static belongsTo = [WMSUser];

    static constraints =
    {
        name(nullable: false, blank: false, unique: true);
        type(nullable: true);
    }
   
    WMSRole(String newName, String newDescription)
    {
        this.name = newName;
        this.description = newDescription;
    }
}
</code>

cfitch
Champ in-the-making
Champ in-the-making
One Correction: taskCandidateQuery should be taskCandidateUser query. Apologies for the typo.

cfitch
Champ in-the-making
Champ in-the-making
Update: the issue is related to the fact that taskCandidateUser calls getGroupsForCandidateUser(). In that method, it generates the list of groups for the user by calling
groupIds.add(group.getId());

The relevant code from TaskQueryImpl.java
<code>
  protected List<String> getGroupsForCandidateUser(String candidateUser) {
    // TODO: Discuss about removing this feature? Or document it properly and maybe recommend to not use it
    // and explain alternatives
    List<Group> groups = Context
      .getCommandContext()
      .getGroupManager()
      .findGroupsByUser(candidateUser);
    List<String> groupIds = new ArrayList<String>();
    for (Group group : groups) {
      groupIds.add(group.getId());
    }
    return groupIds;
  }
</code>

Since the id's for my groups (really Shiro roles) are Grails generated Id's eg. numbers, the list passed in to the SQL is correct. However, since the GROUP_ID column in ACT_RU_IDENTITYLINK table contains the group names, the query will return null.

So why are the names being stored in the ACT_RU_IDENTITYLINK table? Shouldn't those be the id's of the groups?

jbarrez
Star Contributor
Star Contributor
They should be id's. But by coincidence (in the demo data) the name and id are the same.

So are you sure that always the name is stored? Cause that would really be a bug!

cfitch
Champ in-the-making
Champ in-the-making
I noticed that as well and I changed my test groups to have id's matching the group names and everything started working correctly. I can't answer if they are always being stored as names but I'm pretty certain there is some issue. I also ran in to a similar issue with user id's and names with a taskAssignee query.

I don't have much time right now but I'll see if I can create some test code in order to recreate the issue.

benwilson
Champ in-the-making
Champ in-the-making
Is there a way to configure activiti to use group names instead of IDs? We use UUIDs in our grails app, and putting UUIDs into the workflow instead of group names is just stupid.

jbarrez
Star Contributor
Star Contributor
You can tweak the GroupManager to facilitate that. Check the activiti-ldap source code to get an idea of how you can do that.