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