View Javadoc

1   /*
2    * Copyright 2010 Fraunhofer Gesellschaft, Munich, Germany,
3    * for its Fraunhofer Institute for Computer Architecture and Software
4    * Technology (FIRST), Berlin, Germany. All rights reserved.
5    * http://www.first.fraunhofer.de/
6    */
7   
8   package net.kwfgrid.gwes;
9   
10  import net.kwfgrid.gwes.jaxen.*;
11  import net.kwfgrid.gwes.util.StringUtils;
12  import net.kwfgrid.gworkflowdl.structure.*;
13  import org.apache.log4j.Logger;
14  import org.jaxen.*;
15  import org.jaxen.jdom.JDOMXPath;
16  import org.jdom.Document;
17  
18  /**
19   * This class is used to check conditions expressed in the XPath syntax.
20   *
21   * @author Andreas Hoheisel
22   *         (<a href="http://www.andreas-hoheisel.de">www.andreas-hoheisel.de</a>)
23   * @version $Id: ConditionChecker.java 1433 2010-11-29 18:06:07Z hoheisel $
24   */
25  public class ConditionChecker {
26  
27      /**
28       * log4j logger.
29       */
30      private final static Logger logger = Logger.getLogger(ConditionChecker.class);
31  
32      public final static int CONDITION_TRUE = 1;
33      public final static int CONDITION_FALSE_STATIC = 2;
34      public final static int CONDITION_FALSE_DYNAMIC = 4;
35  
36      private final SimpleFunctionContext fnContext;
37      private final SimpleNamespaceContext defaultNsContext;
38  
39      private Document context;
40  
41      /**
42       * Constructor for the condition checker.
43       *
44       * @param workflow   The workflow context of the condition.
45       * @param transition The parent transition.
46       */
47      public ConditionChecker() {
48          // register add-on functions
49          fnContext = new XPathFunctionContext();
50          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "current-dateTimeMs", new CurrentDateTimeMsFunction());
51          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "dateTime2ms", new DateTime2msFunction());
52          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2dateTime", new Ms2dateTimeFunction());
53          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2year", new Ms2yearFunction());
54          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2month", new Ms2monthFunction());
55          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2day", new Ms2dayFunction());
56          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2hour", new Ms2hourFunction());
57          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2minute", new Ms2minuteFunction());
58          fnContext.registerFunction("http://www.gridworkflow.org/gworkflowdl", "ms2second", new Ms2secondFunction());
59          // add default namespaces
60          defaultNsContext = new SimpleNamespaceContext();
61          defaultNsContext.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
62          defaultNsContext.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
63          defaultNsContext.addNamespace("gwdl", "http://www.gridworkflow.org/gworkflowdl");
64          defaultNsContext.addNamespace("soapenv", "http://www.w3.org/2003/05/soap-envelope");
65      }
66  
67      /**
68       * Set the evaluation context for conditions.
69       * @param context Context as JDOM Document.
70       */
71      public void setContext(Document context) {
72          this.context = context; 
73      }
74  
75      /**
76       * Check a set of conditions.
77       * Variables (e.g. <code>$input/</code>) that are expanded by the corresponding
78       * context XPath (e.g. <code>/gwdl:data/input</code>).
79       *
80       * @param to The transition occurrence that contains the conditios to check.
81       * @return <code>CONDITION_TRUE</code> if all conditions evaluate to true and <code>CONDITION_FALSE_STATIC</code>
82       * if a least one static condition evaluates to false. <code>CONDITION_FALSE_DYNAMIC</code> if condition is false
83       * just because of time-depend conditions that may change in future.
84       * @throws WorkflowFormatException If the syntax of the condition is wrong.
85       */
86      public int check(TransitionOccurrence to) throws WorkflowFormatException {
87          int ret = CONDITION_TRUE;
88  
89          // set context
90          // e.g., <gwdl:data><i>5</i><j>6</j></gwdl:data>
91          setContext(to.getContext());
92  
93          // expand namespace context
94          SimpleNamespaceContext nsContext = defaultNsContext;
95          GenericProperties transProps = to.transition.getProperties();
96          for (Property prop : transProps.getProperties()) {
97              String key = prop.getKey();
98              if (key.startsWith("xmlns:") && key.length() > 6) {
99  //                logger.info("==> adding namespace: "+key.substring(6)+":"+prop.getValue());
100                 nsContext.addNamespace(key.substring(6),prop.getValue());
101             }
102         }
103 
104         // loop through all conditions.
105         for (String condition1 : to.transition.getConditions()) {
106             String condition = StringUtils.expandContextVariables(condition1.trim());
107 
108             // if one of the conditions is always false, then return CONDITION_FALSE_STATIC.
109             try {
110                 if (!check(condition,nsContext)) {
111                     if ((ret & CONDITION_TRUE) != 0) ret -= CONDITION_TRUE;
112                     if (condition.contains("current-dateTimeMs(")) {
113                         ret |= CONDITION_FALSE_DYNAMIC;
114                     } else {
115                         ret |= CONDITION_FALSE_STATIC;
116                         return ret;
117                     }
118                 }
119             } catch (JaxenException e) {
120                 logger.error("exception: "+e, e);
121                 throw new WorkflowFormatException("There is an error with condition '" + condition + "':"+ e, e);
122             }
123         }
124 
125         return ret;
126     }
127 
128     public boolean check(String xpathExpression, NamespaceContext nsContext) throws JaxenException {
129         boolean ret;
130         // quick check if string is "true" or "false"
131         if (xpathExpression.equalsIgnoreCase(Constants.TRUE)) ret = true;
132         else if (xpathExpression.equalsIgnoreCase(Constants.FALSE)) ret = false;
133         else {
134             // real xpath evaluation
135             XPath xpath = new JDOMXPath(xpathExpression);
136             xpath.setFunctionContext(fnContext);
137             xpath.setNamespaceContext(nsContext);
138 //            logger.info("Condition  '" + xpathExpression + "' string value: "+ xpath.stringValueOf(xpathExpression));
139             ret = xpath.booleanValueOf(context);
140         }
141 
142         if (logger.isDebugEnabled()) {
143             logger.debug("Condition '" + xpathExpression + "' is " + ret);
144         }
145         return ret;
146     }
147 
148 }