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.routetemplate.xmlrouting;
018:
019: import java.io.BufferedReader;
020: import java.io.StringReader;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.regex.Pattern;
027:
028: import javax.xml.parsers.DocumentBuilderFactory;
029: import javax.xml.xpath.XPath;
030: import javax.xml.xpath.XPathConstants;
031: import javax.xml.xpath.XPathExpressionException;
032:
033: import org.w3c.dom.Element;
034: import org.w3c.dom.NamedNodeMap;
035: import org.w3c.dom.Node;
036: import org.w3c.dom.NodeList;
037: import org.xml.sax.InputSource;
038:
039: import edu.iu.uis.eden.WorkflowServiceErrorImpl;
040: import edu.iu.uis.eden.lookupable.Field;
041: import edu.iu.uis.eden.lookupable.Row;
042: import edu.iu.uis.eden.plugin.attributes.WorkflowAttributeXmlValidator;
043: import edu.iu.uis.eden.routeheader.DocumentContent;
044: import edu.iu.uis.eden.routetemplate.RuleAttribute;
045: import edu.iu.uis.eden.routetemplate.RuleExtension;
046: import edu.iu.uis.eden.routetemplate.RuleExtensionValue;
047: import edu.iu.uis.eden.routetemplate.WorkflowAttributeValidationError;
048: import edu.iu.uis.eden.util.KeyLabelPair;
049: import edu.iu.uis.eden.util.Utilities;
050: import edu.iu.uis.eden.util.XmlHelper;
051:
052: /**
053: * A generic WorkflowAttribute implementation that can be defined completely by XML.
054: * <ol>
055: * <li>This attribute implementation takes "properties" defined on the the {@link edu.iu.uis.eden.clientapp.vo.WorkflowAttributeDefinitionVO}
056: * and maps them to the param map of {@link GenericXMLRuleAttribute}, which relate directly to a set of fields defined by the
057: * XML <code><routingConfig></code> configuration.</li>
058: * <li>Application of the properties defined on the WorkflowAttributeDefinition
059: * to the actual attribute is performed in {@link org.kuali.rice.resourceloader.ObjectDefinitionResolver#invokeProperties(Object, java.util.Collection)}</li>
060: * <li>These params are then used to perform one of either EITHER:
061: * <ul>
062: * <li>Replace parameters of the syntax <code>%<i>field name</i>%</code> in the doc content if doc content is
063: * defined in the <code><xmlDocumentContent></code></li>
064: * <li>Generate a generic doc content, containing the parameter key/value pairs, e.g.:
065: * <blockquote>
066: * <code><pre>
067: * <xmlrouting>
068: * <field name="color"><value>red</value></field>
069: * <field name="shape"><value>circle</value></field>
070: * </xmlrouting>
071: * </pre></code>
072: * </blockquote>
073: * </li>
074: * </ul>
075: * Currently, only parameters that match fields configured in the routingConfig are honored (the others are ignored)
076: * (NOTE: to make this even more reusable we might want to consider generating content for all parameters, even those that
077: * do not have corresponding fields)
078: * </li>
079: * <li>The routingConfig element defines a set of <code>fieldDef</code>s, each of which may have an <code>xpathexpression</code> for field evaluation.
080: * This <code>xpathexpression</code> is used to determine whether the attribute's {@link #isMatch(DocumentContent, List)} will
081: * succeed. Each fieldDef may also have a <code>validation</code> element which supplies a regular expression against which
082: * to validate the field value (given by the param map)</li>
083: * </ol>
084: *
085: * @author jhopf
086: * @author rkirkend
087: * @author ahamid
088: */
089: public class StandardGenericXMLRuleAttribute implements
090: GenericXMLRuleAttribute, WorkflowAttributeXmlValidator {
091: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
092: .getLogger(StandardGenericXMLRuleAttribute.class);
093:
094: private static final String FIELD_DEF_E = "fieldDef";
095:
096: private static NodeList getFields(XPath xpath, Element root,
097: String[] types) throws XPathExpressionException {
098: final String OR = " or ";
099: StringBuffer findField = new StringBuffer("//routingConfig/"
100: + FIELD_DEF_E);
101: if (types != null && types.length > 0) {
102: findField.append("[");
103: for (int i = 0; i < types.length; i++) {
104: findField.append("@workflowType='" + types[i] + "'"
105: + OR);
106: // missing workflowType is equivalent ("defaults") to ALL
107: if ("ALL".equals(types[i])) {
108: findField.append("not(@workflowType)" + OR);
109: }
110: }
111: if (types.length > 0) {
112: // remove trailing " or "
113: findField.setLength(findField.length() - OR.length());
114: }
115: findField.append("]");
116: }
117:
118: try {
119: return (NodeList) xpath.evaluate(findField.toString(),
120: root, XPathConstants.NODESET);
121: } catch (XPathExpressionException e) {
122: LOG.error("Error evaluating expression: '" + findField
123: + "'");
124: throw e;
125: }
126: }
127:
128: private static List getRows(Element root, String[] types) {
129: List rows = new ArrayList();
130: XPath xpath = XPathHelper.newXPath();
131: NodeList fieldNodeList;
132: try {
133: fieldNodeList = getFields(xpath, root, types);
134: } catch (XPathExpressionException e) {
135: LOG.error("Error evaluating fields expression");
136: return rows;
137: }
138: if (fieldNodeList != null) {
139: for (int i = 0; i < fieldNodeList.getLength(); i++) {
140: Node field = fieldNodeList.item(i);
141: NamedNodeMap fieldAttributes = field.getAttributes();
142:
143: List fields = new ArrayList();
144: Field myField = new Field(fieldAttributes.getNamedItem(
145: "title").getNodeValue(), "", "", false,
146: fieldAttributes.getNamedItem("name")
147: .getNodeValue(), "", null, "");
148: String quickfinderService = null;
149: for (int j = 0; j < field.getChildNodes().getLength(); j++) {
150: Node childNode = field.getChildNodes().item(j);
151: if ("value".equals(childNode.getNodeName())) {
152: myField.setPropertyValue(childNode
153: .getFirstChild().getNodeValue());
154: } else if ("display"
155: .equals(childNode.getNodeName())) {
156: List options = new ArrayList();
157: List selectedOptions = new ArrayList();
158: for (int k = 0; k < childNode.getChildNodes()
159: .getLength(); k++) {
160: Node displayChildNode = childNode
161: .getChildNodes().item(k);
162: if ("type".equals(displayChildNode
163: .getNodeName())) {
164: myField
165: .setFieldType(convertTypeToFieldType(displayChildNode
166: .getFirstChild()
167: .getNodeValue()));
168: } else if ("meta".equals(displayChildNode
169: .getNodeName())) {
170: // i don't think the rule creation support things in this node.
171: // i don't think the flex Routing report supports things in this node.
172: } else if ("values".equals(displayChildNode
173: .getNodeName())) {
174: NamedNodeMap valuesAttributes = displayChildNode
175: .getAttributes();
176: String valuesKey = (displayChildNode
177: .getFirstChild() != null) ? displayChildNode
178: .getFirstChild().getNodeValue()
179: : "";
180: options
181: .add(new KeyLabelPair(
182: valuesKey,
183: valuesAttributes
184: .getNamedItem(
185: "title")
186: .getNodeValue()));
187: if (valuesAttributes
188: .getNamedItem("selected") != null) {
189: selectedOptions.add(valuesKey);
190: }
191: } else if ("parameters"
192: .equals(displayChildNode
193: .getNodeName())) {
194: NamedNodeMap parametersAttributes = displayChildNode
195: .getAttributes();
196: String parameterValue = (displayChildNode
197: .getFirstChild() == null) ? ""
198: : displayChildNode
199: .getFirstChild()
200: .getNodeValue();
201: myField.addDisplayParameter(
202: parametersAttributes
203: .getNamedItem("name")
204: .getNodeValue(),
205: parameterValue);
206: }
207: }
208: if (!options.isEmpty()) {
209: myField.setFieldValidValues(options);
210: if (!selectedOptions.isEmpty()) {
211: if (Field.MULTI_VALUE_FIELD_TYPES
212: .contains(myField
213: .getFieldType())) {
214: String[] newSelectedOptions = new String[selectedOptions
215: .size()];
216: int k = 0;
217: for (Iterator iter = selectedOptions
218: .iterator(); iter.hasNext();) {
219: String option = (String) iter
220: .next();
221: newSelectedOptions[k] = option;
222: k++;
223: }
224: myField
225: .setPropertyValues(newSelectedOptions);
226: } else {
227: myField
228: .setPropertyValue((String) selectedOptions
229: .get(0));
230: }
231: }
232: }
233: } else if ("quickfinder".equals(childNode
234: .getNodeName())) {
235: NamedNodeMap quickfinderAttributes = childNode
236: .getAttributes();
237: String drawQuickfinder = quickfinderAttributes
238: .getNamedItem("draw").getNodeValue();
239: if (!Utilities.isEmpty(drawQuickfinder)
240: && "true".equals(drawQuickfinder)) {
241: quickfinderService = quickfinderAttributes
242: .getNamedItem("service")
243: .getNodeValue();
244: }
245: myField
246: .setQuickFinderClassNameImpl(quickfinderAttributes
247: .getNamedItem("service")
248: .getNodeValue());
249: myField.setHasLookupable(true);
250: myField
251: .setDefaultLookupableName(quickfinderAttributes
252: .getNamedItem("appliesTo")
253: .getNodeValue());
254: }
255: }
256: fields.add(myField);
257: if (!Utilities.isEmpty(quickfinderService)) {
258: fields.add(new Field("", "", Field.QUICKFINDER,
259: false, "", "", null, quickfinderService));
260: }
261: rows.add(new Row(fields));
262: }
263: }
264: return rows;
265: }
266:
267: private static String convertTypeToFieldType(String type) {
268: if ("text".equals(type)) {
269: return Field.TEXT;
270: } else if ("select".equals(type)) {
271: return Field.DROPDOWN;
272: } else if ("radio".equals(type)) {
273: return Field.RADIO;
274: } else if ("quickfinder".equals(type)) {
275: return Field.QUICKFINDER;
276: }
277: return null;
278: }
279:
280: private static interface ErrorGenerator {
281: Object generateInvalidFieldError(Node field, String fieldName,
282: String message);
283:
284: Object generateMissingFieldError(Node field, String fieldName,
285: String message);
286: }
287:
288: private RuleAttribute ruleAttribute;
289: private Map paramMap = new HashMap();
290: private List ruleRows = new ArrayList();
291: private List routingDataRows = new ArrayList();
292: private boolean required;
293:
294: public StandardGenericXMLRuleAttribute() {
295: }
296:
297: public void setRuleAttribute(RuleAttribute ruleAttribute) {
298: this .ruleAttribute = ruleAttribute;
299: }
300:
301: public boolean isMatch(DocumentContent docContent,
302: List ruleExtensions) {
303: XPath xpath = XPathHelper.newXPath(docContent.getDocument());
304: WorkflowFunctionResolver resolver = XPathHelper
305: .extractFunctionResolver(xpath);
306: for (Iterator iter = ruleExtensions.iterator(); iter.hasNext();) {
307: RuleExtension extension = (RuleExtension) iter.next();
308: if (extension.getRuleTemplateAttribute().getRuleAttribute()
309: .getName().equals(ruleAttribute.getName())) {
310: resolver.setRuleExtension(extension);
311: //xpath.setXPathFunctionResolver(resolver);
312: for (Iterator iterator = extension.getExtensionValues()
313: .iterator(); iterator.hasNext();) {
314: RuleExtensionValue value = (RuleExtensionValue) iterator
315: .next();
316: String findXpathExpression = "//routingConfig/"
317: + FIELD_DEF_E + "[@name='" + value.getKey()
318: + "']/fieldEvaluation/xpathexpression";
319: String xpathExpression = null;
320: try {
321: xpathExpression = (String) xpath.evaluate(
322: findXpathExpression, getConfigXML(),
323: XPathConstants.STRING);
324: LOG.debug("routingConfig XPath expression: "
325: + xpathExpression);
326: if (!Utilities.isEmpty(xpathExpression)) {
327: LOG.debug("DocContent: "
328: + docContent.getDocContent());
329: Boolean match = (Boolean) xpath.evaluate(
330: xpathExpression, docContent
331: .getDocument(),
332: XPathConstants.BOOLEAN);
333: LOG.debug("routingConfig match? " + match);
334: if (match != null && !match.booleanValue()) {
335: return false;
336: }
337: }
338: } catch (XPathExpressionException e) {
339: LOG.error("error in isMatch ", e);
340: throw new RuntimeException(
341: "Error trying to find xml content with xpath expressions: "
342: + findXpathExpression + " or "
343: + xpathExpression, e);
344: }
345: }
346: resolver.setRuleExtension(null);
347: }
348: }
349: String findXpathExpression = "//routingConfig/globalEvaluations/xpathexpression";
350: String xpathExpression = "";
351: try {
352: NodeList xpathExpressions = (NodeList) xpath.evaluate(
353: findXpathExpression, getConfigXML(),
354: XPathConstants.NODESET);
355: for (int i = 0; i < xpathExpressions.getLength(); i++) {
356: Node xpathNode = xpathExpressions.item(i);
357: xpathExpression = xpathNode.getFirstChild()
358: .getNodeValue();
359: LOG
360: .debug("global XPath expression: "
361: + xpathExpression);
362: if (!Utilities.isEmpty(xpathExpression)) {
363: LOG.debug("DocContent: "
364: + docContent.getDocContent());
365: Boolean match = (Boolean) xpath.evaluate(
366: xpathExpression, docContent.getDocument(),
367: XPathConstants.BOOLEAN);
368: LOG.debug("Global match? " + match);
369: if (match != null && !match.booleanValue()) {
370: return false;
371: }
372: }
373: }
374: } catch (XPathExpressionException e) {
375: LOG.error("error in isMatch ", e);
376: throw new RuntimeException(
377: "Error trying to find xml content with xpath expressions: "
378: + findXpathExpression, e);
379: }
380: return true;
381: }
382:
383: public List getRuleRows() {
384: if (ruleRows.isEmpty()) {
385: ruleRows = getRows(getConfigXML(), new String[] { "ALL",
386: "RULE" });
387: }
388: return ruleRows;
389: }
390:
391: private static String getValidationErrorMessage(XPath xpath,
392: Element root, String fieldName)
393: throws XPathExpressionException {
394: String findErrorMessage = "//routingConfig/" + FIELD_DEF_E
395: + "[@name='" + fieldName + "']/validation/message";
396: return (String) xpath.evaluate(findErrorMessage, root,
397: XPathConstants.STRING);
398: }
399:
400: private static List validate(Element root, String[] types, Map map,
401: ErrorGenerator errorGenerator)
402: throws XPathExpressionException {
403: List errors = new ArrayList();
404: XPath xpath = XPathHelper.newXPath();
405:
406: NodeList nodes = getFields(xpath, root, types);
407: for (int i = 0; i < nodes.getLength(); i++) {
408: Node field = nodes.item(i);
409: NamedNodeMap fieldAttributes = field.getAttributes();
410: String fieldName = fieldAttributes.getNamedItem("name")
411: .getNodeValue();
412:
413: LOG.debug("evaluating field: " + fieldName);
414: String findValidation = "//routingConfig/" + FIELD_DEF_E
415: + "[@name='" + fieldName + "']/validation";
416:
417: Node validationNode = (Node) xpath.evaluate(findValidation,
418: root, XPathConstants.NODE);
419: boolean fieldIsRequired = false;
420: if (validationNode != null) {
421: NamedNodeMap validationAttributes = validationNode
422: .getAttributes();
423: Node reqAttribNode = validationAttributes
424: .getNamedItem("required");
425: fieldIsRequired = reqAttribNode != null
426: && "true".equalsIgnoreCase(reqAttribNode
427: .getNodeValue());
428: }
429:
430: String findRegex = "//routingConfig/" + FIELD_DEF_E
431: + "[@name='" + fieldName + "']/validation/regex";
432:
433: String regex = null;
434: Node regexNode = (Node) xpath.evaluate(findRegex, root,
435: XPathConstants.NODE);
436:
437: if (regexNode != null && regexNode.getFirstChild() != null) {
438: regex = regexNode.getFirstChild().getNodeValue();
439: if (regex == null) {
440: throw new RuntimeException("Null regex text node");
441: }
442: }/* else {
443: if (fieldIsRequired) {
444: fieldIsOnlyRequired = true;
445: LOG.debug("Setting empty regex to .+ as field is required");
446: // NOTE: ok, so technically .+ is not the same as checking merely
447: // for existence, because a field can be extant but "empty"
448: // however this has no relevance to the user as an empty field
449: // is for all intents and purposes non-existent (not-filled-in)
450: // so let's just use this regex to simplify the logic and
451: // pass everything through a regex check
452: regex = ".+";
453: } else {
454: LOG.debug("Setting empty regex to .* as field is NOT required");
455: regex = ".*";
456: }
457: }*/
458:
459: LOG.debug("regex for field '" + fieldName + "': '" + regex
460: + "'");
461:
462: String fieldValue = null;
463: if (map != null) {
464: fieldValue = (String) map.get(fieldName);
465: }
466:
467: LOG.debug("field value: " + fieldValue);
468:
469: // fix up non-existent value for regex purposes only
470: if (fieldValue == null) {
471: fieldValue = "";
472: }
473:
474: if (regex == null) {
475: if (fieldIsRequired) {
476: if (fieldValue.length() == 0) {
477: errors
478: .add(errorGenerator
479: .generateMissingFieldError(
480: field,
481: fieldName,
482: getValidationErrorMessage(
483: xpath, root,
484: fieldName)));
485: }
486: }
487: } else {
488: if (!Pattern.compile(regex).matcher(fieldValue)
489: .matches()) {
490: LOG
491: .debug("field value does not match validation regex");
492: errors.add(errorGenerator
493: .generateInvalidFieldError(field,
494: fieldName,
495: getValidationErrorMessage(xpath,
496: root, fieldName)));
497: }
498: }
499: }
500: return errors;
501: }
502:
503: public List getRoutingDataRows() {
504: if (routingDataRows.isEmpty()) {
505: routingDataRows = getRows(getConfigXML(), new String[] {
506: "ALL", "REPORT" });
507: }
508: return routingDataRows;
509: }
510:
511: public String getDocContent() {
512: XPath xpath = XPathHelper.newXPath();
513: final String findDocContent = "//routingConfig/xmlDocumentContent";
514: try {
515: Node xmlDocumentContent = (Node) xpath
516: .evaluate(findDocContent, getConfigXML(),
517: XPathConstants.NODE);
518:
519: NodeList nodes = getFields(xpath, getConfigXML(),
520: new String[] { "ALL", "REPORT", "RULE" });
521: // if (nodes == null || nodes.getLength() == 0) {
522: // return "";
523: // }
524:
525: if (xmlDocumentContent != null
526: && xmlDocumentContent.hasChildNodes()) {
527: // Custom doc content in the routingConfig xml.
528: String documentContent = "";
529: NodeList customNodes = xmlDocumentContent
530: .getChildNodes();
531: for (int i = 0; i < customNodes.getLength(); i++) {
532: Node childNode = customNodes.item(i);
533: documentContent += XmlHelper.writeNode(childNode);
534: }
535:
536: for (int i = 0; i < nodes.getLength(); i++) {
537: Node field = nodes.item(i);
538: NamedNodeMap fieldAttributes = field
539: .getAttributes();
540: String fieldName = fieldAttributes.getNamedItem(
541: "name").getNodeValue();
542: LOG.debug("Replacing field '" + fieldName + "'");
543: Map map = getParamMap();
544: String fieldValue = (String) map.get(fieldName);
545: if (map != null && !Utilities.isEmpty(fieldValue)) {
546: LOG.debug("Replacing %" + fieldName
547: + "% with field value: '" + fieldValue
548: + "'");
549: documentContent = documentContent.replaceAll(
550: "%" + fieldName + "%", fieldValue);
551: } else {
552: LOG
553: .debug("Field map is null or fieldValue is empty");
554: }
555: }
556: return documentContent;
557: } else {
558: // Standard doc content if no doc content is found in the routingConfig xml.
559: StringBuffer documentContent = new StringBuffer(
560: "<xmlRouting>");
561: for (int i = 0; i < nodes.getLength(); i++) {
562: Node field = nodes.item(i);
563: NamedNodeMap fieldAttributes = field
564: .getAttributes();
565: String fieldName = fieldAttributes.getNamedItem(
566: "name").getNodeValue();
567: Map map = getParamMap();
568: if (map != null
569: && !Utilities.isEmpty((String) map
570: .get(fieldName))) {
571: documentContent.append("<field name=\"");
572: documentContent.append(fieldName);
573: documentContent.append("\"><value>");
574: documentContent.append((String) map
575: .get(fieldName));
576: documentContent.append("</value></field>");
577: }
578: }
579: documentContent.append("</xmlRouting>");
580: return documentContent.toString();
581: }
582: } catch (XPathExpressionException e) {
583: LOG.error("error in getDocContent ", e);
584: throw new RuntimeException(
585: "Error trying to find xml content with xpath expression",
586: e);
587: } catch (Exception e) {
588: LOG
589: .error(
590: "error in getDocContent attempting to find xml doc content",
591: e);
592: throw new RuntimeException(
593: "Error trying to get xml doc content.", e);
594: }
595: }
596:
597: public List getRuleExtensionValues() {
598: List extensionValues = new ArrayList();
599:
600: XPath xpath = XPathHelper.newXPath();
601: try {
602: NodeList nodes = getFields(xpath, getConfigXML(),
603: new String[] { "ALL", "RULE" });
604: for (int i = 0; i < nodes.getLength(); i++) {
605: Node field = nodes.item(i);
606: NamedNodeMap fieldAttributes = field.getAttributes();
607: String fieldName = fieldAttributes.getNamedItem("name")
608: .getNodeValue();
609: Map map = getParamMap();
610: if (map != null
611: && !Utilities.isEmpty((String) map
612: .get(fieldName))) {
613: RuleExtensionValue value = new RuleExtensionValue();
614: value.setKey(fieldName);
615: value.setValue((String) map.get(fieldName));
616: extensionValues.add(value);
617: }
618: }
619: } catch (XPathExpressionException e) {
620: LOG.error("error in getRuleExtensionValues ", e);
621: throw new RuntimeException(
622: "Error trying to find xml content with xpath expression",
623: e);
624: }
625: return extensionValues;
626: }
627:
628: public List validateRoutingData(Map paramMap) {
629: this .paramMap = paramMap;
630: try {
631: return validate(getConfigXML(), new String[] { "ALL",
632: "REPORT" }, paramMap, new ErrorGenerator() {
633: public Object generateInvalidFieldError(Node field,
634: String fieldName, String message) {
635: return new WorkflowAttributeValidationError(
636: "routetemplate.xmlattribute.error", message);
637: }
638:
639: public Object generateMissingFieldError(Node field,
640: String fieldName, String message) {
641: return new WorkflowAttributeValidationError(
642: "routetemplate.xmlattribute.required.error",
643: field.getAttributes().getNamedItem("title")
644: .getNodeValue());
645: }
646: });
647: } catch (XPathExpressionException e) {
648: LOG.error("error in validateRoutingData ", e);
649: throw new RuntimeException(
650: "Error trying to find xml content with xpath expression",
651: e);
652: }
653: }
654:
655: public List validateRuleData(Map paramMap) {
656: this .paramMap = paramMap;
657: try {
658: return validate(getConfigXML(), new String[] { "ALL",
659: "RULE" }, paramMap, new ErrorGenerator() {
660: public Object generateInvalidFieldError(Node field,
661: String fieldName, String message) {
662: return new WorkflowServiceErrorImpl(
663: "Xml attribute error.",
664: "routetemplate.xmlattribute.error", message);
665: }
666:
667: public Object generateMissingFieldError(Node field,
668: String fieldName, String message) {
669: return new WorkflowServiceErrorImpl(
670: "Xml attribute error.",
671: "routetemplate.xmlattribute.required.error",
672: field.getAttributes().getNamedItem("title")
673: .getNodeValue());
674: }
675: });
676: } catch (XPathExpressionException e) {
677: LOG.error("error in validateRoutingData ", e);
678: throw new RuntimeException(
679: "Error trying to find xml content with xpath expression",
680: e);
681: }
682: }
683:
684: public void setRequired(boolean required) {
685: this .required = required;
686: }
687:
688: public boolean isRequired() {
689: return required;
690: }
691:
692: public Element getConfigXML() {
693: try {
694: return DocumentBuilderFactory.newInstance()
695: .newDocumentBuilder().parse(
696: new InputSource(new BufferedReader(
697: new StringReader(ruleAttribute
698: .getXmlConfigData()))))
699: .getDocumentElement();
700: } catch (Exception e) {
701: String str = ruleAttribute == null ? "null" : ruleAttribute
702: .getName();
703: LOG.error("error parsing xml data from rule attribute: "
704: + str, e);
705: throw new RuntimeException(
706: "error parsing xml data from rule attribute: "
707: + str, e);
708: }
709: }
710:
711: // TODO: possibly simplify this even further by making WorkflowAttributeValidationError a WorkflowServiceError, and
712: // dispense with custom error generators...
713: public List validateClientRoutingData() {
714: LOG.debug("validating client routing data");
715: try {
716: return validate(getConfigXML(), new String[] { "ALL",
717: "RULE" }, getParamMap(), new ErrorGenerator() {
718: public Object generateInvalidFieldError(Node field,
719: String fieldName, String message) {
720: if (Utilities.isEmpty(message)) {
721: message = "invalid field value";
722: } else {
723: LOG.info("Message: '" + message + "'");
724: }
725: return new WorkflowAttributeValidationError(
726: fieldName, message);
727: }
728:
729: public Object generateMissingFieldError(Node field,
730: String fieldName, String message) {
731: return new WorkflowAttributeValidationError(
732: fieldName, "Attribute is required; "
733: + message);
734: }
735: });
736: } catch (XPathExpressionException e) {
737: LOG.error("error in validateClientRoutingData ", e);
738: throw new RuntimeException(
739: "Error trying to find xml content with xpath expression",
740: e);
741: }
742: }
743:
744: public Map getParamMap() {
745: return paramMap;
746: }
747:
748: public void setParamMap(Map paramMap) {
749: this.paramMap = paramMap;
750: }
751: }
|