View Javadoc
1   /**
2    * Copyright 2005-2014 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 static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
19  
20  import java.sql.Timestamp;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Date;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import javax.jws.WebParam;
33  import javax.sql.DataSource;
34  import javax.xml.namespace.QName;
35  
36  import org.apache.commons.collections.CollectionUtils;
37  import org.apache.commons.lang.StringUtils;
38  import org.apache.commons.lang.exception.ExceptionUtils;
39  import org.apache.log4j.Logger;
40  import org.joda.time.DateTime;
41  import org.kuali.rice.core.api.CoreConstants;
42  import org.kuali.rice.core.api.cache.CacheKeyUtils;
43  import org.kuali.rice.core.api.criteria.QueryByCriteria;
44  import org.kuali.rice.core.api.criteria.QueryResults;
45  import org.kuali.rice.core.api.delegation.DelegationType;
46  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
47  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
48  import org.kuali.rice.core.api.membership.MemberType;
49  import org.kuali.rice.core.api.mo.ModelObjectUtils;
50  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
51  import org.kuali.rice.core.api.util.VersionHelper;
52  import org.kuali.rice.kim.api.KimConstants;
53  import org.kuali.rice.kim.api.common.delegate.DelegateMember;
54  import org.kuali.rice.kim.api.common.delegate.DelegateType;
55  import org.kuali.rice.kim.api.identity.principal.Principal;
56  import org.kuali.rice.kim.api.role.DelegateMemberQueryResults;
57  import org.kuali.rice.kim.api.role.Role;
58  import org.kuali.rice.kim.api.role.RoleMember;
59  import org.kuali.rice.kim.api.role.RoleMemberQueryResults;
60  import org.kuali.rice.kim.api.role.RoleMembership;
61  import org.kuali.rice.kim.api.role.RoleMembershipQueryResults;
62  import org.kuali.rice.kim.api.role.RoleQueryResults;
63  import org.kuali.rice.kim.api.role.RoleResponsibility;
64  import org.kuali.rice.kim.api.role.RoleResponsibilityAction;
65  import org.kuali.rice.kim.api.role.RoleService;
66  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
67  import org.kuali.rice.kim.api.type.KimType;
68  import org.kuali.rice.kim.api.type.KimTypeUtils;
69  import org.kuali.rice.kim.framework.common.delegate.DelegationTypeService;
70  import org.kuali.rice.kim.framework.role.RoleTypeService;
71  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
72  import org.kuali.rice.kim.framework.type.KimTypeService;
73  import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
74  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
75  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberAttributeDataBo;
76  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
77  import org.kuali.rice.kim.impl.common.delegate.DelegateTypeBo;
78  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
79  import org.kuali.rice.kim.impl.type.KimTypeBo;
80  import org.kuali.rice.krad.data.platform.MaxValueIncrementerFactory;
81  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
82  import org.kuali.rice.ksb.api.bus.Endpoint;
83  import org.kuali.rice.ksb.api.bus.ServiceBus;
84  import org.springframework.cache.Cache;
85  import org.springframework.cache.CacheManager;
86  import org.springframework.cache.support.NoOpCacheManager;
87  import org.springframework.util.LinkedMultiValueMap;
88  import org.springframework.util.MultiValueMap;
89  
90  public class RoleServiceImpl extends RoleServiceBase implements RoleService {
91      private static final Logger LOG = Logger.getLogger(RoleServiceImpl.class);
92  
93      private static final Map<String, RoleDaoAction> memberTypeToRoleDaoActionMap = populateMemberTypeToRoleDaoActionMap();
94  
95      private static Map<String, RoleDaoAction> populateMemberTypeToRoleDaoActionMap() {
96          Map<String, RoleDaoAction> map = new HashMap<String, RoleDaoAction>();
97          map.put(MemberType.GROUP.getCode(), RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS);
98          map.put(MemberType.PRINCIPAL.getCode(), RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS);
99          map.put(MemberType.ROLE.getCode(), RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS);
100         return Collections.unmodifiableMap(map);
101     }
102 
103     private RoleService proxiedRoleService;
104     private CacheManager cacheManager;
105 
106     public RoleServiceImpl() {
107         this.cacheManager = new NoOpCacheManager();
108     }
109 
110     @Override
111     public Role createRole(final Role role) throws RiceIllegalArgumentException, RiceIllegalStateException {
112         incomingParamCheck(role, "role");
113 
114         if (StringUtils.isNotBlank(role.getId()) && getRole(role.getId()) != null) {
115             throw new RiceIllegalStateException("the role to create already exists: " + role);
116         }
117         RoleBo bo = RoleBo.from(role);
118         return RoleBo.to(getDataObjectService().save(bo));
119     }
120 
121     @Override
122     public Role updateRole(final Role role) throws RiceIllegalArgumentException, RiceIllegalStateException {
123         incomingParamCheck(role, "role");
124 
125         RoleBoLite originalRole = getRoleBoLite(role.getId());
126         if (StringUtils.isBlank(role.getId()) || originalRole == null) {
127             throw new RiceIllegalStateException("the role does not exist: " + role);
128         }
129 
130         RoleBo bo = RoleBo.from(role);
131 
132         RoleBo updatedRole = getDataObjectService().save(bo);
133         if (originalRole.isActive()
134                 && !updatedRole.isActive()) {
135             KimImplServiceLocator.getRoleInternalService().roleInactivated(updatedRole.getId());
136         }
137         return RoleBo.to(updatedRole);
138     }
139 
140     /**
141      * This method tests to see if assigning a roleBo to another roleBo will create a circular reference.
142      * The Role is checked to see if it is a member (direct or nested) of the roleBo to be assigned as a member.
143      *
144      * @param newMemberId
145      * @param roleBo
146      * @return true  - assignment is allowed, no circular reference will be created.
147      *         false - illegal assignment, it will create a circular membership
148      */
149     protected boolean checkForCircularRoleMembership(String newMemberId, RoleBo roleBo) {
150         // get all nested roleBo members that are of type roleBo
151         Set<String> newRoleMemberIds = getRoleTypeRoleMemberIds(newMemberId);
152         return !newRoleMemberIds.contains(roleBo.getId());
153     }
154 
155     protected RoleMember findRoleMember(String roleMemberId) {
156         final List<RoleMember> roleMembers = findRoleMembers(QueryByCriteria.Builder.fromPredicates(equal(KimConstants.PrimaryKeyConstants.ID, roleMemberId))).getResults();
157         if (roleMembers != null && !roleMembers.isEmpty()) {
158             return roleMembers.get(0);
159         }
160         return null;
161     }
162 
163     @Override
164     public RoleMemberQueryResults findRoleMembers(QueryByCriteria queryByCriteria) throws RiceIllegalStateException {
165         incomingParamCheck(queryByCriteria, "queryByCriteria");
166 
167         QueryResults<RoleMemberBo> results = getDataObjectService().findMatching(RoleMemberBo.class,
168                 AttributeTransform.getInstance().apply(queryByCriteria));
169 
170         RoleMemberQueryResults.Builder builder = RoleMemberQueryResults.Builder.create();
171         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
172         builder.setTotalRowCount(results.getTotalRowCount());
173 
174         final List<RoleMember.Builder> ims = new ArrayList<RoleMember.Builder>();
175         for (RoleMemberBo bo : results.getResults()) {
176             ims.add(RoleMember.Builder.create(bo));
177         }
178 
179         builder.setResults(ims);
180         return builder.build();
181 
182     }
183 
184 
185 
186     @Override
187     public Set<String> getRoleTypeRoleMemberIds(String roleId) throws RiceIllegalArgumentException  {
188         incomingParamCheck(roleId, "roleId");
189 
190         Set<String> results = new HashSet<String>();
191         getNestedRoleTypeMemberIds(roleId, results);
192         return Collections.unmodifiableSet(results);
193     }
194 
195     @Override
196     public List<String> getMemberParentRoleIds(String memberType, String memberId) throws RiceIllegalStateException  {
197         incomingParamCheck(memberType, "memberType");
198         incomingParamCheck(memberId, "memberId");
199 
200         List<RoleMemberBo> parentRoleMembers = getRoleMembershipsForMemberId(memberType, memberId,
201                 Collections.<String, String>emptyMap());
202 
203         List<String> parentRoleIds = new ArrayList<String>(parentRoleMembers.size());
204         for (RoleMemberBo parentRoleMember : parentRoleMembers) {
205             parentRoleIds.add(parentRoleMember.getRoleId());
206         }
207 
208         return parentRoleIds;
209     }
210 
211     @Override
212     public List<RoleResponsibilityAction> getRoleMemberResponsibilityActions(String roleMemberId) throws RiceIllegalStateException  {
213         incomingParamCheck(roleMemberId, "roleMemberId");
214 
215         QueryResults<RoleResponsibilityActionBo> responsibilityActionBoList = getDataObjectService().findMatching(RoleResponsibilityActionBo.class, QueryByCriteria.Builder.forAttribute(KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMemberId).build());
216 
217         List<RoleResponsibilityAction> roleResponsibilityActionsList = new ArrayList<RoleResponsibilityAction>();
218         for (RoleResponsibilityActionBo roleResponsibilityActionBo : responsibilityActionBoList.getResults()) {
219             RoleResponsibilityAction roleResponsibility = RoleResponsibilityActionBo.to(roleResponsibilityActionBo);
220             roleResponsibilityActionsList.add(roleResponsibility);
221         }
222         return roleResponsibilityActionsList;
223     }
224 
225     @Override
226     public DelegateMemberQueryResults findDelegateMembers(QueryByCriteria queryByCriteria) throws RiceIllegalStateException  {
227         incomingParamCheck(queryByCriteria, "queryByCriteria");
228 
229         QueryResults<DelegateMemberBo> results = getDataObjectService().findMatching(DelegateMemberBo.class,
230                 AttributeTransform.getInstance().apply(queryByCriteria));
231 
232         DelegateMemberQueryResults.Builder builder = DelegateMemberQueryResults.Builder.create();
233         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
234         builder.setTotalRowCount(results.getTotalRowCount());
235 
236         final List<DelegateMember.Builder> ims = new ArrayList<DelegateMember.Builder>();
237         for (DelegateMemberBo bo : results.getResults()) {
238             ims.add(DelegateMember.Builder.create(bo));
239         }
240 
241         builder.setResults(ims);
242         return builder.build();
243     }
244 
245     @Override
246     public Role getRole(String roleId) throws RiceIllegalStateException  {
247         incomingParamCheck(roleId, "roleId");
248         return loadRole(roleId);
249     }
250 
251     /**
252      * Loads the role with the given id, leveraging the cache where possible and querying the database
253      * if role not already in the cache. If the role is not in the cache, then it will be placed in
254      * the cache once it is loaded.
255      */
256     protected Role loadRole(String roleId) {
257         Role role = getRoleFromCache(roleId);
258         if (role == null) {
259             RoleBoLite roleBo = getRoleBoLite(roleId);
260             if (roleBo != null) {
261                 role = RoleBoLite.to(roleBo);
262                 putRoleInCache(role);
263             }
264         }
265         return role;
266     }
267 
268     protected Role getRoleFromCache(String id) {
269         Cache cache = cacheManager.getCache(Role.Cache.NAME);
270         Cache.ValueWrapper cachedValue = cache.get("id=" + id);
271         if (cachedValue != null) {
272             return (Role)cachedValue.get();
273         }
274         return null;
275     }
276 
277     protected Role getRoleFromCache(String namespaceCode, String name) {
278         Cache cache = cacheManager.getCache(Role.Cache.NAME);
279         Cache.ValueWrapper cachedValue = cache.get("namespaceCode=" + namespaceCode + "|name=" + name);
280         if (cachedValue != null) {
281             return (Role)cachedValue.get();
282         }
283         return null;
284     }
285 
286     protected void putRoleInCache(Role role) {
287         if (role != null) {
288             Cache cache = cacheManager.getCache(Role.Cache.NAME);
289             String idKey = "id=" + role.getId();
290             String nameKey = "namespaceCode=" + role.getNamespaceCode() + "|name=" + role.getName();
291             cache.put(idKey, role);
292             cache.put(nameKey, role);
293         }
294     }
295 
296     protected Map<String, RoleBoLite> getRoleBoLiteMap(Collection<String> roleIds) {
297         Map<String, RoleBoLite> result;
298         // check for a non-null result in the cache, return it if found
299         if (roleIds.size() == 1) {
300             String roleId = roleIds.iterator().next();
301             RoleBoLite bo = getRoleBoLite(roleId);
302             if (bo == null) {
303                 return Collections.<String, RoleBoLite>emptyMap();
304             }
305             result = bo.isActive() ? Collections.singletonMap(roleId, bo) :  Collections.<String, RoleBoLite>emptyMap();
306         } else {
307             result = new HashMap<String, RoleBoLite>(roleIds.size());
308             for (String roleId : roleIds) {
309                 RoleBoLite bo = getRoleBoLite(roleId);
310                 if (bo != null && bo.isActive()) {
311                     result.put(roleId, bo);
312                 }
313             }
314         }
315         return result;
316     }
317 
318     @Override
319     public List<Role> getRoles(List<String> roleIds) throws RiceIllegalStateException  {
320         if (CollectionUtils.isEmpty(roleIds)) {
321             throw new RiceIllegalArgumentException("roleIds is null or empty");
322         }
323         return Collections.unmodifiableList(loadRoles(roleIds));
324     }
325 
326     /**
327      * Loads the roles with the given ids, leveraging the cache where possible and querying the database
328      * for role ids not already in the cache. If the role is not in the cache, then it will be placed in
329      * the cache once it is loaded.
330      */
331     protected List<Role> loadRoles(List<String> roleIds) {
332         List<String> remainingRoleIds = new ArrayList<String>();
333         Map<String, Role> roleMap = new HashMap<String, Role>(roleIds.size());
334         for (String roleId : roleIds) {
335             Role role = getRoleFromCache(roleId);
336             if (role != null) {
337                 roleMap.put(roleId, role);
338             } else {
339                 remainingRoleIds.add(roleId);
340             }
341         }
342         if (!remainingRoleIds.isEmpty()) {
343             Map<String, RoleBoLite> roleBoMap = getRoleBoLiteMap(remainingRoleIds);
344             for (String roleId : roleBoMap.keySet()) {
345                 RoleBoLite roleBo = roleBoMap.get(roleId);
346                 if (roleBo != null) {
347                     Role role = RoleBoLite.to(roleBo);
348                     roleMap.put(roleId, role);
349                     putRoleInCache(role);
350                 }
351             }
352         }
353         List<Role> roles = new ArrayList<Role>(roleMap.values());
354         return roles;
355     }
356 
357     @Override
358     public Role getRoleByNamespaceCodeAndName(String namespaceCode, String roleName) throws RiceIllegalStateException  {
359         incomingParamCheck(namespaceCode, "namespaceCode");
360         incomingParamCheck(roleName, "roleName");
361         return loadRoleByName(namespaceCode, roleName);
362     }
363 
364     /**
365      * Loads the role with the given name, leveraging the cache where possible and querying the database
366      * if role not already in the cache. If the role is not in the cache, then it will be placed in
367      * the cache once it is loaded.
368      */
369     protected Role loadRoleByName(String namespaceCode, String roleName) {
370         Role role = getRoleFromCache(namespaceCode, roleName);
371         if (role == null) {
372             RoleBoLite roleBo = getRoleBoLiteByName(namespaceCode, roleName);
373             if (roleBo != null) {
374                 role = getRoleFromCache(roleBo.getId());
375                 if (role == null) {
376                     role = RoleBoLite.to(roleBo);
377                 }
378                 putRoleInCache(role);
379             }
380         }
381         return role;
382     }
383 
384     @Override
385     public String getRoleIdByNamespaceCodeAndName(String namespaceCode, String roleName) throws RiceIllegalStateException  {
386         incomingParamCheck(namespaceCode, "namespaceCode");
387         incomingParamCheck(roleName, "roleName");
388 
389         Role role = getRoleByNamespaceCodeAndName(namespaceCode, roleName);
390         if (role != null) {
391             return role.getId();
392         } else {
393             return null;
394         }
395     }
396 
397     @Override
398     public boolean isRoleActive(String roleId) throws RiceIllegalStateException  {
399         incomingParamCheck(roleId, "roleId");
400         Role role = getRole(roleId);
401         return role != null && role.isActive();
402     }
403 
404     @Override
405     public List<Map<String, String>> getRoleQualifersForPrincipalByRoleIds(String principalId, List<String> roleIds,
406             Map<String, String> qualification) throws RiceIllegalStateException  {
407         incomingParamCheck(principalId, "principalId");
408         incomingParamCheck(roleIds, "roleIds");
409 
410         List<Map<String, String>> results = new ArrayList<Map<String, String>>();
411 
412         List<RoleMemberBo> roleMemberBoList = getStoredRoleMembersUsingExactMatchOnQualification(principalId, null,
413                 roleIds, qualification);
414 
415         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
416         for (RoleMemberBo roleMemberBo : roleMemberBoList) {
417             // gather up the qualifier sets and the service they go with
418             if (MemberType.PRINCIPAL.equals(roleMemberBo.getType())) {
419                 RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
420                 if (roleTypeService != null) {
421                     List<RoleMembership> las = roleIdToMembershipMap.get(roleMemberBo.getRoleId());
422                     if (las == null) {
423                         las = new ArrayList<RoleMembership>();
424                         roleIdToMembershipMap.put(roleMemberBo.getRoleId(), las);
425                     }
426                     RoleMembership mi = RoleMembership.Builder.create(
427                             roleMemberBo.getRoleId(),
428                             roleMemberBo.getId(),
429                             roleMemberBo.getMemberId(),
430                             roleMemberBo.getType(),
431                             roleMemberBo.getAttributes()).build();
432 
433                     las.add(mi);
434                 } else {
435                     results.add(roleMemberBo.getAttributes());
436                 }
437             }
438         }
439         for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
440             RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
441             //it is possible that the the roleTypeService is coming from a remote application
442             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
443             try {
444                 List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification, entry.getValue());
445                 for (RoleMembership rmi : matchingMembers) {
446                     results.add(rmi.getQualifier());
447                 }
448             } catch (Exception ex) {
449                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(), ex);
450             }
451         }
452         return Collections.unmodifiableList(results);
453     }
454 
455     @Override
456     public List<Map<String, String>> getRoleQualifersForPrincipalByNamespaceAndRolename(String principalId,
457             String namespaceCode, String roleName, Map<String, String> qualification)
458             throws RiceIllegalStateException {
459         incomingParamCheck(principalId, "principalId");
460         incomingParamCheck(namespaceCode, "namespaceCode");
461         incomingParamCheck(roleName, "roleName");
462 
463         String roleId = getRoleIdByNamespaceCodeAndName(namespaceCode, roleName);
464         if (roleId == null) {
465             return Collections.emptyList();
466         }
467         return getNestedRoleQualifiersForPrincipalByRoleIds(principalId, Collections.singletonList(roleId),
468                 qualification);
469     }
470 
471     @Override
472     public List<Map<String, String>> getNestedRoleQualifersForPrincipalByNamespaceAndRolename(String principalId,
473             String namespaceCode, String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
474         incomingParamCheck(principalId, "principalId");
475         incomingParamCheck(namespaceCode, "namespaceCode");
476         incomingParamCheck(roleName, "roleName");
477 
478         String roleId = getRoleIdByNamespaceCodeAndName(namespaceCode, roleName);
479         if (roleId == null) {
480             return new ArrayList<Map<String, String>>(0);
481         }
482         return getNestedRoleQualifiersForPrincipalByRoleIds(principalId, Collections.singletonList(roleId),
483                 qualification);
484     }
485 
486     @Override
487     public List<Map<String, String>> getNestedRoleQualifiersForPrincipalByRoleIds(String principalId,
488             List<String> roleIds, Map<String, String> qualification) throws RiceIllegalStateException {
489         incomingParamCheck(principalId, "principalId");
490         incomingParamCheck(roleIds, "roleIds");
491 
492 
493         List<Map<String, String>> results = new ArrayList<Map<String, String>>();
494 
495         Map<String, RoleBoLite> roleBosById = getRoleBoLiteMap(roleIds);
496 
497         // get the person's groups
498         List<String> groupIds = getGroupService().getGroupIdsByPrincipalId(principalId);
499         List<RoleMemberBo> roleMemberBos = getStoredRoleMembersUsingExactMatchOnQualification(principalId, groupIds, roleIds, qualification);
500 
501         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
502         for (RoleMemberBo roleMemberBo : roleMemberBos) {
503             RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
504             // gather up the qualifier sets and the service they go with
505             if (MemberType.PRINCIPAL.equals(roleMemberBo.getType())
506                     || MemberType.GROUP.equals(roleMemberBo.getType())) {
507                 if (roleTypeService != null) {
508                     List<RoleMembership> las = roleIdToMembershipMap.get(roleMemberBo.getRoleId());
509                     if (las == null) {
510                         las = new ArrayList<RoleMembership>();
511                         roleIdToMembershipMap.put(roleMemberBo.getRoleId(), las);
512                     }
513                     RoleMembership mi = RoleMembership.Builder.create(
514                             roleMemberBo.getRoleId(),
515                             roleMemberBo.getId(),
516                             roleMemberBo.getMemberId(),
517                             roleMemberBo.getType(),
518                             roleMemberBo.getAttributes()).build();
519 
520                     las.add(mi);
521                 } else {
522                     results.add(roleMemberBo.getAttributes());
523                 }
524             } else if (MemberType.ROLE.equals(roleMemberBo.getType())) {
525                 // find out if the user has the role
526                 // need to convert qualification using this role's service
527                 Map<String, String> nestedQualification = qualification;
528                 if (roleTypeService != null) {
529                     RoleBoLite roleBo = roleBosById.get(roleMemberBo.getRoleId());
530                     // pulling from here as the nested roleBo is not necessarily (and likely is not)
531                     // in the roleBosById Map created earlier
532                     RoleBoLite nestedRole = getRoleBoLite(roleMemberBo.getMemberId());
533                     //it is possible that the the roleTypeService is coming from a remote application
534                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
535                     try {
536                         nestedQualification = getNestedQualification(nestedRole,
537                                 roleBo.getNamespaceCode(), roleBo.getName(), nestedRole.getNamespaceCode(),
538                                 nestedRole.getName(), qualification, roleMemberBo.getAttributes());
539                     } catch (Exception ex) {
540                         LOG.warn("Not able to retrieve RoleTypeService from remote system for roleBo Id: " + roleBo.getId(), ex);
541                     }
542                 }
543                 List<String> nestedRoleId = new ArrayList<String>(1);
544                 nestedRoleId.add(roleMemberBo.getMemberId());
545                 // if the user has the given role, add the qualifier the *nested role* has with the
546                 // originally queries role
547                 if (this.getProxiedRoleService().principalHasRole(principalId, nestedRoleId, nestedQualification, false)) {
548                     results.add(roleMemberBo.getAttributes());
549                 }
550             }
551         }
552         for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
553             RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
554             //it is possible that the the roleTypeService is coming from a remote
555             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
556             try {
557                 List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification,
558                         entry.getValue());
559                 for (RoleMembership roleMembership : matchingMembers) {
560                     results.add(roleMembership.getQualifier());
561                 }
562             } catch (Exception ex) {
563                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(), ex);
564             }
565         }
566         return Collections.unmodifiableList(results);
567     }
568 
569     @Override
570     public List<RoleMembership> getRoleMembers(List<String> roleIds, Map<String, String> qualification) throws RiceIllegalStateException {
571         incomingParamCheck(roleIds, "roleIds");
572 
573         Set<String> foundRoleTypeMembers = new HashSet<String>();
574         List<RoleMembership> roleMembers = getRoleMembers(roleIds, qualification, true, foundRoleTypeMembers);
575 
576         return Collections.unmodifiableList(roleMembers);
577     }
578 
579     @Override
580     public Collection<String> getRoleMemberPrincipalIds(String namespaceCode, String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
581         incomingParamCheck(namespaceCode, "namespaceCode");
582         incomingParamCheck(roleName, "roleName");
583 
584         Set<String> principalIds = new HashSet<String>();
585         Set<String> foundRoleTypeMembers = new HashSet<String>();
586         List<String> roleIds = Collections.singletonList(getRoleIdByNamespaceCodeAndName(namespaceCode, roleName));
587         for (RoleMembership roleMembership : getRoleMembers(roleIds, qualification, false, foundRoleTypeMembers)) {
588             if (MemberType.GROUP.equals(roleMembership.getType())) {
589                 principalIds.addAll(getGroupService().getMemberPrincipalIds(roleMembership.getMemberId()));
590             } else {
591                 principalIds.add(roleMembership.getMemberId());
592             }
593         }
594 
595         return Collections.unmodifiableSet(principalIds);
596     }
597 
598     @Override
599     public boolean principalHasRole(String principalId, List<String> roleIds, Map<String, String> qualification) throws RiceIllegalStateException {
600 
601         if ( LOG.isDebugEnabled() ) {
602             logPrincipalHasRoleCheck(principalId, roleIds, qualification);
603         }
604 
605         boolean hasRole = this.getProxiedRoleService().principalHasRole(principalId, roleIds, qualification, true);
606 
607         if ( LOG.isDebugEnabled() ) {
608             LOG.debug( "Result: " + hasRole );
609         }
610 
611         return hasRole;
612     }
613 
614     @Override
615     public List<String> getPrincipalIdSubListWithRole(List<String> principalIds,
616             String roleNamespaceCode, String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
617         incomingParamCheck(principalIds, "principalIds");
618         incomingParamCheck(roleNamespaceCode, "roleNamespaceCode");
619         incomingParamCheck(roleName, "roleName");
620 
621         List<String> subList = new ArrayList<String>();
622         RoleBoLite role = getRoleBoLiteByName(roleNamespaceCode, roleName);
623         for (String principalId : principalIds) {
624             if (this.getProxiedRoleService().principalHasRole(principalId, Collections.singletonList(role.getId()), qualification)) {
625                 subList.add(principalId);
626             }
627         }
628         return Collections.unmodifiableList(subList);
629     }
630 
631     @Override
632     public RoleQueryResults findRoles(QueryByCriteria queryByCriteria) throws RiceIllegalStateException {
633         incomingParamCheck(queryByCriteria, "queryByCriteria");
634 
635         QueryResults<RoleBoLite> results = getDataObjectService().findMatching(RoleBoLite.class, queryByCriteria);
636 
637         RoleQueryResults.Builder builder = RoleQueryResults.Builder.create();
638         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
639         builder.setTotalRowCount(results.getTotalRowCount());
640 
641         final List<Role.Builder> ims = new ArrayList<Role.Builder>();
642         for (RoleBoLite bo : results.getResults()) {
643             ims.add(Role.Builder.create(bo));
644         }
645 
646         builder.setResults(ims);
647         return builder.build();
648     }
649 
650     @Override
651     public List<RoleMembership> getFirstLevelRoleMembers(List<String> roleIds) throws RiceIllegalStateException {
652         incomingParamCheck(roleIds, "roleIds");
653         if (roleIds.isEmpty()) {
654             return Collections.emptyList();
655         }
656 
657         List<RoleMemberBo> roleMemberBoList = getStoredRoleMembersForRoleIds(roleIds, null, null);
658         List<RoleMembership> roleMemberships = new ArrayList<RoleMembership>();
659         for (RoleMemberBo roleMemberBo : roleMemberBoList) {
660             RoleMembership roleMembeship = RoleMembership.Builder.create(
661                     roleMemberBo.getRoleId(),
662                     roleMemberBo.getId(),
663                     roleMemberBo.getMemberId(),
664                     roleMemberBo.getType(),
665                     roleMemberBo.getAttributes()).build();
666             roleMemberships.add(roleMembeship);
667         }
668         return Collections.unmodifiableList(roleMemberships);
669     }
670 
671     @Override
672     public RoleMembershipQueryResults findRoleMemberships( QueryByCriteria queryByCriteria) throws RiceIllegalStateException {
673         incomingParamCheck(queryByCriteria, "queryByCriteria");
674 
675         QueryResults<RoleMemberBo> results = getDataObjectService().findMatching(RoleMemberBo.class,
676                 AttributeTransform.getInstance().apply(queryByCriteria));
677 
678         RoleMembershipQueryResults.Builder builder = RoleMembershipQueryResults.Builder.create();
679         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
680         builder.setTotalRowCount(results.getTotalRowCount());
681 
682         final List<RoleMembership.Builder> ims = new ArrayList<RoleMembership.Builder>();
683         for (RoleMemberBo bo : results.getResults()) {
684             RoleMembership.Builder roleMembership = RoleMembership.Builder.create(
685                     bo.getRoleId(),
686                     bo.getId(),
687                     bo.getMemberId(),
688                     bo.getType(),
689                     bo.getAttributes());
690             ims.add(roleMembership);
691         }
692 
693         builder.setResults(ims);
694         return builder.build();
695     }
696 
697     @Override
698     public List<DelegateMember> getDelegationMembersByDelegationId(String delegationId) throws RiceIllegalStateException {
699         incomingParamCheck(delegationId, "delegationId");
700 
701         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
702         if (delegateBo == null) {return Collections.emptyList();}
703 
704         return getDelegateMembersForDelegation(delegateBo);
705     }
706 
707     @Override
708     public DelegateMember getDelegationMemberByDelegationAndMemberId(String delegationId, String memberId) throws RiceIllegalStateException {
709         incomingParamCheck(delegationId, "delegationId");
710         incomingParamCheck(memberId, "memberId");
711 
712         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
713         DelegateMemberBo delegationMember = getKimDelegationMemberImplByDelegationAndId(delegationId, memberId);
714 
715         return getDelegateCompleteInfo(delegateBo, delegationMember);
716     }
717 
718     @Override
719     public DelegateMember getDelegationMemberById(String delegationMemberId) throws RiceIllegalStateException {
720         incomingParamCheck(delegationMemberId, "delegationMemberId");
721 
722         DelegateMemberBo delegateMemberBo = getDelegateMemberBo(delegationMemberId);
723         if (delegateMemberBo == null) {
724             return null;
725         }
726 
727         DelegateTypeBo delegateBo = getKimDelegationImpl(delegateMemberBo.getDelegationId());
728 
729         return getDelegateCompleteInfo(delegateBo, delegateMemberBo);
730     }
731 
732     @Override
733     public List<RoleResponsibility> getRoleResponsibilities(String roleId) throws RiceIllegalStateException {
734         incomingParamCheck(roleId, "roleId");
735 
736         List<RoleResponsibilityBo> roleResponsibilityBos =
737                 getDataObjectService().findMatching(RoleResponsibilityBo.class, QueryByCriteria.Builder.forAttribute(KimConstants.PrimaryKeyConstants.SUB_ROLE_ID, roleId).build()).getResults();
738         List<RoleResponsibility> roleResponsibilities = new ArrayList<RoleResponsibility>();
739 
740         for (RoleResponsibilityBo roleResponsibilityImpl : roleResponsibilityBos) {
741             roleResponsibilities.add(RoleResponsibilityBo.to(roleResponsibilityImpl));
742         }
743         return Collections.unmodifiableList(roleResponsibilities);
744     }
745 
746     @Override
747     public DelegateType getDelegateTypeByRoleIdAndDelegateTypeCode(String roleId, DelegationType delegationType) throws RiceIllegalStateException {
748         incomingParamCheck(roleId, "roleId");
749         incomingParamCheck(delegationType, "delegationType");
750 
751         DelegateTypeBo delegateBo = getDelegationOfType(roleId, delegationType);
752         return DelegateTypeBo.to(delegateBo);
753     }
754 
755     @Override
756     public DelegateType getDelegateTypeByDelegationId(String delegationId) throws RiceIllegalStateException {
757         incomingParamCheck(delegationId, "delegationId");
758 
759         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
760         return DelegateTypeBo.to(delegateBo);
761     }
762 
763     protected List<RoleMembership> getRoleMembers(List<String> roleIds, Map<String, String> qualification, boolean followDelegations, Set<String> foundRoleTypeMembers) {
764         List<RoleMembership> results = new ArrayList<RoleMembership>();
765         Set<String> allRoleIds = new HashSet<String>();
766         for (String roleId : roleIds) {
767             if (this.getProxiedRoleService().isRoleActive(roleId)) {
768                 allRoleIds.add(roleId);
769             }
770         }
771         // short-circuit if no roles match
772         if (allRoleIds.isEmpty()) {
773             return Collections.emptyList();
774         }
775         Set<String> matchingRoleIds = new HashSet<String>(allRoleIds.size());
776         // for efficiency, retrieve all roles and store in a map
777         Map<String, RoleBoLite> roles = getRoleBoLiteMap(allRoleIds);
778 
779         List<String> copyRoleIds = new ArrayList<String>(allRoleIds);
780         List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
781 
782         for (String roleId : allRoleIds) {
783             RoleTypeService roleTypeService = getRoleTypeService(roleId);
784             if (roleTypeService != null) {
785                 List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
786                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
787                     copyRoleIds.remove(roleId);
788                     rms.addAll(getStoredRoleMembersForRoleIds(Collections.singletonList(roleId), null, populateQualifiersForExactMatch(qualification, attributesForExactMatch)));
789                 }
790             }
791         }
792         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
793             rms.addAll(getStoredRoleMembersForRoleIds(copyRoleIds, null, null));
794         }
795 
796         // build a map of role ID to membership information
797         // this will be used for later qualification checks
798         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
799         for (RoleMemberBo roleMemberBo : rms) {
800             RoleMembership mi = RoleMembership.Builder.create(
801                     roleMemberBo.getRoleId(),
802                     roleMemberBo.getId(),
803                     roleMemberBo.getMemberId(),
804                     roleMemberBo.getType(),
805                     roleMemberBo.getAttributes()).build();
806 
807             // if the qualification check does not need to be made, just add the result
808             if ((qualification == null || qualification.isEmpty())) {
809                 if (MemberType.ROLE.equals(roleMemberBo.getType())) {
810                     // if a role member type, do a non-recursive role member check
811                     // to obtain the group and principal members of that role
812                     // given the qualification
813                     Map<String, String> nestedRoleQualification = qualification;
814                     RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
815                     if (roleTypeService != null) {
816                         // get the member role object
817                         RoleBoLite memberRole = getRoleBoLite(mi.getMemberId());
818                         nestedRoleQualification = getNestedQualification(memberRole, roles.get(
819                                 roleMemberBo.getRoleId()).getNamespaceCode(), roles.get(roleMemberBo.getRoleId())
820                                 .getName(), memberRole.getNamespaceCode(), memberRole.getName(), qualification, roleMemberBo.getAttributes());
821                     }
822                     if (this.getProxiedRoleService().isRoleActive(roleMemberBo.getRoleId())) {
823                         Collection<RoleMembership> nestedRoleMembers = getNestedRoleMembers(nestedRoleQualification, mi, foundRoleTypeMembers);
824                         if (!nestedRoleMembers.isEmpty()) {
825                             results.addAll(nestedRoleMembers);
826                             matchingRoleIds.add(roleMemberBo.getRoleId());
827                         }
828                     }
829                 } else { // not a role member type
830                     results.add(mi);
831                     matchingRoleIds.add(roleMemberBo.getRoleId());
832                 }
833                 matchingRoleIds.add(roleMemberBo.getRoleId());
834             } else {
835                 List<RoleMembership> lrmi = roleIdToMembershipMap.get(mi.getRoleId());
836                 if (lrmi == null) {
837                     lrmi = new ArrayList<RoleMembership>();
838                     roleIdToMembershipMap.put(mi.getRoleId(), lrmi);
839                 }
840                 lrmi.add(mi);
841             }
842         }
843         // if there is anything in the role to membership map, we need to check the role type services
844         // for those entries
845         if (!roleIdToMembershipMap.isEmpty()) {
846             // for each role, send in all the qualifiers for that role to the type service
847             // for evaluation, the service will return those which match
848             for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
849                 //it is possible that the the roleTypeService is coming from a remote application
850                 // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
851                 try {
852                     RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
853                     List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification,
854                             entry.getValue());
855                     // loop over the matching entries, adding them to the results
856                     for (RoleMembership roleMemberships : matchingMembers) {
857                         if (MemberType.ROLE.equals(roleMemberships.getType())) {
858                             // if a role member type, do a non-recursive role member check
859                             // to obtain the group and principal members of that role
860                             // given the qualification
861                             // get the member role object
862                             RoleBoLite memberRole = getRoleBoLite(roleMemberships.getMemberId());
863                             if (memberRole.isActive()) {
864                                 Map<String, String> nestedRoleQualification = getNestedQualification(memberRole,
865                                         roles.get(roleMemberships.getRoleId()).getNamespaceCode(), roles.get(
866                                         roleMemberships.getRoleId()).getName(), memberRole.getNamespaceCode(),
867                                         memberRole.getName(), qualification, roleMemberships.getQualifier());
868                                 Collection<RoleMembership> nestedRoleMembers = getNestedRoleMembers(nestedRoleQualification, roleMemberships, foundRoleTypeMembers);
869                                 if (!nestedRoleMembers.isEmpty()) {
870                                     results.addAll(nestedRoleMembers);
871                                     matchingRoleIds.add(roleMemberships.getRoleId());
872                                 }
873                             }
874                         } else { // not a role member
875                             results.add(roleMemberships);
876                             matchingRoleIds.add(roleMemberships.getRoleId());
877                         }
878                     }
879                 } catch (Exception ex) {
880                     LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(), ex);
881                 }
882             }
883         }
884 
885         // handle derived roles
886         for ( String roleId : allRoleIds ) {
887             RoleTypeService roleTypeService = getRoleTypeService( roleId );
888             RoleBoLite role = roles.get( roleId );
889             // check if a derived role
890             try {
891                 if ( isDerivedRoleType(roleTypeService) ) {
892                     // for each derived role, get the list of principals and groups which are in that role given the qualification (per the role type service)
893                     List<RoleMembership> roleMembers = roleTypeService.getRoleMembersFromDerivedRole(role.getNamespaceCode(), role.getName(), qualification);
894                     if ( !roleMembers.isEmpty()  ) {
895                         matchingRoleIds.add( roleId );
896                     }
897                     for ( RoleMembership rm : roleMembers ) {
898                         RoleMembership.Builder builder = RoleMembership.Builder.create(rm);
899                         builder.setRoleId(roleId);
900                         builder.setId("*");
901                         results.add(builder.build());
902                     }
903                 }
904             } catch (Exception ex) {
905                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
906             }
907         }
908 
909         if ( followDelegations && !matchingRoleIds.isEmpty() ) {
910             // we have a list of RoleMembershipInfo objects
911             // need to get delegations for distinct list of roles in that list
912             Map<String, DelegateTypeBo> delegationIdToDelegationMap = getStoredDelegationImplMapFromRoleIds(matchingRoleIds);
913             if (!delegationIdToDelegationMap.isEmpty()) {
914                 List<RoleMembership.Builder> membershipsWithDelegations =
915                         applyDelegationsToRoleMembers(results, delegationIdToDelegationMap.values(), qualification);
916                 resolveDelegationMemberRoles(membershipsWithDelegations, qualification, foundRoleTypeMembers);
917                 results = ModelObjectUtils.buildImmutableCopy(membershipsWithDelegations);
918             }
919         }
920 
921         // sort the results if a single role type service can be identified for
922         // all the matching role members
923         if ( results.size() > 1 ) {
924             // if a single role: easy case
925             if ( matchingRoleIds.size() == 1 ) {
926                 String roleId = matchingRoleIds.iterator().next();
927                 RoleTypeService roleTypeService = getRoleTypeService( roleId );
928                 //it is possible that the the roleTypeService is coming from a remote application
929                 // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
930                 try {
931                     if ( roleTypeService != null ) {
932                         results = roleTypeService.sortRoleMembers( results );
933                     }
934                 } catch (Exception ex) {
935                     LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
936                 }
937             } else if ( matchingRoleIds.size() > 1 ) {
938                 // if more than one, check if there is only a single role type service
939                 String prevServiceName = null;
940                 boolean multipleServices = false;
941                 for ( String roleId : matchingRoleIds ) {
942                     String serviceName = KimApiServiceLocator.getKimTypeInfoService().getKimType(getRole(roleId).getKimTypeId()).getServiceName();
943                     if ( prevServiceName != null && !StringUtils.equals( prevServiceName, serviceName ) ) {
944                         multipleServices = true;
945                         break;
946                     }
947                     prevServiceName = serviceName;
948                 }
949                 if ( !multipleServices ) {
950                     String roleId = matchingRoleIds.iterator().next();
951                     //it is possible that the the roleTypeService is coming from a remote application
952                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
953                     try {
954                         RoleTypeService kimRoleTypeService = getRoleTypeService( roleId );
955                         if ( kimRoleTypeService != null ) {
956                             results = kimRoleTypeService.sortRoleMembers( results );
957                         }
958                     } catch (Exception ex) {
959                         LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
960                     }
961                 } else {
962                     LOG.warn( "Did not sort role members - multiple role type services found.  Role Ids: " + matchingRoleIds );
963                 }
964             }
965         }
966         return Collections.unmodifiableList(results);
967     }
968 
969     /**
970      * Checks each of the result records to determine if there are potentially applicable delegation members for that
971      * role membership.  If there are, applicable delegations and members will be linked to the RoleMemberships in the
972      * given list.  An updated list will be returned from this method which includes the appropriate linked delegations.
973      */
974     protected List<RoleMembership.Builder> applyDelegationsToRoleMembers(List<RoleMembership> roleMemberships,
975             Collection<DelegateTypeBo> delegations, Map<String, String> qualification) {
976         MultiValueMap<String, String> roleIdToRoleMembershipIds = new LinkedMultiValueMap<String, String>();
977         Map<String, RoleMembership.Builder> roleMembershipIdToBuilder = new HashMap<String, RoleMembership.Builder>();
978         List<RoleMembership.Builder> roleMembershipBuilders = new ArrayList<RoleMembership.Builder>();
979         // to make our algorithm less painful, let's do some indexing and load the given list of RoleMemberships into
980         // builders
981         for (RoleMembership roleMembership : roleMemberships) {
982             roleIdToRoleMembershipIds.add(roleMembership.getRoleId(), roleMembership.getId());
983             RoleMembership.Builder builder = RoleMembership.Builder.create(roleMembership);
984             roleMembershipBuilders.add(builder);
985             roleMembershipIdToBuilder.put(roleMembership.getId(), builder);
986         }
987         for (DelegateTypeBo delegation : delegations) {
988             // determine the candidate role memberships where this delegation can be mapped
989             List<String> candidateRoleMembershipIds = roleIdToRoleMembershipIds.get(delegation.getRoleId());
990             if (CollectionUtils.isNotEmpty(candidateRoleMembershipIds)) {
991                 DelegationTypeService delegationTypeService = getDelegationTypeService(delegation.getDelegationId());
992                 for (DelegateMemberBo delegationMember : delegation.getMembers()) {
993                     // Make sure that the delegation member is active
994                     if (delegationMember.isActive(DateTime.now()) && (delegationTypeService == null ||
995                             delegationTypeService.doesDelegationQualifierMatchQualification(qualification, delegationMember.getQualifier()))) {
996                         DelegateMember.Builder delegateMemberBuilder = DelegateMember.Builder.create(delegationMember);
997                         // if the member has no role member id, check qualifications and apply to all matching role memberships on the role
998                         if (StringUtils.isBlank(delegationMember.getRoleMemberId())) {
999                             RoleTypeService roleTypeService = getRoleTypeService(delegation.getRoleId());
1000                             for (String roleMembershipId : candidateRoleMembershipIds) {
1001                                 RoleMembership.Builder roleMembershipBuilder = roleMembershipIdToBuilder.get(roleMembershipId);
1002                                 if (roleTypeService == null || roleTypeService.doesRoleQualifierMatchQualification(roleMembershipBuilder.getQualifier(), delegationMember.getQualifier())) {
1003                                     linkDelegateToRoleMembership(delegation, delegateMemberBuilder, roleMembershipBuilder);
1004                                 }
1005                             }
1006                         } else if (candidateRoleMembershipIds.contains(delegationMember.getRoleMemberId())) {
1007                             RoleMembership.Builder roleMembershipBuilder = roleMembershipIdToBuilder.get(delegationMember.getRoleMemberId());
1008                             linkDelegateToRoleMembership(delegation, delegateMemberBuilder, roleMembershipBuilder);
1009                         }
1010                     }
1011                 }
1012             }
1013         }
1014         return roleMembershipBuilders;
1015     }
1016 
1017     protected void linkDelegateToRoleMembership(DelegateTypeBo delegation, DelegateMember.Builder delegateMemberBuilder,
1018             RoleMembership.Builder roleMembershipBuilder) {
1019         DelegateType.Builder delegateBuilder = null;
1020         for(DelegateType.Builder existingDelegateBuilder : roleMembershipBuilder.getDelegates()) {
1021             if (existingDelegateBuilder.getDelegationId().equals(delegation.getDelegationId())) {
1022                 delegateBuilder = existingDelegateBuilder;
1023             }
1024         }
1025         if (delegateBuilder == null) {
1026             delegateBuilder = DelegateType.Builder.create(delegation);
1027             delegateBuilder.setMembers(new ArrayList<DelegateMember.Builder>());
1028             roleMembershipBuilder.getDelegates().add(delegateBuilder);
1029         }
1030         delegateBuilder.getMembers().add(delegateMemberBuilder);
1031 
1032     }
1033 
1034     /**
1035      * Once the delegations for a RoleMembershipInfo object have been determined,
1036      * any "role" member types need to be resolved into groups and principals so that
1037      * further KIM requests are not needed.
1038      */
1039     protected void resolveDelegationMemberRoles(List<RoleMembership.Builder> membershipBuilders,
1040             Map<String, String> qualification, Set<String> foundRoleTypeMembers) {
1041         // check delegations assigned to this role
1042         for (RoleMembership.Builder roleMembership : membershipBuilders) {
1043             // the applicable delegation IDs will already be set in the RoleMembership.Builder
1044             // this code examines those delegations and obtains the member groups and principals
1045             for (DelegateType.Builder delegation : roleMembership.getDelegates()) {
1046                 List<DelegateMember.Builder> newMembers = new ArrayList<DelegateMember.Builder>();
1047                 for (DelegateMember.Builder member : delegation.getMembers()) {
1048                     if (MemberType.ROLE.equals(member.getType())) {
1049                         // loop over delegation roles and extract the role IDs where the qualifications match
1050                         Collection<RoleMembership> delegateMembers = getRoleMembers(Collections.singletonList(
1051                                 member.getMemberId()), qualification, false, foundRoleTypeMembers);
1052                         // loop over the role members and create the needed DelegationMember builders
1053                         for (RoleMembership rmi : delegateMembers) {
1054                             DelegateMember.Builder delegateMember = DelegateMember.Builder.create(member);
1055                             delegateMember.setMemberId(rmi.getMemberId());
1056                             delegateMember.setType(rmi.getType());
1057                             newMembers.add(delegateMember);
1058                         }
1059                     } else {
1060                         newMembers.add(member);
1061                     }
1062                 }
1063                 delegation.setMembers(newMembers);
1064             }
1065         }
1066     }
1067 
1068     @Override
1069     public boolean principalHasRole(String principalId, List<String> roleIds, Map<String, String> qualification, boolean checkDelegations) {
1070 
1071         incomingParamCheck(principalId, "principalId");
1072         incomingParamCheck(roleIds, "roleIds");
1073         return principalHasRole(new Context(principalId), principalId, roleIds, qualification, checkDelegations);
1074     }
1075 
1076     /**
1077      * An internal helper class which is used to keep context for an invocation of principalHasRole.
1078      */
1079     private final class Context {
1080 
1081         private String principalId;
1082         private List<String> principalGroupIds;
1083         private Map<String, RoleTypeService> roleTypeServiceCache;
1084         private Map<String, Boolean> isDerivedRoleTypeCache;
1085 
1086         Context(String principalId) {
1087             this.principalId = principalId;
1088             this.roleTypeServiceCache = new HashMap<String, RoleTypeService>();
1089             this.isDerivedRoleTypeCache = new HashMap<String, Boolean>();
1090         }
1091 
1092         String getPrincipalId() {
1093             return principalId;
1094         }
1095 
1096         List<String> getPrincipalGroupIds() {
1097             if (principalGroupIds == null) {
1098                 principalGroupIds = getGroupService().getGroupIdsByPrincipalId(principalId);
1099             }
1100             return principalGroupIds;
1101         }
1102 
1103         RoleTypeService getRoleTypeService(String kimTypeId) {
1104             if (roleTypeServiceCache.containsKey(kimTypeId)) {
1105                 return roleTypeServiceCache.get(kimTypeId);
1106             }
1107             RoleTypeService roleTypeService = null;
1108             if (kimTypeId != null) {
1109                 KimType roleType = KimApiServiceLocator.getKimTypeInfoService().getKimType(kimTypeId);
1110                 if (roleType != null && StringUtils.isNotBlank(roleType.getServiceName())) {
1111                     roleTypeService = getRoleTypeServiceByName(roleType.getServiceName());
1112                 }
1113             }
1114             if (roleTypeService == null) {
1115                 roleTypeService = KimImplServiceLocator.getDefaultRoleTypeService();
1116             }
1117             roleTypeServiceCache.put(kimTypeId, roleTypeService);
1118             return roleTypeService;
1119         }
1120 
1121         boolean isDerivedRoleType(String kimTypeId) {
1122             Boolean isDerived = isDerivedRoleTypeCache.get(kimTypeId);
1123             if (isDerived == null) {
1124                 isDerived = Boolean.valueOf(RoleServiceImpl.this.isDerivedRoleType(getRoleTypeService(kimTypeId)));
1125                 isDerivedRoleTypeCache.put(kimTypeId, isDerived);
1126             }
1127             return isDerived.booleanValue();
1128         }
1129 
1130     }
1131 
1132 
1133     protected boolean principalHasRole(Context context, String principalId, List<String> roleIds, Map<String, String> qualification, boolean checkDelegations) {
1134 
1135         /**
1136          * This method uses a multi-phase approach to determining if the given principal of any of the roles given based
1137          * on the qualification map that is pased.
1138          *
1139          * Phase 1: Check the cache to find if it's already been determined that the principal is a member of any of
1140          *          the roles with the given ids.
1141          * Phase 2: Perform exact database-level matching. This can be done for all roles if the given qualification map
1142          *          is null or empty since that means qualification matching does not need to be performed. It can also
1143          *          be done for roles who's RoleTypeService defines qualifiers for exact match.
1144          * Phase 3: Use RoleTypeService matching for roles which have not already been checked. Will need to determine
1145          *          which role memberships match the given principal then delegate to the appropriate RoleTypeService
1146          *          to execute matching logic.
1147          * Phase 4: Check nested roles.
1148          * Phase 5: For any cases where derived roles are used, determine if the principal is a member of those
1149          *          derived roles.
1150          * Phase 6: If checkDelegations is true, check if any delegations match
1151          */
1152         try {
1153             // Phase 1: first check if any of the role membership is cached, only proceed with checking the role ids that
1154             // aren't already cached
1155 
1156             List<String> roleIdsToCheck = new ArrayList<String>(roleIds.size());
1157             for (String roleId : roleIds) {
1158                 Boolean hasRole = getPrincipalHasRoleFromCache(principalId, roleId, qualification, checkDelegations);
1159                 if (hasRole != null) {
1160                     if (hasRole.booleanValue()) {
1161                         return true;
1162                     }
1163                 } else {
1164                     roleIdsToCheck.add(roleId);
1165                 }
1166             }
1167 
1168             // load the roles, this will also filter out inactive roles!
1169             List<Role> roles = loadRoles(roleIdsToCheck);
1170             // short-circuit if no roles match
1171             if (roles.isEmpty()) {
1172                 return false;
1173             }
1174 
1175             // Phase 2: If they didn't pass any qualifications or they are using exact qualifier matching, we can go
1176             // straight to the database
1177 
1178             Set<String> rolesCheckedForExactMatch = new HashSet<String>();
1179             for (Role role : roles) {
1180                 Map<String, String> qualificationForExactMatch = null;
1181                 if (qualification == null || qualification.isEmpty()) {
1182                     qualificationForExactMatch = new HashMap<String, String>();
1183                 } else {
1184                     RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1185                     if (roleTypeService != null) {
1186                         List<String> attributesForExactMatch = getQualifiersForExactMatch(role.getKimTypeId(), roleTypeService);
1187                         if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1188                             qualificationForExactMatch = populateQualifiersForExactMatch(qualification, attributesForExactMatch);
1189                             if (qualificationForExactMatch.isEmpty()) {
1190                                 // None of the attributes passed into principalHasRole matched attribute qualifiers on
1191                                 // the roleTypeService.  In this case we want to skip the remaining processing and
1192                                 // go onto the next role.
1193                                 continue;
1194                             }
1195                         }
1196                     }
1197                 }
1198                 if (qualificationForExactMatch != null) {
1199                     rolesCheckedForExactMatch.add(role.getId());
1200                     List<RoleMemberBo> matchingRoleMembers = getStoredRolePrincipalsForPrincipalIdAndRoleIds(
1201                             Collections.singletonList(role.getId()), principalId, qualificationForExactMatch);
1202                     // if a role member matched our principal, we're good to go
1203                     if (CollectionUtils.isNotEmpty(matchingRoleMembers)) {
1204                         return putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1205                     }
1206                     // now check groups
1207                     if (!context.getPrincipalGroupIds().isEmpty()) {
1208                         List<RoleMemberBo> matchingRoleGroupMembers =
1209                                 getStoredRoleGroupsUsingExactMatchOnQualification(context.getPrincipalGroupIds(), role.getId(), qualification);
1210                         if (CollectionUtils.isNotEmpty(matchingRoleGroupMembers)) {
1211                             return putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1212                         }
1213                     }
1214                     // if we drop to this point, either we didn't match or the role has nested or derived role membership,
1215                     // we'll check that later
1216                 }
1217             }
1218 
1219             // Phase 3: If we couldn't do an exact match, we need to work with the RoleTypeService in order to
1220             // perform matching
1221 
1222             for (Role role : roles) {
1223                 // if we didn't do an exact match, we need to do a manual match
1224                 if (!rolesCheckedForExactMatch.contains(role.getId())) {
1225                     List<RoleMemberBo> matchingPrincipalRoleMembers = getRoleMembersForPrincipalId(Collections.singletonList(role.getId()), principalId);
1226                     List<RoleMemberBo> matchingGroupRoleMembers = getRoleMembersForGroupIds(role.getId(), context.getPrincipalGroupIds());
1227                     List<RoleMembership> roleMemberships = convertToRoleMemberships(matchingPrincipalRoleMembers, matchingGroupRoleMembers);
1228                     try {
1229                         RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1230                         if (!roleTypeService.getMatchingRoleMemberships(qualification, roleMemberships).isEmpty()) {
1231                             return putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1232                         }
1233                     } catch (Exception ex) {
1234                         LOG.warn("Unable to find role type service with id: " + role.getKimTypeId());
1235                     }
1236                 }
1237             }
1238 
1239             // Phase 4: If we have nested roles, execute a recursive check on those
1240 
1241             // first, check that the qualifiers on the role membership match
1242             // then, perform a principalHasRole on the embedded role
1243             Map<String, Role> roleIndex = new HashMap<String, Role>();
1244             for (Role role :roles) {
1245                 roleIndex.put(role.getId(), role);
1246             }
1247             List<RoleMemberBo> roleMemberBos = getStoredRoleMembersForRoleIds(new ArrayList<String>(roleIndex.keySet()),
1248                     MemberType.ROLE.getCode(), null);
1249             for (RoleMemberBo roleMemberBo : roleMemberBos) {
1250                 Role role = roleIndex.get(roleMemberBo.getRoleId());
1251                 RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1252                 if (roleTypeService != null) {
1253                     //it is possible that the the roleTypeService is coming from a remote application
1254                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1255                     try {
1256                         if (roleTypeService.doesRoleQualifierMatchQualification(qualification,
1257                                 roleMemberBo.getAttributes())) {
1258                             RoleBoLite memberRole = getRoleBoLite(roleMemberBo.getMemberId());
1259                             Map<String, String> nestedRoleQualification =
1260                                     getNestedQualification(memberRole, role.getNamespaceCode(),
1261                                             role.getName(), memberRole.getNamespaceCode(), memberRole.getName(),
1262                                             qualification, roleMemberBo.getAttributes());
1263                             if (principalHasRole(context, principalId,
1264                                     Collections.singletonList(roleMemberBo.getMemberId()), nestedRoleQualification, true)) {
1265                                 return putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1266                             }
1267                         }
1268                     } catch (Exception ex) {
1269                         LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleMemberBo
1270                                 .getRoleId(), ex);
1271                     }
1272                 } else {
1273                     // no qualifiers - role is always used - check membership
1274                     // no role type service, so can't convert qualification - just pass as is
1275                     if (principalHasRole(context, principalId, Collections.singletonList(roleMemberBo.getMemberId()),
1276                             qualification, true)) {
1277                         return putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1278                     }
1279                 }
1280 
1281             }
1282 
1283             // Phase 5: derived roles
1284 
1285             // check for derived roles and extract principals and groups from that - then check them against the
1286             // role type service passing in the qualification and principal - the qualifier comes from the
1287             // external system (application)
1288             for (Role role : roles) {
1289                 // check if an derived role
1290                 //it is possible that the the roleTypeService is coming from a remote application
1291                 // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1292                 try {
1293                     boolean isDerivedRoleType = context.isDerivedRoleType(role.getKimTypeId());
1294                     if (isDerivedRoleType) {
1295                         RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1296                         if (roleTypeService.hasDerivedRole(principalId,
1297                                 context.getPrincipalGroupIds(), role.getNamespaceCode(), role.getName(), qualification)) {
1298                             if (!roleTypeService.dynamicRoleMembership(role.getNamespaceCode(), role.getName())) {
1299                                 putPrincipalHasRoleInCache(true, principalId, role.getId(), qualification, checkDelegations);
1300                             }
1301                             return true;
1302                         }
1303                     } else {
1304                         if(!checkDelegations) {
1305                             putPrincipalHasRoleInCache(false, principalId, role.getId(), qualification, checkDelegations);
1306                         }
1307                     }
1308                 } catch (Exception ex) {
1309                     LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + role.getId(), ex);
1310                 }
1311             }
1312 
1313             // Phase 6: delegations
1314 
1315             if (checkDelegations) {
1316                 if (matchesOnDelegation(roleIndex.keySet(), principalId, context.getPrincipalGroupIds(), qualification, context)) {
1317                     return true;
1318                 }
1319             }
1320         } catch (Exception e) {
1321             LOG.warn("Caught exception during a principalHasRole check", e);
1322         }
1323         return false;
1324     }
1325 
1326     protected Boolean getPrincipalHasRoleFromCache(String principalId, String roleId, Map<String, String> qualification, boolean checkDelegations) {
1327         String key = buildPrincipalHasRoleCacheKey(principalId, roleId, qualification, checkDelegations);
1328         Cache.ValueWrapper value = cacheManager.getCache(Role.Cache.NAME).get(key);
1329         return value == null ? null : (Boolean)value.get();
1330     }
1331 
1332     protected boolean putPrincipalHasRoleInCache(boolean principalHasRole, String principalId, String roleId,
1333             Map<String, String> qualification, boolean checkDelegations) {
1334         String key = buildPrincipalHasRoleCacheKey(principalId, roleId, qualification, checkDelegations);
1335         cacheManager.getCache(Role.Cache.NAME).put(key, Boolean.valueOf(principalHasRole));
1336         return principalHasRole;
1337     }
1338 
1339     private String buildPrincipalHasRoleCacheKey(String principalId, String roleId, Map<String, String> qualification, boolean checkDelegations) {
1340         return new StringBuilder("{principalHasRole}")
1341                 .append("principalId=").append(principalId).append("|")
1342                 .append("roleId=").append(roleId).append("|")
1343                 .append("qualification=").append(CacheKeyUtils.mapKey(qualification)).append("|")
1344                 .append("checkDelegations=").append(checkDelegations).toString();
1345     }
1346 
1347     protected List<String> getQualifiersForExactMatch(String kimTypeId, RoleTypeService roleTypeService) {
1348         String cacheKey = "{getQualifiersForExactMatch}kimTypeId=" + kimTypeId;
1349         Cache cache = cacheManager.getCache(Role.Cache.NAME);
1350         Cache.ValueWrapper value = cache.get(cacheKey);
1351         List<String> qualifiers = new ArrayList<String>();
1352         if (value == null) {
1353             try {
1354                 qualifiers = roleTypeService.getQualifiersForExactMatch();
1355                 cache.put(cacheKey, qualifiers);
1356             } catch (Exception e) {
1357                 LOG.warn("Caught exception when attempting to invoke a role type service", e);
1358             }
1359         } else {
1360             qualifiers = (List<String>)value.get();
1361         }
1362         return qualifiers;
1363     }
1364 
1365     public boolean isDerivedRoleType(RoleTypeService service) {
1366         return service != null && service.isDerivedRoleType();
1367     }
1368 
1369     private boolean dynamicRoleMembership(RoleTypeService service, Role role) {
1370         return service != null && role !=null && service.dynamicRoleMembership(role.getNamespaceCode(), role.getName());
1371     }
1372 
1373     @Override
1374     public boolean isDerivedRole(String roleId) {
1375         incomingParamCheck(roleId, "roleId");
1376         RoleTypeService service = getRoleTypeService(roleId);
1377         return isDerivedRoleType(service);
1378     }
1379 
1380     @Override
1381     public boolean isDynamicRoleMembership(String roleId) {
1382         incomingParamCheck(roleId, "roleId");
1383         RoleTypeService service = getRoleTypeService(roleId);
1384         try {
1385             return dynamicRoleMembership(service, getRole(roleId));
1386         } catch (Exception e) {
1387             LOG.warn("Caught exception while invoking a role type service for role " + roleId, e);
1388             // Returning true so the role won't be cached
1389             return true;
1390         }
1391     }
1392 
1393     /**
1394      * Support method for principalHasRole.  Checks delegations on the passed in roles for the given principal and groups.  (It's assumed that the principal
1395      * belongs to the given groups.)
1396      * <p/>
1397      * Delegation checks are mostly the same as role checks except that the delegateBo itself is qualified against the original role (like a RolePrincipal
1398      * or RoleGroup.)  And then, the members of that delegateBo may have additional qualifiers which are not part of the original role qualifiers.
1399      * <p/>
1400      * For example:
1401      * <p/>
1402      * A role could be qualified by organization.  So, there is a person in the organization with primary authority for that org.  But, then they delegate authority
1403      * for that organization (not their authority - the delegateBo is attached to the org.)  So, in this case the delegateBo has a qualifier of the organization
1404      * when it is attached to the role.
1405      * <p/>
1406      * The principals then attached to that delegateBo (which is specific to the organization), may have additional qualifiers.
1407      * For Example: dollar amount range, effective dates, document types.
1408      * As a subsequent step, those qualifiers are checked against the qualification passed in from the client.
1409      */
1410     protected boolean matchesOnDelegation(Set<String> allRoleIds, String principalId, List<String> principalGroupIds, Map<String, String> qualification, Context context) {
1411         // get the list of delegations for the roles
1412         Map<String, DelegateTypeBo> delegations = getStoredDelegationImplMapFromRoleIds(allRoleIds);
1413 
1414         // If there are no delegations then we should cache that the principal
1415         // doesn't have the given roles if those roles do not have dynamic
1416         // membership
1417         if(delegations.isEmpty()) {
1418             for(String roleId : allRoleIds) {
1419                 Role role = loadRole(roleId);
1420                 RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1421                 if(!context.isDerivedRoleType(role.getKimTypeId()) || roleTypeService == null || !roleTypeService.dynamicRoleMembership(role.getNamespaceCode(), role.getName())) {
1422                     putPrincipalHasRoleInCache(false, principalId, roleId, qualification, true);
1423                 }
1424             }
1425             return false;
1426         }
1427 
1428         // Build a map from a role ID to the delegations for that role ID
1429         Map<String, List<DelegateTypeBo>> roleToDelegations = new HashMap<String, List<DelegateTypeBo>>();
1430         for(DelegateTypeBo delegation : delegations.values()) {
1431             List<DelegateTypeBo> roleDelegations = roleToDelegations.get(delegation.getRoleId());
1432             if(roleDelegations == null) {
1433                 roleDelegations = new ArrayList<DelegateTypeBo>();
1434                 roleToDelegations.put(delegation.getRoleId(), roleDelegations);
1435             }
1436             roleDelegations.add(delegation);
1437         }
1438         // Iterate through each role and check its delegations to determine if
1439         // the principal has one of the roles
1440         for(String roleId : roleToDelegations.keySet()) {
1441             boolean matchesOnRoleDelegation = false;
1442             Role role = getRole(roleId);
1443             RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1444             // Iterate through each delegation for this role and determine if
1445             // the principal has the role through this delegation
1446             for(DelegateTypeBo delegation : roleToDelegations.get(roleId)) {
1447                 // If the delegation isn't active skip it
1448                 if (!delegation.isActive()) {
1449                     continue;
1450                 }
1451                 // Now iterate through all of the members of the delegation to
1452                 // determine if any of them apply to this principal
1453                 for (DelegateMemberBo delegateMemberBo : delegation.getMembers()) {
1454                     // If the membership isn't active skip the rest of the checks
1455                     if (!delegateMemberBo.isActive(new Timestamp(new Date().getTime()))) {
1456                         continue;
1457                     }
1458                     // If the membership is a principal type then check the
1459                     // delegate's member ID against the principal ID
1460                     if (MemberType.PRINCIPAL.equals(delegateMemberBo.getType())
1461                             && !delegateMemberBo.getMemberId().equals(principalId)) {
1462                         continue; // no match on principal
1463                     }
1464                     // If the membership is a group type then check to see if
1465                     // the group's ID is contained in the list of groups the
1466                     // principal belongs to
1467                     if (MemberType.GROUP.equals(delegateMemberBo.getType())
1468                             && !principalGroupIds.contains(delegateMemberBo.getMemberId())) {
1469                         continue; // No match on group
1470                     }
1471                     // If the membership is a role type then we need to recurse
1472                     // into the principalHasRole method to check if this
1473                     // principal is a member of that role
1474                     if (MemberType.ROLE.equals(delegateMemberBo.getType())
1475                             && !principalHasRole(principalId, Collections.singletonList(delegateMemberBo.getMemberId()), qualification, false)) {
1476                         continue; // No match on role
1477                     }
1478 
1479                     // OK, the member matches the current user, now check the qualifications
1480 
1481                     // NOTE: this compare is slightly different than the member enumeration
1482                     // since the requested qualifier is always being used rather than
1483                     // the role qualifier for the member (which is not available)
1484 
1485                     //it is possible that the the roleTypeService is coming from a remote application
1486                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1487                     try {
1488                         if (roleTypeService != null && !roleTypeService.doesRoleQualifierMatchQualification(qualification, delegateMemberBo.getQualifier())) {
1489                             continue; // no match - skip to next record
1490                         }
1491                     } catch (Exception ex) {
1492                         LOG.warn("Unable to call doesRoleQualifierMatchQualification on role type service for role Id: " + delegation.getRoleId() + " / " + qualification + " / " + delegateMemberBo.getQualifier(), ex);
1493                         continue;
1494                     }
1495 
1496                     // role service matches this qualifier
1497                     // now try the delegateBo service
1498                     DelegationTypeService delegationTypeService = getDelegationTypeService(delegateMemberBo.getDelegationId());
1499                     // QUESTION: does the qualifier map need to be merged with the main delegateBo qualification?
1500                     if (delegationTypeService != null && !delegationTypeService.doesDelegationQualifierMatchQualification(qualification, delegateMemberBo.getQualifier())) {
1501                         continue; // no match - skip to next record
1502                     }
1503                     // check if a role member ID is present on the delegateBo record
1504                     // if so, check that the original role member would match the given qualifiers
1505                     if (StringUtils.isNotBlank(delegateMemberBo.getRoleMemberId())) {
1506                         RoleMemberBo rm = getRoleMemberBo(delegateMemberBo.getRoleMemberId());
1507                         if (rm != null) {
1508                             // check that the original role member's is active and that their
1509                             // qualifier would have matched this request's
1510                             // qualifications (that the original person would have the permission/responsibility
1511                             // for an action)
1512                             // this prevents a role-membership based delegateBo from surviving the inactivation/
1513                             // changing of the main person's role membership
1514                             if (!rm.isActive(new Timestamp(new Date().getTime()))) {
1515                                 continue;
1516                             }
1517                             Map<String, String> roleQualifier = rm.getAttributes();
1518                             //it is possible that the the roleTypeService is coming from a remote application
1519                             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1520                             try {
1521                                 if (roleTypeService != null && !roleTypeService.doesRoleQualifierMatchQualification(qualification, roleQualifier)) {
1522                                     continue;
1523                                 }
1524                             } catch (Exception ex) {
1525                                 LOG.warn("Unable to call doesRoleQualifierMatchQualification on role type service for role Id: " + delegation.getRoleId() + " / " + qualification + " / " + roleQualifier, ex);
1526                                 continue;
1527                             }
1528                         } else {
1529                             LOG.warn("Unknown role member ID cited in the delegateBo member table:");
1530                             LOG.warn("       assignedToId: " + delegateMemberBo.getDelegationMemberId() + " / roleMemberId: " + delegateMemberBo.getRoleMemberId());
1531                         }
1532                     }
1533                     // If we've made it here then all of the tests pass so the
1534                     // principal must belong to this delegation so set the flag
1535                     // to true and break out of this loop
1536                     matchesOnRoleDelegation = true;
1537                     break;
1538                 }
1539 
1540                 // If we've found a match for one of the delegations break out
1541                 // of this loop
1542                 if(matchesOnRoleDelegation) {
1543                     break;
1544                 }
1545             }
1546             // If the role is not derived nor dynamic then cache the result of
1547             // this since the principal has the role through one of these
1548             // delegations
1549             if(!context.isDerivedRoleType(role.getKimTypeId()) || roleTypeService == null || !roleTypeService.dynamicRoleMembership(role.getNamespaceCode(), role.getName())) {
1550                 putPrincipalHasRoleInCache(matchesOnRoleDelegation, principalId, roleId, qualification, true);
1551             }
1552             // If we've found a matching delegation skip processing the rest of
1553             // the roles
1554             if(matchesOnRoleDelegation) {
1555                 return matchesOnRoleDelegation;
1556             }
1557         }
1558         // If we get here we didn't find a matching delegation so return false
1559         return false;
1560     }
1561 
1562     protected List<RoleMembership> convertToRoleMemberships(List<RoleMemberBo>... roleMemberLists) {
1563         List<RoleMembership> roleMemberships = new ArrayList<RoleMembership>();
1564         for (List<RoleMemberBo> roleMembers : roleMemberLists) {
1565             for (RoleMemberBo roleMember : roleMembers) {
1566                 RoleMembership roleMembership = RoleMembership.Builder.create(
1567                         roleMember.getRoleId(),
1568                         roleMember.getId(),
1569                         roleMember.getMemberId(),
1570                         roleMember.getType(),
1571                         roleMember.getAttributes()).build();
1572                 roleMemberships.add(roleMembership);
1573             }
1574         }
1575         return roleMemberships;
1576     }
1577 
1578     /**
1579      * Helper method used by principalHasRole to build the role ID -> list of members map.
1580      *
1581      * @return <b>true</b> if no further checks are needed because no role service is defined
1582      */
1583     protected boolean getRoleIdToMembershipMap(Map<String, List<RoleMembership>> roleIdToMembershipMap, List<RoleMemberBo> roleMembers) {
1584         for (RoleMemberBo roleMemberBo : roleMembers) {
1585             RoleMembership roleMembership = RoleMembership.Builder.create(
1586                     roleMemberBo.getRoleId(),
1587                     roleMemberBo.getId(),
1588                     roleMemberBo.getMemberId(),
1589                     roleMemberBo.getType(),
1590                     roleMemberBo.getAttributes()).build();
1591 
1592             // if the role type service is null, assume that all qualifiers match
1593             if (getRoleTypeService(roleMemberBo.getRoleId()) == null) {
1594                 return true;
1595             }
1596             List<RoleMembership> lrmi = roleIdToMembershipMap.get(roleMembership.getRoleId());
1597             if (lrmi == null) {
1598                 lrmi = new ArrayList<RoleMembership>();
1599                 roleIdToMembershipMap.put(roleMembership.getRoleId(), lrmi);
1600             }
1601             lrmi.add(roleMembership);
1602         }
1603         return false;
1604     }
1605 
1606     /**
1607      * Retrieves a KimDelegationImpl object by its ID. If the delegateBo already exists in the cache, this method will return the cached
1608      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
1609      */
1610     protected DelegateTypeBo getKimDelegationImpl(String delegationId) {
1611         if (StringUtils.isBlank(delegationId)) {
1612             return null;
1613         }
1614 
1615         return getDataObjectService().find(DelegateTypeBo.class,delegationId);
1616     }
1617 
1618     protected DelegationTypeService getDelegationTypeService(String delegationId) {
1619         DelegationTypeService service = null;
1620         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
1621         KimType delegationType = KimApiServiceLocator.getKimTypeInfoService().getKimType(delegateBo.getKimTypeId());
1622         if (delegationType != null) {
1623             KimTypeService tempService = KimFrameworkServiceLocator.getKimTypeService(delegationType);
1624             if (tempService != null && tempService instanceof DelegationTypeService) {
1625                 service = (DelegationTypeService) tempService;
1626             } else {
1627                 LOG.error("Service returned for type " + delegationType + "(" + delegationType.getName() + ") was not a DelegationTypeService.  Was a " + (tempService != null ? tempService.getClass() : "(null)"));
1628             }
1629         } else { // delegateBo has no type - default to role type if possible
1630             RoleTypeService roleTypeService = getRoleTypeService(delegateBo.getRoleId());
1631             if (roleTypeService != null && roleTypeService instanceof DelegationTypeService) {
1632                 service = (DelegationTypeService) roleTypeService;
1633             }
1634         }
1635         return service;
1636     }
1637 
1638     protected Collection<RoleMembership> getNestedRoleMembers(Map<String, String> qualification, RoleMembership rm, Set<String> foundRoleTypeMembers) {
1639         // If this role has already been traversed, skip it
1640         if (foundRoleTypeMembers.contains(rm.getMemberId())) {
1641             return new ArrayList<RoleMembership>();  // return an empty list
1642         }
1643         foundRoleTypeMembers.add(rm.getMemberId());
1644 
1645         ArrayList<String> roleIdList = new ArrayList<String>(1);
1646         roleIdList.add(rm.getMemberId());
1647 
1648         // get the list of members from the nested role - ignore delegations on those sub-roles
1649         Collection<RoleMembership> currentNestedRoleMembers = getRoleMembers(roleIdList, qualification, false, foundRoleTypeMembers);
1650 
1651         // add the roles whose members matched to the list for delegateBo checks later
1652         Collection<RoleMembership> returnRoleMembers = new ArrayList<RoleMembership>();
1653         for (RoleMembership roleMembership : currentNestedRoleMembers) {
1654             RoleMembership.Builder rmBuilder = RoleMembership.Builder.create(roleMembership);
1655 
1656             // use the member ID of the parent role (needed for responsibility joining)
1657             rmBuilder.setId(rm.getId());
1658             // store the role ID, so we know where this member actually came from
1659             rmBuilder.setRoleId(rm.getRoleId());
1660             rmBuilder.setEmbeddedRoleId(rm.getMemberId());
1661             returnRoleMembers.add(rmBuilder.build());
1662         }
1663         return returnRoleMembers;
1664     }
1665 
1666     /**
1667      * Retrieves a KimDelegationMemberImpl object by its ID and the ID of the delegation it belongs to. If the delegation member exists in the cache,
1668      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
1669      */
1670     protected DelegateMemberBo getKimDelegationMemberImplByDelegationAndId(String delegationId, String delegationMemberId) {
1671         if (StringUtils.isBlank(delegationId) || StringUtils.isBlank(delegationMemberId)) {
1672             return null;
1673         }
1674 
1675         Map<String, String> searchCriteria = new HashMap<String, String>(2);
1676         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
1677         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMemberId);
1678         QueryResults<DelegateMemberBo> memberList = getDataObjectService().findMatching(DelegateMemberBo.class, QueryByCriteria.Builder.andAttributes(searchCriteria).build());
1679         if (!memberList.getResults().isEmpty()) {
1680             return memberList.getResults().get(0);
1681         }
1682         return null;
1683     }
1684 
1685     private List<RoleMemberBo> getStoredRoleMembersUsingExactMatchOnQualification(String principalId, List<String> groupIds, List<String> roleIds, Map<String, String> qualification) {
1686         List<String> copyRoleIds = new ArrayList<String>(roleIds);
1687         List<RoleMemberBo> roleMemberBoList = new ArrayList<RoleMemberBo>();
1688 
1689         for (String roleId : roleIds) {
1690             RoleTypeService roleTypeService = getRoleTypeService(roleId);
1691             if (roleTypeService != null) {
1692                 List<String> attributesForExactMatch = null;
1693                 try {
1694                     attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
1695                 } catch (Exception e) {
1696                     LOG.warn("Caught exception when attempting to invoke a role type service for role " + roleId, e);
1697                 }
1698                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1699                     copyRoleIds.remove(roleId);
1700                     roleMemberBoList.addAll(getStoredRoleMembersForRoleIdsWithFilters(Collections.singletonList(roleId), principalId, groupIds, populateQualifiersForExactMatch(qualification, attributesForExactMatch)));
1701                 }
1702             }
1703         }
1704         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
1705             roleMemberBoList.addAll(getStoredRoleMembersForRoleIdsWithFilters(copyRoleIds, principalId, groupIds, null));
1706         }
1707         return roleMemberBoList;
1708     }
1709 
1710     private List<RoleMemberBo> getStoredRoleGroupsUsingExactMatchOnQualification(List<String> groupIds, String roleId, Map<String, String> qualification) {
1711         Set<String> roleIds = new HashSet<String>();
1712         if (roleId != null) {
1713             roleIds.add(roleId);
1714         }
1715         return getStoredRoleGroupsUsingExactMatchOnQualification(groupIds, roleIds, qualification);
1716     }
1717 
1718     private List<RoleMemberBo> getStoredRoleGroupsUsingExactMatchOnQualification(List<String> groupIds, Set<String> roleIds, Map<String, String> qualification) {
1719         List<String> copyRoleIds = new ArrayList<String>(roleIds);
1720         List<RoleMemberBo> roleMemberBos = new ArrayList<RoleMemberBo>();
1721 
1722         for (String roleId : roleIds) {
1723             RoleTypeService roleTypeService = getRoleTypeService(roleId);
1724             if (roleTypeService != null) {
1725                 List<String> attributesForExactMatch = null;
1726                 try {
1727                     attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
1728                 } catch (Exception e) {
1729                     LOG.warn("Caught exception when attempting to invoke a role type service for role " + roleId, e);
1730                 }
1731                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1732                     copyRoleIds.remove(roleId);
1733                     roleMemberBos.addAll(getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(roleId), groupIds, populateQualifiersForExactMatch(qualification, attributesForExactMatch)));
1734                 }
1735             }
1736         }
1737         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
1738             roleMemberBos.addAll(getStoredRoleGroupsForGroupIdsAndRoleIds(copyRoleIds, groupIds, null));
1739         }
1740         return roleMemberBos;
1741     }
1742 
1743     private List<DelegateMember> getDelegateMembersForDelegation(DelegateTypeBo delegateBo) {
1744         if (delegateBo == null || delegateBo.getMembers() == null) {return null;}
1745         List<DelegateMember> delegateMembersReturnList = new ArrayList<DelegateMember>();
1746         for (DelegateMemberBo delegateMemberBo : delegateBo.getMembers()) {
1747             //FIXME: What is up with this!?!
1748             DelegateMember delegateMember = getDelegateCompleteInfo(delegateBo, delegateMemberBo);
1749 
1750             delegateMembersReturnList.add(DelegateMemberBo.to(delegateMemberBo));
1751         }
1752         return Collections.unmodifiableList(delegateMembersReturnList);
1753     }
1754 
1755     private DelegateMember getDelegateCompleteInfo(DelegateTypeBo delegateBo, DelegateMemberBo delegateMemberBo) {
1756         if (delegateBo == null || delegateMemberBo == null) {return null;}
1757 
1758         DelegateMember.Builder delegateMemberBuilder = DelegateMember.Builder.create(delegateMemberBo);
1759         delegateMemberBuilder.setType(delegateMemberBo.getType());
1760         return delegateMemberBuilder.build();
1761     }
1762 
1763     @Override
1764     public RoleMember assignPrincipalToRole(String principalId,
1765             String namespaceCode, String roleName, Map<String, String> qualifier)
1766             throws RiceIllegalArgumentException {
1767         incomingParamCheck(principalId, "principalId");
1768         incomingParamCheck(namespaceCode, "namespaceCode");
1769         incomingParamCheck(roleName, "roleName");
1770         incomingParamCheck(qualifier, "qualifier");
1771 
1772         // look up the role
1773         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
1774 
1775         // check that identical member does not already exist
1776         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(role, principalId, memberTypeToRoleDaoActionMap.get(MemberType.PRINCIPAL.getCode()), qualifier);
1777         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1778             return membersMatchByExactQualifiers.get(0);
1779         }
1780         List<String> roleIds = new ArrayList<String>();
1781         roleIds.add(role.getId());
1782         List<RoleMemberBo> roleMembers = getRoleDao().getRoleMembersForRoleIds(roleIds, MemberType.PRINCIPAL.getCode(),
1783                 qualifier);
1784         RoleMember anyMemberMatch = doAnyMemberRecordsMatch( roleMembers, principalId, MemberType.PRINCIPAL.getCode(), qualifier );
1785         if (null != anyMemberMatch) {
1786             return anyMemberMatch;
1787         }
1788 
1789         // create the new role member object
1790         RoleMemberBo newRoleMember = new RoleMemberBo();
1791 
1792         newRoleMember.setRoleId(role.getId());
1793         newRoleMember.setMemberId(principalId);
1794         newRoleMember.setType(MemberType.PRINCIPAL);
1795 
1796         // build role member attribute objects from the given Map<String, String>
1797         addMemberAttributeData(newRoleMember, qualifier, role.getKimTypeId());
1798 
1799         // add row to member table
1800         // When members are added to roles, clients must be notified.
1801         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1802     }
1803 
1804     @Override
1805     public RoleMember assignGroupToRole(String groupId, String namespaceCode,
1806             String roleName, Map<String, String> qualifier) throws RiceIllegalStateException {
1807         incomingParamCheck(groupId, "groupId");
1808         incomingParamCheck(namespaceCode, "namespaceCode");
1809         incomingParamCheck(roleName, "roleName");
1810         incomingParamCheck(qualifier, "qualifier");
1811 
1812         // look up the role
1813         RoleBo role = getRoleBoByName(namespaceCode, roleName);
1814 
1815         // check that identical member does not already exist
1816         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(role, groupId, memberTypeToRoleDaoActionMap.get(MemberType.GROUP.getCode()), qualifier);
1817         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1818             return membersMatchByExactQualifiers.get(0);
1819         }
1820         RoleMember anyMemberMatch = doAnyMemberRecordsMatch( role.getMembers(), groupId, MemberType.GROUP.getCode(), qualifier );
1821         if (null != anyMemberMatch) {
1822             return anyMemberMatch;
1823         }
1824 
1825         // create the new role member object
1826         RoleMemberBo newRoleMember = new RoleMemberBo();
1827         newRoleMember.setRoleId(role.getId());
1828         newRoleMember.setMemberId(groupId);
1829         newRoleMember.setType(MemberType.GROUP);
1830 
1831         // build role member attribute objects from the given Map<String, String>
1832         addMemberAttributeData(newRoleMember, qualifier, role.getKimTypeId());
1833 
1834         // When members are added to roles, clients must be notified.
1835         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1836     }
1837 
1838     @Override
1839     public RoleMember assignRoleToRole(String roleId,
1840             String namespaceCode, String roleName, Map<String, String> qualifier)
1841             throws RiceIllegalStateException {
1842         incomingParamCheck(roleId, "roleId");
1843         incomingParamCheck(namespaceCode, "namespaceCode");
1844         incomingParamCheck(roleName, "roleName");
1845         incomingParamCheck(qualifier, "qualifier");
1846 
1847         // look up the roleBo
1848         RoleBo roleBo = getRoleBoByName(namespaceCode, roleName);
1849 
1850         // check that identical member does not already exist
1851         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(roleBo, roleId, memberTypeToRoleDaoActionMap.get(MemberType.ROLE.getCode()), qualifier);
1852         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1853             return membersMatchByExactQualifiers.get(0);
1854         }
1855         RoleMember anyMemberMatch = doAnyMemberRecordsMatch( roleBo.getMembers(), roleId, MemberType.ROLE.getCode(), qualifier);
1856         if (null != anyMemberMatch) {
1857             return anyMemberMatch;
1858         }
1859 
1860 
1861         // Check to make sure this doesn't create a circular membership
1862         if (!checkForCircularRoleMembership(roleId, roleBo)) {
1863             throw new IllegalArgumentException("Circular roleBo reference.");
1864         }
1865         // create the new roleBo member object
1866         RoleMemberBo newRoleMember = new RoleMemberBo();
1867         newRoleMember.setRoleId(roleBo.getId());
1868         newRoleMember.setMemberId(roleId);
1869         newRoleMember.setType(MemberType.ROLE);
1870         // build roleBo member attribute objects from the given Map<String, String>
1871         addMemberAttributeData(newRoleMember, qualifier, roleBo.getKimTypeId());
1872 
1873         // When members are added to roles, clients must be notified.
1874         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1875     }
1876 
1877     @Override
1878     public RoleMember createRoleMember(RoleMember roleMember) throws RiceIllegalStateException {
1879         incomingParamCheck(roleMember, "roleMember");
1880 
1881         if (StringUtils.isNotBlank(roleMember.getId()) && getRoleMemberBo(roleMember.getId()) != null) {
1882             throw new RiceIllegalStateException("the roleMember to create already exists: " + roleMember);
1883         }
1884 
1885         String kimTypeId = getRoleBoLite(roleMember.getRoleId()).getKimTypeId();
1886         List<RoleMemberAttributeDataBo> attrBos = Collections.emptyList();
1887         attrBos = KimAttributeDataBo.createFrom(RoleMemberAttributeDataBo.class, roleMember.getAttributes(), kimTypeId);
1888 
1889         RoleMemberBo bo = RoleMemberBo.from(roleMember);
1890         bo.setAttributeDetails(attrBos);
1891         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(bo));
1892     }
1893 
1894     @Override
1895     public RoleMember updateRoleMember(@WebParam(
1896             name = "roleMember") RoleMember roleMember) throws RiceIllegalArgumentException, RiceIllegalStateException {
1897         incomingParamCheck(roleMember, "roleMember");
1898 
1899         RoleMemberBo roleMemberBo = null;
1900         if (StringUtils.isNotBlank(roleMember.getId())) {
1901             roleMemberBo = getRoleMemberBo(roleMember.getId());
1902         }
1903         if (StringUtils.isBlank(roleMember.getId()) || roleMemberBo == null) {
1904             throw new RiceIllegalStateException("the roleMember to update does not exists: " + roleMember);
1905         }
1906 
1907         //RoleMemberBo bo = RoleMemberBo.from(roleMember);
1908         //List<RoleMemberAttributeDataBo> updateAttrBos =   new ArrayList<RoleMemberAttributeDataBo>();
1909 
1910         // Copy the main data
1911         roleMemberBo.setMemberId(roleMember.getMemberId());
1912         roleMemberBo.setTypeCode(roleMember.getType().getCode());
1913         roleMemberBo.setActiveFromDateValue(roleMember.getActiveFromDate() == null ? null : new Timestamp(roleMember.getActiveFromDate().getMillis()));
1914         roleMemberBo.setActiveToDateValue(roleMember.getActiveToDate() == null ? null : new Timestamp(roleMember.getActiveToDate().getMillis()));
1915 
1916         Iterator<RoleResponsibilityActionBo> actions = roleMemberBo.getRoleRspActions().iterator();
1917         List<RoleResponsibilityAction> newActionList = new ArrayList( roleMember.getRoleRspActions() );
1918         // loop over the existing actions
1919         while ( actions.hasNext() ) {
1920             RoleResponsibilityActionBo action = actions.next();
1921             // look for a match in the new list
1922             boolean matched = false;
1923             Iterator<RoleResponsibilityAction> newActions = newActionList.iterator();
1924             while ( newActions.hasNext() ) {
1925                 RoleResponsibilityAction newAction = newActions.next();
1926                 if (newAction.getId().equals(action.getId())) {
1927                     matched = true;
1928                     action.setActionPolicyCode( newAction.getActionPolicyCode() );
1929                     action.setActionTypeCode( newAction.getActionTypeCode() );
1930                     action.setPriorityNumber( newAction.getPriorityNumber() );
1931                     action.setForceAction( newAction.isForceAction() );
1932                     newActions.remove(); // processed this one - we don't want to see it again
1933                     break;
1934                 }
1935             }
1936             if ( !matched ) {
1937                 // nothing in the new list matched - this means this action was deleted
1938                 actions.remove();
1939             }
1940         }
1941         // now - anything left in the new attribute list needs to be added
1942         for ( RoleResponsibilityAction rra : newActionList ) {
1943             roleMemberBo.getRoleRspActions().add(RoleResponsibilityActionBo.from(rra));
1944         }
1945 
1946         String kimTypeId = getRoleBoLite(roleMember.getRoleId()).getKimTypeId();
1947         List<RoleMemberAttributeDataBo> newAttributeBos = KimAttributeDataBo.createFrom(RoleMemberAttributeDataBo.class, roleMember.getAttributes(), kimTypeId);
1948         Iterator<RoleMemberAttributeDataBo> attributes = roleMemberBo.getAttributeDetails().iterator();
1949         // loop over the existing attributes
1950         while ( attributes.hasNext() ) {
1951             RoleMemberAttributeDataBo attr = attributes.next();
1952             // look for a match in the new list
1953             boolean matched = false;
1954             Iterator<RoleMemberAttributeDataBo> newAttributes = newAttributeBos.iterator();
1955             while ( newAttributes.hasNext() ) {
1956                 RoleMemberAttributeDataBo newAttr = newAttributes.next();
1957                 if (newAttr.getKimTypeId().equals(attr.getKimTypeId())
1958                         && newAttr.getKimAttributeId().equals(attr.getKimAttributeId())) {
1959                     matched = true;
1960                     attr.setAttributeValue( newAttr.getAttributeValue() );
1961                     newAttributes.remove(); // processed this one - we don't want to see it again
1962                     break;
1963                 }
1964             }
1965             if ( !matched ) {
1966                 // nothing in the new list matched - this means this attribute was deleted
1967                 attributes.remove();
1968             }
1969         }
1970         // now - anything left in the new attribute list needs to be added
1971         roleMemberBo.getAttributeDetails().addAll(newAttributeBos);
1972 
1973         // FIXME : this code does not delete removed attributes
1974 //        while ( newAttributes.hasNext() ) {
1975 //            RoleMemberAttributeDataBo newAttr = newAttributes.next();
1976 //            boolean matched = false;
1977 //            for (RoleMemberAttributeDataBo roleMemberAttrDataBo :  roleMemberBo.getAttributeDetails()) {
1978 //                if (newAttr.getKimTypeId().equals(roleMemberAttrDataBo.getKimTypeId())
1979 //                        && newAttr.getKimAttributeId().equals(roleMemberAttrDataBo.getKimAttributeId())) {
1980 //                    roleMemberAttrDataBo.setAttributeValue( newAttr.getAttributeValue() );
1981 //                    matched = true;
1982 //                    newAttributes.remove();
1983 //                    break;
1984 //                }
1985 //            }
1986 //            if (!matched) {
1987 //                newAttr.setAssignedToId(roleMemberBo.getId());
1988 //                roleMemberBo.getAttributeDetails().add(newAttr);
1989 //                newAttributes.remove();
1990 //            }
1991 //        }
1992 
1993         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(roleMemberBo));
1994     }
1995 
1996     @Override
1997     public DelegateMember updateDelegateMember(@WebParam(
1998             name = "delegateMember") DelegateMember delegateMember) throws RiceIllegalArgumentException, RiceIllegalStateException {
1999 
2000         //check delegateMember not empty
2001         incomingParamCheck(delegateMember, "delegateMember");
2002 
2003         //check delegate exists
2004         String delegationId =  delegateMember.getDelegationId();
2005         incomingParamCheck(delegationId,"delegationId");
2006 
2007         DelegateTypeBo delegate = getKimDelegationImpl(delegationId);
2008         DelegateMemberBo  delegateMemberBo = null;
2009 
2010         if (StringUtils.isNotEmpty(delegateMember.getDelegationMemberId())) {
2011             delegateMemberBo = getDelegateMemberBo(delegateMember.getDelegationMemberId());
2012         }
2013         if(delegateMemberBo==null)   {
2014             throw new RiceIllegalStateException("the delegate does not exist: " + delegationId);
2015         }
2016 
2017         // copy the main data
2018         delegateMemberBo.setActiveFromDateValue(delegateMember.getActiveFromDate() == null ? null : new Timestamp(delegateMember.getActiveFromDate().getMillis()));
2019         delegateMemberBo.setActiveToDateValue(delegateMember.getActiveToDate() == null ? null : new Timestamp(delegateMember.getActiveToDate().getMillis()));
2020         delegateMemberBo.setMemberId(delegateMember.getMemberId());
2021         delegateMemberBo.setRoleMemberId(delegateMember.getRoleMemberId());
2022         delegateMemberBo.setTypeCode(delegateMember.getType().getCode());
2023 
2024         String kimTypeId = delegate.getKimTypeId();
2025         List<DelegateMemberAttributeDataBo> newAttributeBos = KimAttributeDataBo.createFrom(DelegateMemberAttributeDataBo.class, delegateMember.getAttributes(), kimTypeId);
2026         Iterator<DelegateMemberAttributeDataBo> attributes = delegateMemberBo.getAttributeDetails().iterator();
2027         // loop over the existing attributes
2028         while ( attributes.hasNext() ) {
2029             DelegateMemberAttributeDataBo attr = attributes.next();
2030             // look for a match in the new list
2031             boolean matched = false;
2032             Iterator<DelegateMemberAttributeDataBo> newAttributes = newAttributeBos.iterator();
2033             while ( newAttributes.hasNext() ) {
2034                 DelegateMemberAttributeDataBo newAttr = newAttributes.next();
2035                 if (newAttr.getKimTypeId().equals(attr.getKimTypeId())
2036                         && newAttr.getKimAttributeId().equals(attr.getKimAttributeId())) {
2037                     matched = true;
2038                     attr.setAttributeValue( newAttr.getAttributeValue() );
2039                     newAttributes.remove(); // processed this one - we don't want to see it again
2040                     break;
2041                 }
2042             }
2043             if ( !matched ) {
2044                 // nothing in the new list matched - this means this attribute was deleted
2045                 attributes.remove();
2046             }
2047         }
2048         // now - anything left in the new attribute list needs to be added
2049         delegateMemberBo.getAttributeDetails().addAll(newAttributeBos);
2050 
2051         return DelegateMemberBo.to(getResponsibilityInternalService().saveDelegateMember(delegateMemberBo));
2052     }
2053 
2054     @Override
2055     public DelegateMember createDelegateMember(@WebParam(
2056             name = "delegateMember") DelegateMember delegateMember) throws RiceIllegalArgumentException, RiceIllegalStateException {
2057         //ensure object not empty
2058         incomingParamCheck(delegateMember, "delegateMember");
2059 
2060         //check key is null
2061         if(delegateMember.getDelegationMemberId()!=null )   {
2062             throw new RiceIllegalStateException("the delegate member already exists: " + delegateMember.getDelegationMemberId());
2063         }
2064 
2065         //check delegate exists
2066         String delegationId =  delegateMember.getDelegationId();
2067         incomingParamCheck(delegationId,"delegationId");
2068         DelegateTypeBo delegate = getKimDelegationImpl(delegationId);
2069         if(delegate==null)   {
2070             throw new RiceIllegalStateException("the delegate does not exist: " + delegationId);
2071         }
2072 
2073         //check member exists
2074         String memberId = delegateMember.getMemberId();
2075         incomingParamCheck(memberId,"memberId");
2076         Principal kPrincipal = KimApiServiceLocator.getIdentityService().getPrincipal(memberId);
2077         if(kPrincipal==null){
2078             throw new RiceIllegalStateException("the user does not exist: " + memberId);
2079         }
2080 
2081         //create member delegate
2082         String kimTypeId = getRoleBoLite(delegate.getRoleId()).getKimTypeId();
2083         List<DelegateMemberAttributeDataBo> attrBos = Collections.emptyList();
2084         attrBos = KimAttributeDataBo.createFrom(DelegateMemberAttributeDataBo.class, delegateMember.getAttributes(), kimTypeId);
2085         DelegateMemberBo bo = DelegateMemberBo.from(delegateMember);
2086         bo.setAttributeDetails(attrBos);
2087         return DelegateMemberBo.to(getResponsibilityInternalService().saveDelegateMember(bo));
2088     }
2089 
2090     @Override
2091     public void removeDelegateMembers(@WebParam(
2092             name = "delegateMembers") List<DelegateMember> delegateMembers) throws RiceIllegalArgumentException, RiceIllegalStateException {
2093         incomingParamCheck(delegateMembers, "delegateMembers");
2094         for (DelegateMember delegateMember : delegateMembers) {
2095             DelegateMember.Builder delegateMemberInfo = DelegateMember.Builder.create();
2096             delegateMemberInfo.setDelegationMemberId(delegateMember.getDelegationMemberId());
2097             delegateMemberInfo.setAttributes(delegateMember.getAttributes());
2098             delegateMemberInfo.setDelegationId(delegateMember.getDelegationId());
2099             delegateMemberInfo.setMemberId(delegateMember.getMemberId());
2100             delegateMemberInfo.setRoleMemberId(delegateMember.getRoleMemberId());
2101             delegateMemberInfo.setType(delegateMember.getType());
2102             delegateMemberInfo.setActiveFromDate(delegateMember.getActiveFromDate());
2103             delegateMemberInfo.setActiveToDate(DateTime.now());
2104             updateDelegateMember(delegateMemberInfo.build());
2105         }
2106     }
2107 
2108     @Override
2109     public RoleResponsibilityAction createRoleResponsibilityAction(RoleResponsibilityAction roleResponsibilityAction)
2110             throws RiceIllegalArgumentException, RiceIllegalStateException {
2111         incomingParamCheck(roleResponsibilityAction, "roleResponsibilityAction");
2112 
2113 
2114         if (StringUtils.isNotBlank(roleResponsibilityAction.getId())
2115                 && getRoleResponsibilityActionBo(roleResponsibilityAction.getId()) != null) {
2116             throw new RiceIllegalStateException("the roleResponsibilityAction to create already exists: " + roleResponsibilityAction);
2117         }
2118 
2119         RoleResponsibilityActionBo bo = RoleResponsibilityActionBo.from(roleResponsibilityAction);
2120         return RoleResponsibilityActionBo.to(getDataObjectService().save(bo));
2121     }
2122 
2123     /**
2124      * Queues ActionRequest refresh/regeneration for RoleResponsbilityAction change
2125      * @param bo the changed or deleted RoleResponsibilityActionBo
2126      */
2127     protected void updateActionRequestsForRoleResponsibilityActionChange(RoleResponsibilityActionBo bo) {
2128         RoleResponsibilityBo rr = bo.getRoleResponsibility();
2129         if (rr != null) {
2130             getResponsibilityInternalService().updateActionRequestsForResponsibilityChange(Collections.singleton(rr.getResponsibilityId()));
2131         }
2132     }
2133 
2134     @Override
2135     public RoleResponsibilityAction updateRoleResponsibilityAction(RoleResponsibilityAction roleResponsibilityAction)
2136             throws RiceIllegalArgumentException, RiceIllegalStateException {
2137         incomingParamCheck(roleResponsibilityAction, "roleResponsibilityAction");
2138 
2139         if (StringUtils.isBlank(roleResponsibilityAction.getId()) || getRoleResponsibilityActionBo(roleResponsibilityAction.getId()) == null) {
2140             throw new RiceIllegalStateException("the roleResponsibilityAction to create does not exist: " + roleResponsibilityAction);
2141         }
2142 
2143         RoleResponsibilityActionBo bo = RoleResponsibilityActionBo.from(roleResponsibilityAction);
2144         roleResponsibilityAction = RoleResponsibilityActionBo.to(getDataObjectService().save(bo));
2145 
2146         // update action requests
2147         updateActionRequestsForRoleResponsibilityActionChange(bo);
2148 
2149         return roleResponsibilityAction;
2150     }
2151 
2152     @Override
2153     public void deleteRoleResponsibilityAction(String roleResponsibilityActionId)
2154             throws RiceIllegalArgumentException, RiceIllegalStateException {
2155         incomingParamCheck(roleResponsibilityActionId, "roleResponsibilityActionId");
2156 
2157         RoleResponsibilityActionBo bo = getRoleResponsibilityActionBo(roleResponsibilityActionId);
2158         if (StringUtils.isBlank(roleResponsibilityActionId) || bo == null) {
2159             throw new RiceIllegalStateException("the roleResponsibilityAction to delete does not exist: " + roleResponsibilityActionId);
2160         }
2161 
2162         getDataObjectService().delete(bo);
2163 
2164         // update action requests
2165         updateActionRequestsForRoleResponsibilityActionChange(bo);
2166     }
2167 
2168     @Override
2169     public DelegateType createDelegateType(DelegateType delegateType) throws RiceIllegalArgumentException, RiceIllegalStateException {
2170         incomingParamCheck(delegateType, "delegateType");
2171 
2172         if (StringUtils.isNotBlank(delegateType.getDelegationId())
2173                 && getDelegateTypeByDelegationId(delegateType.getDelegationId()) != null) {
2174             throw new RiceIllegalStateException("the delegateType to create already exists: " + delegateType);
2175         }
2176 
2177         DelegateTypeBo bo = DelegateTypeBo.from(delegateType);
2178         return DelegateTypeBo.to(getDataObjectService().save(bo));
2179     }
2180 
2181     @Override
2182     public DelegateType updateDelegateType(DelegateType delegateType) throws RiceIllegalArgumentException, RiceIllegalStateException {
2183         incomingParamCheck(delegateType, "delegateType");
2184 
2185         if (StringUtils.isBlank(delegateType.getDelegationId())
2186                 || getDelegateTypeByDelegationId(delegateType.getDelegationId()) == null) {
2187             throw new RiceIllegalStateException("the delegateType to update does not exist: " + delegateType);
2188         }
2189 
2190         DelegateTypeBo bo = DelegateTypeBo.from(delegateType);
2191         return DelegateTypeBo.to(getDataObjectService().save(bo));
2192     }
2193 
2194 
2195     private void removeRoleMembers(List<RoleMemberBo> members) {
2196         if(CollectionUtils.isNotEmpty(members)) {
2197             for ( RoleMemberBo rm : members ) {
2198                 getResponsibilityInternalService().removeRoleMember(rm);
2199             }
2200         }
2201     }
2202 
2203 
2204     private List<RoleMemberBo> getRoleMembersByDefaultStrategy(String roleId, String memberId, String memberTypeCode, Map<String, String> qualifier) {
2205         List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
2206         List<RoleMemberBo> roleMem = getRoleMembershipsForMemberId(memberTypeCode,memberId,qualifier);
2207         for ( RoleMemberBo rm : roleMem ) {
2208             if ( rm.getRoleId().equals(roleId) ) {
2209                 // if found, remove
2210                 rms.add(rm);
2211             }
2212         }
2213         return rms;
2214     }
2215 
2216     @Override
2217     public void removePrincipalFromRole(String principalId,
2218             String namespaceCode, String roleName, Map<String, String> qualifier) throws RiceIllegalArgumentException {
2219         if (StringUtils.isBlank(principalId)) {
2220             throw new RiceIllegalArgumentException("principalId is null");
2221         }
2222 
2223         if (StringUtils.isBlank(namespaceCode)) {
2224             throw new RiceIllegalArgumentException("namespaceCode is null");
2225         }
2226 
2227         if (StringUtils.isBlank(roleName)) {
2228             throw new RiceIllegalArgumentException("roleName is null");
2229         }
2230 
2231         if (qualifier == null) {
2232             throw new RiceIllegalArgumentException("qualifier is null");
2233         }
2234         // look up the role
2235         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
2236         // pull all the principal members
2237         // look for an exact qualifier match
2238         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(role, principalId, memberTypeToRoleDaoActionMap.get(MemberType.PRINCIPAL.getCode()), qualifier);
2239         if(CollectionUtils.isEmpty(rms)) {
2240             rms = getRoleMembersByDefaultStrategy(role.getId(), principalId, MemberType.PRINCIPAL.getCode(), qualifier);
2241         }
2242         removeRoleMembers(rms);
2243     }
2244 
2245     @Override
2246     public void removeGroupFromRole(String groupId,
2247             String namespaceCode, String roleName, Map<String, String> qualifier) throws RiceIllegalArgumentException {
2248         if (StringUtils.isBlank(groupId)) {
2249             throw new RiceIllegalArgumentException("groupId is null");
2250         }
2251 
2252         if (StringUtils.isBlank(namespaceCode)) {
2253             throw new RiceIllegalArgumentException("namespaceCode is null");
2254         }
2255 
2256         if (StringUtils.isBlank(roleName)) {
2257             throw new RiceIllegalArgumentException("roleName is null");
2258         }
2259 
2260         if (qualifier == null) {
2261             throw new RiceIllegalArgumentException("qualifier is null");
2262         }
2263 
2264         // look up the roleBo
2265         RoleBoLite roleBo = getRoleBoLiteByName(namespaceCode, roleName);
2266         // pull all the group roleBo members
2267         // look for an exact qualifier match
2268         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(roleBo, groupId, memberTypeToRoleDaoActionMap.get(MemberType.GROUP.getCode()), qualifier);
2269         if(CollectionUtils.isEmpty(rms)) {
2270             rms = getRoleMembersByDefaultStrategy(roleBo.getId(), groupId, MemberType.GROUP.getCode(), qualifier);
2271         }
2272         removeRoleMembers(rms);
2273     }
2274 
2275     @Override
2276     public void removeRoleFromRole(String roleId,
2277             String namespaceCode, String roleName, Map<String, String> qualifier) throws RiceIllegalArgumentException {
2278         incomingParamCheck(roleId, "roleId");
2279         incomingParamCheck(namespaceCode, "namespaceCode");
2280         incomingParamCheck(roleName, "roleName");
2281         incomingParamCheck(qualifier, "qualifier");
2282 
2283 
2284         // look up the role
2285         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
2286         // pull all the group role members
2287         // look for an exact qualifier match
2288         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(role, roleId, memberTypeToRoleDaoActionMap.get(MemberType.ROLE.getCode()), qualifier);
2289         if(CollectionUtils.isEmpty(rms)) {
2290             rms = getRoleMembersByDefaultStrategy(role.getId(), roleId, MemberType.ROLE.getCode(), qualifier);
2291         }
2292         removeRoleMembers(rms);
2293     }
2294 
2295     @Override
2296     public void assignPermissionToRole(String permissionId, String roleId) throws RiceIllegalArgumentException {
2297         incomingParamCheck(permissionId, "permissionId");
2298         incomingParamCheck(roleId, "roleId");
2299 
2300         RolePermissionBo newRolePermission = new RolePermissionBo();
2301 
2302         Long nextSeq = new Long(MaxValueIncrementerFactory.getIncrementer(getDataSource(),
2303                 KimConstants.SequenceNames.KRIM_ROLE_PERM_ID_S).nextLongValue());
2304 
2305         newRolePermission.setId(nextSeq.toString());
2306         newRolePermission.setRoleId(roleId);
2307         newRolePermission.setPermissionId(permissionId);
2308         newRolePermission.setActive(true);
2309 
2310         getDataObjectService().save(newRolePermission);
2311     }
2312 
2313     @Override
2314     public void revokePermissionFromRole(String permissionId, String roleId) throws RiceIllegalArgumentException {
2315         incomingParamCheck(permissionId, "permissionId");
2316         incomingParamCheck(roleId, "roleId");
2317 
2318         Map<String, Object> params = new HashMap<String, Object>();
2319         params.put("roleId", roleId);
2320         params.put("permissionId", permissionId);
2321         params.put("active", Boolean.TRUE);
2322         QueryResults<RolePermissionBo> rolePermissionBos = getDataObjectService().findMatching(RolePermissionBo.class, QueryByCriteria.Builder.andAttributes(params).build());
2323         List<RolePermissionBo> rolePermsToSave = new ArrayList<RolePermissionBo>();
2324         for (RolePermissionBo rolePerm : rolePermissionBos.getResults()) {
2325             rolePerm.setActive(false);
2326             rolePermsToSave.add(rolePerm);
2327         }
2328 
2329         getDataObjectService().save(rolePermsToSave);
2330     }
2331 
2332     protected void addMemberAttributeData(RoleMemberBo roleMember, Map<String, String> qualifier, String kimTypeId) {
2333         List<RoleMemberAttributeDataBo> attributes = new ArrayList<RoleMemberAttributeDataBo>();
2334         for (Map.Entry<String, String> entry : qualifier.entrySet()) {
2335             RoleMemberAttributeDataBo roleMemberAttrBo = new RoleMemberAttributeDataBo();
2336             roleMemberAttrBo.setAttributeValue(entry.getValue());
2337             roleMemberAttrBo.setKimTypeId(kimTypeId);
2338             roleMemberAttrBo.setAssignedToId(roleMember.getId());
2339             // look up the attribute ID
2340             roleMemberAttrBo.setKimAttributeId(getKimAttributeId(kimTypeId, entry.getKey()));
2341 
2342             Map<String, String> criteria = new HashMap<String, String>();
2343             criteria.put(KimConstants.PrimaryKeyConstants.KIM_ATTRIBUTE_ID, roleMemberAttrBo.getKimAttributeId());
2344             //criteria.put(KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMember.getId());
2345             criteria.put("assignedToId", roleMember.getId());
2346             QueryResults<RoleMemberAttributeDataBo> origRoleMemberAttributes =
2347                     getDataObjectService().findMatching(RoleMemberAttributeDataBo.class, QueryByCriteria.Builder.andAttributes(criteria).build());
2348             RoleMemberAttributeDataBo origRoleMemberAttribute =
2349                     (!origRoleMemberAttributes.getResults().isEmpty()) ? origRoleMemberAttributes.getResults().get(0) : null;
2350             if (origRoleMemberAttribute != null) {
2351                 roleMemberAttrBo.setId(origRoleMemberAttribute.getId());
2352                 roleMemberAttrBo.setVersionNumber(origRoleMemberAttribute.getVersionNumber());
2353             }
2354             attributes.add(roleMemberAttrBo);
2355         }
2356         roleMember.setAttributeDetails(attributes);
2357     }
2358 
2359     protected void addDelegationMemberAttributeData( DelegateMemberBo delegationMember, Map<String, String> qualifier, String kimTypeId ) {
2360         List<DelegateMemberAttributeDataBo> attributes = new ArrayList<DelegateMemberAttributeDataBo>();
2361         for (  Map.Entry<String, String> entry : qualifier.entrySet() ) {
2362             DelegateMemberAttributeDataBo delegateMemberAttrBo = new DelegateMemberAttributeDataBo();
2363             delegateMemberAttrBo.setAttributeValue(entry.getValue());
2364             delegateMemberAttrBo.setKimTypeId(kimTypeId);
2365             delegateMemberAttrBo.setAssignedToId(delegationMember.getDelegationMemberId());
2366             // look up the attribute ID
2367             delegateMemberAttrBo.setKimAttributeId(getKimAttributeId(kimTypeId, entry.getKey()));
2368             Map<String, String> criteria = new HashMap<String, String>();
2369             criteria.put(KimConstants.PrimaryKeyConstants.KIM_ATTRIBUTE_ID, delegateMemberAttrBo.getKimAttributeId());
2370             criteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMember.getDelegationMemberId());
2371             QueryResults<DelegateMemberAttributeDataBo> origDelegationMemberAttributes =
2372                     getDataObjectService().findMatching(DelegateMemberAttributeDataBo.class, QueryByCriteria.Builder.andAttributes(criteria).build());
2373             DelegateMemberAttributeDataBo origDelegationMemberAttribute =
2374                     (!origDelegationMemberAttributes.getResults().isEmpty()) ? origDelegationMemberAttributes.getResults().get(0) : null;
2375             if(origDelegationMemberAttribute!=null){
2376                 delegateMemberAttrBo.setId(origDelegationMemberAttribute.getId());
2377                 delegateMemberAttrBo.setVersionNumber(origDelegationMemberAttribute.getVersionNumber());
2378             }
2379             attributes.add( delegateMemberAttrBo );
2380         }
2381         delegationMember.setAttributeDetails( attributes );
2382     }
2383 
2384 
2385 
2386     // --------------------
2387     // Persistence Methods
2388     // --------------------
2389 
2390     private void deleteNullMemberAttributeData(List<RoleMemberAttributeDataBo> attributes) {
2391         List<RoleMemberAttributeDataBo> attributesToDelete = new ArrayList<RoleMemberAttributeDataBo>();
2392         for(RoleMemberAttributeDataBo attribute: attributes){
2393             if(attribute.getAttributeValue()==null){
2394                 attributesToDelete.add(attribute);
2395             }
2396         }
2397         getDataObjectService().delete(attributesToDelete);
2398     }
2399 
2400     private void deleteNullDelegationMemberAttributeData(List<DelegateMemberAttributeDataBo> attributes) {
2401         List<DelegateMemberAttributeDataBo> attributesToDelete = new ArrayList<DelegateMemberAttributeDataBo>();
2402 
2403         for(DelegateMemberAttributeDataBo attribute: attributes){
2404             if(attribute.getAttributeValue()==null){
2405                 attributesToDelete.add(attribute);
2406             }
2407         }
2408         getDataObjectService().delete(attributesToDelete);
2409     }
2410 
2411     protected void logPrincipalHasRoleCheck(String principalId, List<String> roleIds, Map<String, String> roleQualifiers ) {
2412         StringBuilder sb = new StringBuilder();
2413         sb.append(  '\n' );
2414         sb.append( "Has Role     : " ).append( roleIds ).append( '\n' );
2415         if ( roleIds != null ) {
2416             for ( String roleId : roleIds ) {
2417                 Role role = getRole( roleId );
2418                 if ( role != null ) {
2419                     sb.append( "        Name : " ).append( role.getNamespaceCode() ).append( '/').append( role.getName() );
2420                     sb.append( " (" ).append( roleId ).append( ')' );
2421                     sb.append( '\n' );
2422                 }
2423             }
2424         }
2425         sb.append( "   Principal : " ).append( principalId );
2426         if ( principalId != null ) {
2427             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
2428             if ( principal != null ) {
2429                 sb.append( " (" ).append( principal.getPrincipalName() ).append( ')' );
2430             }
2431         }
2432         sb.append( '\n' );
2433         sb.append( "     Details :\n" );
2434         if ( roleQualifiers != null ) {
2435             sb.append( roleQualifiers );
2436         } else {
2437             sb.append( "               [null]\n" );
2438         }
2439         if (LOG.isTraceEnabled()) {
2440             LOG.trace( sb.append(ExceptionUtils.getStackTrace(new Throwable())));
2441         } else {
2442             LOG.debug(sb.toString());
2443         }
2444     }
2445 
2446     private void incomingParamCheck(Object object, String name) {
2447         if (object == null) {
2448             throw new RiceIllegalArgumentException(name + " was null");
2449         } else if (object instanceof String
2450                 && StringUtils.isBlank((String) object)) {
2451             throw new RiceIllegalArgumentException(name + " was blank");
2452         }
2453     }
2454 
2455     /**
2456      * This gets the proxied version of the role service which will go through
2457      * Spring's caching mechanism for method calls rather than skipping it when
2458      * methods are called directly.
2459      *
2460      * @return The proxied role service
2461      */
2462     protected RoleService getProxiedRoleService() {
2463         if(this.proxiedRoleService == null) {
2464             this.proxiedRoleService = KimApiServiceLocator.getRoleService();
2465         }
2466         return this.proxiedRoleService;
2467     }
2468 
2469     /**
2470      * Sets the cache manager which this service implementation can for internal caching.
2471      * Calling this setter is optional, though the value passed to it must not be null.
2472      *
2473      * @param cacheManager the cache manager to use for internal caching, must not be null
2474      * @throws IllegalArgumentException if a null cache manager is passed
2475      */
2476     public void setCacheManager(CacheManager cacheManager) {
2477         if (cacheManager == null) {
2478             throw new IllegalArgumentException("cacheManager must not be null");
2479         }
2480         this.cacheManager = cacheManager;
2481     }
2482 
2483     protected DataSource getDataSource() {
2484         return KimImplServiceLocator.getDataSource();
2485     }
2486 
2487     private static class VersionedService<T> {
2488 
2489         String version;
2490         T service;
2491 
2492         VersionedService(String version, T service) {
2493             this.version = version;
2494             this.service = service;
2495         }
2496 
2497         T getService() {
2498             return this.service;
2499         }
2500 
2501         String getVersion() {
2502             return this.version;
2503         }
2504 
2505     }
2506 
2507     protected VersionedService<RoleTypeService> getVersionedRoleTypeService(KimType typeInfo) {
2508         QName serviceName = KimTypeUtils.resolveKimTypeServiceName(typeInfo.getServiceName());
2509         if (serviceName != null) {
2510             // default version since the base services have been available since then
2511             String version = CoreConstants.Versions.VERSION_2_0_0;
2512             RoleTypeService roleTypeService = null;
2513 
2514             try {
2515 
2516                 ServiceBus serviceBus = KsbApiServiceLocator.getServiceBus();
2517                 Endpoint endpoint = serviceBus.getEndpoint(serviceName);
2518                 if (endpoint != null) {
2519                     version = endpoint.getServiceConfiguration().getServiceVersion();
2520                 }
2521                 KimTypeService service = GlobalResourceLoader.getService(serviceName);
2522                 if (service != null && service instanceof RoleTypeService) {
2523                     roleTypeService = (RoleTypeService) service;
2524                 } else {
2525                     roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
2526                 }
2527             } catch (Exception ex) {
2528                 roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
2529             }
2530 
2531             return new VersionedService<RoleTypeService>(version, roleTypeService);
2532         }
2533 
2534         return null;
2535     }
2536 
2537     private Map<String, String> getNestedQualification(RoleBoLite memberRole, String namespaceCode, String roleName,
2538             String memberNamespaceCode, String memberName, Map<String, String> qualification,
2539             Map<String, String> memberQualification) {
2540         VersionedService<RoleTypeService> versionedRoleTypeService = getVersionedRoleTypeService(KimTypeBo.to(memberRole.getKimRoleType()));
2541         // if null service - just return the original qualification (pre 2.3.4 - ignoring memberQualifications)
2542         if ( versionedRoleTypeService == null ) {
2543             return qualification;
2544         }
2545         boolean versionOk = VersionHelper.compareVersion(versionedRoleTypeService.getVersion(),
2546                 CoreConstants.Versions.VERSION_2_3_4) != -1;
2547         if (versionOk) {
2548             return versionedRoleTypeService.getService().convertQualificationForMemberRolesAndMemberAttributes(namespaceCode, roleName,
2549                     memberNamespaceCode, memberName, qualification, memberQualification);
2550         } else {
2551             return versionedRoleTypeService.getService().convertQualificationForMemberRoles(namespaceCode, roleName,
2552                     memberNamespaceCode, memberName, qualification);
2553         }
2554 
2555     }
2556 
2557 }