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