Commits

Anonymous committed d5b3f65

Allow custom variable resolvers. Fixed WF-381

  • Participants
  • Parent commits 3931f24

Comments (0)

Files changed (9)

File src/java/com/opensymphony/workflow/AbstractWorkflow.java

 import com.opensymphony.workflow.query.WorkflowExpressionQuery;
 import com.opensymphony.workflow.query.WorkflowQuery;
 import com.opensymphony.workflow.spi.*;
-import com.opensymphony.workflow.util.ScriptVariableParser;
+import com.opensymphony.workflow.util.VariableResolver;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
     protected WorkflowContext context;
     private Configuration configuration;
     private ThreadLocal stateCache = new ThreadLocal();
-    private TypeResolver resolver;
+    private TypeResolver typeResolver;
 
     //~ Constructors ///////////////////////////////////////////////////////////
 
     }
 
     public void setResolver(TypeResolver resolver) {
-        this.resolver = resolver;
+        this.typeResolver = resolver;
     }
 
     public TypeResolver getResolver() {
-        if (resolver == null) {
-            resolver = TypeResolver.getResolver();
+        if (typeResolver == null) {
+            typeResolver = TypeResolver.getResolver();
         }
 
-        return resolver;
+        return typeResolver;
     }
 
     /**
             for (Iterator iterator = args.entrySet().iterator();
                     iterator.hasNext();) {
                 Map.Entry mapEntry = (Map.Entry) iterator.next();
-                mapEntry.setValue(ScriptVariableParser.translateVariables((String) mapEntry.getValue(), transientVars, ps));
+                mapEntry.setValue(getConfiguration().getVariableResolver().translateVariables((String) mapEntry.getValue(), transientVars, ps));
             }
 
             FunctionProvider provider = getResolver().getFunction(type, args);
         for (Iterator iterator = args.entrySet().iterator();
                 iterator.hasNext();) {
             Map.Entry mapEntry = (Map.Entry) iterator.next();
-            mapEntry.setValue(ScriptVariableParser.translateVariables((String) mapEntry.getValue(), transientVars, ps));
+            mapEntry.setValue(getConfiguration().getVariableResolver().translateVariables((String) mapEntry.getValue(), transientVars, ps));
         }
 
         if (currentStepId != -1) {
                 for (Iterator iterator2 = args.entrySet().iterator();
                         iterator2.hasNext();) {
                     Map.Entry mapEntry = (Map.Entry) iterator2.next();
-                    mapEntry.setValue(ScriptVariableParser.translateVariables((String) mapEntry.getValue(), transientVars, ps));
+                    mapEntry.setValue(getConfiguration().getVariableResolver().translateVariables((String) mapEntry.getValue(), transientVars, ps));
                 }
 
                 Validator validator = getResolver().getValidator(type, args);
 
             String owner = theResult.getOwner();
 
+            VariableResolver variableResolver = getConfiguration().getVariableResolver();
+
             if (owner != null) {
-                Object o = ScriptVariableParser.translateVariables(owner, transientVars, ps);
+                Object o = variableResolver.translateVariables(owner, transientVars, ps);
                 owner = (o != null) ? o.toString() : null;
             }
 
             String oldStatus = theResult.getOldStatus();
-            oldStatus = ScriptVariableParser.translateVariables(oldStatus, transientVars, ps).toString();
+            oldStatus = variableResolver.translateVariables(oldStatus, transientVars, ps).toString();
 
             String status = theResult.getStatus();
-            status = ScriptVariableParser.translateVariables(status, transientVars, ps).toString();
+            status = variableResolver.translateVariables(status, transientVars, ps).toString();
 
             if (currentStep != null) {
                 store.markFinished(currentStep, actionId, new Date(), oldStatus, context.getCaller());
             Date dueDate = null;
 
             if ((theResult.getDueDate() != null) && (theResult.getDueDate().length() > 0)) {
-                Object dueDateObject = ScriptVariableParser.translateVariables(theResult.getDueDate(), transientVars, ps);
+                Object dueDateObject = variableResolver.translateVariables(theResult.getDueDate(), transientVars, ps);
 
                 if (dueDateObject instanceof Date) {
                     dueDate = (Date) dueDateObject;

File src/java/com/opensymphony/workflow/config/Configuration.java

 import com.opensymphony.workflow.StoreException;
 import com.opensymphony.workflow.loader.WorkflowDescriptor;
 import com.opensymphony.workflow.spi.WorkflowStore;
+import com.opensymphony.workflow.util.VariableResolver;
 
 import java.net.URL;
 
     Map getPersistenceArgs();
 
     /**
+     * Return the resolver to use for all variables specified in scripts
+     */
+    VariableResolver getVariableResolver();
+
+    /**
      * Get the named workflow descriptor.
      * @param name the workflow name
      * @throws FactoryException if there was an error looking up the descriptor or if it could not be found.

File src/java/com/opensymphony/workflow/config/DefaultConfiguration.java

 import com.opensymphony.workflow.loader.*;
 import com.opensymphony.workflow.loader.ClassLoaderUtil;
 import com.opensymphony.workflow.spi.WorkflowStore;
+import com.opensymphony.workflow.util.DefaultVariableResolver;
+import com.opensymphony.workflow.util.VariableResolver;
 
 import org.w3c.dom.*;
 
  * rather than in the calling client.
  *
  * @author Hani Suleiman
- * @version $Revision: 1.11 $
+ * @version $Revision: 1.12 $
  */
 public class DefaultConfiguration implements Configuration, Serializable {
     //~ Static fields/initializers /////////////////////////////////////////////
     private String persistenceClass;
     private WorkflowFactory factory = new URLWorkflowFactory();
     private transient WorkflowStore store = null;
+    private VariableResolver variableResolver = new DefaultVariableResolver();
     private boolean initialized;
 
     //~ Methods ////////////////////////////////////////////////////////////////
         return persistenceArgs;
     }
 
+    public VariableResolver getVariableResolver() {
+        return variableResolver;
+    }
+
     public WorkflowDescriptor getWorkflow(String name) throws FactoryException {
         WorkflowDescriptor workflow = factory.getWorkflow(name);
 
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
             dbf.setNamespaceAware(true);
 
-            DocumentBuilder db = null;
+            DocumentBuilder db;
 
             try {
                 db = dbf.newDocumentBuilder();
 
             Element root = (Element) doc.getElementsByTagName("osworkflow").item(0);
             Element p = XMLUtil.getChildElement(root, "persistence");
+            Element resolver = XMLUtil.getChildElement(root, "resolver");
             Element factoryElement = XMLUtil.getChildElement(root, "factory");
 
+            if (resolver != null) {
+                String resolverClass = resolver.getAttribute("class");
+
+                if (resolverClass != null) {
+                    variableResolver = (VariableResolver) ClassLoaderUtil.loadClass(resolverClass, getClass()).newInstance();
+                }
+            }
+
             persistenceClass = p.getAttribute("class");
 
             List args = XMLUtil.getChildElements(p, "property");

File src/java/com/opensymphony/workflow/config/SpringConfiguration.java

 import com.opensymphony.workflow.loader.WorkflowDescriptor;
 import com.opensymphony.workflow.loader.WorkflowFactory;
 import com.opensymphony.workflow.spi.WorkflowStore;
+import com.opensymphony.workflow.util.DefaultVariableResolver;
+import com.opensymphony.workflow.util.VariableResolver;
 
 import java.net.URL;
 
 /**
  * @author        Quake Wang
  * @since        2004-5-2
- * @version $Revision: 1.3 $
+ * @version $Revision: 1.4 $
  *
  **/
 public class SpringConfiguration implements Configuration {
     //~ Instance fields ////////////////////////////////////////////////////////
 
+    //we init this for backward compat since existing spring configs likely don't specify this
+    private VariableResolver variableResolver = new DefaultVariableResolver();
     private WorkflowFactory factory;
     private WorkflowStore store;
 
         this.factory = factory;
     }
 
-    /* (non-Javadoc)
-     * @see com.opensymphony.workflow.config.Configuration#isInitialized()
-     */
     public boolean isInitialized() {
-        // TODO Auto-generated method stub
         return false;
     }
 
         return factory.isModifiable(name);
     }
 
-    /* (non-Javadoc)
-     * @see com.opensymphony.workflow.config.Configuration#getPersistence()
-     */
     public String getPersistence() {
-        // TODO Auto-generated method stub
         return null;
     }
 
-    /* (non-Javadoc)
-     * @see com.opensymphony.workflow.config.Configuration#getPersistenceArgs()
-     */
     public Map getPersistenceArgs() {
-        // TODO Auto-generated method stub
         return null;
     }
 
         this.store = store;
     }
 
+    public void setVariableResolver(VariableResolver variableResolver) {
+        this.variableResolver = variableResolver;
+    }
+
+    public VariableResolver getVariableResolver() {
+        return variableResolver;
+    }
+
     public WorkflowDescriptor getWorkflow(String name) throws FactoryException {
         WorkflowDescriptor workflow = factory.getWorkflow(name);
 
         return store;
     }
 
-    /* (non-Javadoc)
-     * @see com.opensymphony.workflow.config.Configuration#load(java.net.URL)
-     */
     public void load(URL url) throws FactoryException {
-        // TODO Auto-generated method stub
     }
 
     public boolean removeWorkflow(String workflow) throws FactoryException {

File src/java/com/opensymphony/workflow/util/DefaultVariableResolver.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.util;
+
+import com.opensymphony.module.propertyset.PropertySet;
+
+import com.opensymphony.provider.BeanProvider;
+import com.opensymphony.provider.bean.DefaultBeanProvider;
+
+import java.io.Serializable;
+
+import java.util.Map;
+
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: Oct 14, 2003
+ * Time: 11:58:12 PM
+ */
+public class DefaultVariableResolver implements VariableResolver, Serializable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private transient BeanProvider beanProvider = null;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Object getVariableFromMaps(String var, Map transientVars, PropertySet ps) {
+        int firstDot = var.indexOf('.');
+        String actualVar = var;
+
+        if (firstDot != -1) {
+            actualVar = var.substring(0, firstDot);
+        }
+
+        Object o = transientVars.get(actualVar);
+
+        if (o == null) {
+            o = ps.getAsActualType(actualVar);
+        }
+
+        if (firstDot != -1) {
+            if (beanProvider == null) {
+                beanProvider = new DefaultBeanProvider();
+            }
+
+            o = beanProvider.getProperty(o, var.substring(firstDot + 1));
+        }
+
+        return o;
+    }
+
+    /**
+       * Parses a string for instances of "${foo}" and returns a string with all instances replaced
+       * with the string value of the foo object (<b>foo.toString()</b>). If the string being passed
+       * in only refers to a single variable and contains no other characters (for example: ${foo}),
+       * then the actual object is returned instead of converting it to a string.
+       */
+    public Object translateVariables(String s, Map transientVars, PropertySet ps) {
+        String temp = s.trim();
+
+        if (temp.startsWith("${") && temp.endsWith("}") && (temp.indexOf('$', 1) == -1)) {
+            // the string is just a variable reference, don't convert it to a string
+            String var = temp.substring(2, temp.length() - 1);
+
+            return getVariableFromMaps(var, transientVars, ps);
+        } else {
+            // the string passed in contains multiple variables (or none!) and should be treated as a string
+            while (true) {
+                int x = s.indexOf("${");
+                int y = s.indexOf("}", x);
+
+                if ((x != -1) && (y != -1)) {
+                    String var = s.substring(x + 2, y);
+                    String t = null;
+                    Object o = getVariableFromMaps(var, transientVars, ps);
+
+                    if (o != null) {
+                        t = o.toString();
+                    }
+
+                    if (t != null) {
+                        s = s.substring(0, x) + t + s.substring(y + 1);
+                    } else {
+                        // the variable doesn't exist, so don't display anything
+                        s = s.substring(0, x) + s.substring(y + 1);
+                    }
+                } else {
+                    break;
+                }
+            }
+
+            return s;
+        }
+    }
+}

File src/java/com/opensymphony/workflow/util/ScriptVariableParser.java

-/*
- * Copyright (c) 2002-2003 by OpenSymphony
- * All rights reserved.
- */
-package com.opensymphony.workflow.util;
-
-import com.opensymphony.module.propertyset.PropertySet;
-
-import com.opensymphony.provider.BeanProvider;
-import com.opensymphony.provider.bean.DefaultBeanProvider;
-
-import java.util.Map;
-
-
-/**
- * @author Hani Suleiman (hani@formicary.net)
- * Date: Oct 14, 2003
- * Time: 11:58:12 PM
- */
-public class ScriptVariableParser {
-    //~ Static fields/initializers /////////////////////////////////////////////
-
-    private static BeanProvider beanProvider = new DefaultBeanProvider();
-
-    //~ Methods ////////////////////////////////////////////////////////////////
-
-    public static Object getVariableFromMaps(String var, Map transientVars, PropertySet ps) {
-        int firstDot = var.indexOf('.');
-        String actualVar = var;
-
-        if (firstDot != -1) {
-            actualVar = var.substring(0, firstDot);
-        }
-
-        Object o = transientVars.get(actualVar);
-
-        if (o == null) {
-            o = ps.getAsActualType(actualVar);
-        }
-
-        if (firstDot != -1) {
-            o = beanProvider.getProperty(o, var.substring(firstDot + 1));
-        }
-
-        return o;
-    }
-
-    /**
-       * Parses a string for instances of "${foo}" and returns a string with all instances replaced
-       * with the string value of the foo object (<b>foo.toString()</b>). If the string being passed
-       * in only refers to a single variable and contains no other characters (for example: ${foo}),
-       * then the actual object is returned instead of converting it to a string.
-       */
-    public static Object translateVariables(String s, Map transientVars, PropertySet ps) {
-        String temp = s.trim();
-
-        if (temp.startsWith("${") && temp.endsWith("}") && (temp.indexOf('$', 1) == -1)) {
-            // the string is just a variable reference, don't convert it to a string
-            String var = temp.substring(2, temp.length() - 1);
-
-            return getVariableFromMaps(var, transientVars, ps);
-        } else {
-            // the string passed in contains multiple variables (or none!) and should be treated as a string
-            while (true) {
-                int x = s.indexOf("${");
-                int y = s.indexOf("}", x);
-
-                if ((x != -1) && (y != -1)) {
-                    String var = s.substring(x + 2, y);
-                    String t = null;
-                    Object o = getVariableFromMaps(var, transientVars, ps);
-
-                    if (o != null) {
-                        t = o.toString();
-                    }
-
-                    if (t != null) {
-                        s = s.substring(0, x) + t + s.substring(y + 1);
-                    } else {
-                        // the variable doesn't exist, so don't display anything
-                        s = s.substring(0, x) + s.substring(y + 1);
-                    }
-                } else {
-                    break;
-                }
-            }
-
-            return s;
-        }
-    }
-}

File src/java/com/opensymphony/workflow/util/SendEmail.java

 import com.opensymphony.module.propertyset.PropertySet;
 
 import com.opensymphony.workflow.FunctionProvider;
+import com.opensymphony.workflow.config.Configuration;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
         String m = (String) args.get("message");
         String smtpHost = (String) args.get("smtpHost");
         boolean parseVariables = "true".equals(args.get("parseVariables"));
+        Configuration config = (Configuration) transientVars.get("configuration");
 
         try {
             Properties props = new Properties();
             message.setFrom(new InternetAddress(from));
 
             Set toSet = new HashSet();
-            StringTokenizer st = new StringTokenizer(parseVariables ? ScriptVariableParser.translateVariables(to, transientVars, ps).toString() : to, ", ");
+            VariableResolver variableResolver = config.getVariableResolver();
+            StringTokenizer st = new StringTokenizer(parseVariables ? variableResolver.translateVariables(to, transientVars, ps).toString() : to, ", ");
 
             while (st.hasMoreTokens()) {
                 String user = st.nextToken();
                 ccSet = new HashSet();
 
                 if (parseVariables) {
-                    cc = ScriptVariableParser.translateVariables(cc, transientVars, ps).toString();
+                    cc = variableResolver.translateVariables(cc, transientVars, ps).toString();
                 }
 
                 st = new StringTokenizer(cc, ", ");
                 message.setRecipients(Message.RecipientType.CC, (InternetAddress[]) ccSet.toArray(new InternetAddress[ccSet.size()]));
             }
 
-            message.setSubject(parseVariables ? ScriptVariableParser.translateVariables(subject, transientVars, ps).toString() : subject);
+            message.setSubject(parseVariables ? variableResolver.translateVariables(subject, transientVars, ps).toString() : subject);
             message.setSentDate(new Date());
-            message.setText(parseVariables ? ScriptVariableParser.translateVariables(m, transientVars, ps).toString() : m);
+            message.setText(parseVariables ? variableResolver.translateVariables(m, transientVars, ps).toString() : m);
             message.saveChanges();
 
             transport.connect();

File src/java/com/opensymphony/workflow/util/VariableResolver.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.util;
+
+import com.opensymphony.module.propertyset.PropertySet;
+
+import java.util.Map;
+
+
+/**
+ * @author hani Date: Mar 29, 2005 Time: 8:17:31 PM
+ */
+public interface VariableResolver {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    Object translateVariables(String s, Map transientVars, PropertySet ps);
+}

File src/test/com/opensymphony/workflow/TestAbstractWorkflow.java

 
 import com.opensymphony.module.propertyset.memory.MemoryPropertySet;
 
-import com.opensymphony.workflow.util.ScriptVariableParser;
+import com.opensymphony.workflow.util.DefaultVariableResolver;
 
 import junit.framework.TestCase;
 
         ps.setObject("a2", a2);
         ps.setString("foo", "bar");
 
-        assertEquals("aName", ScriptVariableParser.getVariableFromMaps("a.name", transients, ps));
-        assertEquals(a, ScriptVariableParser.getVariableFromMaps("a", transients, ps));
-        assertEquals("blah", ScriptVariableParser.getVariableFromMaps("blah", transients, ps));
-        assertEquals("jack", ScriptVariableParser.getVariableFromMaps("a2.b.name", transients, ps));
-        assertEquals(new Integer(-1), ScriptVariableParser.getVariableFromMaps("a2.b.age", transients, ps));
-        assertEquals("bar", ScriptVariableParser.getVariableFromMaps("foo", transients, ps));
+        DefaultVariableResolver resolver = new DefaultVariableResolver();
+        assertEquals("aName", resolver.getVariableFromMaps("a.name", transients, ps));
+        assertEquals(a, resolver.getVariableFromMaps("a", transients, ps));
+        assertEquals("blah", resolver.getVariableFromMaps("blah", transients, ps));
+        assertEquals("jack", resolver.getVariableFromMaps("a2.b.name", transients, ps));
+        assertEquals(new Integer(-1), resolver.getVariableFromMaps("a2.b.age", transients, ps));
+        assertEquals("bar", resolver.getVariableFromMaps("foo", transients, ps));
 
-        assertEquals("hello, jack, what is your age? -1", ScriptVariableParser.translateVariables("hello, ${a2.b.name}, what is your age? ${a2.b.age}", transients, ps));
-        assertEquals("hello, , what is your age? -1", ScriptVariableParser.translateVariables("hello, ${I.Don't.EXIST}, what is your age? ${a2.b.age}", transients, ps));
+        assertEquals("hello, jack, what is your age? -1", resolver.translateVariables("hello, ${a2.b.name}, what is your age? ${a2.b.age}", transients, ps));
+        assertEquals("hello, , what is your age? -1", resolver.translateVariables("hello, ${I.Don't.EXIST}, what is your age? ${a2.b.age}", transients, ps));
     }
 
     //~ Inner Classes //////////////////////////////////////////////////////////