View Javadoc

1   package org.kuali.rice.kim.impl.role;
2   
3   import org.apache.commons.collections.CollectionUtils;
4   import org.apache.commons.lang.StringUtils;
5   import org.apache.log4j.Logger;
6   import org.kuali.rice.kew.api.action.DelegationType;
7   import org.kuali.rice.kim.api.KimApiConstants;
8   import org.kuali.rice.kim.api.common.delegate.DelegateMember;
9   import org.kuali.rice.kim.api.group.Group;
10  import org.kuali.rice.kim.api.group.GroupService;
11  import org.kuali.rice.kim.api.identity.IdentityService;
12  import org.kuali.rice.kim.api.identity.principal.Principal;
13  import org.kuali.rice.kim.api.role.Role;
14  import org.kuali.rice.kim.api.role.RoleMember;
15  import org.kuali.rice.kim.api.role.RoleResponsibilityAction;
16  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
17  import org.kuali.rice.kim.api.type.KimType;
18  import org.kuali.rice.kim.framework.role.RoleTypeService;
19  import org.kuali.rice.kim.framework.type.KimTypeService;
20  import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
21  import org.kuali.rice.kim.impl.common.delegate.DelegateBo;
22  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
23  import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
24  import org.kuali.rice.kim.impl.services.KIMServiceLocatorInternal;
25  import org.kuali.rice.kim.impl.type.KimTypeBo;
26  import org.kuali.rice.kim.util.KIMPropertyConstants;
27  import org.kuali.rice.kim.util.KimConstants;
28  import org.kuali.rice.krad.service.BusinessObjectService;
29  import org.kuali.rice.krad.service.KRADServiceLocator;
30  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
31  import org.kuali.rice.krad.service.LookupService;
32  import org.kuali.rice.krad.service.SequenceAccessorService;
33  import org.kuali.rice.krad.util.KRADPropertyConstants;
34  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
35  
36  import javax.xml.namespace.QName;
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Set;
45  
46  public class RoleServiceBase {
47      private static final Logger LOG = Logger.getLogger( RoleServiceBase.class );
48  
49      private BusinessObjectService businessObjectService;
50      private LookupService lookupService;
51      private SequenceAccessorService sequenceAccessorService;
52      private IdentityService identityService;
53      private GroupService groupService;
54      private ResponsibilityInternalService responsibilityInternalService;
55      private RoleDao roleDao;
56  
57      /**
58       * A helper enumeration for indicating which KimRoleDao method to use when attempting to get role/delegation-related lists that are not in the cache.
59       *
60       * @author Kuali Rice Team (rice.collab@kuali.org)
61       */
62      protected static enum RoleDaoAction {
63          ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS,
64          ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS,
65          ROLE_MEMBERS_FOR_ROLE_IDS,
66          ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS,
67          ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS,
68          DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS,
69          DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS,
70          DELEGATION_MEMBERS_FOR_DELEGATION_IDS
71      }
72  
73      /**
74       * Converts the Qualifier Name/Value Role qualification set into Qualifier AttributeID/Value set
75       *
76       * @param qualification The original role qualification attribute set
77       * @return Converted Map<String, String> containing ID/value pairs
78       */
79      private Map<String, String> convertQualifierKeys(Map<String, String> qualification) {
80          Map<String, String> convertedQualification = new HashMap<String, String>();
81          if (qualification != null && CollectionUtils.isNotEmpty(qualification.keySet())) {
82              for (Map.Entry<String, String> entry : qualification.entrySet()) {
83                  if (StringUtils.isNotEmpty(getKimAttributeId(entry.getKey()))) {
84                      convertedQualification.put(getKimAttributeId(entry.getKey()), entry.getValue());
85                  }
86              }
87          }
88          return convertedQualification;
89      }
90  
91      public Set<String> getRoleTypeRoleMemberIds(String roleId) {
92          Set<String> results = new HashSet<String>();
93          getNestedRoleTypeMemberIds(roleId, results);
94          return results;
95      }
96  
97      protected void getNestedRoleTypeMemberIds(String roleId, Set<String> members) {
98          ArrayList<String> roleList = new ArrayList<String>(1);
99          roleList.add(roleId);
100         List<RoleMemberBo> firstLevelMembers = getStoredRoleMembersForRoleIds(roleList, KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE, Collections.<String, String>emptyMap());
101         for (RoleMemberBo member : firstLevelMembers) {
102             if (KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE.equals(member.getMemberTypeCode())) {
103                 if (!members.contains(member.getMemberId())) {
104                     members.add(member.getMemberId());
105                     getNestedRoleTypeMemberIds(member.getMemberId(), members);
106                 }
107             }
108         }
109     }
110 
111     public List<String> getMemberParentRoleIds(String memberType, String memberId) {
112         List<RoleMemberBo> parentRoleMembers = roleDao.getRoleMembershipsForMemberId(memberType, memberId, Collections.<String, String>emptyMap());
113 
114         List<String> parentRoleIds = new ArrayList<String>(parentRoleMembers.size());
115         for (RoleMemberBo parentRoleMember : parentRoleMembers) {
116             parentRoleIds.add(parentRoleMember.getRoleId());
117         }
118 
119         return parentRoleIds;
120     }
121 
122     /**
123      * Retrieves a list of RoleMemberBo instances from the KimRoleDao.
124      *
125      * @param daoActionToTake An indicator for which KimRoleDao method should be used to get the results if the desired RoleMemberBos are not cached.
126      * @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.
127      * @param principalId     The principal ID to filter by; may get ignored depending on the daoActionToTake value.
128      * @param groupIds        The group IDs to filter by; may get ignored depending on the daoActionToTake value.
129      * @param memberTypeCode  The member type code to filter by; may get overridden depending on the daoActionToTake value.
130      * @param qualification   The original role qualification attribute set
131      * @return A list of RoleMemberBo instances based on the provided parameters.
132      * @throws IllegalArgumentException if daoActionToTake refers to an enumeration constant that is not role-member-related.
133      */
134     protected List<RoleMemberBo> getRoleMemberBoList(RoleDaoAction daoActionToTake, Collection<String> roleIds, String principalId,
135                                                      Collection<String> groupIds, String memberTypeCode, Map<String, String> qualification) {
136         Map<String, String> convertedQualification = convertQualifierKeys(qualification);
137 
138         if (roleIds == null || roleIds.isEmpty()) {
139             roleIds = Collections.emptyList();
140         }
141         if (groupIds == null || groupIds.isEmpty()) {
142             groupIds = Collections.emptyList();
143         }
144 
145         switch (daoActionToTake) {
146             case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS: // Search for principal role members only.
147                 return roleDao.getRolePrincipalsForPrincipalIdAndRoleIds(roleIds, principalId, convertedQualification);
148             case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS: // Search for group role members only.
149                return roleDao.getRoleGroupsForGroupIdsAndRoleIds(roleIds, groupIds, convertedQualification);
150             case ROLE_MEMBERS_FOR_ROLE_IDS: // Search for role members with the given member type code.
151                return roleDao.getRoleMembersForRoleIds(roleIds, memberTypeCode, convertedQualification);
152             case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS: // Search for role members who are also roles.
153                 return roleDao.getRoleMembershipsForRoleIdsAsMembers(roleIds, convertedQualification);
154             case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS: // Search for role members that might be roles, principals, or groups.
155                 return roleDao.getRoleMembersForRoleIdsWithFilters(roleIds, principalId, groupIds, convertedQualification);
156             default: // This should never happen, since the previous switch block should handle this case appropriately.
157                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
158         }
159     }
160 
161     /**
162      * Calls the KimRoleDao's "getRolePrincipalsForPrincipalIdAndRoleIds" method and/or retrieves any corresponding members from the cache.
163      */
164     protected List<RoleMemberBo> getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collection<String> roleIds, String principalId, Map<String, String> qualification) {
165         return getRoleMemberBoList(RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS, roleIds, principalId, Collections.<String>emptyList(), null, qualification);
166     }
167 
168     /**
169      * Calls the KimRoleDao's "getRoleGroupsForGroupIdsAndRoleIds" method and/or retrieves any corresponding members from the cache.
170      */
171     protected List<RoleMemberBo> getStoredRoleGroupsForGroupIdsAndRoleIds(Collection<String> roleIds, Collection<String> groupIds, Map<String, String> qualification) {
172         return getRoleMemberBoList(RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS, roleIds, null, groupIds, null, qualification);
173     }
174 
175     /**
176      * Calls the KimRoleDao's "getRoleMembersForRoleIds" method and/or retrieves any corresponding members from the cache.
177      */
178     protected List<RoleMemberBo> getStoredRoleMembersForRoleIds(Collection<String> roleIds, String memberTypeCode, Map<String, String> qualification) {
179         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS, roleIds, null, Collections.<String>emptyList(), memberTypeCode, qualification);
180     }
181 
182     /**
183      * Calls the KimRoleDao's "getRoleMembershipsForRoleIdsAsMembers" method and/or retrieves any corresponding members from the cache.
184      */
185     protected List<RoleMemberBo> getStoredRoleMembershipsForRoleIdsAsMembers(Collection<String> roleIds, Map<String, String> qualification) {
186         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS, roleIds, null, Collections.<String>emptyList(), null, qualification);
187     }
188 
189     /**
190      * Calls the KimRoleDao's "getRoleMembersForRoleIdsWithFilters" method and/or retrieves any corresponding members from the cache.
191      */
192     protected List<RoleMemberBo> getStoredRoleMembersForRoleIdsWithFilters(Collection<String> roleIds, String principalId, List<String> groupIds, Map<String, String> qualification) {
193         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS, roleIds, principalId, groupIds, null, qualification);
194     }
195 
196     /**
197      * Determines whether or not the given role should allow its members to be cached. The default implementation always returns true, but
198      * subclasses can override this method if other non-default Role implementations forbid their members from being cached.
199      *
200      * @param roleId The ID of the role to check for determining whether or not to allow caching of its members.
201      * @return True if the given role allows its members to be cached; false otherwise.
202      */
203     protected boolean shouldCacheMembersOfRole(String roleId) {
204         return true;
205     }
206 
207     /**
208      * Retrieves a RoleMemberBo object by its ID. If the role member already exists in the cache, this method will return the cached
209      * version; otherwise, it will retrieve the uncached version from the database and then cache it (if it belongs to a role that allows
210      * its members to be cached) before returning it.
211      */
212     protected RoleMemberBo getRoleMemberBo(String roleMemberId) {
213         if (StringUtils.isBlank(roleMemberId)) {
214             return null;
215         }
216 
217         return getBusinessObjectService().findByPrimaryKey(RoleMemberBo.class,
218                 Collections.singletonMap(KIMPropertyConstants.RoleMember.ROLE_MEMBER_ID, roleMemberId));
219     }
220 
221     /**
222      * Calls the KimRoleDao's "getDelegationImplMapFromRoleIds" method and/or retrieves any corresponding delegations from the cache.
223      */
224     protected Map<String, DelegateBo> getStoredDelegationImplMapFromRoleIds(Collection<String> roleIds) {
225         if (roleIds != null && !roleIds.isEmpty()) {
226             return roleDao.getDelegationImplMapFromRoleIds(roleIds);
227         }
228 
229         return Collections.emptyMap();
230     }
231 
232     /**
233      * Calls the KimRoleDao's "getDelegationBosForRoleIds" method and/or retrieves any corresponding delegations from the cache.
234      */
235     protected List<DelegateBo> getStoredDelegationImplsForRoleIds(Collection<String> roleIds) {
236         if (roleIds != null && !roleIds.isEmpty()) {
237             return roleDao.getDelegationBosForRoleIds(roleIds);
238         }
239         return Collections.emptyList();
240     }
241 
242     /**
243      * Retrieves a List of delegation members from the KimRoleDao as appropriate.
244      *
245      * @param daoActionToTake An indicator for which KimRoleDao method to use for retrieving results.
246      * @param delegationIds   The IDs of the delegations that the members belong to.
247      * @param principalId     The principal ID of the principal delegation members; may get ignored depending on the RoleDaoAction value.
248      * @param groupIds        The group IDs of the group delegation members; may get ignored depending on the RoleDaoAction value.
249      * @return A List of DelegateMemberBo objects based on the provided parameters.
250      * @throws IllegalArgumentException if daoActionToTake does not represent a delegation-member-list-related enumeration value.
251      */
252     protected List<DelegateMemberBo> getDelegationMemberBoList(RoleDaoAction daoActionToTake, Collection<String> delegationIds,
253                                                                String principalId, List<String> groupIds) {
254         if (delegationIds == null || delegationIds.isEmpty()) {
255             delegationIds = Collections.emptyList();
256         }
257         if (groupIds == null || groupIds.isEmpty()) {
258             groupIds = Collections.emptyList();
259         }
260 
261         switch (daoActionToTake) {
262             case DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS: // Search for principal delegation members.
263                 return roleDao.getDelegationPrincipalsForPrincipalIdAndDelegationIds(delegationIds, principalId);
264             case DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS: // Search for group delegation members.
265                 return roleDao.getDelegationGroupsForGroupIdsAndDelegationIds(delegationIds, groupIds);
266             default: // This should never happen since the previous switch block should handle this case appropriately.
267                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-delegation-member-list-related value!");
268         }
269     }
270 
271     /**
272      * Calls the KimRoleDao's "getDelegationPrincipalsForPrincipalIdAndDelegationIds" method and/or retrieves any corresponding members from the cache.
273      */
274     protected List<DelegateMemberBo> getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(Collection<String> delegationIds, String principalId) {
275         return getDelegationMemberBoList(RoleDaoAction.DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS, delegationIds, principalId, null);
276     }
277 
278     /**
279      * Calls the KimRoleDao's "getDelegationGroupsForGroupIdAndDelegationIds" method and/or retrieves any corresponding members from the cache.
280      */
281     protected List<DelegateMemberBo> getStoredDelegationGroupsForGroupIdsAndDelegationIds(Collection<String> delegationIds, List<String> groupIds) {
282         return getDelegationMemberBoList(RoleDaoAction.DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS, delegationIds, null, groupIds);
283     }
284 
285     /**
286      * Calls the KimRoleDao's "getDelegationMembersForDelegationIds" method and/or retrieves any corresponding members from the cache.
287      */
288     protected Map<String, List<DelegateMemberBo>> getStoredDelegationMembersForDelegationIds(List<String> delegationIds) {
289         if (delegationIds != null && !delegationIds.isEmpty()) {
290             return roleDao.getDelegationMembersForDelegationIds(delegationIds);
291         }
292 
293         return Collections.emptyMap();
294     }
295 
296     /**
297      * Retrieves a DelegateMemberBo object by its ID. If the delegation member already exists in the cache, this method will return the cached
298      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
299      */
300     protected DelegateMemberBo getDelegateMemberBo(String delegationMemberId) {
301         if (StringUtils.isBlank(delegationMemberId)) {
302             return null;
303         }
304 
305         return getBusinessObjectService().findByPrimaryKey(DelegateMemberBo.class,
306                 Collections.singletonMap(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMemberId));
307     }
308 
309     /**
310      * Retrieves a DelegateMemberBo List by (principal/group/role) member ID and delegation ID. If the List already exists in the cache,
311      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
312      */
313     protected List<DelegateMemberBo> getDelegationMemberBoListByMemberAndDelegationId(String memberId, String delegationId) {
314 
315         Map<String, String> searchCriteria = new HashMap<String, String>();
316         searchCriteria.put(KimConstants.PrimaryKeyConstants.MEMBER_ID, memberId);
317         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
318         return new ArrayList<DelegateMemberBo>(getBusinessObjectService().findMatching(DelegateMemberBo.class, searchCriteria));
319     }
320 
321     public RoleMember findRoleMember(String roleMemberId) {
322         Map<String, String> fieldValues = new HashMap<String, String>();
323         fieldValues.put(KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMemberId);
324         List<RoleMember> roleMembers = findRoleMembers(fieldValues);
325         if (roleMembers != null && !roleMembers.isEmpty()) {
326             return roleMembers.get(0);
327         }
328         return null;
329     }
330 
331     public List<RoleMember> findRoleMembers(Map<String, String> fieldValues) {
332         List<RoleMember> roleMembers = new ArrayList<RoleMember>();
333         List<RoleMemberBo> roleMemberBos = (List<RoleMemberBo>) getLookupService().findCollectionBySearchHelper(
334                 RoleMemberBo.class, fieldValues, true);
335 
336         for (RoleMemberBo bo : roleMemberBos) {
337             RoleMember roleMember = RoleMemberBo.to(bo);
338             roleMembers.add(roleMember);
339         }
340         return roleMembers;
341     }
342 
343     public List<RoleResponsibilityAction> getRoleMemberResponsibilityActions(String roleMemberId) {
344         Map<String, String> criteria = new HashMap<String, String>(1);
345         criteria.put(KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMemberId);
346 
347         List<RoleResponsibilityActionBo> responsibilityActionBoList = (List<RoleResponsibilityActionBo>)
348                 getBusinessObjectService().findMatching(RoleResponsibilityActionBo.class, criteria);
349 
350         List<RoleResponsibilityAction> roleResponsibilityActionsList = new ArrayList<RoleResponsibilityAction>();
351         for (RoleResponsibilityActionBo roleResponsibilityActionBo : responsibilityActionBoList) {
352             RoleResponsibilityAction roleResponsibility = RoleResponsibilityActionBo.to(roleResponsibilityActionBo);
353             roleResponsibilityActionsList.add(roleResponsibility);
354         }
355         return roleResponsibilityActionsList;
356     }
357 
358     public List<DelegateMember> findDelegateMembers(final Map<String, String> fieldValues) {
359         List<DelegateMember> delegateMembers = new ArrayList<DelegateMember>();
360         List<DelegateBo> delegateBoList = (List<DelegateBo>) getLookupService().findCollectionBySearchHelper(
361                 DelegateBo.class, fieldValues, true);
362 
363         if (delegateBoList != null && !delegateBoList.isEmpty()) {
364             Map<String, String> delegationMemberFieldValues = new HashMap<String, String>();
365             for (Map.Entry<String, String> entry : fieldValues.entrySet()) {
366                 if (entry.getKey().startsWith(KimConstants.KimUIConstants.MEMBER_ID_PREFIX)) {
367                     delegationMemberFieldValues.put(
368                             entry.getKey().substring(entry.getKey().indexOf(
369                                     KimConstants.KimUIConstants.MEMBER_ID_PREFIX) + KimConstants.KimUIConstants.MEMBER_ID_PREFIX.length()),
370                             entry.getValue());
371                 }
372             }
373 
374             StringBuilder memberQueryString = new StringBuilder();
375             for (DelegateBo delegate : delegateBoList) {
376                 memberQueryString.append(delegate.getDelegationId()).append(KimConstants.KimUIConstants.OR_OPERATOR);
377             }
378             delegationMemberFieldValues.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID,
379                     StringUtils.stripEnd(memberQueryString.toString(), KimConstants.KimUIConstants.OR_OPERATOR));
380             List<DelegateMemberBo> delegateMemberBoList = (List<DelegateMemberBo>) getLookupService().findCollectionBySearchHelper(
381                     DelegateMemberBo.class, delegationMemberFieldValues, true);
382 
383 
384             for (DelegateMemberBo delegateMemberBo : delegateMemberBoList) {
385                 DelegateMember delegateMember = DelegateMemberBo.to(delegateMemberBo);
386                 delegateMembers.add(delegateMember);
387             }
388         }
389         return delegateMembers;
390     }
391 
392     protected Object getMember(String memberTypeCode, String memberId) {
393         if (StringUtils.isBlank(memberId)) {
394             return null;
395         }
396         if (KimConstants.KimUIConstants.MEMBER_TYPE_PRINCIPAL_CODE.equals(memberTypeCode)) {
397             return getIdentityService().getPrincipal(memberId);
398         } else if (KimConstants.KimUIConstants.MEMBER_TYPE_GROUP_CODE.equals(memberTypeCode)) {
399             return getGroupService().getGroup(memberId);
400         } else if (KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE.equals(memberTypeCode)) {
401             return getRoleBo(memberId);
402         }
403         return null;
404     }
405 
406     protected String getMemberName(Object member) {
407         if (member == null) {
408             return "";
409         }
410         if (member instanceof Principal) {
411             return ((Principal) member).getPrincipalName();
412         }
413         if (member instanceof Group) {
414             return ((Group) member).getName();
415         }
416         if (member instanceof Role) {
417             return ((Role) member).getName();
418         }
419         return member.toString();
420     }
421 
422     protected RoleBo getRoleBo(String roleId) {
423         if (StringUtils.isBlank(roleId)) {
424             return null;
425         }
426         return getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, roleId);
427     }
428 
429     protected DelegateBo getDelegationOfType(String roleId, String delegationTypeCode) {
430         List<DelegateBo> roleDelegates = getRoleDelegations(roleId);
431         if (isDelegationPrimary(delegationTypeCode)) {
432             return getPrimaryDelegation(roleId, roleDelegates);
433         } else {
434             return getSecondaryDelegation(roleId, roleDelegates);
435         }
436     }
437 
438     private DelegateBo getSecondaryDelegation(String roleId, List<DelegateBo> roleDelegates) {
439         DelegateBo secondaryDelegate = null;
440         RoleBo roleBo = getRoleBo(roleId);
441         for (DelegateBo delegate : roleDelegates) {
442             if (isDelegationSecondary(delegate.getDelegationTypeCode())) {
443                 secondaryDelegate = delegate;
444             }
445         }
446         if (secondaryDelegate == null) {
447             secondaryDelegate = new DelegateBo();
448             secondaryDelegate.setRoleId(roleId);
449             secondaryDelegate.setDelegationId(getNewDelegationId());
450             secondaryDelegate.setDelegationTypeCode(DelegationType.PRIMARY.getCode());
451             secondaryDelegate.setKimTypeId(roleBo.getKimTypeId());
452         }
453         return secondaryDelegate;
454     }
455 
456     protected DelegateBo getPrimaryDelegation(String roleId, List<DelegateBo> roleDelegates) {
457         DelegateBo primaryDelegate = null;
458         RoleBo roleBo = getRoleBo(roleId);
459         for (DelegateBo delegate : roleDelegates) {
460             if (isDelegationPrimary(delegate.getDelegationTypeCode())) {
461                 primaryDelegate = delegate;
462             }
463         }
464         if (primaryDelegate == null) {
465             primaryDelegate = new DelegateBo();
466             primaryDelegate.setRoleId(roleId);
467             primaryDelegate.setDelegationId(getNewDelegationId());
468             primaryDelegate.setDelegationTypeCode(DelegationType.PRIMARY.getCode());
469             primaryDelegate.setKimTypeId(roleBo.getKimTypeId());
470         }
471         return primaryDelegate;
472     }
473 
474     protected RoleMemberBo matchingMemberRecord(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
475         for (RoleMemberBo rm : roleMembers) {
476             if (doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
477                 return rm;
478             }
479         }
480         return null;
481     }
482 
483     protected boolean isDelegationPrimary(String delegationTypeCode) {
484         return DelegationType.PRIMARY.getCode().equals(delegationTypeCode);
485     }
486 
487     protected boolean isDelegationSecondary(String delegationTypeCode) {
488         return DelegationType.PRIMARY.getCode().equals(delegationTypeCode);
489     }
490 
491 
492     private List<DelegateBo> getRoleDelegations(String roleId) {
493         if (roleId == null) {
494             return new ArrayList<DelegateBo>();
495         }
496         return getStoredDelegationImplsForRoleIds(Collections.singletonList(roleId));
497 
498     }
499 
500     protected RoleBo getRoleBoByName(String namespaceCode, String roleName) {
501         if (StringUtils.isBlank(namespaceCode)
502                 || StringUtils.isBlank(roleName)) {
503             return null;
504         }
505         Map<String, String> criteria = new HashMap<String, String>();
506         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
507         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
508         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
509         // while this is not actually the primary key - there will be at most one row with these criteria
510         return getBusinessObjectService().findByPrimaryKey(RoleBo.class, criteria);
511     }
512 
513 	protected boolean doAnyMemberRecordsMatchByExactQualifier( RoleBo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier ) {
514 		if(CollectionUtils.isNotEmpty(getRoleMembersByExactQualifierMatch(role, memberId, daoActionToTake, qualifier))) {
515 			return true;
516 		}
517 
518 		return false;
519 	}
520 	
521 	protected List<RoleMemberBo> getRoleMembersByExactQualifierMatch(RoleBo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier) {
522 		List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
523 		RoleTypeService roleTypeService = getRoleTypeService( role.getId() );
524 		if(roleTypeService != null) {
525     		List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
526     		if(CollectionUtils.isNotEmpty(attributesForExactMatch)) {
527     			switch (daoActionToTake) {
528 	    			case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : // Search for group role members only.
529 	        			rms = getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(role.getId()), Collections.singletonList(memberId), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
530 	    				break;
531 	    			case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : // Search for principal role members only.
532 	        			rms = getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collections.singletonList(role.getId()), memberId, populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
533 	    				break;
534 	    			case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : // Search for roles as role members only.
535 	    				List<RoleMemberBo> allRoleMembers = getStoredRoleMembershipsForRoleIdsAsMembers(Collections.singletonList(role.getId()), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
536 	        			for(RoleMemberBo rm : allRoleMembers) {
537 	        				if ( rm.getMemberId().equals(memberId) ) { 
538 	        					rms.add(rm);
539 	        				}
540 	        			}
541 	        			break;	    				
542 	    			default : // The daoActionToTake parameter is invalid; throw an exception.
543 	    				throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
544     			}
545     			
546     		} 
547 		}
548 		return rms;
549 	}
550     
551     protected boolean doAnyMemberRecordsMatch(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
552         for (RoleMemberBo rm : roleMembers) {
553             if (doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
554                 return true;
555             }
556         }
557         return false;
558     }
559 
560     protected boolean doesMemberMatch(RoleMemberBo roleMember, String memberId, String memberTypeCode, Map<String, String> qualifier) {
561         if (roleMember.getMemberId().equals(memberId) && roleMember.getMemberTypeCode().equals(memberTypeCode)) {
562             // member ID/type match
563             Map<String, String> roleQualifier = roleMember.getAttributes();
564             if ((qualifier == null || qualifier.isEmpty())
565                     && (roleQualifier == null || roleQualifier.isEmpty())) {
566                 return true; // blank qualifier match
567             } else {
568                 if (qualifier != null && roleQualifier != null && qualifier.equals(roleQualifier)) {
569                     return true; // qualifier match
570                 }
571             }
572         }
573         return false;
574     }
575     
576     /**
577      * Retrieves the role type service associated with the given role ID
578      *
579      * @param roleId the role ID to get the role type service for
580      * @return the Role Type Service
581      */
582     protected RoleTypeService getRoleTypeService(String roleId) {
583         RoleBo roleBo = getRoleBo(roleId);
584         KimType roleType = KimTypeBo.to(roleBo.getKimRoleType());
585         if (roleType != null) {
586             return getRoleTypeService(roleType);
587         }
588         return null;
589     }
590 
591     protected RoleTypeService getRoleTypeService(KimType typeInfo) {
592         String serviceName = typeInfo.getServiceName();
593         if (serviceName != null) {
594             try {
595                 KimTypeService service = (KimTypeService) KIMServiceLocatorInternal.getService(serviceName);
596                 if (service != null && service instanceof RoleTypeService) {
597                     return (RoleTypeService) service;
598                 }
599                 return (RoleTypeService) KIMServiceLocatorInternal.getService("kimNoMembersRoleTypeService");
600             } catch (Exception ex) {
601                 LOG.error("Unable to find role type service with name: " + serviceName, ex);
602                 return (RoleTypeService) KIMServiceLocatorInternal.getService("kimNoMembersRoleTypeService");
603             }
604         }
605         return null;
606     }
607     
608     protected Map<String, String> populateQualifiersForExactMatch(Map<String, String> defaultQualification, List<String> attributes) {
609         Map<String,String> qualifiersForExactMatch = new HashMap<String,String>();
610         if (defaultQualification != null && CollectionUtils.isNotEmpty(defaultQualification.keySet())) {
611             for (String attributeName : attributes) {
612                 if (StringUtils.isNotEmpty(defaultQualification.get(attributeName))) {
613                     qualifiersForExactMatch.put(attributeName, defaultQualification.get(attributeName));
614                 }
615             }
616         }
617         return qualifiersForExactMatch;
618     }
619 
620     /**
621      * This method tests to see if assigning a roleBo to another roleBo will create a circular reference.
622      * The Role is checked to see if it is a member (direct or nested) of the roleBo to be assigned as a member.
623      *
624      * @param newMemberId
625      * @param roleBo
626      * @return true  - assignment is allowed, no circular reference will be created.
627      *         false - illegal assignment, it will create a circular membership
628      */
629     protected boolean checkForCircularRoleMembership(String newMemberId, RoleBo roleBo) {
630         // get all nested roleBo members that are of type roleBo
631         Set<String> newRoleMemberIds = getRoleTypeRoleMemberIds(newMemberId);
632         return !newRoleMemberIds.contains(roleBo.getId());
633     }
634 
635     // TODO: pulling attribute IDs repeatedly is inefficient - consider caching the entire list as a map
636     protected String getKimAttributeId(String attributeName) {
637 
638         Map<String, Object> critieria = new HashMap<String, Object>(1);
639         critieria.put("attributeName", attributeName);
640         Collection<KimAttributeBo> defs = getBusinessObjectService().findMatching(KimAttributeBo.class, critieria);
641         String result = null;
642         if (CollectionUtils.isNotEmpty(defs)) {
643             result = defs.iterator().next().getId();
644         }
645         return result;
646     }
647 
648     protected String getNewDelegationId() {
649         SequenceAccessorService sas = getSequenceAccessorService();
650         Long nextSeq = sas.getNextAvailableSequenceNumber(
651                 KimConstants.SequenceNames.KRIM_DLGN_ID_S,
652                 DelegateBo.class);
653         return nextSeq.toString();
654     }
655 
656     protected String getNewAttributeDataId() {
657         SequenceAccessorService sas = getSequenceAccessorService();
658         Long nextSeq = sas.getNextAvailableSequenceNumber(
659                 KimConstants.SequenceNames.KRIM_ATTR_DATA_ID_S,
660                 RoleMemberAttributeDataBo.class);
661         return nextSeq.toString();
662     }
663 
664     protected String getNewDelegationMemberId() {
665         SequenceAccessorService sas = getSequenceAccessorService();
666         Long nextSeq = sas.getNextAvailableSequenceNumber(
667                 KimConstants.SequenceNames.KRIM_DLGN_MBR_ID_S,
668                 DelegateBo.class);
669         return nextSeq.toString();
670     }
671 
672     protected BusinessObjectService getBusinessObjectService() {
673         if (businessObjectService == null) {
674             businessObjectService = KRADServiceLocator.getBusinessObjectService();
675         }
676         return businessObjectService;
677     }
678 
679     /**
680      * @return the lookupService
681      */
682     protected LookupService getLookupService() {
683         if (lookupService == null) {
684             lookupService = KRADServiceLocatorWeb.getLookupService();
685         }
686         return lookupService;
687     }
688 
689     protected IdentityService getIdentityService() {
690         if (identityService == null) {
691             identityService = KimApiServiceLocator.getIdentityService();
692         }
693 
694         return identityService;
695     }
696     
697     protected GroupService getGroupService() {
698         if (groupService == null) {
699             groupService = KimApiServiceLocator.getGroupService();
700         }
701 
702         return groupService;
703     }
704 
705     protected SequenceAccessorService getSequenceAccessorService() {
706         if (sequenceAccessorService == null) {
707             sequenceAccessorService = KRADServiceLocator.getSequenceAccessorService();
708         }
709         return sequenceAccessorService;
710     }
711 
712     protected ResponsibilityInternalService getResponsibilityInternalService() {
713         if (responsibilityInternalService == null) {
714             responsibilityInternalService = KIMServiceLocatorInternal.getResponsibilityInternalService();
715         }
716         return responsibilityInternalService;
717     }
718 
719     /**
720      * @return the roleDao
721      */
722     public RoleDao getRoleDao() {
723         return this.roleDao;
724     }
725 
726     /**
727      * @param roleDao the roleDao to set
728      */
729     public void setRoleDao(RoleDao roleDao) {
730         this.roleDao = roleDao;
731     }
732 
733 }