View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.impl.role;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.persistence.NonUniqueResultException;
28  import javax.xml.namespace.QName;
29  
30  import org.apache.commons.collections.CollectionUtils;
31  import org.apache.commons.lang.StringUtils;
32  import org.apache.log4j.Logger;
33  import org.joda.time.DateTime;
34  import org.kuali.rice.core.api.CoreApiServiceLocator;
35  import org.kuali.rice.core.api.criteria.Predicate;
36  import org.kuali.rice.core.api.criteria.PredicateFactory;
37  import org.kuali.rice.core.api.criteria.QueryByCriteria;
38  import org.kuali.rice.core.api.criteria.QueryResults;
39  import org.kuali.rice.core.api.datetime.DateTimeService;
40  import org.kuali.rice.core.api.delegation.DelegationType;
41  import org.kuali.rice.core.api.membership.MemberType;
42  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
43  import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
44  import org.kuali.rice.coreservice.api.namespace.Namespace;
45  import org.kuali.rice.coreservice.api.namespace.NamespaceService;
46  import org.kuali.rice.kim.api.KimConstants;
47  import org.kuali.rice.kim.api.group.Group;
48  import org.kuali.rice.kim.api.group.GroupService;
49  import org.kuali.rice.kim.api.identity.IdentityService;
50  import org.kuali.rice.kim.api.identity.principal.Principal;
51  import org.kuali.rice.kim.api.role.Role;
52  import org.kuali.rice.kim.api.role.RoleMember;
53  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
54  import org.kuali.rice.kim.api.type.KimType;
55  import org.kuali.rice.kim.api.type.KimTypeAttribute;
56  import org.kuali.rice.kim.api.type.KimTypeInfoService;
57  import org.kuali.rice.kim.framework.role.RoleEbo;
58  import org.kuali.rice.kim.framework.role.RoleTypeService;
59  import org.kuali.rice.kim.framework.type.KimTypeService;
60  import org.kuali.rice.kim.impl.KIMPropertyConstants;
61  import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
62  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
63  import org.kuali.rice.kim.impl.common.delegate.DelegateTypeBo;
64  import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
65  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
66  import org.kuali.rice.kim.impl.type.KimTypeBo;
67  import org.kuali.rice.krad.data.DataObjectService;
68  import org.kuali.rice.krad.data.KradDataServiceLocator;
69  import org.kuali.rice.krad.util.KRADPropertyConstants;
70  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
71  import org.kuali.rice.ksb.api.registry.ServiceInfo;
72  
73  abstract class RoleServiceBase {
74      private static final Logger LOG = Logger.getLogger( RoleServiceBase.class );
75  
76      protected DataObjectService dataObjectService;
77      protected IdentityService identityService;
78      protected NamespaceService namespaceService;
79      protected KimTypeInfoService kimTypeInfoService;
80      protected GroupService groupService;
81      protected ResponsibilityInternalService responsibilityInternalService;
82      protected RoleDao roleDao;
83      protected DateTimeService dateTimeService;
84  
85      /**
86       * A helper enumeration for indicating which KimRoleDao method to use when attempting to get role/delegation-related lists that are not in the cache.
87       *
88       * @author Kuali Rice Team (rice.collab@kuali.org)
89       */
90      protected static enum RoleDaoAction {
91          ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS,
92          ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS,
93          ROLE_MEMBERS_FOR_ROLE_IDS,
94          ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS,
95          ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS
96      }
97  
98      /**
99       * Converts the Qualifier Name/Value Role qualification set into Qualifier AttributeID/Value set
100      *
101      * @param qualification The original role qualification attribute set
102      * @param validAttributeIds The mapping of attribute names to their matching attribute ids
103      * @return Converted Map<String, String> containing ID/value pairs
104      */
105     protected Map<String, String> convertQualifierKeys(Map<String, String> qualification, Map<String, String> validAttributeIds) {
106         Map<String, String> convertedQualification = new HashMap<String, String>();
107 
108         if (qualification != null && CollectionUtils.isNotEmpty(qualification.entrySet())) {
109             for (Map.Entry<String, String> entry : qualification.entrySet()) {
110                 String attributeId = validAttributeIds.get(entry.getKey());
111                 if (StringUtils.isNotEmpty(attributeId)) {
112                     convertedQualification.put(attributeId, entry.getValue());
113                 }
114             }
115         }
116 
117         return convertedQualification;
118     }
119 
120     protected void getNestedRoleTypeMemberIds(String roleId, Set<String> members) {
121         ArrayList<String> roleList = new ArrayList<String>(1);
122         roleList.add(roleId);
123         List<RoleMemberBo> firstLevelMembers = getStoredRoleMembersForRoleIds(roleList, MemberType.ROLE.getCode(), Collections.<String, String>emptyMap());
124         for (RoleMemberBo member : firstLevelMembers) {
125             if (MemberType.ROLE.equals(member.getType())) {
126                 if (!members.contains(member.getMemberId())) {
127                     members.add(member.getMemberId());
128                     getNestedRoleTypeMemberIds(member.getMemberId(), members);
129                 }
130             }
131         }
132     }
133 
134     protected List<RoleMemberBo> getRoleMembersForPrincipalId(Collection<String> roleIds, String principalId) {
135         return getRoleMembersForPrincipalId(roleIds, principalId, new HashMap<String, String>(0) );
136     }
137 
138     protected List<RoleMemberBo> getRoleMembersForPrincipalId(Collection<String> roleIds, String principalId, Map<String,String> qualification ) {
139         List<Predicate> criteria = new ArrayList<Predicate>();
140 
141         if (CollectionUtils.isNotEmpty(roleIds)) {
142             if (roleIds.size() == 1) {
143                 criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.ROLE_ID, roleIds.iterator().next()) );
144             } else {
145                 criteria.add( PredicateFactory.in(KIMPropertyConstants.RoleMember.ROLE_ID, roleIds) );
146             }
147         }
148 
149         if ( StringUtils.isNotBlank(principalId) ) {
150             criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_ID, principalId) );
151         }
152 
153         criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.PRINCIPAL.getCode()));
154 
155         Predicate roleQualificationPredicate = getRoleQualificationPredicate(qualification);
156         if ( roleQualificationPredicate != null ) {
157             criteria.add( roleQualificationPredicate );
158         }
159 
160         return getRoleMembershipsForPredicates(criteria);
161     }
162 
163     protected List<RoleMemberBo> getRoleMembersForGroupIds(String roleId, List<String> groupIds) {
164         if (CollectionUtils.isEmpty(groupIds)) {
165             return new ArrayList<RoleMemberBo>();
166         }
167 
168         List<RoleMemberBo> coll = getDataObjectService().findMatching( RoleMemberBo.class,
169                 QueryByCriteria.Builder.fromPredicates(
170                         PredicateFactory.equal(KIMPropertyConstants.RoleMember.ROLE_ID, roleId),
171                         PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.GROUP.getCode()),
172                         PredicateFactory.in(KIMPropertyConstants.RoleMember.MEMBER_ID, groupIds) ) ).getResults();
173         List<RoleMemberBo> results = new ArrayList<RoleMemberBo>(coll.size());
174         DateTime now = new DateTime( getDateTimeService().getCurrentTimestamp().getTime() );
175 
176         for (RoleMemberBo rm : coll) {
177             if (rm.isActive(now)) {
178                 results.add(rm);
179             }
180         }
181 
182         return results;
183     }
184 
185     /**
186      * Retrieves a list of RoleMemberBo instances from the KimRoleDao.
187      *
188      * @param daoActionToTake An indicator for which KimRoleDao method should be used to get the results if the desired RoleMemberBos are not cached.
189      * @param roleIds         The role IDs to filter by; may get used as the IDs for members that are also roles, depending on the daoActionToTake value.
190      * @param principalId     The principal ID to filter by; may get ignored depending on the daoActionToTake value.
191      * @param groupIds        The group IDs to filter by; may get ignored depending on the daoActionToTake value.
192      * @param memberTypeCode  The member type code to filter by; may get overridden depending on the daoActionToTake value.
193      * @param qualification   The original role qualification attribute set
194      * @return A list of RoleMemberBo instances based on the provided parameters.
195      * @throws IllegalArgumentException if daoActionToTake refers to an enumeration constant that is not role-member-related.
196      */
197     protected List<RoleMemberBo> getRoleMemberBoList(RoleDaoAction daoActionToTake, Collection<String> roleIds, String principalId,
198             Collection<String> groupIds, String memberTypeCode, Map<String, String> qualification) {
199         if (roleIds == null || roleIds.isEmpty()) {
200             roleIds = Collections.emptyList();
201         }
202 
203         if (groupIds == null || groupIds.isEmpty()) {
204             groupIds = Collections.emptyList();
205         }
206 
207         Map<String, String> validAttributeIds = new HashMap<String, String>();
208 
209         HashSet <String> kimTypeIds = new HashSet<String>();
210 
211         //Getting unique kim types
212         for (String roleId : roleIds) {
213             RoleBoLite role = getRoleBoLite(roleId);
214             kimTypeIds.add(role.getKimTypeId());
215         }
216 
217         if (qualification != null && CollectionUtils.isNotEmpty(qualification.entrySet())) {
218             for (String kimTypeId : kimTypeIds) {
219                 for (Map.Entry<String, String> entry : qualification.entrySet()) {
220                     validAttributeIds.put(entry.getKey(), getKimAttributeId(kimTypeId, entry.getKey()));
221                 }
222             }
223         }
224 
225 
226         Map<String, String> convertedQualification = convertQualifierKeys(qualification, validAttributeIds);
227 
228         switch (daoActionToTake) {
229             case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS: // Search for principal role members only.
230                 return getRoleMembersForPrincipalId(roleIds, principalId, convertedQualification);
231             case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS: // Search for group role members only.
232                 return getRoleGroupsForGroupIdsAndRoleIds(roleIds, groupIds, convertedQualification);
233             case ROLE_MEMBERS_FOR_ROLE_IDS: // Search for role members with the given member type code.
234                 return roleDao.getRoleMembersForRoleIds(roleIds, memberTypeCode, convertedQualification);
235             case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS: // Search for role members who are also roles.
236                 return getRoleMembershipsForRoleIdsAsMembers(roleIds, convertedQualification);
237             case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS: // Search for role members that might be roles, principals, or groups.
238                 return getRoleMembersForRoleIdsWithFilters(roleIds, principalId, groupIds, convertedQualification);
239             default: // This should never happen, since the previous switch block should handle this case appropriately.
240                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
241         }
242     }
243 
244     public List<RoleMemberBo> getRoleGroupsForGroupIdsAndRoleIds(Collection<String> roleIds, Collection<String> groupIds, Map<String, String> qualification) {
245         List<Predicate> criteria = new ArrayList<Predicate>();
246 
247         if (CollectionUtils.isNotEmpty(roleIds)) {
248             criteria.add( PredicateFactory.in(KIMPropertyConstants.RoleMember.ROLE_ID, roleIds) );
249         }
250 
251         if (CollectionUtils.isNotEmpty(groupIds)) {
252             criteria.add( PredicateFactory.in(KIMPropertyConstants.RoleMember.MEMBER_ID, groupIds) );
253         }
254         criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.GROUP.getCode()));
255 
256         Predicate roleQualificationPredicate = getRoleQualificationPredicate(qualification);
257         if ( roleQualificationPredicate != null ) {
258             criteria.add( roleQualificationPredicate );
259         }
260 
261         return getRoleMembershipsForPredicates(criteria);
262     }
263 
264     protected List<RoleMemberBo> getRoleMembershipsForRoleIdsAsMembers(Collection<String> roleIds,
265             Map<String, String> qualification) {
266         List<Predicate> criteria = new ArrayList<Predicate>();
267 
268         if (CollectionUtils.isNotEmpty(roleIds)) {
269             criteria.add( PredicateFactory.in(KIMPropertyConstants.RoleMember.ROLE_ID, roleIds) );
270         }
271 
272         criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.ROLE.getCode()));
273 
274         Predicate roleQualificationPredicate = getRoleQualificationPredicate(qualification);
275         if ( roleQualificationPredicate != null ) {
276             criteria.add( roleQualificationPredicate );
277         }
278 
279         return getRoleMembershipsForPredicates(criteria);
280     }
281 
282     protected List<RoleMemberBo> getRoleMembersForRoleIdsWithFilters(Collection<String> roleIds,
283             String principalId, Collection<String> groupIds, Map<String, String> qualification) {
284         List<Predicate> criteria = new ArrayList<Predicate>();
285 
286         if (CollectionUtils.isNotEmpty(roleIds)) {
287             criteria.add( PredicateFactory.in(KIMPropertyConstants.RoleMember.ROLE_ID, roleIds) );
288         }
289 
290         List<Predicate> principalPredicates = new ArrayList<Predicate>(2);
291         principalPredicates.add(PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.PRINCIPAL.getCode()));
292 
293         if ( StringUtils.isNotBlank(principalId) ) {
294             principalPredicates.add(PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_ID, principalId));
295         }
296 
297         List<Predicate> groupPredicates = new ArrayList<Predicate>(2);
298         groupPredicates.add(PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.GROUP.getCode()));
299 
300         if (CollectionUtils.isNotEmpty(groupIds)) {
301             groupPredicates.add(PredicateFactory.in(KIMPropertyConstants.RoleMember.MEMBER_ID, groupIds));
302         }
303 
304         criteria.add( PredicateFactory.or(
305                 PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, MemberType.ROLE.getCode()),
306                 PredicateFactory.and(principalPredicates.toArray(new Predicate[0])),
307                 PredicateFactory.and(groupPredicates.toArray(new Predicate[0]))
308         ) );
309 
310         Predicate roleQualificationPredicate = getRoleQualificationPredicate(qualification);
311         if ( roleQualificationPredicate != null ) {
312             criteria.add( roleQualificationPredicate );
313         }
314 
315         return getRoleMembershipsForPredicates(criteria);
316     }
317 
318     protected List<RoleMemberBo> getRoleMembershipsForPredicates( Collection<Predicate> criteria ) {
319         Collection<RoleMemberBo> coll = getDataObjectService().findMatching(RoleMemberBo.class, QueryByCriteria.Builder.fromPredicates(criteria) ).getResults();
320         ArrayList<RoleMemberBo> results = new ArrayList<RoleMemberBo>(coll.size());
321         DateTime now = new DateTime( getDateTimeService().getCurrentTimestamp().getTime() );
322 
323         for (RoleMemberBo rm : coll) {
324             if (rm.isActive(now)) {
325                 results.add(rm);
326             }
327         }
328 
329         return results;
330     }
331 
332     /**
333      * Attempts to add predicates to the query to filter based on subqueries against the
334      * role member attribute data table.
335      *
336      * An "EXISTS" subquery will be created for
337      * each non-blank attribute value passed to this method and they will be anded together
338      * and returned to the calling code.  The attribute value of the qualification will be compared
339      * using a "LIKE" operation.  So, any non-escaped wildcard values (* or ?) will be respected.
340      *
341      * @param qualification An "and" predicate containing the exists predicates if at least one
342      *                      qualification has a non-blank value.  <b>null</b> if all values
343      *                      are blank or the passed in qualification is <b>null</b> or empty.
344      */
345     protected Predicate getRoleQualificationPredicate(Map<String, String> qualification) {
346         if (qualification == null || CollectionUtils.isEmpty(qualification.keySet())) {
347             return null;
348         }
349 
350         List<Predicate> attributePredicates = new ArrayList<Predicate>( qualification.size() );
351 
352         for (Map.Entry<String, String> qualifier : qualification.entrySet()) {
353             if (StringUtils.isNotBlank(qualifier.getValue())) {
354                 Predicate subQueryCriteria = PredicateFactory.and(
355                         PredicateFactory.like("attributeValue", qualifier.getValue()),
356                         PredicateFactory.equal("kimAttributeId", qualifier.getKey()),
357                         PredicateFactory.equalsProperty("assignedToId", null, "parent.id" ));
358                 Predicate existsSubquery = PredicateFactory.existsSubquery(RoleMemberAttributeDataBo.class.getName(), subQueryCriteria);
359 
360                 attributePredicates.add(existsSubquery);
361             }
362         }
363 
364         if ( attributePredicates.isEmpty() ) {
365             return null;
366         }
367 
368         if ( attributePredicates.size() == 1 ) {
369             return attributePredicates.get(0);
370         } else {
371             return PredicateFactory.and( attributePredicates.toArray( new Predicate[attributePredicates.size()] ) );
372         }
373     }
374 
375     protected List<RoleMemberBo> getRoleMembershipsForMemberId(String memberType, String memberId, Map<String, String> qualification) {
376         if (StringUtils.isBlank(memberId) || StringUtils.isBlank(memberType)) {
377             return new ArrayList<RoleMemberBo>(0);
378         }
379 
380         List<Predicate> criteria = new ArrayList<Predicate>();
381 
382         criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_ID, memberId) );
383         criteria.add( PredicateFactory.equal(KIMPropertyConstants.RoleMember.MEMBER_TYPE_CODE, memberType) );
384 
385         Predicate roleQualificationPredicate = getRoleQualificationPredicate(qualification);
386         if ( roleQualificationPredicate != null ) {
387             criteria.add( roleQualificationPredicate );
388         }
389 
390         return getRoleMembershipsForPredicates(criteria);
391     }
392 
393     /**
394      * Calls the KimRoleDao's "getRolePrincipalsForPrincipalIdAndRoleIds" method and/or retrieves any corresponding members from the cache.
395      */
396     protected List<RoleMemberBo> getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collection<String> roleIds, String principalId, Map<String, String> qualification) {
397         return getRoleMemberBoList(RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS, roleIds, principalId, Collections.<String>emptyList(), null, qualification);
398     }
399 
400     /**
401      * Calls the KimRoleDao's "getRoleGroupsForGroupIdsAndRoleIds" method and/or retrieves any corresponding members from the cache.
402      */
403     protected List<RoleMemberBo> getStoredRoleGroupsForGroupIdsAndRoleIds(Collection<String> roleIds, Collection<String> groupIds, Map<String, String> qualification) {
404         return getRoleMemberBoList(RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS, roleIds, null, groupIds, null, qualification);
405     }
406 
407     /**
408      * Calls the KimRoleDao's "getRoleMembersForRoleIds" method and/or retrieves any corresponding members from the cache.
409      */
410     protected List<RoleMemberBo> getStoredRoleMembersForRoleIds(Collection<String> roleIds, String memberTypeCode, Map<String, String> qualification) {
411         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS, roleIds, null, Collections.<String>emptyList(), memberTypeCode, qualification);
412     }
413 
414     /**
415      * Calls the KimRoleDao's "getRoleMembershipsForRoleIdsAsMembers" method and/or retrieves any corresponding members from the cache.
416      */
417     protected List<RoleMemberBo> getStoredRoleMembershipsForRoleIdsAsMembers(Collection<String> roleIds, Map<String, String> qualification) {
418         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS, roleIds, null, Collections.<String>emptyList(), null, qualification);
419     }
420 
421     /**
422      * Calls the KimRoleDao's "getRoleMembersForRoleIdsWithFilters" method and/or retrieves any corresponding members from the cache.
423      */
424     protected List<RoleMemberBo> getStoredRoleMembersForRoleIdsWithFilters(Collection<String> roleIds, String principalId, List<String> groupIds, Map<String, String> qualification) {
425         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS, roleIds, principalId, groupIds, null, qualification);
426     }
427 
428     /**
429      * Retrieves a RoleMemberBo object by its ID. If the role member already exists in the cache, this method will return the cached
430      * version; otherwise, it will retrieve the uncached version from the database and then cache it (if it belongs to a role that allows
431      * its members to be cached) before returning it.
432      */
433     protected RoleMemberBo getRoleMemberBo(String roleMemberId) {
434         if (StringUtils.isBlank(roleMemberId)) {
435             return null;
436         }
437 
438         return getDataObjectService().find(RoleMemberBo.class, roleMemberId);
439     }
440 
441     /**
442      * Retrieves a RoleResponsibilityActionBo object by its ID.
443      */
444     protected RoleResponsibilityActionBo getRoleResponsibilityActionBo(String roleResponsibilityActionId) {
445         if (StringUtils.isBlank(roleResponsibilityActionId)) {
446             return null;
447         }
448 
449         return getDataObjectService().find(RoleResponsibilityActionBo.class, roleResponsibilityActionId);
450     }
451 
452     /**
453      *
454      */
455     protected Map<String, DelegateTypeBo> getStoredDelegationImplMapFromRoleIds(Collection<String> roleIds) {
456         if (roleIds != null && !roleIds.isEmpty()) {
457             Map<String, DelegateTypeBo> results = new HashMap<String, DelegateTypeBo>();
458             Collection<DelegateTypeBo> coll = getDataObjectService().findMatching(DelegateTypeBo.class,
459                     QueryByCriteria.Builder.fromPredicates(
460                             PredicateFactory.in(KIMPropertyConstants.Delegation.ROLE_ID, roleIds),
461                             PredicateFactory.equal(KIMPropertyConstants.Delegation.ACTIVE, Boolean.TRUE) ) ).getResults();
462 
463             for (DelegateTypeBo delegateBo : coll) {
464                 results.put(delegateBo.getDelegationId(), delegateBo);
465             }
466 
467             return results;
468         }
469 
470         return Collections.emptyMap();
471     }
472 
473     /**
474      *
475      */
476     protected List<DelegateTypeBo> getStoredDelegationImplsForRoleIds(Collection<String> roleIds) {
477         if (roleIds != null && !roleIds.isEmpty()) {
478             List<DelegateTypeBo> coll = getDataObjectService().findMatching(DelegateTypeBo.class,
479                     QueryByCriteria.Builder.fromPredicates(
480                             PredicateFactory.in(KIMPropertyConstants.Delegation.ROLE_ID, roleIds),
481                             PredicateFactory.equal(KIMPropertyConstants.Delegation.ACTIVE, Boolean.TRUE) ) ).getResults();
482 
483             return new ArrayList<DelegateTypeBo>( coll );
484         }
485 
486         return Collections.emptyList();
487     }
488 
489     /**
490      * Calls the KimRoleDao's "getDelegationPrincipalsForPrincipalIdAndDelegationIds" method and/or retrieves any corresponding members from the cache.
491      */
492     protected List<DelegateMemberBo> getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(Collection<String> delegationIds, String principalId) {
493         List<Predicate> criteria = new ArrayList<Predicate>();
494 
495         if ( StringUtils.isNotBlank(principalId) ) {
496             criteria.add( PredicateFactory.equal(KIMPropertyConstants.DelegationMember.MEMBER_ID, principalId) );
497         } else {
498             return Collections.emptyList(); // no principal ID - abort
499         }
500 
501         criteria.add( PredicateFactory.equal(KIMPropertyConstants.DelegationMember.MEMBER_TYPE_CODE, MemberType.PRINCIPAL.getCode()));
502 
503         if (delegationIds != null && !delegationIds.isEmpty()) {
504             criteria.add( PredicateFactory.in(KIMPropertyConstants.DelegationMember.DELEGATION_ID, delegationIds) );
505         }
506 
507         List<DelegateMemberBo> coll = getDataObjectService().findMatching(DelegateMemberBo.class, QueryByCriteria.Builder.fromPredicates(criteria) ).getResults();
508         ArrayList<DelegateMemberBo> results = new ArrayList<DelegateMemberBo>(coll.size());
509         DateTime now = new DateTime( getDateTimeService().getCurrentTimestamp().getTime() );
510 
511         for (DelegateMemberBo rm : coll) {
512             if (rm.isActive(now)) {
513                 results.add(rm);
514             }
515         }
516 
517         return results;
518     }
519 
520     /**
521      * Retrieves a DelegateMemberBo object by its ID. If the delegation member already exists in the cache, this method will return the cached
522      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
523      */
524     protected DelegateMemberBo getDelegateMemberBo(String delegationMemberId) {
525         if (StringUtils.isBlank(delegationMemberId)) {
526             return null;
527         }
528 
529         return getDataObjectService().find(DelegateMemberBo.class,delegationMemberId);
530     }
531 
532     /**
533      * Retrieves a DelegateMemberBo List by (principal/group/role) member ID and delegation ID. If the List already exists in the cache,
534      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
535      */
536     protected List<DelegateMemberBo> getDelegationMemberBoListByMemberAndDelegationId(String memberId, String delegationId) {
537 
538         Map<String, String> searchCriteria = new HashMap<String, String>(2);
539         searchCriteria.put(KimConstants.PrimaryKeyConstants.MEMBER_ID, memberId);
540         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
541         return new ArrayList<DelegateMemberBo>(getDataObjectService().findMatching(DelegateMemberBo.class, QueryByCriteria.Builder.andAttributes(searchCriteria).build()).getResults());
542     }
543 
544     protected Object getMember(String memberTypeCode, String memberId) {
545         if (StringUtils.isBlank(memberId)) {
546             return null;
547         }
548 
549         if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) {
550             return getIdentityService().getPrincipal(memberId);
551         } else if (MemberType.GROUP.getCode().equals(memberTypeCode)) {
552             return getGroupService().getGroup(memberId);
553         } else if (MemberType.ROLE.getCode().equals(memberTypeCode)) {
554             return getRoleBo(memberId);
555         }
556 
557         return null;
558     }
559 
560     protected String getMemberName(Object member) {
561         if (member == null) {
562             return "";
563         }
564 
565         if (member instanceof Principal) {
566             return ((Principal) member).getPrincipalName();
567         }
568 
569         if (member instanceof Group) {
570             return ((Group) member).getName();
571         }
572 
573         if (member instanceof Role) {
574             return ((Role) member).getName();
575         }
576 
577         return member.toString();
578     }
579 
580     protected RoleBo getRoleBo(String roleId) {
581         if (StringUtils.isBlank(roleId)) {
582             return null;
583         }
584 
585         return getDataObjectService().find(RoleBo.class, roleId);
586     }
587 
588     protected RoleBoLite getRoleBoLite(String roleId) {
589         if (StringUtils.isBlank(roleId)) {
590             return null;
591         }
592 
593         return getDataObjectService().find(RoleBoLite.class, roleId);
594     }
595 
596     protected DelegateTypeBo getDelegationOfType(String roleId, DelegationType delegationType) {
597         List<DelegateTypeBo> roleDelegates = getRoleDelegations(roleId);
598         if (isDelegationPrimary(delegationType)) {
599             return getPrimaryDelegation(roleId, roleDelegates);
600         } else {
601             return getSecondaryDelegation(roleId, roleDelegates);
602         }
603     }
604 
605     private DelegateTypeBo getSecondaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
606         DelegateTypeBo secondaryDelegate = null;
607         RoleBoLite roleBo = getRoleBoLite(roleId);
608         for (DelegateTypeBo delegate : roleDelegates) {
609             if (isDelegationSecondary(delegate.getDelegationType())) {
610                 secondaryDelegate = delegate;
611             }
612         }
613 
614         if (secondaryDelegate == null) {
615             secondaryDelegate = new DelegateTypeBo();
616             secondaryDelegate.setRoleId(roleId);
617             secondaryDelegate.setDelegationType(DelegationType.SECONDARY);
618             secondaryDelegate.setKimTypeId(roleBo.getKimTypeId());
619         }
620 
621         return secondaryDelegate;
622     }
623 
624     protected DelegateTypeBo getPrimaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
625         DelegateTypeBo primaryDelegate = null;
626         RoleBoLite roleBo = getRoleBoLite(roleId);
627         for (DelegateTypeBo delegate : roleDelegates) {
628             if (isDelegationPrimary(delegate.getDelegationType())) {
629                 primaryDelegate = delegate;
630             }
631         }
632 
633         if (primaryDelegate == null) {
634             primaryDelegate = new DelegateTypeBo();
635             primaryDelegate.setRoleId(roleId);
636             primaryDelegate.setDelegationType(DelegationType.PRIMARY);
637             primaryDelegate.setKimTypeId(roleBo.getKimTypeId());
638         }
639 
640         return primaryDelegate;
641     }
642 
643     protected RoleMemberBo matchingMemberRecord(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
644         for (RoleMemberBo rm : roleMembers) {
645             if (doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
646                 return rm;
647             }
648         }
649 
650         return null;
651     }
652 
653     protected boolean isDelegationPrimary(DelegationType delegationType) {
654         return DelegationType.PRIMARY.equals(delegationType);
655     }
656 
657     protected boolean isDelegationSecondary(DelegationType delegationType) {
658         return DelegationType.SECONDARY.equals(delegationType);
659     }
660 
661 
662     private List<DelegateTypeBo> getRoleDelegations(String roleId) {
663         if (roleId == null) {
664             return new ArrayList<DelegateTypeBo>();
665         }
666 
667         return getStoredDelegationImplsForRoleIds(Collections.singletonList(roleId));
668 
669     }
670 
671     protected RoleBo getRoleBoByName(String namespaceCode, String roleName) {
672         if (StringUtils.isBlank(namespaceCode)
673                 || StringUtils.isBlank(roleName)) {
674             return null;
675         }
676 
677         Map<String, Object> criteria = new HashMap<String, Object>(3);
678         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
679         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
680         criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
681 
682         QueryResults<RoleBo> results =
683                 getDataObjectService().findMatching(RoleBo.class, QueryByCriteria.Builder.andAttributes(criteria).build());
684         if (results.getResults().isEmpty()) {
685             return null;
686         } else if (results.getResults().size() > 1) {
687             throw new NonUniqueResultException("Finding a role by name should return a unique role, "
688                     + "but encountered multiple. namespaceCode='" + namespaceCode + "', name='" + roleName +"'");
689         }
690 
691         return results.getResults().get(0);
692     }
693 
694     protected RoleBoLite getRoleBoLiteByName(String namespaceCode, String roleName) {
695         if (StringUtils.isBlank(namespaceCode)
696                 || StringUtils.isBlank(roleName)) {
697             return null;
698         }
699 
700         Map<String, Object> criteria = new HashMap<String, Object>(3);
701         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
702         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
703         criteria.put(KRADPropertyConstants.ACTIVE, Boolean.TRUE);
704 
705         QueryResults<RoleBoLite> results =
706                 getDataObjectService().findMatching(RoleBoLite.class, QueryByCriteria.Builder.andAttributes(criteria).build());
707         if (results.getResults().isEmpty()) {
708             return null;
709         } else if (results.getResults().size() > 1) {
710             throw new NonUniqueResultException("Finding a role by name should return a unique role, "
711                     + "but encountered multiple. namespaceCode='" + namespaceCode + "', name='" + roleName +"'");
712         }
713 
714         return results.getResults().get(0);
715     }
716 
717     protected List<RoleMember> doAnyMemberRecordsMatchByExactQualifier( RoleEbo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier ) {
718         List<RoleMemberBo> roleMemberBos = getRoleMembersByExactQualifierMatch(role, memberId, daoActionToTake, qualifier);
719         List<RoleMember> roleMembers = new ArrayList<RoleMember>();
720         if (CollectionUtils.isNotEmpty(roleMemberBos)) {
721             for (RoleMemberBo bo : roleMemberBos) {
722                 roleMembers.add(RoleMemberBo.to(bo));
723             }
724 
725             return roleMembers;
726         }
727 
728         return Collections.emptyList();
729     }
730 
731     protected List<RoleMemberBo> getRoleMembersByExactQualifierMatch(RoleEbo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier) {
732         List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
733         RoleTypeService roleTypeService = getRoleTypeService( role.getId() );
734         if (roleTypeService != null) {
735             List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
736             if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
737                 switch (daoActionToTake) {
738                     case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : // Search for group role members only.
739                         rms = getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(role.getId()), Collections.singletonList(memberId), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
740                         break;
741                     case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : // Search for principal role members only.
742                         rms = getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collections.singletonList(role.getId()), memberId, populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
743                         break;
744                     case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : // Search for roles as role members only.
745                         List<RoleMemberBo> allRoleMembers = getStoredRoleMembershipsForRoleIdsAsMembers(Collections.singletonList(role.getId()), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
746                         for(RoleMemberBo rm : allRoleMembers) {
747                             if ( rm.getMemberId().equals(memberId) ) {
748                                 rms.add(rm);
749                             }
750                         }
751                         break;
752                     default : // The daoActionToTake parameter is invalid; throw an exception.
753                         throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
754                 }
755 
756             }
757         }
758 
759         return rms;
760     }
761 
762     //return roleMemberId of match or null if no match
763     protected RoleMember doAnyMemberRecordsMatch(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
764         for (RoleMemberBo rm : roleMembers) {
765             if (rm.isActive() && doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
766                 return RoleMemberBo.to(rm);
767             }
768         }
769 
770         return null;
771     }
772 
773     protected boolean doesMemberMatch(RoleMemberBo roleMember, String memberId, String memberTypeCode, Map<String, String> qualifier) {
774         if (roleMember.getMemberId().equals(memberId) && roleMember.getType().getCode().equals(memberTypeCode)) {
775             // member ID/type match
776             Map<String, String> roleQualifier = roleMember.getAttributes();
777             if ((qualifier == null || qualifier.isEmpty())
778                     && (roleQualifier == null || roleQualifier.isEmpty())) {
779                 return true; // blank qualifier match
780             } else {
781                 if (qualifier != null && roleQualifier != null && qualifier.equals(roleQualifier)) {
782                     return true; // qualifier match
783                 }
784             }
785         }
786 
787         return false;
788     }
789 
790     /**
791      * Retrieves the role type service associated with the given role ID
792      *
793      * @param roleId the role ID to get the role type service for
794      * @return the Role Type Service
795      */
796     protected RoleTypeService getRoleTypeService(String roleId) {
797         RoleBoLite roleBo = getRoleBoLite(roleId);
798         if (roleBo != null){
799             KimType roleType = KimTypeBo.to(roleBo.getKimRoleType());
800             if (roleType != null) {
801                 return getRoleTypeService(roleType);
802             }
803         }
804 
805         return KimImplServiceLocator.getDefaultRoleTypeService();
806     }
807 
808     /**
809      * Retrieves the role type service for the given service name.
810      *
811      * @param serviceName the name of the service to retrieve
812      * @return the Role Type Service
813      */
814     protected RoleTypeService getRoleTypeServiceByName(String serviceName) {
815         try {
816             KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
817             if (service != null && service instanceof RoleTypeService) {
818                 return (RoleTypeService) service;
819             }
820             LOG.warn("Unable to find role type service by name: " + serviceName + ". Defaulting to: kimNoMembersRoleTypeService ");
821             return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
822         } catch (Exception ex) {
823             LOG.warn("Unable to find role type service by name: " + serviceName, ex);
824             return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
825         }
826     }
827 
828     protected RoleTypeService getRoleTypeService(KimType typeInfo) {
829         String serviceName = typeInfo.getServiceName();
830         if (serviceName != null) {
831             try {
832                 KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
833                 if (service != null && service instanceof RoleTypeService) {
834                     return (RoleTypeService) service;
835                 }
836                 LOG.warn("Unable to find role type service with name: " + serviceName + ". Defaulting to: kimNoMembersRoleTypeService ");
837                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
838             } catch (Exception ex) {
839                 LOG.error("Unable to find role type service with name: " + serviceName, ex);
840                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
841             }
842         }
843 
844         return KimImplServiceLocator.getDefaultRoleTypeService();
845     }
846 
847     protected Map<String, String> populateQualifiersForExactMatch(Map<String, String> defaultQualification, List<String> attributes) {
848         Map<String,String> qualifiersForExactMatch = new HashMap<String,String>();
849         if (defaultQualification != null && CollectionUtils.isNotEmpty(defaultQualification.keySet())) {
850             for (String attributeName : attributes) {
851                 if (StringUtils.isNotEmpty(defaultQualification.get(attributeName))) {
852                     qualifiersForExactMatch.put(attributeName, defaultQualification.get(attributeName));
853                 }
854             }
855         }
856 
857         return qualifiersForExactMatch;
858     }
859 
860     // TODO: pulling attribute IDs repeatedly is inefficient - consider caching the entire list as a map
861     // TODO: KULRICE-12100: Most of the time there should be only one result for the kimTypeId and attributeName, but it is not guaranteed, which it should be.
862     // TODO: pulling attribute IDs repeatedly is inefficient - consider caching the entire list as a map
863     /*
864      * search by attribute name, if none return null, if there is only one, return. If there are multiple, then
865      * search by kimType: if found return else
866      *     search by appId of kimType : if found return else
867      *          search by rice app id : if found return else
868      *              search by kuali app id : if found return else
869      *                  return null.
870      */
871     protected String getKimAttributeId(String kimTypeId, String attributeName) {
872         Collection<KimAttributeBo> attributeData = getAttributeByName(attributeName);
873         String kimAttributeId = null;
874 
875         if (CollectionUtils.isNotEmpty(attributeData)) {
876             if (CollectionUtils.size(attributeData) == 1) {
877                 kimAttributeId = attributeData.iterator().next().getId();
878             } else {
879                 kimAttributeId = getCorrectAttributeId(kimTypeId, attributeName, attributeData);
880             }
881         }
882 
883         return kimAttributeId;
884     }
885 
886     /*
887      * Searches the KimAttributeBo for the attribute by name
888      */
889     protected Collection<KimAttributeBo> getAttributeByName(String attributeName) {
890         /*Map<String, Object> critieria = new HashMap<String, Object>(1);
891         critieria.put(KimConstants.AttributeConstants.ATTRIBUTE_NAME, attributeName);*/
892         QueryResults<KimAttributeBo> attributeData = getDataObjectService().findMatching(KimAttributeBo.class, QueryByCriteria.Builder.forAttribute("attributeName", attributeName).build());
893 
894         return attributeData.getResults();
895     }
896 
897     /*
898      * Attempts to get the right attribute for the kimType. If it fails, then tries by namespace.
899      */
900     protected String getCorrectAttributeId(String kimTypeId, String attributeName, Collection<KimAttributeBo> attributeData) {
901         KimType kimType = getKimTypeInfoService().getKimType(kimTypeId);
902         String attribute = getAttributeFromKimType(kimType, attributeName);
903 
904         if (attribute != null) {
905             return attribute;
906         } else {
907             return getAttributeFromNamespace(kimType, attributeName, attributeData);
908         }
909     }
910 
911     protected String getAttributeFromKimType(KimType kimType, String attributeName) {
912         if (kimType != null) {
913             for (KimTypeAttribute attribute : kimType.getAttributeDefinitions()) {
914                 if (attribute.getKimAttribute() != null
915                         && StringUtils.equals(attributeName, attribute.getKimAttribute().getAttributeName())) {
916                     return attribute.getKimAttribute().getId();
917                 }
918             }
919         }
920 
921         return null;
922     }
923 
924     /*
925      * Gets the attribute based on the app namespace, if it cannot find then tries Rice namespace and then Kuali.
926      */
927     protected String getAttributeFromNamespace(KimType kimType, String attributeName, Collection<KimAttributeBo> attributes) {
928         String appId = getAppIdFromNamespace(kimType.getNamespaceCode());
929         String attributeId = getAttributeFromAppId(attributes, appId);
930 
931         if (attributeId == null) {
932             attributeId = getAttributeFromAppId(attributes, KimConstants.KIM_TYPE_RICE_NAMESPACE);
933             if (attributeId == null) {
934                 attributeId = getAttributeFromAppId(attributes, KimConstants.KIM_TYPE_DEFAULT_NAMESPACE);
935             }
936         }
937 
938         return attributeId;
939     }
940 
941     protected String getAppIdFromNamespace(String namespaceCode) {
942         Namespace appNamespace = getNamespaceService().getNamespace(namespaceCode);
943         if (appNamespace == null) {
944             throw new RuntimeException("Namespace " + namespaceCode + " not mapped in namespace table.");
945         }
946 
947         return appNamespace.getApplicationId();
948     }
949 
950     /*
951      * Compares the appId of the attribute with the given appId.
952      * Here we make the assumption that there are not multiple attributes with the same name
953      * for a given application.
954      */
955     protected String getAttributeFromAppId(Collection<KimAttributeBo> attributes, String appId) {
956         for (KimAttributeBo attribute : attributes) {
957             if (StringUtils.equalsIgnoreCase(getAppIdFromNamespace(attribute.getNamespaceCode()), appId)) {
958                 return attribute.getId();
959             }
960         }
961 
962         return null;
963     }
964 
965     protected KimTypeInfoService getKimTypeInfoService() {
966         if (kimTypeInfoService == null) {
967             kimTypeInfoService = KimApiServiceLocator.getKimTypeInfoService();
968         }
969 
970         return kimTypeInfoService;
971     }
972 
973     protected NamespaceService getNamespaceService() {
974         if (namespaceService == null) {
975             namespaceService = CoreServiceApiServiceLocator.getNamespaceService();
976         }
977 
978         return namespaceService;
979     }
980 
981     protected IdentityService getIdentityService() {
982         if (identityService == null) {
983             identityService = KimApiServiceLocator.getIdentityService();
984         }
985 
986         return identityService;
987     }
988 
989     protected GroupService getGroupService() {
990         if (groupService == null) {
991             groupService = KimApiServiceLocator.getGroupService();
992         }
993 
994         return groupService;
995     }
996 
997     protected ResponsibilityInternalService getResponsibilityInternalService() {
998         if (responsibilityInternalService == null) {
999             responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
1000         }
1001 
1002         return responsibilityInternalService;
1003     }
1004 
1005     protected RoleDao getRoleDao() {
1006         return this.roleDao;
1007     }
1008 
1009     public void setRoleDao(RoleDao roleDao) {
1010         this.roleDao = roleDao;
1011     }
1012 
1013     public DataObjectService getDataObjectService() {
1014         if ( dataObjectService == null ) {
1015             dataObjectService = KradDataServiceLocator.getDataObjectService();
1016         }
1017 
1018         return dataObjectService;
1019     }
1020 
1021     public void setDataObjectService(DataObjectService dataObjectService) {
1022         this.dataObjectService = dataObjectService;
1023     }
1024 
1025     public DateTimeService getDateTimeService() {
1026         if ( dateTimeService == null ) {
1027             dateTimeService = CoreApiServiceLocator.getDateTimeService();
1028         }
1029 
1030         return dateTimeService;
1031     }
1032 
1033     public void setDateTimeService(DateTimeService dateTimeService) {
1034         this.dateTimeService = dateTimeService;
1035     }
1036     public void notifyOnMemberRemoval(RoleMember member) {
1037         RoleBoLite roleBo = getRoleBoLite(member.getRoleId());
1038         if (roleBo != null) {
1039             KimType roleType = KimTypeBo.to(roleBo.getKimRoleType());
1040             if (roleType != null) {
1041                 String serviceName = roleType.getServiceName();
1042                 if (serviceName != null) {
1043                     KimTypeService service = null;
1044                     try {
1045                         // Check service version for compatibility since this was added in 2.1.2
1046                         boolean validVersion = false;
1047 
1048                         List<ServiceInfo> serviceInfos = KsbApiServiceLocator.getServiceRegistry().getOnlineServicesByName(QName.valueOf(serviceName));
1049                         for(ServiceInfo serviceInfo : serviceInfos) {
1050                             String version = serviceInfo.getServiceVersion();
1051                             if (StringUtils.isNotBlank(version) && version.compareTo("2.1.2") >= 0) {
1052                                 validVersion = true;
1053                                 break;
1054                             }
1055                         }
1056 
1057                         if (validVersion) {
1058                             service = (KimTypeService) KsbApiServiceLocator.getMessageHelper().getServiceAsynchronously(QName.valueOf(serviceName));
1059                             if (service != null && service instanceof RoleTypeService) {
1060                                 ((RoleTypeService) service).roleMemberRemoved(member);
1061                             }
1062                         }
1063                     } catch (Exception ex) {
1064                         LOG.error("Unable to find role type service with name: " + serviceName, ex);
1065                     }
1066                 }
1067             }
1068         }
1069     }
1070 
1071 
1072 }