1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.kew.xml;
18
19 import org.apache.commons.lang.BooleanUtils;
20 import org.apache.commons.lang.StringUtils;
21 import org.jdom.Attribute;
22 import org.jdom.Document;
23 import org.jdom.Element;
24 import org.jdom.JDOMException;
25 import org.kuali.rice.core.util.RiceConstants;
26 import org.kuali.rice.core.util.xml.XmlException;
27 import org.kuali.rice.core.util.xml.XmlHelper;
28 import org.kuali.rice.kew.rule.RuleBaseValues;
29 import org.kuali.rice.kew.rule.RuleDelegation;
30 import org.kuali.rice.kew.rule.RuleTemplateOption;
31 import org.kuali.rice.kew.rule.bo.RuleAttribute;
32 import org.kuali.rice.kew.rule.bo.RuleTemplate;
33 import org.kuali.rice.kew.rule.bo.RuleTemplateAttribute;
34 import org.kuali.rice.kew.service.KEWServiceLocator;
35 import org.kuali.rice.kew.util.KEWConstants;
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<RuleTemplate> 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<RuleTemplate> parseRuleTemplates(Element element) throws XmlException {
92 List<RuleTemplate> ruleTemplates = new ArrayList<RuleTemplate>();
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 RuleTemplate parseRuleTemplate(Element element, List<RuleTemplate> 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 RuleTemplate ruleTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(name);
125
126 if (ruleTemplate == null) {
127
128 ruleTemplate = new RuleTemplate();
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 KEWServiceLocator.getRuleTemplateService().save(ruleTemplate);
152
153
154 updateRuleTemplateDefaultOptions(element, ruleTemplate);
155
156 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, RuleTemplate 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, RuleTemplate 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, KEWConstants.ACTION_REQUEST_DEFAULT_CD, defaultActionRequested);
230 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KEWConstants.ACTION_REQUEST_APPROVE_REQ, supportsApprove);
231 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, supportsAcknowledge);
232 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KEWConstants.ACTION_REQUEST_FYI_REQ, supportsFYI);
233 updateOrDeleteRuleTemplateOption(updatedRuleTemplate, KEWConstants.ACTION_REQUEST_COMPLETE_REQ, supportsComplete);
234 }
235
236 }
237
238
239
240
241
242
243
244
245 protected boolean updateRuleDefaults(Element defaultsElement, RuleTemplate updatedRuleTemplate) throws XmlException {
246
247
248 if (updatedRuleTemplate.getRuleTemplateId() != null) {
249 RuleBaseValues ruleDefaults = KEWServiceLocator.getRuleService().findDefaultRuleByRuleTemplateId(updatedRuleTemplate.getRuleTemplateId());
250 if (ruleDefaults != null) {
251 List ruleDelegationDefaults = KEWServiceLocator.getRuleDelegationService().findByDelegateRuleId(ruleDefaults.getRuleBaseValuesId());
252
253 KEWServiceLocator.getRuleService().delete(ruleDefaults.getRuleBaseValuesId());
254
255 for (Iterator iterator = ruleDelegationDefaults.iterator(); iterator.hasNext();) {
256 RuleDelegation ruleDelegation = (RuleDelegation) iterator.next();
257 KEWServiceLocator.getRuleDelegationService().delete(ruleDelegation.getRuleDelegationId());
258 }
259 }
260 }
261
262 boolean isDelegation = false;
263
264 if (defaultsElement != null) {
265 String delegationType = defaultsElement.getChildText(DELEGATION_TYPE, RULE_TEMPLATE_NAMESPACE);
266 isDelegation = !org.apache.commons.lang.StringUtils.isEmpty(delegationType);
267
268 String description = defaultsElement.getChildText(DESCRIPTION, RULE_TEMPLATE_NAMESPACE);
269
270
271 if (description == null) {
272 throw new XmlException("Description must be specified in rule defaults");
273 }
274
275 String fromDate = defaultsElement.getChildText(FROM_DATE, RULE_TEMPLATE_NAMESPACE);
276 String toDate = defaultsElement.getChildText(TO_DATE, RULE_TEMPLATE_NAMESPACE);
277
278 Boolean forceAction = BooleanUtils.toBooleanObject(defaultsElement.getChildText(FORCE_ACTION, RULE_TEMPLATE_NAMESPACE));
279 Boolean active = BooleanUtils.toBooleanObject(defaultsElement.getChildText(ACTIVE, RULE_TEMPLATE_NAMESPACE));
280
281 if (isDelegation && !KEWConstants.DELEGATION_PRIMARY.equals(delegationType) && !KEWConstants.DELEGATION_SECONDARY.equals(delegationType)) {
282 throw new XmlException("Invalid delegation type '" + delegationType + "'." + " Expected one of: "
283 + KEWConstants.DELEGATION_PRIMARY + "," + KEWConstants.DELEGATION_SECONDARY);
284 }
285
286
287 RuleBaseValues ruleDefaults = new RuleBaseValues();
288
289
290 ruleDefaults.setRuleTemplate(updatedRuleTemplate);
291 ruleDefaults.setDocTypeName(DUMMY_DOCUMENT_TYPE);
292 ruleDefaults.setTemplateRuleInd(Boolean.TRUE);
293 ruleDefaults.setCurrentInd(Boolean.TRUE);
294 ruleDefaults.setVersionNbr(new Integer(0));
295 ruleDefaults.setDescription(description);
296
297
298 ruleDefaults.setForceAction(Boolean.valueOf(BooleanUtils.isTrue(forceAction)));
299 ruleDefaults.setActiveInd(Boolean.valueOf(BooleanUtils.isTrue(active)));
300
301 if (ruleDefaults.getActivationDate() == null) {
302 ruleDefaults.setActivationDate(new Timestamp(System.currentTimeMillis()));
303 }
304
305 ruleDefaults.setFromDate(formatDate("fromDate",fromDate));
306 ruleDefaults.setToDate(formatDate("toDate", toDate));
307
308
309
310 RuleDelegation ruleDelegationDefaults = null;
311 if (isDelegation) {
312 ruleDelegationDefaults = new RuleDelegation();
313 ruleDelegationDefaults.setDelegationRuleBaseValues(ruleDefaults);
314 ruleDelegationDefaults.setDelegationType(delegationType);
315 ruleDelegationDefaults.setResponsibilityId(new Long(-1));
316 }
317
318
319 KEWServiceLocator.getRuleTemplateService().saveRuleDefaults(ruleDelegationDefaults, ruleDefaults);
320 } else {
321
322 }
323
324 return isDelegation;
325 }
326
327
328
329
330
331
332
333
334 protected void updateOrDeleteRuleTemplateOption(RuleTemplate updatedRuleTemplate, String key, Object value) {
335 if (value != null) {
336
337 RuleTemplateOption option = updatedRuleTemplate.getRuleTemplateOption(key);
338 if (option != null) {
339 option.setValue(value.toString());
340 } else {
341 updatedRuleTemplate.getRuleTemplateOptions().add(new RuleTemplateOption(key, value.toString()));
342 }
343 } else {
344
345 Iterator<RuleTemplateOption> options = updatedRuleTemplate.getRuleTemplateOptions().iterator();
346 while (options.hasNext()) {
347 RuleTemplateOption opt = options.next();
348 if (key.equals(opt.getKey())) {
349 options.remove();
350 break;
351 }
352 }
353 }
354 }
355
356
357
358
359
360
361
362
363 protected void updateDelegationTemplate(Element ruleTemplateElement, RuleTemplate updatedRuleTemplate, List<RuleTemplate> parsedRuleTemplates) throws XmlException {
364 String delegateTemplateName = ruleTemplateElement.getChildText(DELEGATION_TEMPLATE, RULE_TEMPLATE_NAMESPACE);
365
366 if (delegateTemplateName != null) {
367
368
369 RuleTemplate delegateTemplate = KEWServiceLocator.getRuleTemplateService().findByRuleTemplateName(delegateTemplateName);
370
371
372 if (delegateTemplate == null) {
373 for (RuleTemplate rt: parsedRuleTemplates) {
374 if (delegateTemplateName.equalsIgnoreCase(rt.getName())) {
375
376 Long ruleTemplateId = KEWServiceLocator.getRuleTemplateService().getNextRuleTemplateId();
377 rt.setRuleTemplateId(ruleTemplateId);
378 delegateTemplate = rt;
379 break;
380 }
381 }
382 }
383
384 if (delegateTemplate == null) {
385 throw new XmlException("Cannot find delegation template " + delegateTemplateName);
386 }
387
388 updatedRuleTemplate.setDelegationTemplateId(delegateTemplate.getDelegationTemplateId());
389 updatedRuleTemplate.setDelegationTemplate(delegateTemplate);
390 } else {
391
392 }
393 }
394
395
396
397
398
399
400
401 protected void updateRuleTemplateAttributes(Element ruleTemplateElement, RuleTemplate updatedRuleTemplate) throws XmlException {
402
403
404
405
406
407 Element attributesElement = ruleTemplateElement.getChild(ATTRIBUTES, RULE_TEMPLATE_NAMESPACE);
408 List<RuleTemplateAttribute> incomingAttributes = new ArrayList<RuleTemplateAttribute>();
409 if (attributesElement != null) {
410 incomingAttributes.addAll(parseRuleTemplateAttributes(attributesElement, updatedRuleTemplate));
411 }
412
413
414 for (RuleTemplateAttribute currentRuleTemplateAttribute: updatedRuleTemplate.getRuleTemplateAttributes()) {
415 String ruleAttributeName = (currentRuleTemplateAttribute.getRuleAttribute() != null) ? currentRuleTemplateAttribute.getRuleAttribute().getName() : "(null)";
416 LOG.debug("Inactivating rule template attribute with id " + currentRuleTemplateAttribute.getRuleTemplateAttributeId() + " and rule attribute with name " + ruleAttributeName);
417 currentRuleTemplateAttribute.setActive(Boolean.FALSE);
418 }
419
420
421
422 for (RuleTemplateAttribute ruleTemplateAttribute: incomingAttributes) {
423 RuleTemplateAttribute potentialExistingTemplateAttribute = updatedRuleTemplate.getRuleTemplateAttribute(ruleTemplateAttribute);
424 if (potentialExistingTemplateAttribute != null) {
425
426 potentialExistingTemplateAttribute.setActive(ruleTemplateAttribute.getActive());
427 potentialExistingTemplateAttribute.setRequired(ruleTemplateAttribute.getRequired());
428 } else {
429
430 updatedRuleTemplate.getRuleTemplateAttributes().add(ruleTemplateAttribute);
431 }
432 }
433 }
434
435
436
437
438
439
440
441
442 private List<RuleTemplateAttribute> parseRuleTemplateAttributes(Element attributesElement, RuleTemplate ruleTemplate) throws XmlException {
443 List<RuleTemplateAttribute> ruleTemplateAttributes = new ArrayList<RuleTemplateAttribute>();
444 Collection<Element> attributeElements = XmlHelper.findElements(attributesElement, ATTRIBUTE);
445 for (Iterator iterator = attributeElements.iterator(); iterator.hasNext();) {
446 ruleTemplateAttributes.add(parseRuleTemplateAttribute((Element) iterator.next(), ruleTemplate));
447 }
448 return ruleTemplateAttributes;
449 }
450
451
452
453
454
455
456
457
458 private RuleTemplateAttribute parseRuleTemplateAttribute(Element element, RuleTemplate ruleTemplate) throws XmlException {
459 String attributeName = element.getChildText(NAME, RULE_TEMPLATE_NAMESPACE);
460 String requiredValue = element.getChildText(REQUIRED, RULE_TEMPLATE_NAMESPACE);
461 String activeValue = element.getChildText(ACTIVE, RULE_TEMPLATE_NAMESPACE);
462 if (org.apache.commons.lang.StringUtils.isEmpty(attributeName)) {
463 throw new XmlException("Attribute name must be non-empty");
464 }
465 boolean required = DEFAULT_ATTRIBUTE_REQUIRED;
466 if (requiredValue != null) {
467 required = Boolean.parseBoolean(requiredValue);
468 }
469 boolean active = DEFAULT_ATTRIBUTE_ACTIVE;
470 if (activeValue != null) {
471 active = Boolean.parseBoolean(activeValue);
472 }
473 RuleAttribute ruleAttribute = KEWServiceLocator.getRuleAttributeService().findByName(attributeName);
474 if (ruleAttribute == null) {
475 throw new XmlException("Could not locate rule attribute for name '" + attributeName + "'");
476 }
477 RuleTemplateAttribute templateAttribute = new RuleTemplateAttribute();
478 templateAttribute.setRuleAttribute(ruleAttribute);
479 templateAttribute.setRuleAttributeId(ruleAttribute.getRuleAttributeId());
480 templateAttribute.setRuleTemplate(ruleTemplate);
481 templateAttribute.setRequired(Boolean.valueOf(required));
482 templateAttribute.setActive(Boolean.valueOf(active));
483 templateAttribute.setDisplayOrder(new Integer(templateAttributeCounter++));
484 return templateAttribute;
485 }
486
487 public Timestamp formatDate(String dateLabel, String dateString) throws XmlException {
488 if (StringUtils.isBlank(dateString)) {
489 return null;
490 }
491 try {
492 return new Timestamp(RiceConstants.getDefaultDateFormat().parse(dateString).getTime());
493 } catch (ParseException e) {
494 throw new XmlException(dateLabel + " is not in the proper format. Should have been: " + RiceConstants.DEFAULT_DATE_FORMAT_PATTERN);
495 }
496 }
497
498 }