View Javadoc

1   /**
2    * Copyright 2005-2012 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 org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.kuali.rice.core.api.criteria.CriteriaLookupService;
22  import org.kuali.rice.core.api.membership.MemberType;
23  import org.kuali.rice.core.api.delegation.DelegationType;
24  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
25  import org.kuali.rice.kim.api.KimConstants;
26  import org.kuali.rice.kim.api.group.Group;
27  import org.kuali.rice.kim.api.group.GroupService;
28  import org.kuali.rice.kim.api.identity.IdentityService;
29  import org.kuali.rice.kim.api.identity.principal.Principal;
30  import org.kuali.rice.kim.api.role.Role;
31  import org.kuali.rice.kim.api.role.RoleMember;
32  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
33  import org.kuali.rice.kim.api.type.KimType;
34  import org.kuali.rice.kim.framework.role.RoleTypeService;
35  import org.kuali.rice.kim.framework.type.KimTypeService;
36  import org.kuali.rice.kim.impl.KIMPropertyConstants;
37  import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
38  import org.kuali.rice.kim.impl.common.delegate.DelegateTypeBo;
39  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
40  import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
41  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
42  import org.kuali.rice.kim.impl.type.KimTypeBo;
43  import org.kuali.rice.krad.service.BusinessObjectService;
44  import org.kuali.rice.krad.service.KRADServiceLocator;
45  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
46  import org.kuali.rice.krad.service.LookupService;
47  import org.kuali.rice.krad.util.KRADPropertyConstants;
48  
49  import javax.xml.namespace.QName;
50  import java.util.ArrayList;
51  import java.util.Collection;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.Set;
57  
58  abstract class RoleServiceBase {
59      private static final Logger LOG = Logger.getLogger( RoleServiceBase.class );
60  
61      private BusinessObjectService businessObjectService;
62      private LookupService lookupService;
63      private IdentityService identityService;
64      private GroupService groupService;
65      private ResponsibilityInternalService responsibilityInternalService;
66      private RoleDao roleDao;
67      private CriteriaLookupService criteriaLookupService;
68  
69      /**
70       * A helper enumeration for indicating which KimRoleDao method to use when attempting to get role/delegation-related lists that are not in the cache.
71       *
72       * @author Kuali Rice Team (rice.collab@kuali.org)
73       */
74      protected static enum RoleDaoAction {
75          ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS,
76          ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS,
77          ROLE_MEMBERS_FOR_ROLE_IDS,
78          ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS,
79          ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS,
80          DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS,
81          DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS,
82          DELEGATION_MEMBERS_FOR_DELEGATION_IDS
83      }
84  
85      /**
86       * Converts the Qualifier Name/Value Role qualification set into Qualifier AttributeID/Value set
87       *
88       * @param qualification The original role qualification attribute set
89       * @return Converted Map<String, String> containing ID/value pairs
90       */
91      private Map<String, String> convertQualifierKeys(Map<String, String> qualification) {
92          Map<String, String> convertedQualification = new HashMap<String, String>();
93          if (qualification != null && CollectionUtils.isNotEmpty(qualification.entrySet())) {
94              for (Map.Entry<String, String> entry : qualification.entrySet()) {
95                  String kimAttributeId = getKimAttributeId(entry.getKey());
96                  if (StringUtils.isNotEmpty(kimAttributeId)) {
97                      convertedQualification.put(kimAttributeId, entry.getValue());
98                  }
99              }
100         }
101         return convertedQualification;
102     }
103 
104     protected void getNestedRoleTypeMemberIds(String roleId, Set<String> members) {
105         ArrayList<String> roleList = new ArrayList<String>(1);
106         roleList.add(roleId);
107         List<RoleMemberBo> firstLevelMembers = getStoredRoleMembersForRoleIds(roleList, MemberType.ROLE.getCode(), Collections.<String, String>emptyMap());
108         for (RoleMemberBo member : firstLevelMembers) {
109             if (MemberType.ROLE.equals(member.getType())) {
110                 if (!members.contains(member.getMemberId())) {
111                     members.add(member.getMemberId());
112                     getNestedRoleTypeMemberIds(member.getMemberId(), members);
113                 }
114             }
115         }
116     }
117 
118     /**
119      * Retrieves a list of RoleMemberBo instances from the KimRoleDao.
120      *
121      * @param daoActionToTake An indicator for which KimRoleDao method should be used to get the results if the desired RoleMemberBos are not cached.
122      * @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.
123      * @param principalId     The principal ID to filter by; may get ignored depending on the daoActionToTake value.
124      * @param groupIds        The group IDs to filter by; may get ignored depending on the daoActionToTake value.
125      * @param memberTypeCode  The member type code to filter by; may get overridden depending on the daoActionToTake value.
126      * @param qualification   The original role qualification attribute set
127      * @return A list of RoleMemberBo instances based on the provided parameters.
128      * @throws IllegalArgumentException if daoActionToTake refers to an enumeration constant that is not role-member-related.
129      */
130     protected List<RoleMemberBo> getRoleMemberBoList(RoleDaoAction daoActionToTake, Collection<String> roleIds, String principalId,
131                                                      Collection<String> groupIds, String memberTypeCode, Map<String, String> qualification) {
132         Map<String, String> convertedQualification = convertQualifierKeys(qualification);
133 
134         if (roleIds == null || roleIds.isEmpty()) {
135             roleIds = Collections.emptyList();
136         }
137         if (groupIds == null || groupIds.isEmpty()) {
138             groupIds = Collections.emptyList();
139         }
140 
141         switch (daoActionToTake) {
142             case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS: // Search for principal role members only.
143                 return roleDao.getRolePrincipalsForPrincipalIdAndRoleIds(roleIds, principalId, convertedQualification);
144             case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS: // Search for group role members only.
145                return roleDao.getRoleGroupsForGroupIdsAndRoleIds(roleIds, groupIds, convertedQualification);
146             case ROLE_MEMBERS_FOR_ROLE_IDS: // Search for role members with the given member type code.
147                return roleDao.getRoleMembersForRoleIds(roleIds, memberTypeCode, convertedQualification);
148             case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS: // Search for role members who are also roles.
149                 return roleDao.getRoleMembershipsForRoleIdsAsMembers(roleIds, convertedQualification);
150             case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS: // Search for role members that might be roles, principals, or groups.
151                 return roleDao.getRoleMembersForRoleIdsWithFilters(roleIds, principalId, groupIds, convertedQualification);
152             default: // This should never happen, since the previous switch block should handle this case appropriately.
153                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
154         }
155     }
156 
157     /**
158      * Calls the KimRoleDao's "getRolePrincipalsForPrincipalIdAndRoleIds" method and/or retrieves any corresponding members from the cache.
159      */
160     protected List<RoleMemberBo> getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collection<String> roleIds, String principalId, Map<String, String> qualification) {
161         return getRoleMemberBoList(RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS, roleIds, principalId, Collections.<String>emptyList(), null, qualification);
162     }
163 
164     /**
165      * Calls the KimRoleDao's "getRoleGroupsForGroupIdsAndRoleIds" method and/or retrieves any corresponding members from the cache.
166      */
167     protected List<RoleMemberBo> getStoredRoleGroupsForGroupIdsAndRoleIds(Collection<String> roleIds, Collection<String> groupIds, Map<String, String> qualification) {
168         return getRoleMemberBoList(RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS, roleIds, null, groupIds, null, qualification);
169     }
170 
171     /**
172      * Calls the KimRoleDao's "getRoleMembersForRoleIds" method and/or retrieves any corresponding members from the cache.
173      */
174     protected List<RoleMemberBo> getStoredRoleMembersForRoleIds(Collection<String> roleIds, String memberTypeCode, Map<String, String> qualification) {
175         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS, roleIds, null, Collections.<String>emptyList(), memberTypeCode, qualification);
176     }
177 
178     /**
179      * Calls the KimRoleDao's "getRoleMembershipsForRoleIdsAsMembers" method and/or retrieves any corresponding members from the cache.
180      */
181     protected List<RoleMemberBo> getStoredRoleMembershipsForRoleIdsAsMembers(Collection<String> roleIds, Map<String, String> qualification) {
182         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS, roleIds, null, Collections.<String>emptyList(), null, qualification);
183     }
184 
185     /**
186      * Calls the KimRoleDao's "getRoleMembersForRoleIdsWithFilters" method and/or retrieves any corresponding members from the cache.
187      */
188     protected List<RoleMemberBo> getStoredRoleMembersForRoleIdsWithFilters(Collection<String> roleIds, String principalId, List<String> groupIds, Map<String, String> qualification) {
189         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS, roleIds, principalId, groupIds, null, qualification);
190     }
191 
192     /**
193      * Retrieves a RoleMemberBo object by its ID. If the role member already exists in the cache, this method will return the cached
194      * version; otherwise, it will retrieve the uncached version from the database and then cache it (if it belongs to a role that allows
195      * its members to be cached) before returning it.
196      */
197     protected RoleMemberBo getRoleMemberBo(String roleMemberId) {
198         if (StringUtils.isBlank(roleMemberId)) {
199             return null;
200         }
201 
202         return getBusinessObjectService().findByPrimaryKey(RoleMemberBo.class, Collections.singletonMap(
203                KimConstants.PrimaryKeyConstants.ID, roleMemberId));
204     }
205 
206     /**
207      * Calls the KimRoleDao's "getDelegationImplMapFromRoleIds" method and/or retrieves any corresponding delegations from the cache.
208      */
209     protected Map<String, DelegateTypeBo> getStoredDelegationImplMapFromRoleIds(Collection<String> roleIds) {
210         if (roleIds != null && !roleIds.isEmpty()) {
211             return roleDao.getDelegationImplMapFromRoleIds(roleIds);
212         }
213 
214         return Collections.emptyMap();
215     }
216 
217     /**
218      * Calls the KimRoleDao's "getDelegationBosForRoleIds" method and/or retrieves any corresponding delegations from the cache.
219      */
220     protected List<DelegateTypeBo> getStoredDelegationImplsForRoleIds(Collection<String> roleIds) {
221         if (roleIds != null && !roleIds.isEmpty()) {
222             return roleDao.getDelegationBosForRoleIds(roleIds);
223         }
224         return Collections.emptyList();
225     }
226 
227     /**
228      * Retrieves a List of delegation members from the KimRoleDao as appropriate.
229      *
230      * @param daoActionToTake An indicator for which KimRoleDao method to use for retrieving results.
231      * @param delegationIds   The IDs of the delegations that the members belong to.
232      * @param principalId     The principal ID of the principal delegation members; may get ignored depending on the RoleDaoAction value.
233      * @param groupIds        The group IDs of the group delegation members; may get ignored depending on the RoleDaoAction value.
234      * @return A List of DelegateMemberBo objects based on the provided parameters.
235      * @throws IllegalArgumentException if daoActionToTake does not represent a delegation-member-list-related enumeration value.
236      */
237     protected List<DelegateMemberBo> getDelegationMemberBoList(RoleDaoAction daoActionToTake, Collection<String> delegationIds,
238                                                                String principalId, List<String> groupIds) {
239         if (delegationIds == null || delegationIds.isEmpty()) {
240             delegationIds = Collections.emptyList();
241         }
242         if (groupIds == null || groupIds.isEmpty()) {
243             groupIds = Collections.emptyList();
244         }
245 
246         switch (daoActionToTake) {
247             case DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS: // Search for principal delegation members.
248                 return roleDao.getDelegationPrincipalsForPrincipalIdAndDelegationIds(delegationIds, principalId);
249             case DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS: // Search for group delegation members.
250                 return roleDao.getDelegationGroupsForGroupIdsAndDelegationIds(delegationIds, groupIds);
251             default: // This should never happen since the previous switch block should handle this case appropriately.
252                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-delegation-member-list-related value!");
253         }
254     }
255 
256     /**
257      * Calls the KimRoleDao's "getDelegationPrincipalsForPrincipalIdAndDelegationIds" method and/or retrieves any corresponding members from the cache.
258      */
259     protected List<DelegateMemberBo> getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(Collection<String> delegationIds, String principalId) {
260         return getDelegationMemberBoList(RoleDaoAction.DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS,
261                 delegationIds, principalId, null);
262     }
263 
264     /**
265      * Retrieves a DelegateMemberBo object by its ID. If the delegation member already exists in the cache, this method will return the cached
266      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
267      */
268     protected DelegateMemberBo getDelegateMemberBo(String delegationMemberId) {
269         if (StringUtils.isBlank(delegationMemberId)) {
270             return null;
271         }
272 
273         return getBusinessObjectService().findByPrimaryKey(DelegateMemberBo.class,
274                 Collections.singletonMap(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMemberId));
275     }
276 
277     /**
278      * Retrieves a DelegateMemberBo List by (principal/group/role) member ID and delegation ID. If the List already exists in the cache,
279      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
280      */
281     protected List<DelegateMemberBo> getDelegationMemberBoListByMemberAndDelegationId(String memberId, String delegationId) {
282 
283         Map<String, String> searchCriteria = new HashMap<String, String>();
284         searchCriteria.put(KimConstants.PrimaryKeyConstants.MEMBER_ID, memberId);
285         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
286         return new ArrayList<DelegateMemberBo>(getBusinessObjectService().findMatching(DelegateMemberBo.class, searchCriteria));
287     }
288 
289     protected Object getMember(String memberTypeCode, String memberId) {
290         if (StringUtils.isBlank(memberId)) {
291             return null;
292         }
293         if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) {
294             return getIdentityService().getPrincipal(memberId);
295         } else if (MemberType.GROUP.getCode().equals(memberTypeCode)) {
296             return getGroupService().getGroup(memberId);
297         } else if (MemberType.ROLE.getCode().equals(memberTypeCode)) {
298             return getRoleBo(memberId);
299         }
300         return null;
301     }
302 
303     protected String getMemberName(Object member) {
304         if (member == null) {
305             return "";
306         }
307         if (member instanceof Principal) {
308             return ((Principal) member).getPrincipalName();
309         }
310         if (member instanceof Group) {
311             return ((Group) member).getName();
312         }
313         if (member instanceof Role) {
314             return ((Role) member).getName();
315         }
316         return member.toString();
317     }
318 
319     protected RoleBo getRoleBo(String roleId) {
320         if (StringUtils.isBlank(roleId)) {
321             return null;
322         }
323         return getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, roleId);
324     }
325 
326     protected DelegateTypeBo getDelegationOfType(String roleId, DelegationType delegationType) {
327         List<DelegateTypeBo> roleDelegates = getRoleDelegations(roleId);
328         if (isDelegationPrimary(delegationType)) {
329             return getPrimaryDelegation(roleId, roleDelegates);
330         } else {
331             return getSecondaryDelegation(roleId, roleDelegates);
332         }
333     }
334 
335     private DelegateTypeBo getSecondaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
336         DelegateTypeBo secondaryDelegate = null;
337         RoleBo roleBo = getRoleBo(roleId);
338         for (DelegateTypeBo delegate : roleDelegates) {
339             if (isDelegationSecondary(delegate.getDelegationType())) {
340                 secondaryDelegate = delegate;
341             }
342         }
343         if (secondaryDelegate == null) {
344             secondaryDelegate = new DelegateTypeBo();
345             secondaryDelegate.setRoleId(roleId);
346             secondaryDelegate.setDelegationType(DelegationType.SECONDARY);
347             secondaryDelegate.setKimTypeId(roleBo.getKimTypeId());
348         }
349         return secondaryDelegate;
350     }
351 
352     protected DelegateTypeBo getPrimaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
353         DelegateTypeBo primaryDelegate = null;
354         RoleBo roleBo = getRoleBo(roleId);
355         for (DelegateTypeBo delegate : roleDelegates) {
356             if (isDelegationPrimary(delegate.getDelegationType())) {
357                 primaryDelegate = delegate;
358             }
359         }
360         if (primaryDelegate == null) {
361             primaryDelegate = new DelegateTypeBo();
362             primaryDelegate.setRoleId(roleId);
363             primaryDelegate.setDelegationType(DelegationType.PRIMARY);
364             primaryDelegate.setKimTypeId(roleBo.getKimTypeId());
365         }
366         return primaryDelegate;
367     }
368 
369     protected RoleMemberBo matchingMemberRecord(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
370         for (RoleMemberBo rm : roleMembers) {
371             if (doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
372                 return rm;
373             }
374         }
375         return null;
376     }
377 
378     protected boolean isDelegationPrimary(DelegationType delegationType) {
379         return DelegationType.PRIMARY.equals(delegationType);
380     }
381 
382     protected boolean isDelegationSecondary(DelegationType delegationType) {
383         return DelegationType.SECONDARY.equals(delegationType);
384     }
385 
386 
387     private List<DelegateTypeBo> getRoleDelegations(String roleId) {
388         if (roleId == null) {
389             return new ArrayList<DelegateTypeBo>();
390         }
391         return getStoredDelegationImplsForRoleIds(Collections.singletonList(roleId));
392 
393     }
394 
395     protected RoleBo getRoleBoByName(String namespaceCode, String roleName) {
396         if (StringUtils.isBlank(namespaceCode)
397                 || StringUtils.isBlank(roleName)) {
398             return null;
399         }
400         Map<String, String> criteria = new HashMap<String, String>();
401         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
402         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
403         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
404         // while this is not actually the primary key - there will be at most one row with these criteria
405         return getBusinessObjectService().findByPrimaryKey(RoleBo.class, criteria);
406     }
407 
408 	protected List<RoleMember> doAnyMemberRecordsMatchByExactQualifier( RoleBo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier ) {
409 		List<RoleMemberBo> roleMemberBos = getRoleMembersByExactQualifierMatch(role, memberId, daoActionToTake, qualifier);
410         List<RoleMember> roleMembers = new ArrayList<RoleMember>();
411         if(CollectionUtils.isNotEmpty(roleMemberBos)) {
412             for (RoleMemberBo bo : roleMemberBos) {
413                 roleMembers.add(RoleMemberBo.to(bo));
414             }
415 			return roleMembers;
416 		}
417 
418 		return Collections.emptyList();
419 	}
420 	
421 	protected List<RoleMemberBo> getRoleMembersByExactQualifierMatch(RoleBo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier) {
422 		List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
423 		RoleTypeService roleTypeService = getRoleTypeService( role.getId() );
424 		if(roleTypeService != null) {
425     		List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
426     		if(CollectionUtils.isNotEmpty(attributesForExactMatch)) {
427     			switch (daoActionToTake) {
428 	    			case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : // Search for group role members only.
429 	        			rms = getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(role.getId()), Collections.singletonList(memberId), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
430 	    				break;
431 	    			case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : // Search for principal role members only.
432 	        			rms = getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collections.singletonList(role.getId()), memberId, populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
433 	    				break;
434 	    			case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : // Search for roles as role members only.
435 	    				List<RoleMemberBo> allRoleMembers = getStoredRoleMembershipsForRoleIdsAsMembers(Collections.singletonList(role.getId()), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
436 	        			for(RoleMemberBo rm : allRoleMembers) {
437 	        				if ( rm.getMemberId().equals(memberId) ) { 
438 	        					rms.add(rm);
439 	        				}
440 	        			}
441 	        			break;	    				
442 	    			default : // The daoActionToTake parameter is invalid; throw an exception.
443 	    				throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
444     			}
445     			
446     		} 
447 		}
448 		return rms;
449 	}
450     
451     //return roleMemberId of match or null if no match
452     protected RoleMember doAnyMemberRecordsMatch(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
453         for (RoleMemberBo rm : roleMembers) {
454             if (rm.isActive() && doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
455                 return RoleMemberBo.to(rm);
456             }
457         }
458         return null;
459     }
460 
461     protected boolean doesMemberMatch(RoleMemberBo roleMember, String memberId, String memberTypeCode, Map<String, String> qualifier) {
462         if (roleMember.getMemberId().equals(memberId) && roleMember.getType().getCode().equals(memberTypeCode)) {
463             // member ID/type match
464             Map<String, String> roleQualifier = roleMember.getAttributes();
465             if ((qualifier == null || qualifier.isEmpty())
466                     && (roleQualifier == null || roleQualifier.isEmpty())) {
467                 return true; // blank qualifier match
468             } else {
469                 if (qualifier != null && roleQualifier != null && qualifier.equals(roleQualifier)) {
470                     return true; // qualifier match
471                 }
472             }
473         }
474         return false;
475     }
476     
477     /**
478      * Retrieves the role type service associated with the given role ID
479      *
480      * @param roleId the role ID to get the role type service for
481      * @return the Role Type Service
482      */
483     protected RoleTypeService getRoleTypeService(String roleId) {
484         RoleBo roleBo = getRoleBo(roleId);
485         KimType roleType = KimTypeBo.to(roleBo.getKimRoleType());
486         if (roleType != null) {
487             return getRoleTypeService(roleType);
488         }
489         return null;
490     }
491 
492     protected RoleTypeService getRoleTypeService(KimType typeInfo) {
493         String serviceName = typeInfo.getServiceName();
494         if (serviceName != null) {
495             try {
496                 KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
497                 if (service != null && service instanceof RoleTypeService) {
498                     return (RoleTypeService) service;
499                 }
500                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
501             } catch (Exception ex) {
502                 LOG.error("Unable to find role type service with name: " + serviceName, ex);
503                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
504             }
505         }
506         return null;
507     }
508     
509     protected Map<String, String> populateQualifiersForExactMatch(Map<String, String> defaultQualification, List<String> attributes) {
510         Map<String,String> qualifiersForExactMatch = new HashMap<String,String>();
511         if (defaultQualification != null && CollectionUtils.isNotEmpty(defaultQualification.keySet())) {
512             for (String attributeName : attributes) {
513                 if (StringUtils.isNotEmpty(defaultQualification.get(attributeName))) {
514                     qualifiersForExactMatch.put(attributeName, defaultQualification.get(attributeName));
515                 }
516             }
517         }
518         return qualifiersForExactMatch;
519     }
520 
521     // TODO: pulling attribute IDs repeatedly is inefficient - consider caching the entire list as a map
522     protected String getKimAttributeId(String attributeName) {
523 
524         Map<String, Object> critieria = new HashMap<String, Object>(1);
525         critieria.put("attributeName", attributeName);
526         Collection<KimAttributeBo> defs = getBusinessObjectService().findMatching(KimAttributeBo.class, critieria);
527         String result = null;
528         if (CollectionUtils.isNotEmpty(defs)) {
529             result = defs.iterator().next().getId();
530         }
531         return result;
532     }
533 
534     protected BusinessObjectService getBusinessObjectService() {
535         if (businessObjectService == null) {
536             businessObjectService = KRADServiceLocator.getBusinessObjectService();
537         }
538         return businessObjectService;
539     }
540 
541     /**
542      * @return the lookupService
543      */
544     protected LookupService getLookupService() {
545         if (lookupService == null) {
546             lookupService = KRADServiceLocatorWeb.getLookupService();
547         }
548         return lookupService;
549     }
550 
551     protected IdentityService getIdentityService() {
552         if (identityService == null) {
553             identityService = KimApiServiceLocator.getIdentityService();
554         }
555 
556         return identityService;
557     }
558     
559     protected GroupService getGroupService() {
560         if (groupService == null) {
561             groupService = KimApiServiceLocator.getGroupService();
562         }
563 
564         return groupService;
565     }
566 
567     protected ResponsibilityInternalService getResponsibilityInternalService() {
568         if (responsibilityInternalService == null) {
569             responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
570         }
571         return responsibilityInternalService;
572     }
573 
574     /**
575      * @return the roleDao
576      */
577     protected RoleDao getRoleDao() {
578         return this.roleDao;
579     }
580 
581     /**
582      * @param roleDao the roleDao to set
583      */
584     public void setRoleDao(RoleDao roleDao) {
585         this.roleDao = roleDao;
586     }
587 
588     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
589         this.criteriaLookupService = criteriaLookupService;
590     }
591 
592     public CriteriaLookupService getCriteriaLookupService() {
593         return criteriaLookupService;
594     }
595 
596 }