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.setDocTypeName(DUMMY_DOCUMENT_TYPE);
296 ruleDefaults.setTemplateRuleInd(Boolean.TRUE);
297 ruleDefaults.setCurrentInd(Boolean.TRUE);
298 ruleDefaults.setVersionNbr(new Integer(0));
299 ruleDefaults.setDescription(description);
300
301
302 ruleDefaults.setForceAction(Boolean.valueOf(BooleanUtils.isTrue(forceAction)));
303 ruleDefaults.setActive(Boolean.valueOf(BooleanUtils.isTrue(active)));
304
305 if (ruleDefaults.getActivationDate() == null) {
306 ruleDefaults.setActivationDate(new Timestamp(System.currentTimeMillis()));
307 }
308
309 ruleDefaults.setFromDateValue(formatDate("fromDate", fromDate));
310 ruleDefaults.setToDateValue(formatDate("toDate", toDate));
311
312
313
314 RuleDelegationBo ruleDelegationDefaults = null;
315 if (isDelegation) {
316 ruleDelegationDefaults = new RuleDelegationBo();
317 ruleDelegationDefaults.setDelegationRule(ruleDefaults);
318 ruleDelegationDefaults.setDelegationType(delegationType);
319 ruleDelegationDefaults.setResponsibilityId(KewApiConstants.ADHOC_REQUEST_RESPONSIBILITY_ID);
320 }
321
322
323 KEWServiceLocator.getRuleTemplateService().saveRuleDefaults(ruleDelegationDefaults, ruleDefaults);
324 } else {
325
326 }
327
328 return isDelegation;
329 }
330
331
332
333
334
335
336
337
338 protected void updateOrDeleteRuleTemplateOption(RuleTemplateBo updatedRuleTemplate, String key, Object value) {
339 if (value != null) {
340
341 RuleTemplateOptionBo option = updatedRuleTemplate.getRuleTemplateOption(key);
342 if (option != null) {
343 option.setValue(value.toString());
344 } else {
345 updatedRuleTemplate.getRuleTemplateOptions().add(new RuleTemplateOptionBo(key, value.toString()));
346 }
347 } else {
348
349 Iterator<RuleTemplateOptionBo> options = updatedRuleTemplate.getRuleTemplateOptions().iterator();
350 while (options.hasNext()) {
351 RuleTemplateOptionBo opt = options.next();
352 if (key.equals(opt.getCode())) {
353 options.remove();
354 break;
355 }
356 }
357 }
358 }
359
360
361
362
363
364
365
366
367 protected void updateDelegationTemplate(Element ruleTemplateElement, RuleTemplateBo updatedRuleTemplate, List<RuleTemplateBo> parsedRuleTemplates) throws XmlException {
368 String delegateTemplateName = ruleTemplateElement.getChildText(DELEGATION_TEMPLATE, RULE_TEMPLATE_NAMESPACE);
369
370 if (delegateTemplateName != null) {
371
372
373 RuleTemplateBo delegateTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(delegateTemplateName);
374
375
376 if (delegateTemplate == null) {
377 for (RuleTemplateBo rt: parsedRuleTemplates) {
378 if (delegateTemplateName.equalsIgnoreCase(rt.getName())) {
379
380 String ruleTemplateId = KEWServiceLocator.getRuleTemplateService().getNextRuleTemplateId();
381 rt.setId(ruleTemplateId);
382 delegateTemplate = rt;
383 break;
384 }
385 }
386 }
387
388 if (delegateTemplate == null) {
389 throw new XmlException("Cannot find delegation template " + delegateTemplateName);
390 }
391
392 updatedRuleTemplate.setDelegationTemplateId(delegateTemplate.getDelegationTemplateId());
393 updatedRuleTemplate.setDelegationTemplate(delegateTemplate);
394 } else {
395
396 }
397 }
398
399
400
401
402
403
404
405 protected void updateRuleTemplateAttributes(Element ruleTemplateElement, RuleTemplateBo updatedRuleTemplate) throws XmlException {
406
407
408
409
410
411 Element attributesElement = ruleTemplateElement.getChild(ATTRIBUTES, RULE_TEMPLATE_NAMESPACE);
412 List<RuleTemplateAttributeBo> incomingAttributes = new ArrayList<RuleTemplateAttributeBo>();
413 if (attributesElement != null) {
414 incomingAttributes.addAll(parseRuleTemplateAttributes(attributesElement, updatedRuleTemplate));
415 }
416
417
418 for (RuleTemplateAttributeBo currentRuleTemplateAttribute: updatedRuleTemplate.getRuleTemplateAttributes()) {
419 String ruleAttributeName = (currentRuleTemplateAttribute.getRuleAttribute() != null) ? currentRuleTemplateAttribute.getRuleAttribute().getName() : "(null)";
420 LOG.debug("Inactivating rule template attribute with id " + currentRuleTemplateAttribute.getId() + " and rule attribute with name " + ruleAttributeName);
421 currentRuleTemplateAttribute.setActive(Boolean.FALSE);
422 }
423
424
425
426 for (RuleTemplateAttributeBo ruleTemplateAttribute: incomingAttributes) {
427 RuleTemplateAttributeBo potentialExistingTemplateAttribute = updatedRuleTemplate.getRuleTemplateAttribute(ruleTemplateAttribute);
428 if (potentialExistingTemplateAttribute != null) {
429
430 potentialExistingTemplateAttribute.setActive(ruleTemplateAttribute.getActive());
431 potentialExistingTemplateAttribute.setRequired(ruleTemplateAttribute.getRequired());
432 } else {
433
434 ruleTemplateAttribute.setRuleTemplate(updatedRuleTemplate);
435 updatedRuleTemplate.getRuleTemplateAttributes().add(ruleTemplateAttribute);
436
437 }
438 }
439 }
440
441
442
443
444
445
446
447
448 private List<RuleTemplateAttributeBo> parseRuleTemplateAttributes(Element attributesElement, RuleTemplateBo ruleTemplate) throws XmlException {
449 List<RuleTemplateAttributeBo> ruleTemplateAttributes = new ArrayList<RuleTemplateAttributeBo>();
450 Collection<Element> attributeElements = XmlHelper.findElements(attributesElement, ATTRIBUTE);
451 for (Iterator iterator = attributeElements.iterator(); iterator.hasNext();) {
452 ruleTemplateAttributes.add(parseRuleTemplateAttribute((Element) iterator.next(), ruleTemplate));
453 }
454 return ruleTemplateAttributes;
455 }
456
457
458
459
460
461
462
463
464 private RuleTemplateAttributeBo parseRuleTemplateAttribute(Element element, RuleTemplateBo ruleTemplate) throws XmlException {
465 String attributeName = element.getChildText(NAME, RULE_TEMPLATE_NAMESPACE);
466 String requiredValue = element.getChildText(REQUIRED, RULE_TEMPLATE_NAMESPACE);
467 String activeValue = element.getChildText(ACTIVE, RULE_TEMPLATE_NAMESPACE);
468 if (org.apache.commons.lang.StringUtils.isEmpty(attributeName)) {
469 throw new XmlException("Attribute name must be non-empty");
470 }
471 boolean required = DEFAULT_ATTRIBUTE_REQUIRED;
472 if (requiredValue != null) {
473 required = Boolean.parseBoolean(requiredValue);
474 }
475 boolean active = DEFAULT_ATTRIBUTE_ACTIVE;
476 if (activeValue != null) {
477 active = Boolean.parseBoolean(activeValue);
478 }
479 RuleAttribute ruleAttribute = KEWServiceLocator.getRuleAttributeService().findByName(attributeName);
480 if (ruleAttribute == null) {
481 throw new XmlException("Could not locate rule attribute for name '" + attributeName + "'");
482 }
483 RuleTemplateAttributeBo templateAttribute = new RuleTemplateAttributeBo();
484 templateAttribute.setRuleAttribute(ruleAttribute);
485 templateAttribute.setRuleAttributeId(ruleAttribute.getId());
486 templateAttribute.setRuleTemplate(ruleTemplate);
487 templateAttribute.setRequired(Boolean.valueOf(required));
488 templateAttribute.setActive(Boolean.valueOf(active));
489 templateAttribute.setDisplayOrder(new Integer(templateAttributeCounter++));
490 return templateAttribute;
491 }
492
493 public Timestamp formatDate(String dateLabel, String dateString) throws XmlException {
494 if (StringUtils.isBlank(dateString)) {
495 return null;
496 }
497 try {
498 return new Timestamp(RiceConstants.getDefaultDateFormat().parse(dateString).getTime());
499 } catch (ParseException e) {
500 throw new XmlException(dateLabel + " is not in the proper format. Should have been: " + RiceConstants.DEFAULT_DATE_FORMAT_PATTERN);
501 }
502 }
503
504 }