View Javadoc

1   package org.kuali.rice.kim.impl.responsibility;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.apache.commons.logging.Log;
5   import org.apache.commons.logging.LogFactory;
6   import org.kuali.rice.core.api.criteria.CriteriaLookupService;
7   import org.kuali.rice.core.api.criteria.GenericQueryResults;
8   import org.kuali.rice.core.api.criteria.LookupCustomizer;
9   import org.kuali.rice.core.api.criteria.Predicate;
10  import org.kuali.rice.core.api.criteria.QueryByCriteria;
11  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
12  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
13  import org.kuali.rice.core.api.mo.common.Attributes;
14  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
15  import org.kuali.rice.core.util.AttributeSet;
16  import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
17  import org.kuali.rice.kim.api.common.delegate.Delegate;
18  import org.kuali.rice.kim.api.common.template.Template;
19  import org.kuali.rice.kim.api.common.template.TemplateQueryResults;
20  import org.kuali.rice.kim.api.responsibility.Responsibility;
21  import org.kuali.rice.kim.api.responsibility.ResponsibilityAction;
22  import org.kuali.rice.kim.api.responsibility.ResponsibilityQueryResults;
23  import org.kuali.rice.kim.api.responsibility.ResponsibilityService;
24  import org.kuali.rice.kim.api.role.Role;
25  import org.kuali.rice.kim.api.role.RoleResponsibilityAction;
26  import org.kuali.rice.kim.api.type.KimType;
27  import org.kuali.rice.kim.api.type.KimTypeInfoService;
28  import org.kuali.rice.kim.bo.role.dto.DelegateInfo;
29  import org.kuali.rice.kim.bo.role.dto.RoleMembershipInfo;
30  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
31  import org.kuali.rice.kim.impl.role.RoleResponsibilityActionBo;
32  import org.kuali.rice.kim.impl.role.RoleResponsibilityBo;
33  import org.kuali.rice.kim.service.RoleService;
34  import org.kuali.rice.kim.util.KIMPropertyConstants;
35  import org.kuali.rice.kns.service.BusinessObjectService;
36  
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
45  
46  public class ResponsibilityServiceImpl implements ResponsibilityService {
47  
48      private static final Integer DEFAULT_PRIORITY_NUMBER = Integer.valueOf(1);
49      private static final Log LOG = LogFactory.getLog(ResponsibilityServiceImpl.class);
50  
51      private BusinessObjectService businessObjectService;
52      private CriteriaLookupService criteriaLookupService;
53      private KimResponsibilityTypeService defaultResponsibilityTypeService;
54      private KimTypeInfoService kimTypeInfoService;
55      private RoleService roleService;
56  
57      @Override
58      public void createResponsibility(final Responsibility responsibility) throws RiceIllegalArgumentException, RiceIllegalStateException {
59          if (responsibility == null) {
60              throw new RiceIllegalArgumentException("responsibility is null");
61          }
62  
63          if (StringUtils.isNotBlank(responsibility.getId()) && getResponsibility(responsibility.getId()) != null) {
64              throw new RiceIllegalStateException("the responsibility to create already exists: " + responsibility);
65          }
66          List<ResponsibilityAttributeBo> attrBos = KimAttributeDataBo.createFrom(ResponsibilityAttributeBo.class, responsibility.getAttributes(), responsibility.getTemplate().getKimTypeId());
67          ResponsibilityBo bo = ResponsibilityBo.from(responsibility);
68          bo.setAttributeDetails(attrBos);
69          businessObjectService.save(bo);
70      }
71  
72      @Override
73      public void updateResponsibility(final Responsibility responsibility) throws RiceIllegalArgumentException, RiceIllegalStateException {
74          if (responsibility == null) {
75              throw new RiceIllegalArgumentException("responsibility is null");
76          }
77  
78          if (StringUtils.isBlank(responsibility.getId()) || getResponsibility(responsibility.getId()) == null) {
79              throw new RiceIllegalStateException("the responsibility does not exist: " + responsibility);
80          }
81  
82          List<ResponsibilityAttributeBo> attrBos = KimAttributeDataBo.createFrom(ResponsibilityAttributeBo.class, responsibility.getAttributes(), responsibility.getTemplate().getKimTypeId());
83          ResponsibilityBo bo = ResponsibilityBo.from(responsibility);
84          bo.getAttributeDetails().addAll(attrBos);
85          businessObjectService.save(bo);
86      }
87  
88      @Override
89      public Responsibility getResponsibility(final String id) {
90          if (id == null) {
91              throw new RiceIllegalArgumentException("id is null");
92          }
93  
94          return ResponsibilityBo.to(businessObjectService.findBySinglePrimaryKey(ResponsibilityBo.class, id));
95      }
96  
97      @Override
98      public List<Responsibility> findRespsByNamespaceCodeAndName(final String namespaceCode, final String name) {
99          if (namespaceCode == null) {
100             throw new RiceIllegalArgumentException("namespaceCode is null");
101         }
102 
103         if (name == null) {
104             throw new RiceIllegalArgumentException("name is null");
105         }
106 
107         final Map<String, String> crit = new HashMap<String, String>();
108         crit.put("namespaceCode", namespaceCode);
109         crit.put("name", name);
110         crit.put("active", "Y");
111 
112         final Collection<ResponsibilityBo> bos = businessObjectService.findMatching(ResponsibilityBo.class, Collections.unmodifiableMap(crit));
113         final List<Responsibility> ims = new ArrayList<Responsibility>();
114         if (bos != null) {
115             for (ResponsibilityBo bo : bos) {
116                 if (bo != null) {
117                     ims.add(ResponsibilityBo.to(bo));
118                 }
119             }
120         }
121 
122         return Collections.unmodifiableList(ims);
123     }
124 
125     @Override
126     public Template getResponsibilityTemplate(final String id) {
127         if (id == null) {
128             throw new RiceIllegalArgumentException("id is null");
129         }
130 
131         return ResponsibilityTemplateBo.to(businessObjectService.findBySinglePrimaryKey(ResponsibilityTemplateBo.class, id));
132     }
133 
134     @Override
135     public List<Template> findRespTemplatesByNamespaceCodeAndName(final String namespaceCode, final String name) {
136         if (namespaceCode == null) {
137             throw new RiceIllegalArgumentException("namespaceCode is null");
138         }
139 
140         if (name == null) {
141             throw new RiceIllegalArgumentException("name is null");
142         }
143 
144         final Map<String, String> crit = new HashMap<String, String>();
145         crit.put("namespaceCode", namespaceCode);
146         crit.put("name", name);
147         crit.put("active", "Y");
148 
149         final Collection<ResponsibilityTemplateBo> bos = businessObjectService.findMatching(ResponsibilityTemplateBo.class, Collections.unmodifiableMap(crit));
150         final List<Template> ims = new ArrayList<Template>();
151         if (bos != null) {
152             for (ResponsibilityTemplateBo bo : bos) {
153                 if (bo != null) {
154                     ims.add(ResponsibilityTemplateBo.to(bo));
155                 }
156             }
157         }
158 
159         return Collections.unmodifiableList(ims);
160     }
161 
162     @Override
163     public boolean hasResponsibility(final String principalId, final String namespaceCode, final String respName, final Attributes qualification, final Attributes responsibilityDetails) {
164         // get all the responsibility objects whose name match that requested
165         final List<Responsibility> responsibilities = findRespsByNamespaceCodeAndName(namespaceCode, respName);
166         return hasResp(principalId, namespaceCode, responsibilities, qualification, responsibilityDetails);
167     }
168 
169     @Override
170     public boolean hasResponsibilityByTemplateName(final String principalId, final String namespaceCode, final String respTemplateName, final Attributes qualification, final Attributes responsibilityDetails) {
171         // get all the responsibility objects whose name match that requested
172         final List<Responsibility> responsibilities = findRespsByNamespaceCodeAndTemplateName(namespaceCode, respTemplateName);
173         return hasResp(principalId, namespaceCode, responsibilities, qualification, responsibilityDetails);
174     }
175 
176     private boolean hasResp(final String principalId, final String namespaceCode, final List<Responsibility> responsibilities, final Attributes qualification, final Attributes responsibilityDetails) {
177         // now, filter the full list by the detail passed
178         final List<String> ids = new ArrayList<String>();
179         for (Responsibility r : getMatchingResponsibilities(responsibilities, responsibilityDetails)) {
180             ids.add(r.getId());
181         }
182         final List<String> roleIds = getRoleIdsForResponsibilities(ids, qualification);
183         return roleService.principalHasRole(principalId, roleIds, new AttributeSet(qualification.toMap()));
184     }
185 
186     @Override
187     public List<ResponsibilityAction> getResponsibilityActions(final String namespaceCode, final String responsibilityName, final Attributes qualification, final Attributes responsibilityDetails) {
188         // get all the responsibility objects whose name match that requested
189         List<Responsibility> responsibilities = findRespsByNamespaceCodeAndName(namespaceCode, responsibilityName);
190         return getRespActions(namespaceCode, responsibilities, qualification, responsibilityDetails);
191     }
192 
193     @Override
194     public List<ResponsibilityAction> getResponsibilityActionsByTemplateName(final String namespaceCode, final String respTemplateName, final Attributes qualification, final Attributes responsibilityDetails) {
195         // get all the responsibility objects whose name match that requested
196         List<Responsibility> responsibilities = findRespsByNamespaceCodeAndTemplateName(namespaceCode, respTemplateName);
197         return getRespActions(namespaceCode, responsibilities, qualification, responsibilityDetails);
198     }
199 
200     private List<ResponsibilityAction> getRespActions(final String namespaceCode, final List<Responsibility> responsibilities, final Attributes qualification, final Attributes responsibilityDetails) {
201         // now, filter the full list by the detail passed
202         List<Responsibility> applicableResponsibilities = getMatchingResponsibilities(responsibilities, responsibilityDetails);
203         List<ResponsibilityAction> results = new ArrayList<ResponsibilityAction>();
204         for (Responsibility r : applicableResponsibilities) {
205             List<String> roleIds = getRoleIdsForResponsibility(r.getId(), qualification);
206             results.addAll(getActionsForResponsibilityRoles(r, roleIds, qualification));
207         }
208         return results;
209     }
210 
211     private List<ResponsibilityAction> getActionsForResponsibilityRoles(Responsibility responsibility, List<String> roleIds, Attributes qualification) {
212         List<ResponsibilityAction> results = new ArrayList<ResponsibilityAction>();
213         Collection<RoleMembershipInfo> roleMembers = roleService.getRoleMembers(roleIds, new AttributeSet(qualification.toMap()));
214         for (RoleMembershipInfo rm : roleMembers) {
215             // only add them to the list if the member ID has been populated
216             if (StringUtils.isNotBlank(rm.getMemberId())) {
217                 final ResponsibilityAction.Builder rai = ResponsibilityAction.Builder.create();
218                 rai.setMemberRoleId(rm.getEmbeddedRoleId());
219                 rai.setRoleId(rm.getRoleId());
220                 rai.setQualifier(Attributes.fromMap(rm.getQualifier()));
221                 final List<Delegate.Builder> bs = new ArrayList<Delegate.Builder>();
222                 for (DelegateInfo d : rm.getDelegates()) {
223                     Delegate.Builder newD = Delegate.Builder.create(d.getDelegationId(), d.getDelegationTypeCode(), d.getMemberId(), d.getMemberTypeCode(), d.getRoleMemberId(), d.getQualifier());
224                     bs.add(newD);
225                 }
226                 rai.setDelegates(bs);
227                 rai.setResponsibilityId(responsibility.getId());
228                 rai.setResponsibilityName(responsibility.getName());
229                 rai.setResponsibilityNamespaceCode(responsibility.getNamespaceCode());
230 
231                 if (rm.getMemberTypeCode().equals(Role.PRINCIPAL_MEMBER_TYPE)) {
232                     rai.setPrincipalId(rm.getMemberId());
233                 } else {
234                     rai.setGroupId(rm.getMemberId());
235                 }
236                 // get associated resp resolution objects
237                 RoleResponsibilityAction action = getResponsibilityAction(rm.getRoleId(), responsibility.getId(), rm.getRoleMemberId());
238                 if (action == null) {
239                     LOG.error("Unable to get responsibility action record for role/responsibility/roleMember: "
240                             + rm.getRoleId() + "/" + responsibility.getId() + "/" + rm.getRoleMemberId());
241                     LOG.error("Skipping this role member in getActionsForResponsibilityRoles()");
242                     continue;
243                 }
244                 // add the data to the ResponsibilityActionInfo objects
245                 rai.setActionTypeCode(action.getActionTypeCode());
246                 rai.setActionPolicyCode(action.getActionPolicyCode());
247                 rai.setPriorityNumber(action.getPriorityNumber() == null ? DEFAULT_PRIORITY_NUMBER : action.getPriorityNumber());
248                 rai.setForceAction(action.isForceAction());
249                 rai.setParallelRoutingGroupingCode((rm.getRoleSortingCode() == null) ? "" : rm.getRoleSortingCode());
250                 rai.setRoleResponsibilityActionId(action.getId());
251                 results.add(rai.build());
252             }
253         }
254         return results;
255     }
256 
257     private RoleResponsibilityAction getResponsibilityAction(String roleId, String responsibilityId, String roleMemberId) {
258         final Predicate p =
259                 or(
260                         and(
261                                 equal("roleResponsibility.responsibilityId", responsibilityId),
262                                 equal("roleResponsibility.roleId", roleId),
263                                 equal("roleResponsibility.active", "Y"),
264                                 or(
265                                         equal(KIMPropertyConstants.RoleMember.ROLE_MEMBER_ID, roleMemberId),
266                                         equal(KIMPropertyConstants.RoleMember.ROLE_MEMBER_ID, "*")
267                                 )
268                         ),
269                         and(
270                                 equal("roleResponsibilityId", "*"),
271                                 equal(KIMPropertyConstants.RoleMember.ROLE_MEMBER_ID, roleMemberId)
272                         )
273                 );
274 
275         final QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
276         builder.setPredicates(p);
277         final GenericQueryResults<RoleResponsibilityActionBo> results = criteriaLookupService.lookup(RoleResponsibilityActionBo.class, builder.build());
278         final List<RoleResponsibilityActionBo> bos = results.getResults();
279         //seems a little dubious that we are just returning the first result...
280         return !bos.isEmpty() ? RoleResponsibilityActionBo.to(bos.get(0)) : null;
281     }
282 
283     @Override
284     public List<String> getRoleIdsForResponsibility(String id, Attributes qualification) {
285         if (StringUtils.isBlank(id)) {
286             throw new RiceIllegalArgumentException("id is blank");
287         }
288 
289         if (qualification == null) {
290             throw new RiceIllegalArgumentException("qualification is null");
291         }
292 
293         final List<String> roleIds = getRoleIdsForPredicate(and(equal("responsibilityId", id), equal("active", "Y")));
294 
295         //TODO filter with qualifiers
296         return roleIds;
297     }
298 
299     @Override
300     public ResponsibilityQueryResults findResponsibilities(final QueryByCriteria queryByCriteria) {
301         if (queryByCriteria == null) {
302             throw new RiceIllegalArgumentException("queryByCriteria is null");
303         }
304 
305         LookupCustomizer.Builder<ResponsibilityBo> lc = LookupCustomizer.Builder.create();
306         lc.setPredicateTransform(AttributeTransform.getInstance());
307 
308         GenericQueryResults<ResponsibilityBo> results = criteriaLookupService.lookup(ResponsibilityBo.class, queryByCriteria, lc.build());
309 
310         ResponsibilityQueryResults.Builder builder = ResponsibilityQueryResults.Builder.create();
311         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
312         builder.setTotalRowCount(results.getTotalRowCount());
313 
314         final List<Responsibility.Builder> ims = new ArrayList<Responsibility.Builder>();
315         for (ResponsibilityBo bo : results.getResults()) {
316             ims.add(Responsibility.Builder.create(bo));
317         }
318 
319         builder.setResults(ims);
320         return builder.build();
321     }
322 
323     @Override
324     public TemplateQueryResults findResponsibilityTemplates(final QueryByCriteria queryByCriteria) {
325         if (queryByCriteria == null) {
326             throw new RiceIllegalArgumentException("queryByCriteria is null");
327         }
328 
329         GenericQueryResults<ResponsibilityTemplateBo> results = criteriaLookupService.lookup(ResponsibilityTemplateBo.class, queryByCriteria);
330 
331         TemplateQueryResults.Builder builder = TemplateQueryResults.Builder.create();
332         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
333         builder.setTotalRowCount(results.getTotalRowCount());
334 
335         final List<Template.Builder> ims = new ArrayList<Template.Builder>();
336         for (ResponsibilityTemplateBo bo : results.getResults()) {
337             ims.add(Template.Builder.create(bo));
338         }
339 
340         builder.setResults(ims);
341         return builder.build();
342     }
343 
344     /**
345      * Compare each of the passed in responsibilities with the given responsibilityDetails.  Those that
346      * match are added to the result list.
347      */
348     private List<Responsibility> getMatchingResponsibilities(List<Responsibility> responsibilities, Attributes responsibilityDetails) {
349         // if no details passed, assume that all match
350         if (responsibilityDetails == null || responsibilityDetails.isEmpty()) {
351             return responsibilities;
352         }
353 
354         final List<Responsibility> applicableResponsibilities = new ArrayList<Responsibility>();
355         // otherwise, attempt to match the permission details
356         // build a map of the template IDs to the type services
357         Map<String, KimResponsibilityTypeService> responsibilityTypeServices = getResponsibilityTypeServicesByTemplateId(responsibilities);
358         // build a map of permissions by template ID
359         Map<String, List<Responsibility>> responsibilityMap = groupResponsibilitiesByTemplate(responsibilities);
360         // loop over the different templates, matching all of the same template against the type
361         // service at once
362         for (Map.Entry<String, List<Responsibility>> respEntry : responsibilityMap.entrySet()) {
363             KimResponsibilityTypeService responsibilityTypeService = responsibilityTypeServices.get(respEntry.getKey());
364             List<Responsibility> responsibilityInfos = respEntry.getValue();
365             if (responsibilityTypeService == null) {
366                 responsibilityTypeService = defaultResponsibilityTypeService;
367             }
368             applicableResponsibilities.addAll(responsibilityTypeService.getMatchingResponsibilities(new AttributeSet(responsibilityDetails.toMap()), responsibilityInfos));
369         }
370         return applicableResponsibilities;
371     }
372 
373     private Map<String, KimResponsibilityTypeService> getResponsibilityTypeServicesByTemplateId(Collection<Responsibility> responsibilities) {
374         Map<String, KimResponsibilityTypeService> responsibilityTypeServices = new HashMap<String, KimResponsibilityTypeService>(responsibilities.size());
375         for (Responsibility responsibility : responsibilities) {
376             final Template t = responsibility.getTemplate();
377             final KimType type = kimTypeInfoService.getKimType(t.getKimTypeId());
378 
379             final String serviceName = type.getServiceName();
380             if (serviceName != null) {
381                 KimResponsibilityTypeService responsibiltyTypeService = GlobalResourceLoader.getService(serviceName);
382                 if (responsibiltyTypeService != null) {
383                     responsibilityTypeServices.put(responsibility.getTemplate().getId(), responsibiltyTypeService);
384                 } else {
385                     responsibilityTypeServices.put(responsibility.getTemplate().getId(), defaultResponsibilityTypeService);
386                 }
387             }
388         }
389         return responsibilityTypeServices;
390     }
391 
392     private Map<String, List<Responsibility>> groupResponsibilitiesByTemplate(Collection<Responsibility> responsibilities) {
393         final Map<String, List<Responsibility>> results = new HashMap<String, List<Responsibility>>();
394         for (Responsibility responsibility : responsibilities) {
395             List<Responsibility> responsibilityInfos = results.get(responsibility.getTemplate().getId());
396             if (responsibilityInfos == null) {
397                 responsibilityInfos = new ArrayList<Responsibility>();
398                 results.put(responsibility.getTemplate().getId(), responsibilityInfos);
399             }
400             responsibilityInfos.add(responsibility);
401         }
402         return results;
403     }
404 
405     private List<String> getRoleIdsForResponsibilities(Collection<String> ids, Attributes qualification) {
406         final List<String> roleIds = getRoleIdsForPredicate(and(in("responsibilityId", ids.toArray()), equal("active", "Y")));
407 
408         //TODO filter with qualifiers
409         return roleIds;
410     }
411 
412     private List<String> getRoleIdsForPredicate(Predicate p) {
413         final QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
414         builder.setPredicates(p);
415         final GenericQueryResults<RoleResponsibilityBo> qr = criteriaLookupService.lookup(RoleResponsibilityBo.class, builder.build());
416 
417         final List<String> roleIds = new ArrayList<String>();
418         for (RoleResponsibilityBo bo : qr.getResults()) {
419             roleIds.add(bo.getRoleId());
420         }
421         return roleIds;
422     }
423 
424     private List<Responsibility> findRespsByNamespaceCodeAndTemplateName(final String namespaceCode, final String templateName) {
425         if (namespaceCode == null) {
426             throw new RiceIllegalArgumentException("namespaceCode is null");
427         }
428 
429         if (templateName == null) {
430             throw new RiceIllegalArgumentException("name is null");
431         }
432 
433         final Map<String, String> crit = new HashMap<String, String>();
434         crit.put("namespaceCode", namespaceCode);
435         crit.put("name", templateName);
436         crit.put("active", "Y");
437 
438         final Collection<ResponsibilityBo> bos = businessObjectService.findMatching(ResponsibilityBo.class, Collections.unmodifiableMap(crit));
439         final List<Responsibility> ims = new ArrayList<Responsibility>();
440         if (bos != null) {
441             for (ResponsibilityBo bo : bos) {
442                 if (bo != null) {
443                     ims.add(ResponsibilityBo.to(bo));
444                 }
445             }
446         }
447 
448         return Collections.unmodifiableList(ims);
449     }
450 
451     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
452         this.businessObjectService = businessObjectService;
453     }
454 
455     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
456         this.criteriaLookupService = criteriaLookupService;
457     }
458 
459     public void setDefaultResponsibilityTypeService(final KimResponsibilityTypeService defaultResponsibilityTypeService) {
460         this.defaultResponsibilityTypeService = defaultResponsibilityTypeService;
461     }
462 
463     public void setKimTypeInfoService(final KimTypeInfoService kimTypeInfoService) {
464         this.kimTypeInfoService = kimTypeInfoService;
465     }
466 
467     public void setRoleService(final RoleService roleService) {
468         this.roleService = roleService;
469     }
470 }