1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.xml;
17
18 import org.apache.commons.lang.BooleanUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.jdom.Attribute;
21 import org.jdom.Document;
22 import org.jdom.Element;
23 import org.jdom.JDOMException;
24 import org.kuali.rice.core.api.delegation.DelegationType;
25 import org.kuali.rice.core.api.util.RiceConstants;
26 import org.kuali.rice.core.api.util.xml.XmlException;
27 import org.kuali.rice.core.api.util.xml.XmlHelper;
28 import org.kuali.rice.kew.rule.RuleBaseValues;
29 import org.kuali.rice.kew.rule.RuleDelegationBo;
30 import org.kuali.rice.kew.rule.RuleTemplateOptionBo;
31 import org.kuali.rice.kew.rule.bo.RuleAttribute;
32 import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
33 import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
34 import org.kuali.rice.kew.service.KEWServiceLocator;
35 import org.kuali.rice.kew.api.KewApiConstants;
36 import org.xml.sax.SAXException;
37
38 import javax.xml.parsers.ParserConfigurationException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.sql.Timestamp;
42 import java.text.ParseException;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Iterator;
46 import java.util.List;
47
48 import static org.kuali.rice.core.api.impex.xml.XmlConstants.*;
49
50
51
52
53
54
55
56 public class RuleTemplateXmlParser {
57
58 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RuleTemplateXmlParser.class);
59
60
61
62
63 private static final boolean DEFAULT_ATTRIBUTE_REQUIRED = true;
64 private static final boolean DEFAULT_ATTRIBUTE_ACTIVE = true;
65
66
67
68
69 private static final String DUMMY_DOCUMENT_TYPE = "dummyDocumentType";
70
71
72
73
74 private int templateAttributeCounter = 0;
75
76 public List<RuleTemplateBo> parseRuleTemplates(InputStream input) throws IOException, XmlException {
77
78 try {
79 Document doc = XmlHelper.trimSAXXml(input);
80 Element root = doc.getRootElement();
81 return parseRuleTemplates(root);
82 } catch (JDOMException e) {
83 throw new XmlException("Parse error.", e);
84 } catch (SAXException e) {
85 throw new XmlException("Parse error.", e);
86 } catch (ParserConfigurationException e) {
87 throw new XmlException("Parse error.", e);
88 }
89 }
90
91 public List<RuleTemplateBo> parseRuleTemplates(Element element) throws XmlException {
92 List<RuleTemplateBo> ruleTemplates = new ArrayList<RuleTemplateBo>();
93
94
95 Collection<Element> ruleTemplatesElements = XmlHelper.findElements(element, RULE_TEMPLATES);
96 Iterator ruleTemplatesIterator = ruleTemplatesElements.iterator();
97 while (ruleTemplatesIterator.hasNext()) {
98 Element ruleTemplatesElement = (Element) ruleTemplatesIterator.next();
99 Collection<Element> ruleTemplateElements = XmlHelper.findElements(ruleTemplatesElement, RULE_TEMPLATE);
100 for (Iterator iterator = ruleTemplateElements.iterator(); iterator.hasNext();) {
101 ruleTemplates.add(parseRuleTemplate((Element) iterator.next(), ruleTemplates));
102 }
103 }
104 return ruleTemplates;
105 }
106
107 private RuleTemplateBo parseRuleTemplate(Element element, List<RuleTemplateBo> ruleTemplates) throws XmlException {
108 String name = element.getChildText(NAME, RULE_TEMPLATE_NAMESPACE);
109 String description = element.getChildText(DESCRIPTION, RULE_TEMPLATE_NAMESPACE);
110 Attribute allowOverwriteAttrib = element.getAttribute("allowOverwrite");
111
112 boolean allowOverwrite = false;
113 if (allowOverwriteAttrib != null) {
114 allowOverwrite = Boolean.valueOf(allowOverwriteAttrib.getValue()).booleanValue();
115 }
116 if (org.apache.commons.lang.StringUtils.isEmpty(name)) {
117 throw new XmlException("RuleTemplate must have a name");
118 }
119 if (org.apache.commons.lang.StringUtils.isEmpty(description)) {
120 throw new XmlException("RuleTemplate must have a description");
121 }
122
123
124 RuleTemplateBo ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(name);
125
126 if (ruleTemplate == null) {
127
128 ruleTemplate = new RuleTemplateBo();
129 } else {
130
131 if (!allowOverwrite) {
132 throw new RuntimeException("Attempting to overwrite template " + name + " without allowOverwrite set");
133 }
134
135
136 assert(name.equals(ruleTemplate.getName())) : "Existing template definition name does not match incoming definition name";
137 }
138
139
140 ruleTemplate.setName(name);
141 ruleTemplate.setDescription(description);
142
143
144 updateDelegationTemplate(element, ruleTemplate, ruleTemplates);
145
146
147 updateRuleTemplateAttributes(element, ruleTemplate);
148
149
150
151 ruleTemplate = KEWServiceLocator.getRuleTemplateService().save(ruleTemplate);
152
153
154 updateRuleTemplateDefaultOptions(element, ruleTemplate);
155
156 ruleTemplate = KEWServiceLocator.getRuleTemplateService().save(ruleTemplate);
157
158 return ruleTemplate;
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 protected void updateRuleTemplateDefaultOptions(Element ruleTemplateElement, RuleTemplateBo updatedRuleTemplate) throws XmlException {
180 Element defaultsElement = ruleTemplateElement.getChild(RULE_DEFAULTS, RULE_TEMPLATE_NAMESPACE);
181
182
183 boolean isDelegation = updateRuleDefaults(defaultsElement, updatedRuleTemplate);
184
185
186 updateRuleTemplateOptions(defaultsElement, updatedRuleTemplate, isDelegation);
187
188 }
189
190
191
192
193
194
195 protected void updateRuleTemplateOptions(Element defaultsElement, RuleTemplateBo updatedRuleTemplate, boolean isDelegation) throws XmlException {
196
197
198
199 String defaultActionRequested = null;
200 Boolean supportsComplete = null;
201 Boolean supportsApprove = null;
202 Boolean supportsAcknowledge = null;
203 Boolean supportsFYI = null;
204
205
206
207 updatedRuleTemplate.removeNonDefaultOptions();
208
209
210 if (defaultsElement != null) {
211
212 defaultActionRequested = defaultsElement.getChildText(DEFAULT_ACTION_REQUESTED, RULE_TEMPLATE_NAMESPACE);
213 supportsComplete = BooleanUtils.toBooleanObject(defaultsElement.getChildText(SUPPORTS_COMPLETE, RULE_TEMPLATE_NAMESPACE));
214 supportsApprove = BooleanUtils.toBooleanObject(defaultsElement.getChildText(SUPPORTS_APPROVE, RULE_TEMPLATE_NAMESPACE));
215 supportsAcknowledge = BooleanUtils.toBooleanObject(defaultsElement.getChildText(SUPPORTS_ACKNOWLEDGE, RULE_TEMPLATE_NAMESPACE));
216 supportsFYI = BooleanUtils.toBooleanObject(defaultsElement.getChildText(SUPPORTS_FYI, RULE_TEMPLATE_NAMESPACE));
217 }
218
219 if (!isDelegation) {
220
221
222
223
224
225
226
227
228
229 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KewApiConstants.ACTION_REQUEST_DEFAULT_CD, defaultActionRequested);
230 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, supportsApprove);
231 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, supportsAcknowledge);
232 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KewApiConstants.ACTION_REQUEST_FYI_REQ, supportsFYI);
233 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, supportsComplete);
234 }
235
236 }
237
238
239
240
241
242
243
244
245 protected boolean updateRuleDefaults(Element defaultsElement, RuleTemplateBo updatedRuleTemplate) throws XmlException {
246
247
248 if (updatedRuleTemplate.getId() != null) {
249 RuleBaseValues ruleDefaults = KEWServiceLocator.getRuleService().findDefaultRuleByRuleTemplateId(updatedRuleTemplate.getId());
250 if (ruleDefaults != null) {
251 List ruleDelegationDefaults = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId(ruleDefaults.getId());
252
253 KEWServiceLocator.getRuleService().delete(ruleDefaults.getId());
254
255 for (Iterator iterator = ruleDelegationDefaults.iterator(); iterator.hasNext();) {
256 RuleDelegationBo ruleDelegation = (RuleDelegationBo) iterator.next();
257 KEWServiceLocator.getRuleDelegationService().delete(ruleDelegation.getRuleDelegationId());
258 }
259 }
260 }
261
262 boolean isDelegation = false;
263
264 if (defaultsElement != null) {
265 String delegationTypeCode = defaultsElement.getChildText(DELEGATION_TYPE, RULE_TEMPLATE_NAMESPACE);
266 DelegationType delegationType = null;
267 isDelegation = !org.apache.commons.lang.StringUtils.isEmpty(delegationTypeCode);
268
269 String description = defaultsElement.getChildText(DESCRIPTION, RULE_TEMPLATE_NAMESPACE);
270
271
272 if (description == null) {
273 throw new XmlException("Description must be specified in rule defaults");
274 }
275
276 String fromDate = defaultsElement.getChildText(FROM_DATE, RULE_TEMPLATE_NAMESPACE);
277 String toDate = defaultsElement.getChildText(TO_DATE, RULE_TEMPLATE_NAMESPACE);
278
279 Boolean forceAction = BooleanUtils.toBooleanObject(defaultsElement.getChildText(FORCE_ACTION, RULE_TEMPLATE_NAMESPACE));
280 Boolean active = BooleanUtils.toBooleanObject(defaultsElement.getChildText(ACTIVE, RULE_TEMPLATE_NAMESPACE));
281
282 if (isDelegation) {
283 delegationType = DelegationType.parseCode(delegationTypeCode);
284 if (delegationType == null) {
285 throw new XmlException("Invalid delegation type '" + delegationType + "'." + " Expected one of: "
286 + DelegationType.PRIMARY.getCode() + "," + DelegationType.SECONDARY.getCode());
287 }
288 }
289
290
291 RuleBaseValues ruleDefaults = new RuleBaseValues();
292
293
294 ruleDefaults.setRuleTemplate(updatedRuleTemplate);
295 ruleDefaults.setRuleTemplateId(updatedRuleTemplate.getId());
296 ruleDefaults.setDocTypeName(DUMMY_DOCUMENT_TYPE);
297 ruleDefaults.setTemplateRuleInd(Boolean.TRUE);
298 ruleDefaults.setCurrentInd(Boolean.TRUE);
299 ruleDefaults.setVersionNbr(new Integer(0));
300 ruleDefaults.setDescription(description);
301
302
303 ruleDefaults.setForceAction(Boolean.valueOf(BooleanUtils.isTrue(forceAction)));
304 ruleDefaults.setActive(Boolean.valueOf(BooleanUtils.isTrue(active)));
305
306 if (ruleDefaults.getActivationDate() == null) {
307 ruleDefaults.setActivationDate(new Timestamp(System.currentTimeMillis()));
308 }
309
310 ruleDefaults.setFromDateValue(formatDate("fromDate", fromDate));
311 ruleDefaults.setToDateValue(formatDate("toDate", toDate));
312
313
314
315 RuleDelegationBo ruleDelegationDefaults = null;
316 if (isDelegation) {
317 ruleDelegationDefaults = new RuleDelegationBo();
318 ruleDelegationDefaults.setDelegationRule(ruleDefaults);
319 ruleDelegationDefaults.setDelegationType(delegationType);
320 ruleDelegationDefaults.setResponsibilityId(KewApiConstants.ADHOC_REQUEST_RESPONSIBILITY_ID);
321 }
322
323
324 KEWServiceLocator.getRuleTemplateService().saveRuleDefaults(ruleDelegationDefaults, ruleDefaults);
325 } else {
326
327 }
328
329 return isDelegation;
330 }
331
332
333
334
335
336
337
338
339 protected void updateOrDeleteRuleTemplateOption(RuleTemplateBo updatedRuleTemplate, String key, Object value) {
340 if (value != null) {
341
342 RuleTemplateOptionBo option = updatedRuleTemplate.getRuleTemplateOption(key);
343 if (option != null) {
344 option.setValue(value.toString());
345 } else {
346 updatedRuleTemplate.getRuleTemplateOptions().add(new RuleTemplateOptionBo(key, value.toString()));
347 }
348 } else {
349
350 Iterator<RuleTemplateOptionBo> options = updatedRuleTemplate.getRuleTemplateOptions().iterator();
351 while (options.hasNext()) {
352 RuleTemplateOptionBo opt = options.next();
353 if (key.equals(opt.getCode())) {
354 options.remove();
355 break;
356 }
357 }
358 }
359 }
360
361
362
363
364
365
366
367
368 protected void updateDelegationTemplate(Element ruleTemplateElement, RuleTemplateBo updatedRuleTemplate, List<RuleTemplateBo> parsedRuleTemplates) throws XmlException {
369 String delegateTemplateName = ruleTemplateElement.getChildText(DELEGATION_TEMPLATE, RULE_TEMPLATE_NAMESPACE);
370
371 if (delegateTemplateName != null) {
372
373
374 RuleTemplateBo delegateTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(delegateTemplateName);
375
376
377 if (delegateTemplate == null) {
378 for (RuleTemplateBo rt: parsedRuleTemplates) {
379 if (delegateTemplateName.equalsIgnoreCase(rt.getName())) {
380
381 String ruleTemplateId = KEWServiceLocator.getRuleTemplateService().getNextRuleTemplateId();
382 rt.setId(ruleTemplateId);
383 delegateTemplate = rt;
384 break;
385 }
386 }
387 }
388
389 if (delegateTemplate == null) {
390 throw new XmlException("Cannot find delegation template " + delegateTemplateName);
391 }
392
393 updatedRuleTemplate.setDelegationTemplateId(delegateTemplate.getDelegationTemplateId());
394 updatedRuleTemplate.setDelegationTemplate(delegateTemplate);
395 } else {
396
397 }
398 }
399
400
401
402
403
404
405
406 protected void updateRuleTemplateAttributes(Element ruleTemplateElement, RuleTemplateBo updatedRuleTemplate) throws XmlException {
407
408
409
410
411
412 Element attributesElement = ruleTemplateElement.getChild(ATTRIBUTES, RULE_TEMPLATE_NAMESPACE);
413 List<RuleTemplateAttributeBo> incomingAttributes = new ArrayList<RuleTemplateAttributeBo>();
414 if (attributesElement != null) {
415 incomingAttributes.addAll(parseRuleTemplateAttributes(attributesElement, updatedRuleTemplate));
416 }
417
418
419 for (RuleTemplateAttributeBo currentRuleTemplateAttribute: updatedRuleTemplate.getRuleTemplateAttributes()) {
420 String ruleAttributeName = (currentRuleTemplateAttribute.getRuleAttribute() != null) ? currentRuleTemplateAttribute.getRuleAttribute().getName() : "(null)";
421 LOG.debug("Inactivating rule template attribute with id " + currentRuleTemplateAttribute.getId() + " and rule attribute with name " + ruleAttributeName);
422 currentRuleTemplateAttribute.setActive(Boolean.FALSE);
423 }
424
425
426
427 for (RuleTemplateAttributeBo ruleTemplateAttribute: incomingAttributes) {
428 RuleTemplateAttributeBo potentialExistingTemplateAttribute = updatedRuleTemplate.getRuleTemplateAttribute(ruleTemplateAttribute);
429 if (potentialExistingTemplateAttribute != null) {
430
431 potentialExistingTemplateAttribute.setActive(ruleTemplateAttribute.getActive());
432 potentialExistingTemplateAttribute.setRequired(ruleTemplateAttribute.getRequired());
433 } else {
434
435 ruleTemplateAttribute.setRuleTemplate(updatedRuleTemplate);
436 updatedRuleTemplate.getRuleTemplateAttributes().add(ruleTemplateAttribute);
437
438 }
439 }
440 }
441
442
443
444
445
446
447
448
449 private List<RuleTemplateAttributeBo> parseRuleTemplateAttributes(Element attributesElement, RuleTemplateBo ruleTemplate) throws XmlException {
450 List<RuleTemplateAttributeBo> ruleTemplateAttributes = new ArrayList<RuleTemplateAttributeBo>();
451 Collection<Element> attributeElements = XmlHelper.findElements(attributesElement, ATTRIBUTE);
452 for (Iterator iterator = attributeElements.iterator(); iterator.hasNext();) {
453 ruleTemplateAttributes.add(parseRuleTemplateAttribute((Element) iterator.next(), ruleTemplate));
454 }
455 return ruleTemplateAttributes;
456 }
457
458
459
460
461
462
463
464
465 private RuleTemplateAttributeBo parseRuleTemplateAttribute(Element element, RuleTemplateBo ruleTemplate) throws XmlException {
466 String attributeName = element.getChildText(NAME, RULE_TEMPLATE_NAMESPACE);
467 String requiredValue = element.getChildText(REQUIRED, RULE_TEMPLATE_NAMESPACE);
468 String activeValue = element.getChildText(ACTIVE, RULE_TEMPLATE_NAMESPACE);
469 if (org.apache.commons.lang.StringUtils.isEmpty(attributeName)) {
470 throw new XmlException("Attribute name must be non-empty");
471 }
472 boolean required = DEFAULT_ATTRIBUTE_REQUIRED;
473 if (requiredValue != null) {
474 required = Boolean.parseBoolean(requiredValue);
475 }
476 boolean active = DEFAULT_ATTRIBUTE_ACTIVE;
477 if (activeValue != null) {
478 active = Boolean.parseBoolean(activeValue);
479 }
480 RuleAttribute ruleAttribute = KEWServiceLocator.getRuleAttributeService().findByName(attributeName);
481 if (ruleAttribute == null) {
482 throw new XmlException("Could not locate rule attribute for name '" + attributeName + "'");
483 }
484 RuleTemplateAttributeBo templateAttribute = new RuleTemplateAttributeBo();
485 templateAttribute.setRuleAttribute(ruleAttribute);
486 templateAttribute.setRuleAttributeId(ruleAttribute.getId());
487 templateAttribute.setRuleTemplate(ruleTemplate);
488 templateAttribute.setRequired(Boolean.valueOf(required));
489 templateAttribute.setActive(Boolean.valueOf(active));
490 templateAttribute.setDisplayOrder(new Integer(templateAttributeCounter++));
491 return templateAttribute;
492 }
493
494 public Timestamp formatDate(String dateLabel, String dateString) throws XmlException {
495 if (StringUtils.isBlank(dateString)) {
496 return null;
497 }
498 try {
499 return new Timestamp(RiceConstants.getDefaultDateFormat().parse(dateString).getTime());
500 } catch (ParseException e) {
501 throw new XmlException(dateLabel + " is not in the proper format. Should have been: " + RiceConstants.DEFAULT_DATE_FORMAT_PATTERN);
502 }
503 }
504
505 }