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