Thank you to Johan Schön for his research in regard to BAPI_USER_GETLIST parameters as well as for his knowledge about SAP composite roles!
Background
One of the features of IBM Business Process Manager (BPM) is the functionality to dynamically allocate the users who are allowed to claim a specific human task. The task will be assigned a team. This team – or rather the the mechanism behind how the users are determined – can be:
- Static in nature. The team is assigned any combination of pre-specified groups and users. Users would need to be managed by the WAS user registry – possibly federated. Groups can either be managed by the WAS user registry or groups specifically created in the BPM Admin portal. These groups can contain WAS federated groups as well as users and will not be visible in the WAS user and groups management dialog.
- Dynamic in nature. This means that at design time it is unknown exactly what users should be able to claim a specific human task. Perhaps the WAS federation sources (including possible LDAP/AD-repositories) only contain authentication information and not authorization information – like access groups or similar. Or perhaps even the systems of record federated by WAS are only shadowed, in the sense that they perhaps only are updated nightly.
For these reasons – and more – it is probable that one would need to dynamically ascertain the users that are allowed to claim a human task. The names retrieved as a part of a dynamic lookup must be found in the BPM (and thus also WAS) user list. The reason is simply that in order to claim the task, the user must be able to logon to the BPM Process portal and the retrieved name is to be used to log in and claim the task. If it is not possible to login using normal authentication algorithms as federated by WAS, it would not be possible to claim the task.
In order to assist with this task there is a very good toolset at hand: all the integration technologies available in BPM – both Process Designer functionality (like Web Service ability) and Process Server integrations. In order to make use of the latter one would typically need to call the Integration using an Advanced Integration Service, Web Service or something similar. In the example I will address here, an Integration Service will implement a Dynamic Team Retrieval approach. A Web Service defined in Process Designer will call a Process Server integration with the name of an team. This name will be used when connecting to SAP, where it will be matched against all users with this team name an ACTIVITYGROUPS entry – thus matching the team name against an SAP role.
Dynamic Lookups
Dynamic lookups can be either:
- Dynamic Team Retrieval. This lookup is static in nature. That is, it is not able to use dynamic BPM instance variables to lookup the required team. Instead it is specific for a swim lane. It is possible to specify many Dynamic Retrieval Teams – each with a set of static input data. Again, however they are specified on a lane/role basis. Not individually per human task.
The input to a team retrieval service is the name variable (tw.local.name) and the output is a Team object. It is possible to add additional input variables. The private variables (wsUserInfo and newUserType) are typically used during processing of the Team Retrieval logic. In this particular case it is used to call a web service, residing as a part of a Process Server Integration, which is to use the team name, match it against an SAP role name and return the users associated with that role.

- Team Filter Service. Whereas the Dynamic Team Retrieval relies on an existing BPM Team, the Team Filter Service calls an Integration Service Directly. For this reason, the lookup is dynamic in nature. Not only is it able to access all instance variables, it is also able to receive information about another team and process a new team based on that input. Likewise, it is able to completely throw away input information and, using BPM services techniques, completely rewrite the user list. Filtered Team Retrieval is added on a per task basis.
The Team Filter Service is provided with the name of the original team and will output the resulting users in the filteredTeam object. In the provided example the static team Z:PAYMENT is the originalTeam when calling the FilteredApprovers Team Filter Service.

Common for both these approaches is that they do have a set of mandatory input and output parameters. In essence – in both cases – it is the input team. it is possible to add input parameters. It is up to the invocation itself to decide the values mapped to the input parameters. In the former case, it should be constant data, that is environment variables and hard coded values. These additional input parameters are associated with the Team. In the latter case, the input variables can be mapped to any instance data, environment variable or constant. The additional input parameters are in this case associated with the Activity. Saying that – in the case of the Team Filter Service, one is able to add a team assignment.

Note: It is possible to update environment variables in BPM, despite of their constant nature. However, environment variables hold a cross instance scope and that means that if many instances run concurrently and one instance update an environment variable, used by all of them, it could lead to undesirable consequences. If one is absolutely certain that only one instance will run at a time, it might be possible to update environment variables to mimic instance variables. The question is why one would consider such an alternative when there is the option of using Filtered Team Retrieval, where instance information is available and supported?
Using Dynamic Lookup to Acquire SAP User’s Associated with a Particular Role
Obviously, some other system System of Record knows more about who is allowed to claim a specific task. This system can be LDAP-based or – as in our case – SAP. If access right information Is managed by SAP and only specific users are allowed to claim specific BPM human tasks it would make sense to actually dynamically ask SAP who is allowed to be a member of the dynamic team. This could in SAP be managed by a role (previously activity group), which is based on the organizational plan of a company. It is possible to specify simple roles and composite roles. It is important to realise however that if a composite role specifies two simple roles, being a member of one or two of the simple roles does not mean that one would also be a member of a composite role. However – being a member of a composite role also means that you are a member of all the simple roles making up the composite role.
SAP Role Access
SAP BAPIs are the standard interfaces to exchange data between SAP components and between SAP and non-SAP components. BAPIs are RFC (Remote Function Call)-enabled functions and can as such typically be called from remote systems. The SAP Business object USER contains user data and can be remotely accessed via a number of BAPI-calls – in order to acquire the users that are of interest for a dynamic team retrieval it is relevant to make use of BAPI_USER_GETLIST BAPI. We now assume that we provide an SAP simple or composite role as the inbound team to the Dynamic Team Retrieval/Team Filter Service.

This picture specifies the team Z:PAYMENT, which has the same name as an SAP role and this is also the name that will be supplied in the input name variable in the Team Retrieval Service.
Above is an example of what the equivalent Team Filter Service would look like: both the team (which will map to originalTeam) and the Team Filter Service is specified.
Creating the Integration
In the Service (Dynamic Team Retrieval Service and Team Filter Service) a call will be performed to an Integration, implemented in IBM Integration Designer. Typically, this can be done with an Advanced Integration Service or as a Web Service Client. The way this is accomplished is not to be addressed in detail. For the sake of illustrating we assume that the Service implements a Web Service client, which will call the Integration with the information necessary to lookup a proper name in SAP – in this case only the team name – and the response message will contain all users found wen calling the BAPI_USER_GETLIST functionality.
A reasonable recommendation is to create two Business Objects at an early stage during Integration Development. It would typically not be very nice to export the full BAPI WSDL to be called by the Process Designer Service. By refining the business need, one would also gain the benefit of a level of redirection. Should additional information be needed it would be possible to make room in the existing Business Object without affecting the BAPI business object – which was automatically created by the SAP Adapter.

The Integration side is not very complicated. What is needed is a receiving Web Service Export, some kind of orchestration logic, for example a BPEL-process (Microflow recommended, since failures for long running processes could lead to administrative problems as well as affecting performance negatively for persistence reasons), or a mediation flow.
In addition, an outbound SAP Adapter component is required, which represents the BAPI call. The intermediate Java component on the picture to the left represent additional mapping in order to enable dynamic SAP authentication. This is not strictly necessary – a technical non dialog user will prove the point equally as well.
The important part in the pre SAP call map is to specify the correct input parameters to BAPI_USER_GET_LIST. Three parameters are required (a big Thank You to Johan Schön for his research effort!):

AGR_NAME is to contain the team name supplied and FROM_DAT and TO_DAT need to contain the current date. This will ensure that users that belong to this specific role on the current day will be targeted – not for example users whose association with the role expired at an earlier point in time.
The before-call mapping below is an example of how the BAPI structure could be filled in. MaxRows decides how many records that at most will be returned. Since we indeed are interested in the resulting usernames, we specify this as well (X). The SapSelectionRange is populated in accordance with the table above – three entries with different cardinality.

In the Process Designer Service, the following is an example of the pre-execution for a team retrieval service. For a Team Filter Service, it would be relevant to change the tw.local.name variable to tw.local.originalTeam.name. This functionality moves the inbound team name to the Business Object field to be sent to the Integration Web Service.
log.info("*** Starting: " + tw.local.name+ " ***" );
var myWS = new tw.object.WsInfo();
myWS.inputTeam = tw.local.name;
log.info("*** Starting: " + myWS.inputTeam + " ***" );
var twc = new tw.object.mainWSClientType();
twc.wsUserInfo = myWS;
tw.local.wsUserInfo = twc;
log.info("*** Done Starting ***" );
After the Integration Web Service has returned – hopefully with the user names corresponding to the role Z:PAYMENT, the example below shows an example of a post-execution for a team retrieval service. Information is read from the return structure returned by the Web Service. If a specific user cannot be found in the BPM User Repository, the user will be discarded. As mentioned previously a user claiming a task must login to the Portal and this would not really be possible should the user not exist in the BPM User repository.
For a Team Filter Service it would be relevant to add the following to the end of the javascript:
tw.local.filteredTeam (instead of tw.local.team) = resultTeam ;
tw.local.filteredTeam.name = "the team name I want to be seen in the Process Portal";
log.info("*** Ending " + tw.local.newUserType.listLength + ". ***" );
var resultTeam = new tw.object.Team();
resultTeam.members = new tw.object.listOf.String();
var myCount = tw.local.newUserType.listLength
log.info("*** Starting Dynamic Team Resolution. Number of records: " + myCount + ". ***" );
for (var i=0; i<myCount; i++) {
var myUserName = tw.local.newUserType[i].Username;
log.info("*** index: " + i + " user: " + myUserName + ". ***" );
var user = tw.system.org.findUserByName(myUserName);
if (user!=null) {
log.info("*** User " + myUserName + " can be found in User Repository. ***" );
resultTeam.members[resultTeam.members.listLength] = user.name;
} else {
log.info("*** User " + myUserName + " can *not* be found inUser Repository! ***" );
}
}
tw.local.team = resultTeam;
log.info("*** Done Ending ***" );