
Previous: Synchronizing User Information with Local Data (Preparatory Chapter)
- Preparatory Chapter
- User Information Chapter
- Organizational Affiliation Chapter
In the previous article, the purpose was to try using “com.questetra.bpms.client.swagger“, a Questetra BPM Suite library, to implement a search solely on Users registered to Questetra BPM Suite. In this article, we are going to implement methods for manipulating registered User information and create a program which synchronizes User account information in a local TSV file with that on Questetra BPM Suite.
To align the User information in the TSV file with that registered in Questetra BPM Suite the following steps are required:
- Compare TSV file contents with User information on Questetra BPM Suite
- Remove from Questetra BPM Suite information that exists only on Questetra BPM Suite
- Add to Questetra BPM Suite User information that exists only on the TSV file
Therefore, the UserDataManager class, which is responsible for exchanging User information with Questetra BPM Suite, requires the following methods.
- Method for retrieving User information registered in Questetra BPM Suite
- Method for adding Users to Questetra BPM Suite
- Method for removing Users from Questetra BPM Suite
In this chapter, we will add these three methods to the UserDataManager class. And we will create a SyncUserWithTSV class and implement the final synchronizing program using the UserDataManager class.
Editing POM.xml
We will use the common-lang library for random generation of the initial password, which will be described in detail later. Since in the Maven project the library to be used needs to be described in POM.xml, so add the common-lang information before changing the UserDataManager class.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>questetra</groupId>
<artifactId>swagger-java</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.questetra</groupId>
<artifactId>bpms.client.swagger</artifactId>
<version>12.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
</project>
Adding method to UserDataManager class
By editing POM.xml, you can now import the required libraries into your Java program. Now let’s implement the three methods mentioned earlier.
Method for retrieving User information registered in Questetra BPM Suite
Implement the list method that retrieves the list of user information registered in Questetra BPM Suite like the following.
/**
* List the users registered on Questetra BPM Suite
* @return QuserList
*/
public QuserList list() throws ApiException {
// List the registered Users
QuserList result = this.apiInstance.listQusers(null, Integer.MAX_VALUE, 0);
return result;
}
We use the listQusers method QuserApi class to enumerate registered users. The QuserApi.listQusers method returns a list of user information that satisfies the conditions specified in the argument as QuserList type. There are three arguments: “query“, which passes the filtering condition, “limit“, which is the upper limit on the number of search results to be returned, and “start“, which is an offset indicating from which search result to return. Since we want to retrieve all user information, we set it as null, maximum value of Integer type, and 0 respectively.
Method for adding Users to Questetra BPM Suite
Implement the add method to register Users in Questetra BPM Suite as follows.
/**
* Newly register users in Questetra BPM Suite
* @param name Name.
* @param email Email address.
* @param password Password. 8 to 100 characters, must contain each of lower/upper case alphabets, numbers and symbols.
* @return QuserWrapper Added user.
*/
public QuserWrapper add(String name, String email, String password) throws ApiException {
// Adding Users.
QuserWrapper result = this.apiInstance.addQuser(name, email, password);
return result;
}
The addQuser method of QuserApi class is used to add a user. As arguments, name for username, email for email address, and password for password are needed. The username and e-mail address may be obtained from the TSV file, but for the password, it would be better prepared by the program. An 8-digit random password is automatically generated if a createPassword method like the following is added to the UserDataManager class.
Although a temporary password is issued, it is not meant to be used even for the User’s first login. Once Users have been registered with Questetra BPM Suite, it is desirable for each User to set their own password using the password reissue function. System administrators should avoid knowing the User’s password. For that reason this program does not output the issued temporary password anywhere.
Method to randomly generate a temporary password
// Import necessary libraries
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang3.RandomStringUtils;
...
/**
* Generate an 8-character temporary password.
* @return String The generated temporary password.
*/
public String createPassword() {
int length = 8; // Number of characters of a password
List<String> chars = new ArrayList<>();
chars.add(RandomStringUtils.randomAlphabetic(1).toLowerCase()); // Lower case alphabet
chars.add(RandomStringUtils.randomAlphabetic(1).toUpperCase()); // Upper case alphabet
chars.add(RandomStringUtils.randomNumeric(1)); // Number
chars.add(RandomStringUtils.random(1, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")); // Symbol
// Put random ASCII characters in the remaining 4 characters
for (int i = 4; i < length; i++) {
chars.add(RandomStringUtils.randomAscii(1));
}
// Shuffle the order of the characters
Collections.shuffle(chars);
return String.join("", chars);
}
The common-lang library added to POM.xml is used here. A random string is easily generated by using org.apache.commons.lang3.RandomStringUtils. As the default settings of Questetra BPM Suite have the following password policy, I implemented it to generate according to these.
- Password must be 8 to 100 characters long
- Password must contain at least one upper case letter
- Password must contain at least one lower case letter
- Password must contain at least one number
- Password must contain at least one symbol
Method for removing User from Questetra BPM Suite
Finally, implement the method to remove the registered User from Questetra BPM Suite as follows.
/**
* Remove User from Questetra BPM Suite.
* @param id The ID of the user to be removed.
*/
public void delete(Long id) throws ApiException {
// Remove User
this.apiInstance.deleteQuser(id, {ENTER delegateQuserId}, {ENTER delegateQgroupId});
}
The deleteQuser method of QuserApi class is used to remove the user. It requires the following arguments:
- id: the User to be removed
- delegateQuserId: the User to whom the removed User’s Tasks will be forcibly assigned
- delegateQgroupId: the Organization where the forcibly assigned User belongs
What is delegateQuserId, delegateQgroupId?
Suppose a User who is to be removed holds items in their My Tasks, then who will handle these? For such cases, there are delegateQuserId and delegateQgroupId. That is, Questetra BPM Suite has a function referred to as Delegate, which forcibly reassigns Tasks of one User to another. And it is possible to specify the forced allocation destination of Tasks that the User to be removed holds in the QuserApi.deleteQuser method, so that the person in charge of Operating the Task changes to the specified User by passing the delegateQuserId (User ID of forced allocation destination) and delegateQgroupId (Organization ID of the forced allocation destination User) to the method. The circumstances of the company, etc. must be considered when specifying the forced allocation destination.

You can confirm the User ID in each User information detail page in “User List” < “System Settings”, and the Organization ID which is indicated on the Organization detail page in “Organization List” < “System Setting”, or jump from the User information detail page. As it is indicated like “u1” and “g1” respectively, please extract only the part with the number.
Implementing SyncUserWithTSV class
Up to this point, you have successfully implemented the UserDataManager class that manipulates User information on Questetra BPM Suite. From now on, we will implement the following functions in the newly created SyncUserWithTSV class.
- Read local TSV files containing User information
- Load User information registered on Questetra BPM Suite
- Compare both and determine which Users to add/remove
- Manipulate User information using the UserDataManager class
Read local TSV files containing User information
For example, suppose you have a TSV file that contains the information of the Users you want to register in Questetra BPM Suite as follows.
Galapagos questetra+Galapagos@gmail.com
Oahu questetra+Oahu@gmail.com
Sumatera questetra+Sumatera@gmail.com
Canarias questetra+Canarias@gmail.com
…
Import this with the SyncUserWithTSV class, and create a set of email addresses of Users who should be registered on Questetra BPM Suite.
# {PROJECT DIRECTORY}/src/main/java/SyncUserWithTSV.java
import com.questetra.bpms.client.swagger.ApiException;
import com.questetra.bpms.client.swagger.model.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
class SyncUserWithTSV {
public static void main(String[] args) throws IOException, ApiException {
// Read TSV data and create a set of email addresses
List<String[]> data = TSVReader.readLines("{FILE PATH OF TSV}");
Set<String> tsvSet = data.stream().map(d -> d[1]).collect(toSet());
...
}
Here, I use my own TSVReader class for reading TSV files.
TSVReader class
Although there are several Java libraries for reading TSV files, in this article, we will use a class for reading which I created by myself. It is an implementation that reads all the lines at one time by putting the ones stored in the String [] type into the List <String []> type.
# {PROJECT DIRECTORY}/src/main/java/TSVReader.java
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Objects;
class TSVReader {
public static List<String[]> readLines(String filePath) throws IOException {
List<String[]> data = new ArrayList<>();
try (InputStream is = ClassLoader.getSystemResourceAsStream((filePath))) {
BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is), "UTF-8"));
String line = br.readLine();
while (line != null) {
String[] d = line.split("\t", 0);
data.add(d);
line = br.readLine();
}
}
return data;
}
}
Load User information registered on Questetra BPM Suite
Next, the User information registered from Questetra BPM Suite is read, and a set of email addresses is created in the same way as the TSV file. For reading, the list method which has been implemented in the UserDataManager class is used.
# {PROJECT DIRECTORY}/src/main/java/SyncUserWithTSV.java
...
// Read User list via Questetra BPM Suite QuserApi and create email set of registered Users
UserDataManager manager = new UserDataManager();
QuserList list = manager.list();
Set<String> qbpmsSet = list.getQusers().stream().map(Quser::getEmail).collect(toSet());
...
Compare both and determine which Users to add/remove
Comparing the set of User’s email addresses written in the TSV file and the set of User’s email addresses registered on Questetra BPM Suite determines the Users to be removed from/added to Questetra BPM Suite.
# {PROJECT DIRECTORY}/src/main/java/SyncUserWithTSV.java
...
// Create sets of "email addresses only being in TSV" and "email addresses only being on Qusetetra BPM Suite".
Set<String> intersection = new HashSet(tsvSet);
intersection.retainAll(qbpmsSet);
tsvSet.removeAll(intersection); // Email addresses only being in TSV
qbpmsSet.removeAll(intersection); // Email addresses only being on Qusetetra BPM Suite
...
There, a product of the two sets is taken. The product is email addresses that are both in the TSV file and on Questetra BPM Suite. Then respectively subtracting these from the TSV file set and Questetra BPM Suite set, email addresses of the Users to be removed and those to be added are identified.
Manipulate user information using the UserDataManager class
Now, you know which Users to add and which to remove, so manipulate the User information on Questetra BPM Suite to be the same as the TSV file.
# {PROJECT DIRECTORY}/src/main/java/SyncUserWithTSV.java
...
// Remove "email addresses only being in Questetra BPM Suite" from Questetra BPM Suite
for (String email : qbpmsSet) {
Quser deleteUser = list.getQusers().stream().filter(user -> Objects.equals(user.getEmail(), email)).collect(toList()).get(0);
manager.delete(deleteUser.getId());
System.out.printf("Delete User / %s%n", deleteUser.getEmail());
}
// Add "email addresses only being in TSV file" to Questetra BPM Suite
for (String email : tsvSet) {
String[] addUser = data.stream().filter(user -> Objects.equals(user[1], email)).collect(toList()).get(0);
String password = manager.createPassword();
manager.add(addUser[0], addUser[1], password);
System.out.printf("Add User / %s%n", addUser[0]);
}
}
}
Since the ID of the user to be removed is required to use the UserDataManager.delete method, the corresponding user ID is checked using the registered User list retrieved from Questetra BPM Suite. Also, in order to use the UserDataManager.add method, a password is required in addition to the username and email address, so a temporary password is created using the UserDataManager.createPassword method. However, as mentioned earlier, temporary passwords are not output because they won’t be used.
Now, when this program is executed, the local TSV file and the User information on Questetra BPM Suite match. At this time, only the QuserApi class has been used among the API libraries, so the User information that can be registered is only the minimum required for login to Questetra BPM Suite; i.e. user name, email address and password. However, of course, that is not all of the User information that can be manipulated with the API library. A lot of useful classes are implemented in Questetra BPM Suite’s API library, such as MembershipApi class, which provides methods to operate Organizational assignment, and RoleMembershipApi class, which provides methods to operate Roles to be granted to users. In the next post, I will describe how to use MembershipApi class, which is capable of listing and manipulating User affiliations to Organizations.
Next: Synchronizing User Information with Local Data (Organizational Belonging Chapter)
Appendix: The Full Set of Codes That Were Used
SyncUserWithTSV.java
import com.questetra.bpms.client.swagger.ApiException;
import com.questetra.bpms.client.swagger.model.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
class SyncUserWithTSV {
public static void main(String[] args) throws IOException, ApiException {
// Read TSV data and create a set of email addresses
List<String[]> data = TSVReader.readLines("{FILE PATH OF TSV}");
Set<String> tsvSet = data.stream().map(d -> d[1]).collect(toSet());
// Read User list via Questetra BPM Suite QuserApi and create email set of registered Users
UserDataManager manager = new UserDataManager();
QuserList list = manager.list();
Set<String> qbpmsSet = list.getQusers().stream().map(Quser::getEmail).collect(toSet());
// Create sets of "email addresses only being in TSV" and "email addresses only being on Qusetetra BPM Suite".
Set<String> intersection = new HashSet<>(tsvSet);
intersection.retainAll(qbpmsSet);
tsvSet.removeAll(intersection); // Email addresses only being in TSV
qbpmsSet.removeAll(intersection); // Email addresses only being on Qusetetra BPM Suite
// Remove "email addresses only being in Questetra BPM Suite" from Questetra BPM Suite
for (String email : qbpmsSet) {
Quser deleteUser = list.getQusers().stream().filter(user -> Objects.equals(user.getEmail(), email)).collect(toList()).get(0);
manager.delete(deleteUser.getId());
System.out.printf("Delete User / %s%n", deleteUser.getEmail());
}
// Add "email addresses only being in TSV file" to Questetra BPM Suite
for (String email : tsvSet) {
String[] addUser = data.stream().filter(user -> Objects.equals(user[1], email)).collect(toList()).get(0);
String password = manager.createPassword();
manager.add(addUser[0], addUser[1], password);
System.out.printf("Add User / %s%n", addUser[0]);
}
}
}
TSVReader.java
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Objects;
class TSVReader {
public static List<String[]> readLines(String filePath) throws IOException {
List<String[]> data = new ArrayList<>();
try (InputStream is = ClassLoader.getSystemResourceAsStream((filePath))) {
BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is), "UTF-8"));
String line = br.readLine();
while (line != null) {
String[] d = line.split("\t", 0);
data.add(d);
line = br.readLine();
}
}
return data;
}
}
UserDataManager.java
import com.questetra.bpms.client.swagger.model.*;
import com.questetra.bpms.client.swagger.ApiClient;
import com.questetra.bpms.client.swagger.ApiException;
import com.questetra.bpms.client.swagger.Configuration;
import com.questetra.bpms.client.swagger.auth.*;
import com.questetra.bpms.client.swagger.api.QuserApi;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang3.RandomStringUtils;
/**
* Exchange user information with Questetra BPM Suite.
*/
class UserDataManager {
private final QuserApi apiInstance;
public UserDataManager() {
ApiClient defaultClient = Configuration.getDefaultApiClient();
// sET URL
defaultClient.setBasePath("https://example.questetra.net/");
// Basic aUTHENTICATION
HttpBasicAuth basic
= (HttpBasicAuth) defaultClient.getAuthentication("basic");
basic.setUsername("{EMAIL ADDRESS}");
basic.setPassword("{PASSWORD for Basic Autthentication}");
this.apiInstance = new QuserApi();
}
/**
* List the Users registered on Questetra BPM Suite
* @return QuserList
*/
public QuserList list() throws ApiException {
// List Users
QuserList result = this.apiInstance.listQusers(null, Integer.MAX_VALUE, 0);
return result;
}
/**
* Search specified users from users registered in Questetra BPM Suite. Throws an exception if not found.
* @param id: User ID. Pass {@code null} if unknown. If passes other than {@code null}, it takes precedence over {@code email}.
* @param email: Registered email address. Passes {@code null} if unknown.
* @return QuserWithPrimaryQgroupWrapper: Retrieved user.
*/
public QuserWithPrimaryQgroupWrapper find(Long id, String email) throws ApiException {
// Search user
QuserWithPrimaryQgroupWrapper result = this.apiInstance.findQuser(id, email);
return result;
}
/**
* Newly register users in Questetra BPM Suite
* @param name Name.
* @param email Email address.
* @param password Password. 8 to 100 characters, must contain each of lower/upper case alphabets, numbers and symbols.
* @return QuserWrapper Added user.
*/
public QuserWrapper add(String name, String email, String password) throws ApiException {
// Add User
QuserWrapper result = this.apiInstance.addQuser(name, email, password);
return result;
}
/**
* Remove User from Questetra BPM Suite.
* @param id The ID of the user to removed.
*/
public void delete(Long id) throws ApiException {
// ユーザを削除
this.apiInstance.deleteQuser(id, {ENTER delegateQuserId}, {ENTER delegateQgroupId});
}
/**
* Generate an 8-character temporary password.
* @return String The generated temporary password.
*/
public String createPassword() {
int length = 8; // Number of characters of a password
List<String> chars = new ArrayList<>();
chars.add(RandomStringUtils.randomAlphabetic(1).toLowerCase()); // Lower case alphabet
chars.add(RandomStringUtils.randomAlphabetic(1).toUpperCase()); // Upper case alphabet
chars.add(RandomStringUtils.randomNumeric(1)); // Number
chars.add(RandomStringUtils.random(1, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")); // Symbol
// Put random ASCII characters in the remaining 4 characters
for (int i = 4; i < length; i++) {
chars.add(RandomStringUtils.randomAscii(1));
}
// Shuffle the order of the characters
Collections.shuffle(chars);
return String.join("", chars);
}
}
Next: Synchronizing User Information with Local Data (Organizational Belonging Chapter)
Pingback: Synchronizing User Information with Local Data (Preparatory Chapter) – Questetra Support
Pingback: Synchronizing User Information in QBPMS with Local Data (Organizational Affiliation Chapter) – Questetra Support