Problems creating users/groups/memberships in Java

Hi, I am pretty new to Camunda and working on a user/group/membership-sync between the Celum DAM and Camunda, which we will need for our workflows, but I ran into some problems.

Setup
I installed Camunda 7.9. locally, the tomcat version with the default h2 database. Everything worked as expected so far.

Problems
When I tried to create a user or a group I’ve always received the following error:

 org.camunda.bpm.engine.ProcessEngineException: ENGINE-03002 Cannot add TRANSIENT entity with id 'celum_488' and type 'class org.camunda.bpm.engine.impl.persistence.entity.UserEntity' into cache. An entity with the same id and type is already in state 'TRANSIENT'

So I implemented the following workaround (marked by comment):

    User user = identityService.createUserQuery()
            .userId(id).singleResult();
    if (user == null) {
        user = identityService.newUser(id);
        dbEntityManager.getDbEntityCache().remove((UserEntity) user); // here
        identityService.saveUser(user);
    }
    if (!firstName.equals(user.getFirstName()) || !lastName.equals(user.getLastName()) || !email.equals(user.getEmail())) {
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        dbEntityManager.getDbEntityCache().remove((UserEntity) user); // here
        identityService.saveUser(user);
    }

Like that I got rid of the error, but it still didn’t work. I always got a unique index or primary key violation. I noticed that the statements are executed as a batch, but that shouldn’t be a problem. I also checked the constraint that is supposed to be violated, but this seems to be impossible, because user and group ids are unique. Here is an excerpt of the log:

org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "ACT_UNIQ_AUTH_GROUP_INDEX_6 ON PUBLIC.ACT_RU_AUTHORIZATION(TYPE_, GROUP_ID_, RESOURCE_TYPE_, RESOURCE_ID_) VALUES (1, 'celum_489', 2, 'celum_489', 45)"; SQL statement:
insert into ACT_RU_AUTHORIZATION (ID_, TYPE_, GROUP_ID_, USER_ID_, RESOURCE_TYPE_, RESOURCE_ID_, PERMS_, REV_)
    values (?, ?, ?, ?, ?,?, ?, 1) [23505-190]
'. Flush summary: 
 [
  INSERT HistoricJobLogEventEntity[87877f40-4f30-11ea-8ee4-005056c00001]
  INSERT AuthorizationEntity[87752fb0-4f30-11ea-8ee4-005056c00001]
  ...
  INSERT HistoricActivityInstanceEventEntity[End_CelumUserGroupSync:8787311f-4f30-11ea-8ee4-005056c00001]
  ...
  INSERT UserEntity[celum_487]
  ...
  INSERT GroupEntity[celum_489]
  ...
  INSERT MembershipEntity[877fb706-4f30-11ea-8ee4-005056c00001]
  ...
  DELETE MessageEntity[87467e8d-4f30-11ea-8ee4-005056c00001]
  DELETE ExecutionEntity[87467e8b-4f30-11ea-8ee4-005056c00001]
  UPDATE HistoricProcessInstanceEventEntity[87467e8b-4f30-11ea-8ee4-005056c00001]
  DELETE_BULK deleteAuthorizationsForResourceId {resourceId=87467e8b-4f30-11ea-8ee4-005056c00001, resourceType=8}
]

I am glad for any help I can get. I looked at the official example to populate Camunda with some users and groups and it is doing exactly the same as I did.

Hello,
Typically you’ll use IdentityService to manage that stuff, have a look here for an example of using IdentityService for user/group creation. Here is the javadoc for IdentityService.

Joe

Hi Beagler, as you can see in the code fragment I posted, I am using the identity service. And the example you posted is the one I am referring to at the end. I cannot see any difference to my code. If I save after setting the username etc. it doesn’t make any difference.

I upgraded to Camunda 7.12 now and I am experiencing the same issue: unique index or primary key violation. When I’m using the REST API it’s working fine.

Right, but why use dbEntityManager? Why not just use identityService.deleteUser()? I’m afraid I’m not as familiar with dbEntityManager.

Joe

Because the user isn’t existing. This is because the process engine thinks that the user is in a transient state (which shouldn’t be possible), so I removed the user from the database cache. This is the first problem I am describing in my original post (you can see the error message right above the code) and that is how I could get rid of it. I agree that those two lines commented with // here shouldn’t be necessairy.

Ok, where are you executing this code? How are you deploying Camunda? Is it possible to share your project?

Joe

I’ve implemented a very simple workflow with a service task. The code is executed in the execute method of the JavaDelegate.

The code is quite simple: read some user and group information in JSON and create them in Camunda (or update if already existing).

In detail: First I am reading some properties (configuration is in a separate file, like Celum URL etc., so we can always use the same .war file and only have to adjust the configuration which is in CAMUNDA_HOME/config/CelumUserAndGroupSync.properties). Then all the relevant groups and users (configurable) are read through a REST API deployed in Celum (also developed by me). Because the groups are hierarchical in Celum but not in Camunda I have to recursively resolve all the users (users of subgroups also belong to the group). So far so good (this was tested thoroughly and it is working). The problems are starting when I try to create users and groups in Camunda. Even though users and groups have to be unique, because the IDs for users and groups in Celum are unique (a group cannot have the same ID as a user). The syncUser method is basically what I’ve posted above. Also the transient exception shouldn’t happen.

I have the standard tomcat installation of Camunda 7.12. with the default h2 database. The project is deployed by putting the .war file inside the tomcat/webapps folder. I’ve done this already for a POC and there I did it the same way (and everything worked as expected). I used the servlet-war archetype of camunda in IntelliJ.

I could share the rest of the code, but you won’t be able to execute it, because of the Celum dependencies. But I can assure you that the rest is working fine, we are preferred implementation partner of Celum and have done plenty of projects.

i deeply appreciate the fact that you’ve taken your time writing it all in little details. i personally find it extremely helpful and i’m appreciating it a lot. i also have some issues and help like that is deeply appreciated. can i ask you questions in case i’m going to have any? thanks again

@cma OK I think I know what the issue is. It looks like deleting and attempting to recreate a user in the same transaction (ie same delegate) is causing the issue. If you query for the user and then delete it in one service task and then recreate it in another service task it should work. Be sure to put in an asynchronous continuation somewhere between the two services to commit the user updates. I have a working example if you want me to upload it. When I tried deleting and creating the user in the same delegate I got your error above. I had to split up the deleting and creating into two delegates with an async in between. Makes sense?

Joe

@Beagler Thanks for your reply. Unfortunately this is not possible, because I am not deleting any users (this would have been the next step). I am just checking if a user exists and if it doesn’t exist I create it. You can see this in the batch in the log file, all it does is create some users, create some groups and then add some users to some groups. The creation of the users and groups already fails. This DELETE_BULK deleteAuthorizationsForResourceId {resourceId=87467e8b-4f30-11ea-8ee4-005056c00001, resourceType=8} is generated by Camunda and I don’t know where it is coming from. Are you getting the transient exception or the other one with the unique constraint?

I will now create a test where I am just creating 1 group and 1 user and add the user to the group to see if that works, this code can easily be shared then.

@Ligning Thanks. Better post a new question in the forum if you’ve got any questions. I am no expert at all.

Here is a very simple test that fails too with the transient exception. The user and the group aren’t existing yet. The workflow only consists of a start, this service task and end.

private IdentityService identityService;

private static final String userId = "testUser";
private static final String firstName = "John";
private static final String lastName = "Doe";
private static final String email = "john.doe@foo.bar";
private static final String groupId = "testGroup";
private static final String name = "Group Name";

private User syncUser() {
    User user = identityService.createUserQuery()
            .userId(userId).singleResult();
    if (user == null) {
        user = identityService.newUser(userId);
        identityService.saveUser(user);
    }
    if (!firstName.equals(user.getFirstName()) || !lastName.equals(user.getLastName()) || !email.equals(user.getEmail())) {
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        identityService.saveUser(user);
    }
    return user;
}

private Group syncGroup() {
    Group group = identityService.createGroupQuery()
            .groupId(groupId).singleResult();
    if (group == null) {
        group = identityService.newGroup(groupId);
        identityService.saveGroup(group);
    }
    if (!name.equals(group.getName())) {
        group.setName(name);
        identityService.saveGroup(group);
    }
    return group;
}

private void syncMembership(User user, Group group) {
    if (identityService.createUserQuery().userId(user.getId()).memberOfGroup(group.getId()).singleResult() == null) {
        identityService.createMembership(user.getId(), group.getId());
    }
}

public void execute(DelegateExecution execution) throws Exception {
    identityService = execution.getProcessEngineServices().getIdentityService();
    User user = syncUser();
    Group group = syncGroup();
    syncMembership(user, group);
}

Right, so it looks like you’re trying to save the user and group twice in this logic which I think is causing your issue. I updated the following methods and tested it to make sure it works:

private User syncUser() {
    User user = identityService.createUserQuery()
            .userId(userId).singleResult();
    if (user == null) {
      user = identityService.newUser(userId);
      user.setFirstName(firstName);
      user.setLastName(lastName);
      user.setEmail(email);
      identityService.saveUser(user);
    }
    return user;
  }

private Group syncGroup() {
    Group group = identityService.createGroupQuery()
            .groupId(groupId).singleResult();
    if (group == null) {
      group = identityService.newGroup(groupId);
      identityService.saveGroup(group);
    }

    return group;
  }

Joe

1 Like

@Beagler Thanks a lot. This worked! I will change my logic now and cache all created/updated users to avoid this.

You really helped me a lot. Thanks again.

Chris

PS: The original program is now running too with the mentioned adjustments.

1 Like

thanks, will try it myself too, hopefully it is going to work. gonna get some appetizers first. lol will try it and see how it works.

You’re welcome, glad to be of help :slight_smile:

Joe