Sequence generators
Sequence generators
The main goal of sequence generators is to provide ability to create a unique login or employee id during bulk operation. The most famous use case is creating logins for namesakes during bulk user creation. Due to operations are concurrent it may be difficult to track duplicates against Database. The Sequence generator feature provides across entire installation thread-safe method to have new unique number for the named sequence.
Each sequence is a pair of String and Integer value where String is a name of sequence and Integer is current sequence value.
Sequence generators UI
There is an GUI at webconsole -> Administrator -> Sequence Generators where administrator can manage and view current generators. There are following operations are available:
- Create new Sequence Generator with some provided initial value.
- Delete Sequence Generator.
Please, be sure that you understand the problems related to the deletion of Sequence Generator. It may lead the situation of duplicates creation. Please, don't use 'Delete' operation in case you are not sure about the consequences.
Sequence generators API
The usage of the 'Sequence generators' is based on the method call from the Groovy Scripts.
Synchronization Transformation
From the Synchronization Transformation script method getNextValueInSeries can be used
/*** Method returns next number in series and move it to the offset value.** @param offset - the offset to move value from the key.* @param key - the key of the counter that will be used.* @return - moved to the offset value*/protected Long getNextValueInSeries(String key,long offset);
Let's review an example of method usage below:
/* 1 */ Long valueFromSerial=getNextValueInSeries("test.user",0);/* 2 */ Long nextValueFromSerial=getNextValueInSeries("test.user",0);
in the line 1 call of the method getNextValueInSeries will return the next value of sequence name 'test.user' by adding 0 as an offset. If 'test.user' sequence exists then the next value will be returned by method with offset 0. If 'test.user' sequence has not existed before this call, such sequence will be created with initial value = 1 (+ offset) and variable
valueFromSerial will get the Value.There are some special use cases where offset should be used, but in 99% cases offset should be 0.
Theoretically the value of 'nextValueFromSerial' will be one more than value of valueFromSerial. And this is true for the single thread use-case, however in the async systems like OpenIAM this is not 'true'. In this case please don't rely on the difference of value of several values returned by getNextValueInSeries, due to it's not predictable because of multithreading.
To simplify the usage of the sequence generator the following method can be used:
/*** Method returns formatted string for next number value in the row.* <p>* For example getNextFormattedValueInSeries("%05d", "EMPLOYEE_ID")* for the 1st invocation will return "00000",* for the 2nd invocation "00001"* for the 3rd invocation "00002"** @param format - format of the String* @param key - the key of the counter that will be used.* @return - String of value;*/protected String getNextFormattedValueInSeries(String format, String key);
Let's check the following code examples: Imaging that sequence "EMPLOYEE_ID" has never been existed before and this is single-thread use case.
String currentEmployeeId=getNextFormattedValueInSeries("EMPLN%d","EMPLOYEE_ID");
The value of currentEmployeeId will be equals to "EMPLN1" Next call will return String "EMPLN2", next "EMPLN3" and etc..
let's use another format
String currentEmployeeId = getNextFormattedValueInSeries("EMPLN%4d","EMPLOYEE_ID");
The value of currentEmployeeId will be equals to "EMPLN0001" Next call will return String "EMPLN0002", next "EMPLN0003" and etc..
Please read article about String format in Java if required.
Groovy for Policy Map
Since 4.2.1.2 all groovy scripts which extends AbstractUserPrincipalGenerator or AbstractPrincipalGenerator uses Sequence generator to track duplicate logins
By default method
protected long count(String login) ;
is used to get next value for login where login is key for sequence.
The default login is firstname.lastname. If such login exists the sequence generator will be used. By default, the following Logins are generating (for OpenIAM managed system):
firstname.lastname
firstname.lastname1
firstname.lastname2
To overwrite this behavior you can do the following:
Use another firstname and last name separator:
import org.openiam.api.connector.model.ConnectorAttributeimport org.openiam.idm.processor.groovy.AbstractUserPrincipalGeneratorimport org.openiam.idm.provisioning.diff.model.user.ProvisionUserObjectDiffimport org.openiam.exception.BasicDataServiceExceptionclass UserPrincipal extends AbstractUserPrincipalGenerator {@Overridepublic void perform(final ConnectorAttribute attribute, final ProvisionUserObjectDiff diffObject) throws BasicDataServiceException {perform(attribute, diffObject, "-");}}
Output will be like:
firstname-lastname
firstname-lastname1
firstname-lastname2
Use another logic to generate base part of Login
The following example demonstrates how to generate Login as two 1st letters of lastName concatenates with value of first name
import org.apache.commons.collections4.CollectionUtils;import org.openiam.api.connector.model.ConnectorAttribute;import org.openiam.api.connector.model.StringOperationalConnectorValue;import org.openiam.base.AttributeOperationEnum;import org.openiam.base.request.BaseSearchServiceRequest;import org.openiam.base.request.IdServiceRequest;import org.openiam.base.response.data.BooleanResponse;import org.openiam.base.response.data.LongDataResponse;import org.openiam.base.ws.MatchType;import org.openiam.base.ws.ResponseCode;import org.openiam.base.ws.SearchParam;import org.openiam.exception.BasicDataServiceException;import org.openiam.idm.provisioning.diff.model.user.ProvisionUserObjectDiff;import org.openiam.idm.searchbeans.LoginSearchBean;import org.openiam.idm.srvc.auth.dto.Login;import org.openiam.idm.processor.groovy.AbstractUserPrincipalGeneratorclass UserPrincipal extends AbstractUserPrincipalGenerator {public void perform(final ConnectorAttribute attribute, final ProvisionUserObjectDiff diffObject, String splitString) throws BasicDataServiceException {String origLogin = String.format("%s%s", diffObject.getLastNameDiff().getValue().substring(0, 2), diffObject.getFirstNameDiff().getValue());String login = origLogin;while (loginExistsV4211(login)) {long count = count(origLogin);login = origLogin + count;}attribute.addValue(new StringOperationalConnectorValue(login, AttributeOperationEnum.ADD));}}
Output will be like:
lafirstname
lafirstname1
lafirstname2
Add leading zeros to the counting part of the login
The following example demonstrates how to generate Login as two 1st letters of lastName concatenates with value of first name The counting part will contain 4 characters constantly (leading zeros will be added)
import org.apache.commons.collections4.CollectionUtils;import org.openiam.api.connector.model.ConnectorAttribute;import org.openiam.api.connector.model.StringOperationalConnectorValue;import org.openiam.base.AttributeOperationEnum;import org.openiam.base.request.BaseSearchServiceRequest;import org.openiam.base.request.IdServiceRequest;import org.openiam.base.response.data.BooleanResponse;import org.openiam.base.response.data.LongDataResponse;import org.openiam.base.ws.MatchType;import org.openiam.base.ws.ResponseCode;import org.openiam.base.ws.SearchParam;import org.openiam.exception.BasicDataServiceException;import org.openiam.idm.provisioning.diff.model.user.ProvisionUserObjectDiff;import org.openiam.idm.searchbeans.LoginSearchBean;import org.openiam.idm.srvc.auth.dto.Login;import org.openiam.idm.processor.groovy.AbstractUserPrincipalGeneratorclass UserPrincipal extends AbstractUserPrincipalGenerator {public void perform(final ConnectorAttribute attribute, final ProvisionUserObjectDiff diffObject, String splitString) throws BasicDataServiceException {String origLogin = String.format("%s%s", diffObject.getLastNameDiff().getValue().substring(0, 2), diffObject.getFirstNameDiff().getValue());String login = origLogin;while (loginExistsV4211(login)) {long count = count(origLogin);login = String.format("%s%4d", origLogin, count);}attribute.addValue(new StringOperationalConnectorValue(login, AttributeOperationEnum.ADD));}}
Output will be like:
lafirstname
lafirstname0001
lafirstname0002