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