001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.xml;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.sql.Timestamp;
022: import java.text.ParseException;
023: import java.text.SimpleDateFormat;
024: import java.util.ArrayList;
025: import java.util.Iterator;
026: import java.util.List;
027:
028: import javax.xml.parsers.ParserConfigurationException;
029:
030: import org.jdom.Document;
031: import org.jdom.Element;
032: import org.jdom.JDOMException;
033: import org.xml.sax.SAXException;
034:
035: import edu.iu.uis.eden.EdenConstants;
036: import edu.iu.uis.eden.KEWServiceLocator;
037: import edu.iu.uis.eden.doctype.DocumentType;
038: import edu.iu.uis.eden.exception.EdenUserNotFoundException;
039: import edu.iu.uis.eden.exception.InvalidXmlException;
040: import edu.iu.uis.eden.routetemplate.RuleBaseValues;
041: import edu.iu.uis.eden.routetemplate.RuleDelegation;
042: import edu.iu.uis.eden.routetemplate.RuleResponsibility;
043: import edu.iu.uis.eden.routetemplate.RuleService;
044: import edu.iu.uis.eden.routetemplate.RuleTemplate;
045: import edu.iu.uis.eden.user.AuthenticationUserId;
046: import edu.iu.uis.eden.user.WorkflowUser;
047: import edu.iu.uis.eden.util.Utilities;
048: import edu.iu.uis.eden.util.XmlHelper;
049: import edu.iu.uis.eden.workgroup.GroupNameId;
050: import edu.iu.uis.eden.workgroup.Workgroup;
051:
052: /**
053: * Parses rules from XML.
054: *
055: * @see RuleBaseValues
056: *
057: * @author ewestfal
058: * @author temay
059: * @author rkirkend
060: * @author ahamid
061: */
062: public class RuleXmlParser implements XmlConstants {
063:
064: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
065: .getLogger(RuleXmlParser.class);
066:
067: /**
068: * Priority to use if rule responsibility omits priority
069: */
070: private static final int DEFAULT_RULE_PRIORITY = 1;
071: /**
072: * Value of Ignore Previous flag if omitted; default to false, we will NOT ignore previous approvals
073: */
074: private static final boolean DEFAULT_IGNORE_PREVIOUS = false;
075: /**
076: * Default approve policy, if omitted; defaults to FIRST_APPROVE, the request will be satisfied by the first approval
077: */
078: private static final String DEFAULT_APPROVE_POLICY = EdenConstants.APPROVE_POLICY_FIRST_APPROVE;
079: /**
080: * Default action requested, if omitted; defaults to "A"pprove
081: */
082: private static final String DEFAULT_ACTION_REQUESTED = EdenConstants.ACTION_REQUEST_APPROVE_REQ;
083:
084: /** original code
085: public List parseRules(InputStream input) throws IOException, InvalidXmlException {
086:
087: SAXBuilder builder = new SAXBuilder(false);
088: try {
089: Document doc = builder.build(input);
090: Element root = doc.getRootElement();
091: return parseRules(root);
092: } catch (JDOMException e) {
093: throw new InvalidXmlException("Parse error.", e);
094: }
095: }
096: */
097:
098: public List parseRules(InputStream input) throws IOException,
099: InvalidXmlException {
100:
101: try {
102: Document doc = XmlHelper.trimSAXXml(input);
103: Element root = doc.getRootElement();
104: return parseRules(root);
105: } catch (JDOMException e) {
106: throw new InvalidXmlException("Parse error.", e);
107: } catch (SAXException e) {
108: throw new InvalidXmlException("Parse error.", e);
109: } catch (ParserConfigurationException e) {
110: throw new InvalidXmlException("Parse error.", e);
111: }
112: }
113:
114: public List parseRules(Element element) throws InvalidXmlException {
115: List rules = new ArrayList();
116: for (Iterator rulesIt = element.getChildren(RULES,
117: RULE_NAMESPACE).iterator(); rulesIt.hasNext();) {
118:
119: List ruleElements = ((Element) rulesIt.next()).getChildren(
120: RULE, RULE_NAMESPACE);
121: for (Iterator iterator = ruleElements.iterator(); iterator
122: .hasNext();) {
123: rules.add(parseRule((Element) iterator.next(), null));
124: }
125: }
126: for (Iterator iterator = rules.iterator(); iterator.hasNext();) {
127: RuleBaseValues rule = (RuleBaseValues) iterator.next();
128: RuleService ruleService = KEWServiceLocator
129: .getRuleService();
130: try {
131: ruleService.save2(rule);
132: ruleService.notifyCacheOfRuleChange(rule, null);
133: } catch (Exception e) {
134: LOG.error("Error saving rule entered by XML", e);
135: throw new RuntimeException("Error parsing rules.", e);
136: }
137: }
138: return rules;
139: }
140:
141: private RuleBaseValues parseRule(Element element,
142: RuleDelegation ruleDelegation) throws InvalidXmlException {
143: RuleBaseValues rule = new RuleBaseValues();
144: setDefaultRuleValues(rule);
145: rule.setDelegateRule(new Boolean(ruleDelegation != null));
146: String description = element.getChildText(DESCRIPTION,
147: RULE_NAMESPACE);
148: String ignorePreviousValue = element.getChildText(
149: IGNORE_PREVIOUS, RULE_NAMESPACE);
150: Element responsibilitiesElement = element.getChild(
151: RESPONSIBILITIES, RULE_NAMESPACE);
152: Element ruleExtensionsElement = element.getChild(
153: RULE_EXTENSIONS, RULE_NAMESPACE);
154: if (description == null) {
155: throw new InvalidXmlException(
156: "Rule must have a description.");
157: }
158: DocumentType documentType = null;
159: RuleTemplate ruleTemplate = null;
160: String ruleTemplateName = element.getChildText(RULE_TEMPLATE,
161: RULE_NAMESPACE);
162:
163: if (ruleDelegation != null
164: && Utilities.isEmpty(ruleTemplateName)) {
165: RuleBaseValues parentRule = ruleDelegation
166: .getRuleResponsibility().getRuleBaseValues();
167: documentType = KEWServiceLocator.getDocumentTypeService()
168: .findByName(parentRule.getDocTypeName());
169: ruleTemplate = parentRule.getRuleTemplate()
170: .getDelegationTemplate();
171: if (ruleTemplate == null) {
172: throw new InvalidXmlException(
173: "Rule template of parent rule does not have a proper delegation template.");
174: }
175: } else {
176: String documentTypeName = element.getChildText(
177: DOCUMENT_TYPE, RULE_NAMESPACE);
178: if (documentTypeName == null) {
179: throw new InvalidXmlException(
180: "Rule must have a document type.");
181: }
182: if (ruleTemplateName == null) {
183: throw new InvalidXmlException(
184: "Rule must have a rule template.");
185: }
186: documentType = KEWServiceLocator.getDocumentTypeService()
187: .findByName(documentTypeName);
188: ruleTemplate = KEWServiceLocator.getRuleTemplateService()
189: .findByRuleTemplateName(ruleTemplateName);
190: if (documentType == null) {
191: throw new InvalidXmlException(
192: "Could not locate document type '"
193: + documentTypeName + "'");
194: }
195: if (ruleTemplate == null) {
196: throw new InvalidXmlException(
197: "Could not locate rule template '"
198: + ruleTemplateName + "'");
199: }
200: }
201: Boolean ignorePrevious = Boolean
202: .valueOf(DEFAULT_IGNORE_PREVIOUS);
203: if (ignorePreviousValue != null) {
204: ignorePrevious = Boolean.valueOf(ignorePreviousValue);
205: }
206:
207: rule.setDocTypeName(documentType.getName());
208: rule.setRuleTemplateId(ruleTemplate.getRuleTemplateId());
209: rule.setRuleTemplate(ruleTemplate);
210: rule.setDescription(description);
211: rule.setIgnorePrevious(ignorePrevious);
212: rule.setResponsibilities(parseResponsibilities(
213: responsibilitiesElement, rule, ruleDelegation));
214: rule.setRuleExtensions(parseRuleExtensions(
215: ruleExtensionsElement, rule));
216:
217: // check if the rule is a duplicate
218: checkRuleForDuplicate(rule);
219:
220: return rule;
221: }
222:
223: /**
224: * Checks to see whether this rule duplicates an existing rule.
225: * Currently the uniqueness is on ruleResponsibilityName, and extension key/values.
226: * @param rule the rule to check
227: * @throws InvalidXmlException if this incoming rule duplicates an existing rule
228: */
229: private void checkRuleForDuplicate(RuleBaseValues rule)
230: throws InvalidXmlException {
231: List responsibilities = rule.getResponsibilities();
232: List extensions = rule.getRuleExtensions();
233: String docTypeName = rule.getDocTypeName();
234: // should we use name or id? which should we consider the primary key for purposes
235: // of equality evaluation?
236: String ruleTemplateName = rule.getRuleTemplateName();
237: List rules = KEWServiceLocator.getRuleService().fetchAllRules(
238: true);
239: Iterator it = rules.iterator();
240: while (it.hasNext()) {
241: RuleBaseValues r = (RuleBaseValues) it.next();
242: if (Utilities.equals(docTypeName, r.getDocTypeName())
243: && Utilities.equals(ruleTemplateName, r
244: .getRuleTemplateName())
245: && Utilities.collectionsEquivalent(
246: responsibilities, r.getResponsibilities())
247: && Utilities.collectionsEquivalent(extensions, r
248: .getRuleExtensions())) {
249: // we have a duplicate
250: throw new InvalidXmlException("Rule '"
251: + rule.getDescription() + "' on doc '"
252: + rule.getDocTypeName()
253: + "' is a duplicate of rule '"
254: + r.getDescription() + "' on doc '"
255: + r.getDocTypeName() + "'");
256: }
257: }
258: }
259:
260: private void setDefaultRuleValues(RuleBaseValues rule)
261: throws InvalidXmlException {
262: rule.setIgnorePrevious(Boolean.FALSE);
263: rule
264: .setActivationDate(new Timestamp(System
265: .currentTimeMillis()));
266: rule.setActiveInd(Boolean.TRUE);
267: rule.setCurrentInd(Boolean.TRUE);
268: rule.setFromDate(new Timestamp(System.currentTimeMillis()));
269: rule.setTemplateRuleInd(Boolean.FALSE);
270: rule.setVersionNbr(new Integer(0));
271: try {
272: rule.setDeactivationDate(new Timestamp(
273: new SimpleDateFormat("MM/dd/yyyy").parse(
274: "01/01/2100").getTime()));
275: rule.setToDate(new Timestamp(new SimpleDateFormat(
276: "MM/dd/yyyy").parse("01/01/2100").getTime()));
277: } catch (ParseException e) {
278: throw new InvalidXmlException("Could not parse toDate.", e);
279: }
280: }
281:
282: private List parseResponsibilities(Element element,
283: RuleBaseValues rule, RuleDelegation ruleDelegation)
284: throws InvalidXmlException {
285: if (element == null) {
286: throw new InvalidXmlException(
287: "Rule must have at least one responsibility.");
288: }
289: List responsibilities = new ArrayList();
290: List responsibilityElements = element.getChildren(
291: RESPONSIBILITY, RULE_NAMESPACE);
292: for (Iterator iterator = responsibilityElements.iterator(); iterator
293: .hasNext();) {
294: Element responsibilityElement = (Element) iterator.next();
295: responsibilities.add(parseResponsibility(
296: responsibilityElement, rule, ruleDelegation));
297: }
298: if (responsibilities.size() == 0) {
299: throw new InvalidXmlException(
300: "Rule must have at least one responsibility.");
301: }
302: return responsibilities;
303: }
304:
305: private RuleResponsibility parseResponsibility(Element element,
306: RuleBaseValues rule, RuleDelegation ruleDelegation)
307: throws InvalidXmlException {
308: RuleResponsibility responsibility = new RuleResponsibility();
309: responsibility.setRuleBaseValues(rule);
310: String actionRequested = null;
311: String priority = null;
312: if (ruleDelegation == null) {
313: actionRequested = element.getChildText(ACTION_REQUESTED,
314: RULE_NAMESPACE);
315: if (actionRequested == null) {
316: actionRequested = DEFAULT_ACTION_REQUESTED;
317: }
318: priority = element.getChildText(PRIORITY, RULE_NAMESPACE);
319: if (priority == null) {
320: priority = String.valueOf(DEFAULT_RULE_PRIORITY);
321: }
322: } else {
323: actionRequested = ruleDelegation.getRuleResponsibility()
324: .getActionRequestedCd();
325: priority = ruleDelegation.getRuleResponsibility()
326: .getPriority().toString();
327: }
328: String user = element.getChildText(USER, RULE_NAMESPACE);
329: String workgroup = element.getChildText(WORKGROUP,
330: RULE_NAMESPACE);
331: String role = element.getChildText(ROLE, RULE_NAMESPACE);
332: String approvePolicy = element.getChildText(APPROVE_POLICY,
333: RULE_NAMESPACE);
334: Element delegations = element.getChild(DELEGATIONS,
335: RULE_NAMESPACE);
336: if (actionRequested == null) {
337: throw new InvalidXmlException(
338: "actionRequested is required on responsibility");
339: }
340: if (!actionRequested
341: .equals(EdenConstants.ACTION_REQUEST_COMPLETE_REQ)
342: && !actionRequested
343: .equals(EdenConstants.ACTION_REQUEST_APPROVE_REQ)
344: && !actionRequested
345: .equals(EdenConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ)
346: && !actionRequested
347: .equals(EdenConstants.ACTION_REQUEST_FYI_REQ)) {
348: throw new InvalidXmlException(
349: "Invalid action requested code '" + actionRequested
350: + "'");
351: }
352: if (user == null && workgroup == null && role == null) {
353: throw new InvalidXmlException(
354: "One of user, workgroup, or role must be specified on responsibility.");
355: }
356: if (approvePolicy == null) {
357: approvePolicy = DEFAULT_APPROVE_POLICY;
358: }
359: if (!approvePolicy
360: .equals(EdenConstants.APPROVE_POLICY_ALL_APPROVE)
361: && !approvePolicy
362: .equals(EdenConstants.APPROVE_POLICY_FIRST_APPROVE)) {
363: throw new InvalidXmlException("Invalid approve policy '"
364: + approvePolicy + "'");
365: }
366: if (priority == null) {
367: throw new InvalidXmlException(
368: "Must specify a priority on responsibility");
369: }
370: Integer priorityNumber = Integer.valueOf(priority);
371: responsibility.setActionRequestedCd(actionRequested);
372: responsibility.setPriority(priorityNumber);
373: responsibility.setApprovePolicy(approvePolicy);
374: if (user != null) {
375: // allow core config parameter replacement in responsibilities
376: user = Utilities.substituteConfigParameters(user);
377: try {
378: WorkflowUser workflowUser = KEWServiceLocator
379: .getUserService().getWorkflowUser(
380: new AuthenticationUserId(user));
381: if (workflowUser == null) {
382: throw new InvalidXmlException(
383: "Could not locate workflow user for given network id: "
384: + user);
385: }
386: responsibility.setRuleResponsibilityName(workflowUser
387: .getWorkflowId());
388: responsibility
389: .setRuleResponsibilityType(EdenConstants.RULE_RESPONSIBILITY_WORKFLOW_ID);
390: } catch (EdenUserNotFoundException e) {
391: throw new InvalidXmlException(e);
392: }
393: } else if (workgroup != null) {
394: // allow core config parameter replacement in responsibilities
395: workgroup = Utilities.substituteConfigParameters(workgroup);
396: Workgroup workgroupObject = KEWServiceLocator
397: .getWorkgroupService().getWorkgroup(
398: new GroupNameId(workgroup));
399: if (workgroupObject == null) {
400: throw new InvalidXmlException(
401: "Could not locate workgroup: " + workgroup);
402: }
403: responsibility.setRuleResponsibilityName(workgroupObject
404: .getWorkflowGroupId().getGroupId().toString());
405: responsibility
406: .setRuleResponsibilityType(EdenConstants.RULE_RESPONSIBILITY_WORKGROUP_ID);
407: } else if (role != null) {
408: responsibility.setRuleResponsibilityName(role);
409: responsibility
410: .setRuleResponsibilityType(EdenConstants.RULE_RESPONSIBILITY_ROLE_ID);
411: }
412: if (ruleDelegation == null && delegations != null) {
413: responsibility.setDelegationRules(parseRuleDelegations(
414: delegations, responsibility));
415: }
416: return responsibility;
417: }
418:
419: private List parseRuleDelegations(Element element,
420: RuleResponsibility responsibility)
421: throws InvalidXmlException {
422: List ruleDelegations = new ArrayList();
423: List ruleElements = element.getChildren(RULE, RULE_NAMESPACE);
424: for (Iterator iterator = ruleElements.iterator(); iterator
425: .hasNext();) {
426: ruleDelegations.add(parseRuleDelegation((Element) iterator
427: .next(), responsibility));
428: }
429: return ruleDelegations;
430: }
431:
432: private RuleDelegation parseRuleDelegation(Element element,
433: RuleResponsibility responsibility)
434: throws InvalidXmlException {
435: String delegationType = element.getChildText(DELEGATION_TYPE,
436: RULE_NAMESPACE);
437: if (delegationType == null
438: || !(delegationType
439: .equals(EdenConstants.DELEGATION_PRIMARY) || delegationType
440: .equals(EdenConstants.DELEGATION_SECONDARY))) {
441: throw new InvalidXmlException(
442: "Invalid delegation type specified for delegate rule '"
443: + delegationType + "'");
444: }
445: RuleDelegation ruleDelegation = new RuleDelegation();
446: ruleDelegation.setDelegationType(delegationType);
447: ruleDelegation.setRuleResponsibility(responsibility);
448: ruleDelegation.setDelegationRuleBaseValues(parseRule(element,
449: ruleDelegation));
450: return ruleDelegation;
451: }
452:
453: private List parseRuleExtensions(Element element,
454: RuleBaseValues rule) throws InvalidXmlException {
455: if (element == null) {
456: return new ArrayList();
457: }
458: RuleExtensionXmlParser parser = new RuleExtensionXmlParser();
459: return parser.parseRuleExtensions(element, rule);
460: }
461:
462: }
|