[jboss-svn-commits] JBL Code SVN: r25635 - in labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration: rule and 4 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri Mar 13 10:04:27 EDT 2009


Author: adinn
Date: 2009-03-13 10:04:26 -0400 (Fri, 13 Mar 2009)
New Revision: 25635

Added:
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/Compiler.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/StackHeights.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/Helper.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/HelperAdapter.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/InterpretedHelper.java
Modified:
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Script.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanLiteral.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MinusExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ReturnExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ThrowExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/test/TestScript.java
Log:
modified rule language to allow plug-in capability for the helper class which defines the rule language built-ins. added compiler class which specialises the helper to provide a rule interpreter specific to the built-in associated with any given rule

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Script.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Script.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Script.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -9,14 +9,16 @@
     private String name;
     private String targetClass;
     private String targetMethod;
+    private String targetHelper;
     private Location targetLocation;
     private String ruleText;
 
-    Script (String name, String targetClass, String targetMethod, Location targetLocation, String ruleText)
+    Script (String name, String targetClass, String targetMethod, String targetHelper, Location targetLocation, String ruleText)
     {
         this.name = name;
         this.targetClass = targetClass;
         this.targetMethod = targetMethod;
+        this.targetHelper = targetHelper;
         this.targetLocation = targetLocation;
         this.ruleText = ruleText;
     }
@@ -29,6 +31,10 @@
         return targetClass;
     }
 
+    public String getTargetHelper() {
+        return targetHelper;
+    }
+
     public String getTargetMethod() {
         return targetMethod;
     }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -48,6 +48,14 @@
  * byte code transformer used to introduce orchestration events into JBoss code
  */
 public class Transformer implements ClassFileTransformer {
+
+    private static Transformer theTransformer = null;
+
+    public static Transformer getTheTransformer()
+    {
+        return theTransformer;
+    }
+
     /**
      * constructor allowing this transformer to be provided with access to the JVM's instrumentation
      * implementation
@@ -57,6 +65,7 @@
     public Transformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts)
             throws Exception
     {
+        theTransformer = this;
         this.inst = inst;
         targetToScriptMap = new HashMap<String, List<Script>>();
 
@@ -73,6 +82,7 @@
                 String name = null;
                 String targetClass = null;
                 String targetMethod = null;
+                String targetHelper = null;
                 LocationType locationType = null;
                 Location targetLocation = null;
                 int lineNumber = 0;
@@ -98,6 +108,8 @@
                         targetClass = line.substring(6).trim();
                     } else if (line.startsWith("METHOD ")) {
                         targetMethod = line.substring(7).trim();
+                    } else if (line.startsWith("HELPER ")) {
+                        targetHelper = line.substring(7).trim();
                     } else if ((locationType = LocationType.type(line)) != null) {
                         String parameters = LocationType.parameterText(line);
                         targetLocation = Location.create(locationType, parameters);
@@ -120,12 +132,15 @@
                             if (targetLocation == null) {
                                 targetLocation = Location.create(LocationType.ENTRY, "");
                             }
-                            Script script = new Script(name, targetClass, targetMethod, targetLocation, nextRule);
+                            Script script = new Script(name, targetClass, targetMethod, targetHelper, targetLocation, nextRule);
                             scripts.add(script);
                             if (isVerbose()) {
                                 System.out.println("RULE " + script.getName());
                                 System.out.println("CLASS " + script.getTargetClass());
                                 System.out.println("METHOD " + script.getTargetMethod());
+                                if (script.getTargetHelper() != null) {
+                                    System.out.println("HELPER " + script.getTargetHelper());
+                                }
                                 if (targetLocation != null) {
                                     System.out.println(targetLocation);
                                 } else {
@@ -273,6 +288,48 @@
         }
     }
 
+    /**
+     * this is a classloader used to define classes from bytecode
+     *
+     * TODO -- we probably need to use the protection domain of the trigger class somewhere here
+     */
+    private static class ClassbyteClassLoader extends ClassLoader
+    {
+        ClassbyteClassLoader(ClassLoader cl)
+        {
+            super(cl);
+        }
+        
+        public Class addClass(String name, byte[] bytes)
+                throws ClassFormatError
+        {
+            Class cl = defineClass(name, bytes, 0, bytes.length);
+            resolveClass(cl);
+
+            return cl;
+        }
+    }
+
+    /**
+     * a singleton instance of the classloader used to define classes from bytecode
+     */
+    private static ClassbyteClassLoader theLoader = new ClassbyteClassLoader(Transformer.class.getClassLoader());
+
+    /**
+     * a helper method which allows dynamic creation of generated helper adapter classes
+     * @param helperAdapterName
+     * @param classBytes
+     * @return
+     */
+    public Class<?> loadHelperAdapter(Class<?> helperClass, String helperAdapterName, byte[] classBytes)
+    {
+         //return theLoader.addClass(helperAdapterName, classBytes);
+
+        ClassbyteClassLoader loader = new ClassbyteClassLoader(helperClass.getClassLoader());
+
+        return loader.addClass(helperAdapterName, classBytes);
+    }
+
     /* switches controlling behaviour of transformer */
 
     /**
@@ -306,6 +363,12 @@
     public static final String VERBOSE = ORCHESTRATION_PACKAGE_PREFIX + "verbose";
 
     /**
+     * system property set (to any value) in order to switch on compilation of rules and left unset
+     * if rules are to be interpreted.
+     */
+    public static final String COMPILE_TO_BYTECODE = ORCHESTRATION_PACKAGE_PREFIX + "compileToBytecode";
+
+    /**
      * system property set (to any value) in order to switch on dumping of generated bytecode to .class files
      */
     public static final String DUMP_GENERATED_CLASSES = ORCHESTRATION_PACKAGE_PREFIX + "dump.generated.classes";
@@ -321,7 +384,16 @@
     {
         final String handlerClass = script.getTargetClass();
         final String handlerMethod = script.getTargetMethod();
+        final String helperName = script.getTargetHelper();
         final Location handlerLocation = script.getTargetLocation();
+        Class helperClass = null;
+        if (helperName != null) {
+            try {
+                helperClass = loader.loadClass(helperName);
+            } catch (ClassNotFoundException e) {
+                System.out.println("org.jboss.jbossts.orchestration.agent.Transformer : unknown helper class " + helperName + " for rule " + script.getName());
+            }
+        }
         if (isVerbose()) {
             System.out.println("org.jboss.jbossts.orchestration.agent.Transformer : Inserting trigger event");
             System.out.println("  class " + handlerClass);
@@ -331,7 +403,7 @@
         final Rule rule;
         String ruleName = script.getName();
         try {
-            rule = Rule.create(ruleName, handlerClass, handlerMethod, handlerLocation, script.getRuleText(), loader);
+            rule = Rule.create(ruleName, handlerClass, handlerMethod, helperClass, handlerLocation, script.getRuleText(), loader);
         } catch (ParseException pe) {
             System.out.println("org.jboss.jbossts.orchestration.agent.Transformer : error parsing rule " + ruleName + " : " + pe);
             return targetClassBytes;
@@ -395,7 +467,7 @@
     }
 
     /**
-     * check whether verbose mode for rule processing is enabled or disable
+     * check whether verbose mode for rule processing is enabled or disabled
      * @return true if verbose mode is enabled etherwise false
      */
     public static boolean isVerbose()
@@ -404,6 +476,15 @@
     }
 
     /**
+     * check whether compilation of rules is enabled or disabled
+     * @return true if compilation of rules is enabled etherwise false
+     */
+    public static boolean isCompileToBytecode()
+    {
+        return compileToBytecode;
+    }
+
+    /**
      * test whether a class with a given name is a potential candidate for insertion of event notifications
      * @param className
      * @return true if a class is a potential candidate for insertion of event notifications otherwise return false
@@ -438,6 +519,11 @@
     private final static boolean verbose = (System.getProperty(VERBOSE) != null);
 
     /**
+     *  switch to control verbose output during rule processing
+     */
+    private final static boolean compileToBytecode = (System.getProperty(COMPILE_TO_BYTECODE) != null);
+
+    /**
      *  switch to control dumping of generated bytecode to .class files
      */
     private final static boolean dumpGeneratedClasses = (System.getProperty(DUMP_GENERATED_CLASSES) != null);
@@ -461,6 +547,13 @@
         }
     }
 
+    public void maybeDumpClass(String fullName, byte[] bytes)
+    {
+        if (dumpGeneratedClasses) {
+            dumpClass(fullName, bytes);
+        }
+    }
+
     private void dumpClass(String fullName, byte[] bytes)
     {
         int dotIdx = fullName.lastIndexOf('.');

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -33,6 +33,8 @@
 import org.jboss.jbossts.orchestration.rule.exception.ParseException;
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.helper.InterpretedHelper;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -98,7 +100,7 @@
         return Type.VOID;
     }
 
-    public void interpret(Rule.BasicHelper helper)
+    public void interpret(HelperAdapter helper)
             throws ExecuteException
     {
         if (action != null) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -23,19 +23,17 @@
 */
 package org.jboss.jbossts.orchestration.rule;
 
-import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.type.Type;
-import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.expression.ExpressionHelper;
 import org.jboss.jbossts.orchestration.rule.expression.Expression;
-import org.jboss.jbossts.orchestration.rule.expression.BooleanExpression;
-import org.jboss.jbossts.orchestration.rule.expression.BooleanLiteral;
 import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
 import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 import org.jboss.jbossts.orchestration.rule.exception.ParseException;
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.helper.InterpretedHelper;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 
 import java.io.StringWriter;
 import java.io.StringReader;
@@ -93,7 +91,7 @@
         return Type.Z;
     }
 
-    public boolean interpret(Rule.BasicHelper helper)
+    public boolean interpret(HelperAdapter helper)
             throws ExecuteException
     {
         Boolean result = (Boolean)condition.interpret(helper);

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -35,6 +35,8 @@
 import org.jboss.jbossts.orchestration.rule.exception.ParseException;
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.helper.InterpretedHelper;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -247,7 +249,7 @@
         }
     }
 
-    public void interpret(Rule.BasicHelper helper)
+    public void interpret(HelperAdapter helper)
             throws ExecuteException
     {
         Iterator<Binding> iterator = getBindings().iterator();

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -34,16 +34,20 @@
 import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
 import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
-import org.jboss.jbossts.orchestration.synchronization.CountDown;
-import org.jboss.jbossts.orchestration.synchronization.Waiter;
-import org.jboss.jbossts.orchestration.synchronization.Counter;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
+import org.jboss.jbossts.orchestration.rule.helper.Helper;
+import org.jboss.jbossts.orchestration.rule.helper.InterpretedHelper;
 import org.jboss.jbossts.orchestration.agent.Location;
 import org.jboss.jbossts.orchestration.agent.LocationType;
 import org.jboss.jbossts.orchestration.agent.Transformer;
 import org.objectweb.asm.Opcodes;
 
+import org.jboss.jbossts.orchestration.rule.compiler.Compiler;
+
 import java.io.*;
 import java.util.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 
 import java_cup.runtime.Symbol;
 
@@ -136,7 +140,7 @@
      */
     private boolean checkFailed;
 
-    private Rule(String name, String targetClass, String targetMethod, Location targetLocation, String ruleSpec, ClassLoader loader)
+    private Rule(String name, String targetClass, String targetMethod,Class<?> helperClass, Location targetLocation, String ruleSpec, ClassLoader loader)
             throws ParseException, TypeException, CompileException
     {
         ParseNode ruleTree;
@@ -165,6 +169,7 @@
         checked = false;
         this.targetClass = targetClass;
         this.targetMethod = targetMethod;
+        this.helperClass = (helperClass != null ? helperClass : Helper.class);
         this.targetLocation = (targetLocation != null ? targetLocation : Location.create(LocationType.ENTRY, ""));
         triggerClass = null;
         triggerMethod = null;
@@ -211,10 +216,10 @@
         return action;
     }
 
-    public static Rule create(String name, String targetClass, String targetMethod, Location targetLocation, String ruleSpec, ClassLoader loader)
+    public static Rule create(String name, String targetClass, String targetMethod, Class<?> helperClass, Location targetLocation, String ruleSpec, ClassLoader loader)
             throws ParseException, TypeException, CompileException
     {
-            return new Rule(name, targetClass, targetMethod, targetLocation, ruleSpec, loader);
+            return new Rule(name, targetClass, targetMethod, helperClass, targetLocation, ruleSpec, loader);
     }
 
     public void setEvent(String eventSpec) throws ParseException, TypeException
@@ -256,12 +261,17 @@
         if (!checked) {
             try {
                 typeCheck();
-                helperClass = BasicHelper.class;
+                compile();
             } catch (TypeException te) {
-                System.out.println("Rule.typecheck : error typechecking rule " + getName());
+                System.out.println("Rule.ensureTypeCheckedCompiled : error typechecking rule " + getName());
                 te.printStackTrace(System.out);
                 checkFailed = true;
                 return false;
+            } catch (CompileException ce) {
+                System.out.println("Rule.ensureTypeCheckedCompiled : error compiling rule " + getName());
+                ce.printStackTrace(System.out);
+                checkFailed = true;
+                return false;
             }
         }
         
@@ -291,13 +301,38 @@
         checked = true;
     }
 
+    public void compile()
+            throws CompileException
+    {
+        boolean compileToBytecode = isCompileToBytecode();
+
+        if (helperClass == Helper.class && !compileToBytecode) {
+            // we can use the builtin interpreted helper adapter for class Helper
+           helperImplementationClass = InterpretedHelper.class;
+        } else {
+            // we need to generate a helper adapter class which either interprets or compiles
+
+            helperImplementationClass = Compiler.getHelperAdapter(helperClass, compileToBytecode);
+        }
+    }
+
+    /**
+     * should rules be compiled to bytecode
+     * @return true if rules should be compiled to bytecode otherwise false
+     */
+    private boolean isCompileToBytecode()
+    {
+        return Transformer.isCompileToBytecode();
+    }
+
     private void installParameters(boolean isStatic, String className, String descriptor)
             throws TypeException
     {
         List<Binding> parameterBindings = new ArrayList<Binding>();
         Type type;
-        // add a binding for the rule so we can call builting static methods
-        type = typeGroup.create("org.jboss.jbossts.orchestration.rule.Rule$Helper");
+        // add a binding for the rule so we can call builtin static methods
+        type = typeGroup.create(helperClass.getCanonicalName());
+        // type = typeGroup.create("org.jboss.jbossts.orchestration.rule.helper.Helper");
         Binding ruleBinding = new Binding(this, "$", type);
         parameterBindings.add(ruleBinding);
 
@@ -369,35 +404,44 @@
 
     private void execute(Object recipient, Object[] args) throws ExecuteException
     {
-        // type check and compile the rule now if it has not already been done
+        // type check and createHelperAdapter the rule now if it has not already been done
 
         if (ensureTypeCheckedCompiled()) {
 
             // create a helper and get it to execute the rule
-            // eventually we will create a subclass of helper for each rule and compile
+            // eventually we will create a subclass of helper for each rule and createHelperAdapter
             // an implementation of execute from the rule source. for now we create a generic
             // helper and call the generic execute method which interprets the rule
-            BasicHelper helper;
+            HelperAdapter helper;
             try {
-                helper = (BasicHelper)helperClass.newInstance();
-                helper.setRule(this);
+                Constructor constructor = helperImplementationClass.getConstructor(Rule.class);
+                helper = (HelperAdapter)constructor.newInstance(this);
+                //helper = (RuleHelper)helperClass.newInstance();
+                //helper.setRule(this);
+                helper.execute(bindings, recipient, args);
+            } catch (NoSuchMethodException e) {
+                // should not happen!!!
+                System.out.println("cannot find constructor " + helperImplementationClass.getCanonicalName() + "(Rule) for helper class");
+                e.printStackTrace(System.out);
+                return;
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             } catch (InstantiationException e) {
                 // should not happen
-                System.out.println("cannot create instance of " + helperClass.getCanonicalName());
+                System.out.println("cannot create instance of " + helperImplementationClass.getCanonicalName());
                 e.printStackTrace(System.out);
                 return;
             } catch (IllegalAccessException e) {
                 // should not happen
-                System.out.println("cannot access " + helperClass.getCanonicalName());
+                System.out.println("cannot access " + helperImplementationClass.getCanonicalName());
                 e.printStackTrace(System.out);
                 return;
             } catch (ClassCastException e) {
                 // should not happen
-                System.out.println("cast exception " + helperClass.getCanonicalName());
+                System.out.println("cast exception " + helperImplementationClass.getCanonicalName());
                 e.printStackTrace(System.out);
                 return;
             }
-            helper.execute(bindings, recipient, args);
         }
     }
 
@@ -434,6 +478,11 @@
         return nextId++;
     }
 
+    private static boolean compileRules()
+    {
+        return Transformer.isCompileToBytecode();
+    }
+
     /**
      * generate a string representation of the rule
      *
@@ -474,736 +523,27 @@
     }
 
     /**
-     * a helper class instantiated when the rule is triggered which provides an
-     * implementation of execute for the rule and enables the execution context
-     * to be accessed from the executed code
+     * a helper class which defines the builtin methods available to this rule -- by default Helper
      */
-
     private Class helperClass;
 
     /**
-     * Methods provided on this class are automatically made available as builtin operations in
-     * expressions appearing in rule event bindings, conditions and actions. Although Helper
-     * methods are all instance methods the message recipient for the method call is implicit
-     * and does not appear in the builtin call. It does, however, appear in the runtime
-     * invocation, giving the builtin operation access to the rule being fired (the Helper
-     * instance has implicit access to the enclosing rule object since class Helper is
-     * not static).
+     * an extension of the helper class which implements the methods of interface RuleHelper -- by default
+     * InterpretedHelper. This is the class which is instantiated and used as the target for an execute
+     * operation.
      */
-    public static class Helper
-    {
-        protected Rule rule;
 
-        protected Helper(Rule rule)
-        {
-            this.rule = rule;
-        }
-        // tracing support
-        /**
-         * builtin to print a message during rule execution. n.b. this always returns true which
-         * means it can be invoked during condition execution
-         * @param text the message to be printed as trace output
-         * @return true
-         */
-        public boolean debug(String text)
-        {
-            System.out.println("rule.debug{" + rule.getName() + "} : " + text);
-            return true;
-        }
+    private Class helperImplementationClass;
 
-        // file based trace support
-        /**
-         * open a trace output stream identified by identifier to a file located in the current working
-         * directory using a unique generated name
-         * @param identifier an identifier used subsequently to identify the trace output stream
-         * @return true if new file and stream was created, false if a stream identified by identifier
-         * already existed or the identifer is null, "out" or "err"
-         */
-        public boolean openTrace(Object identifier)
-        {
-            return openTrace(identifier, null);
-        }
-
-        /**
-         * open a trace output stream identified by identifier to a file located in the current working
-         * directory using the given file name or a generated name if the supplied name is null
-         * @param identifier an identifier used subsequently to identify the trace output stream
-         * @return true if new file and stream was created, false if a stream identified by identifier
-         * already existed or if a file of the same name already exists or the identifer is null, "out"
-         * or "err"
-         */
-        public boolean openTrace(Object identifier, String fileName)
-        {
-            if (identifier == null) {
-                return false;
-            }
-
-            synchronized(traceMap) {
-                PrintStream stream = traceMap.get(identifier);
-                String name = fileName;
-                if (stream != null) {
-                    return false;
-                }
-                if (fileName == null) {
-                    name = nextFileName();
-                }
-                File file = new File(name);
-
-                if (file.exists() && !file.canWrite()) {
-                    if (fileName == null) {
-                        // keep trying new names until we hit an unused one
-                        do {
-                            name = nextFileName();
-                            file = new File(name);
-                        } while (file.exists() && !file.canWrite());
-                    } else {
-                        // can't open file as requested
-                        return false;
-                    }
-                }
-                
-                FileOutputStream fos;
-
-                try {
-                    if (file.exists()) {
-                         fos = new FileOutputStream(file, true);
-                    } else {
-                        fos = new FileOutputStream(file, true);
-                    }
-                } catch (FileNotFoundException e) {
-                    // oops, just return false
-                    return false;
-                }
-
-                PrintStream ps = new PrintStream(fos, true);
-
-                traceMap.put(identifier, ps);
-
-                return true;
-            }
-        }
-
-        /**
-         * close the trace output stream identified by identifier flushing any pending output
-         * @param identifier an identifier used subsequently to identify the trace output stream
-         * @return true if the stream was flushed and closed, false if no stream is identified by identifier
-         * or the identifer is null, "out" or "err"
-         */
-        public boolean closeTrace(Object identifier)
-        {
-            if (identifier == null ||
-                identifier.equals("out") ||
-                    identifier.equals("err")) {
-                return false;
-            }
-
-            synchronized(traceMap) {
-                PrintStream ps = traceMap.get(identifier);
-                if (ps != null) {
-                    // need to do the close while synchornized so we ensure an open cannot
-                    // proceed until we have flushed all changes to disk
-                    ps.close();
-                    traceMap.put(identifier, null);
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /**
-         * write the supplied message to the trace stream identified by identifier, creating a new stream
-         * if none exists
-         * @param identifier an identifier used subsequently to identify the trace output stream
-         * @param message
-         * @return true
-         * @caveat if identifier is the string "out" or null the message will be written to System.out.
-         * if identifier is the string "err" the message will be written to System.err. 
-         */
-        public boolean trace(Object identifier, String message)
-        {
-            synchronized(traceMap) {
-                PrintStream ps = traceMap.get(identifier);
-                if (ps == null) {
-                    if (openTrace(identifier)) {
-                        ps = traceMap.get(identifier);
-                    } else {
-                        ps = System.out;
-                    }
-                }
-                ps.print(message);
-                ps.flush();
-            }
-            return true;
-        }
-
-        /**
-         * write the supplied message to the trace stream identified by identifier, creating a new stream
-         * if none exists, and append a new line
-         * @param identifier an identifier used subsequently to identify the trace output stream
-         * @param message
-         * @return true
-         * @caveat if identifier is the string "out" or null the message will be written to System.out.
-         * if identifier is the string "err" the message will be written to System.err.
-         */
-        public boolean traceln(Object identifier, String message)
-        {
-            synchronized(traceMap) {
-                PrintStream ps = traceMap.get(identifier);
-                if (ps == null) {
-                    if (openTrace(identifier)) {
-                        ps = traceMap.get(identifier);
-                    } else {
-                        ps = System.out;
-                    }
-                }
-                ps.println(message);
-                ps.flush();
-            }
-            return true;
-        }
-
-        // flag support
-        /**
-         * set a flag keyed by the supplied object if it is not already set
-         * @param identifier the object identifying the relevant flag
-         * @return true if the flag was clear before this call otherwise false
-         */
-        public boolean flag(Object identifier)
-        {
-            synchronized (flagSet) {
-                return flagSet.add(identifier);
-            }
-        }
-
-        /**
-         * test the state of the flag keyed by the supplied object
-         * @param identifier the object identifying the relevant flag
-         * @return true if the flag is set otherwise false
-         */
-        public boolean flagged(Object identifier)
-        {
-            synchronized (flagSet) {
-                return flagSet.contains(identifier);
-            }
-        }
-
-        /**
-         * clear the flag keyed by the supplied object if it is not already clear
-         * @param identifier the object identifying the relevant flag
-         * @return true if the flag was clear before this call otherwise false
-         */
-        public boolean clear(Object identifier)
-        {
-            synchronized (flagSet) {
-                return flagSet.remove(identifier);
-            }
-        }
-
-        // countdown support
-        /**
-         * builtin to test test if a countdown has been installed
-         * @param identifier an object which uniquely identifies the countdown in question
-         * @return true if the countdown is currently installed
-         */
-        public boolean getCountDown(Object identifier)
-        {
-            synchronized (countDownMap) {
-                return (countDownMap.get(identifier) != null);
-            }
-        }
-
-        /**
-         * builtin to test add a countdown identified by a specific object and with the specified
-         * count. n.b. this builtin checks if a countdown identified by the supplied object is
-         * currently installed, returning false if so, otherwise atomically adds the countdown
-         * and returns true. This allows the builtin to be used safely in conditions where concurrent
-         * rule firings (including firings of multiple rules) might otherwise lead to a race condition.
-         * @param identifier an object which uniquely identifies the countdown in question
-         * @param count the number of times the countdown needs to be counted down before the
-         * countdown operation returns true. e.g. if count is supplied as 2 then the first two
-         * calls to @link{#countdown(Object)} will return false and the third call will return true.
-         * @return true if a new countdown is installed, false if one already exists.
-         */
-        public boolean addCountDown(Object identifier, int count)
-        {
-            synchronized (countDownMap) {
-                if (countDownMap.get(identifier) == null) {
-                    countDownMap.put(identifier, new CountDown(count));
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /**
-         * builtin to decrement the countdown identified by a specific object, uninstalling it and
-         * returning true only when the count is zero.
-         * @param identifier an object which uniquely identifies the countdown in question
-         * @return true if the countdown is installed and its count is zero, otherwise false
-         */
-        public boolean countDown(Object identifier)
-        {
-            synchronized (countDownMap) {
-                CountDown countDown = countDownMap.get(identifier);
-
-                if (countDown != null) {
-                    boolean result = countDown.decrement();
-                    if (result) {
-                        countDownMap.remove(identifier);
-                    }
-                    return result;
-                }
-            }
-
-            // we must only fire a decrement event once for a given counter
-
-            return false;
-        }
-
-        // wait/notify support
-        /**
-         * test if there are threads waiting for an event identified by the supplied object to
-         * be signalled
-         * @param identifier an object identifying the event to be signalled
-         * @return true if threads are waiting for the associated event to be signalled
-         */
-        public boolean waiting(Object identifier)
-        {
-            return (getWaiter(identifier, false) != null);
-        }
-        /**
-         * wait for another thread to signal an event with no timeout. see
-         * @link{#waitFor(Object, long)} for details and caveats regarding calling this builtin.
-         * @param identifier an object used to identify the signal that is to be waited on.
-         */
-        public void waitFor(Object identifier)
-        {
-            waitFor(identifier, 0);
-        }
-
-        /**
-         * wait for another thread to signal an event with a specific timeout or no timeout if zero
-         * is supplied as the second argument. this may be called in a rule event, condition or action.
-         * it will suspend the current thread pending signalling of the event at which point rule
-         * processing will either continue or abort depending upon the type of signal. if an exception
-         * is thrown it will be an instance of runtime exception which, in normal circumstances, will
-         * cause the thread to exit. The exception may not kill the thread f the trigger method or
-         * calling code contains a catch-all handler so care must be used to ensure that an abort of
-         * waiting threads has the desired effect. n.b. care must also be employed if the current
-         * thread is inside a synchronized block since there is a potential for the waitFor call to
-         * cause deadlock.
-         * @param identifier an object used to identify the signal that is to be waited on. n.b. the
-         * wait operation is not performed using synchronization on the supplied object as the rule
-         * system cannot safely release and reobtain locks on application data. this argument is used
-         * as a key to identify a synchronization object private to the rule system.
-         */
-        public void waitFor(Object identifier, long millisecs)
-        {
-            Waiter waiter = getWaiter(identifier, true);
-
-            waiter.waitFor(millisecs);
-        }
-
-        /**
-         * signal an event identified by the supplied object, causing all waiting threads to resume
-         * rule processing and clearing the event. if there are no threads waiting either because
-         * there has been no call to @link{#waitFor} or because some other thread has sent the signal
-         * then this call returns false, otherwise it returns true. This operation is atomic,
-         * allowing the builtin to be used in rule conditions.
-         * @param identifier an object used to identify the which waiting threads the signal should
-         * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
-         * this argument is used as a key to identify a synchronization object private to the rule
-         * system.
-         */
-        public boolean signalWake(Object identifier)
-        {
-            Waiter waiter = removeWaiter(identifier);
-
-            if (waiter != null) {
-                return waiter.signalWake();
-            }
-            
-            return false;
-        }
-
-        /**
-         * signal an event identified by the suppied object, causing all waiting threads to throw an
-         * exception and clearing the event. if there are no objects waiting, either because there has been
-         * no call to @link{#waitFor} or because some other thread has already sent the signal, then this
-         * call returns false, otherwise it returns true. This operation is atomic, allowing the builtin
-         * to be used safely in rule conditions.
-         * @param identifier an object used to identify the which waiting threads the signal should
-         * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
-         * this argument is used as a key to identify a synchronization object private to the rule
-         * system.
-         */
-        public boolean signalKill(Object identifier)
-        {
-            Waiter waiter = removeWaiter(identifier);
-
-            if (waiter != null) {
-                return waiter.signalKill();
-            }
-
-            return false;
-        }
-
-        /**
-         * delay execution of the current thread for a specified number of milliseconds
-         * @param millisecs how many milliseconds to delay for
-         */
-
-        public void delay(long millisecs)
-        {
-            try {
-                Thread.sleep(millisecs);
-            } catch (InterruptedException e) {
-                // ignore this
-            }
-        }
-
-        /**
-         * create a counter identified by the given object with count 0 as its initial count
-         * @param o an identifier used to refer to the counter in future
-         * @return true if a new counter was created and false if one already existed under the given identifier
-         */
-        public boolean createCounter(Object o)
-        {
-            return createCounter(o, 0);
-        }
-
-        /**
-         * create a counter identified by the given object with the supplied value as its iniital count
-         * @param o an identifier used to refer to the counter in future
-         * @param value the initial value for the counter
-         * @return true if a new counter was created and false if one already existed under the given identifier
-         */
-        public boolean createCounter(Object o, int value)
-        {
-            synchronized (counterMap) {
-                Counter counter = counterMap.get(o);
-                if  (counter != null) {
-                    return false;
-                } else {
-                    counterMap.put(o, new Counter(value));
-                    return true;
-                }
-            }
-        }
-
-        /**
-         * delete a counter identified by the given object with count 0 as its initial count
-         * @param o the identifier for the coounter
-         * @return true if a counter was deleted and false if no counter existed under the given identifier
-         */
-        public boolean deleteCounter(Object o)
-        {
-            synchronized (counterMap) {
-                Counter counter = counterMap.get(o);
-                if  (counter != null) {
-                    counterMap.put(o, null);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        }
-
-        /**
-         * read the value of the counter associated with given identifier, creating a new one with count zero
-         * if none exists
-         * @param o the identifier for the coounter
-         * @return the value of the counter
-         */
-        public int readCounter(Object o)
-        {
-            synchronized (counterMap) {
-                Counter counter = counterMap.get(o);
-                if (counter == null) {
-                    counter = new Counter();
-                    counterMap.put(o, counter);
-                }
-                return counter.count();
-            }
-        }
-
-        /**
-         * increment the value of the counter associated with given identifier, creating a new one with count zero
-         * if none exists
-         * @param o the identifier for the coounter
-         * @return the value of the counter after the increment
-         */
-        public int incrementCounter(Object o)
-        {
-            synchronized (counterMap) {
-                Counter counter = counterMap.get(o);
-                if (counter == null) {
-                    counter = new Counter();
-                    counterMap.put(o, counter);
-                }
-                return counter.increment();
-            }
-        }
-
-        /**
-         * decrement the value of the counter associated with given identifier, creating a new one with count zero
-         * if none exists
-         * @param o the identifier for the coounter
-         * @return the value of the counter after the decrement
-         */
-        public int decrementCounter(Object o)
-        {
-            synchronized (counterMap) {
-                Counter counter = counterMap.get(o);
-                if (counter == null) {
-                    counter = new Counter();
-                    counterMap.put(o, counter);
-                }
-                return counter.decrement();
-            }
-        }
-
-        /**
-         * cause the current thread to throw a runtime exception which will normally cause it to exit.
-         * The exception may not kill the thread if the trigger method or calling code contains a
-         * catch-all handler so care must be employed to ensure that a call to this builtin has the
-         * desired effect.
-         */
-        public void killThread()
-        {
-            throw new ExecuteException("rule " + rule.getName() + " : killing thread " + Thread.currentThread().getName());
-        }
-
-        /**
-         * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1
-         * is returned
-         */
-
-        public void killJVM()
-        {
-            killJVM(-1);
-        }
-
-        /**
-         * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1
-         * is returned
-         */
-
-        public void killJVM(int exitCode)
-        {
-            java.lang.Runtime.getRuntime().halt(-1);
-        }
-
-        /**
-         * return a unique name for the trigger point associated with this rule. n.b. a single rule may
-         * give rise to more than one trigger point if the rule applies to several methods with the same
-         * name or to several classes with the same (package unqualified) name, or even to several
-         * versions of the same compiled class loaded into distinct class loaders.
-         *
-         * @return a unique name for the trigger point from which this rule was invoked
-         */
-        public String toString()
-        {
-            return rule.getName();
-        }
-
-        /**
-         * lookup the waiter object used to target wait and signal requests associated with a
-         * specific identifying object
-         * @param object the identifer for the waiter
-         * @param createIfAbsent true if the waiter should be (atomically) inserted if it is not present
-         * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
-         */
-        private Waiter getWaiter(Object object, boolean createIfAbsent)
-        {
-            Waiter waiter;
-
-            synchronized(waitMap) {
-                waiter = waitMap.get(object);
-                if (waiter == null && createIfAbsent) {
-                    waiter = new Waiter(object);
-                    waitMap.put(object, waiter);
-                }
-            }
-
-            return waiter;
-        }
-
-        /**
-         * remove the waiter object used to target wait and signal requests associated with a
-         * specific identifying object
-         * @param object the identifer for the waiter
-         * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
-         */
-        private Waiter removeWaiter(Object object)
-        {
-            return waitMap.remove(object);
-        }
-
-        private static int nextFileIndex = 0;
-
-        private static synchronized int nextFileIndex()
-        {
-            return nextFileIndex++;
-        }
-
-        private String nextFileName()
-        {
-            StringWriter writer = new StringWriter();
-            String digits = Integer.toString(nextFileIndex());
-            int numDigits = digits.length();
-            int idx;
-
-            writer.write("trace");
-
-            // this pads up to 9 digits but we may get more if we open enough files!
-            for (idx = 9; idx > numDigits; idx--) {
-                writer.write('0');
-            }
-
-            writer.write(digits);
-
-            return writer.toString();
-        }
-    }
-
     /**
-     * implementation of helper class which executes a compiled rule. it
-     * can be overridden by a generated subclass in order to enable compiled
-     * rule execution. this version inerprets the rule
+     * a getter allowing the helper class for the rule to be identified
+     * 
+     * @return
      */
-    public static class BasicHelper extends Helper
+    public Class getHelperClass()
     {
-        protected HashMap<String, Object> bindingMap;
-        private HashMap<String, Type> bindingTypeMap;
-
-        public BasicHelper()
-        {
-            super(null);
-            bindingMap = new HashMap<String, Object>();
-            bindingTypeMap = new HashMap<String, Type>();
-        }
-
-        public void setRule(Rule rule)
-        {
-            this.rule = rule;
-        }
-
-        /**
-         * install values into the bindings map and then call the execute0 method
-         * to actually execute the rule
-         * @param bindings
-         * @param recipient
-         * @param args
-         */
-        public void execute(Bindings bindings, Object recipient, Object[] args)
-                throws ExecuteException
-        {
-            if (Transformer.isVerbose()) {
-                System.out.println(rule.getName() + " execute");
-            }
-            Iterator<Binding> iterator = bindings.iterator();
-            while (iterator.hasNext()) {
-                Binding binding = iterator.next();
-                String name = binding.getName();
-                Type type = binding.getType();
-                if (binding.isHelper()) {
-                    bindingMap.put(name, this);
-                    bindingTypeMap.put(name, type);
-                } else if (binding.isRecipient()) {
-                    bindingMap.put(name, recipient);
-                    bindingTypeMap.put(name, type);
-                } else if (binding.isParam()) {
-                    bindingMap.put(name, args[binding.getIndex() - 1]);
-                    bindingTypeMap.put(name, type);
-                }
-            }
-
-            // now do the actual execution
-
-            execute0();
-        }
-
-        /**
-         * basic implementation of rule execution
-         *
-         * @throws ExecuteException
-         */
-        
-        protected void execute0()
-                throws ExecuteException
-        {
-            // System.out.println(rule.getName() + " execute0");
-            bind();
-            if (test()) {
-                fire();
-            }
-        }
-
-        public void bindVariable(String name, Object value)
-        {
-            bindingMap.put(name, value);
-        }
-
-        public Object getBinding(String name)
-        {
-            return bindingMap.get(name);
-        }
-
-        private void bind()
-                throws ExecuteException
-        {
-            // System.out.println(rule.getName() + " bind");
-            rule.getEvent().interpret(this);
-        }
-
-        private boolean test()
-                throws ExecuteException
-        {
-            // System.out.println(rule.getName() + " test");
-            return rule.getCondition().interpret(this);
-        }
-        
-        private void fire()
-                throws ExecuteException
-        {
-            // System.out.println(rule.getName() + " fire");
-            rule.getAction().interpret(this);
-        }
-
-        public String getName() {
-            return rule.getName();
-        }
+        return helperClass;
     }
-
-    /**
-     * a hash map used to identify trace streams from their identifying objects
-     */
-    private static HashMap<Object, PrintStream> traceMap = new HashMap<Object, PrintStream>();
-
-    /**
-     * a set used to identify settings for boolean flags associated with arbitrary objects. if
-     * an object is in the set then the flag associated with the object is set (true) otherwise
-     * it is clear (false).
-     */
-    private static Set<Object> flagSet = new HashSet<Object>();
-
-    /**
-     * a hash map used to identify countdowns from their identifying objects
-     */
-    private static HashMap<Object, CountDown> countDownMap = new HashMap<Object, CountDown>();
-
-    /**
-     * a hash map used to identify counters from their identifying objects
-     */
-    private static HashMap<Object, Counter> counterMap = new HashMap<Object, Counter>();
-
-    /**
-     * a hash map used to identify waiters from their identifying objects
-     */
-    private static HashMap<Object, Waiter> waitMap = new HashMap<Object, Waiter>();
-
+    
     private static boolean debugParse = (System.getProperty("org.jboss.jbossts.orchestration.rule.debug") != null ? true : false);
 }

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/Compiler.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/Compiler.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/Compiler.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -0,0 +1,491 @@
+package org.jboss.jbossts.orchestration.rule.compiler;
+
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.CompileException;
+import org.jboss.jbossts.orchestration.rule.helper.InterpretedHelper;
+import org.jboss.jbossts.orchestration.agent.Transformer;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.*;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+/**
+ * A class which compiles a rule by generating a subclass of the rule's helperClass which implements
+ * the HelperAdapter interface
+ */
+public class Compiler implements Opcodes
+{
+    public static Class getHelperAdapter(Class helperClass, boolean compileToBytecode) throws CompileException
+    {
+        // for now we cannot compile to bytecode
+
+        compileToBytecode = false;
+
+        Class adapterClass;
+        if (compileToBytecode) {
+            adapterClass = compiledAdapterMap.get(helperClass);
+        } else {
+            adapterClass = interpretedAdapterMap.get(helperClass);
+        }
+
+        if (adapterClass != null) {
+            return adapterClass;
+        }
+        // ok we have to create the adapter class
+
+        // n.b. we don't bother synchronizing here -- if another rule is racing to create an adapter
+        // in parallel we don't really care about generating two of them -- we can use whichever
+        // one gets intalled last
+
+        try {
+            String helperName = Type.getInternalName(helperClass);
+            String compiledHelperName;
+
+            if (compileToBytecode) {
+                compiledHelperName = helperName + "_HelperAdapter_Compiled";
+            } else {
+                compiledHelperName = helperName + "_HelperAdapter_Interpreted";
+            }
+
+            byte[] classBytes = compileBytes(helperClass, helperName, compiledHelperName, compileToBytecode);
+            String externalName = compiledHelperName.replaceAll("/", ".");
+            // dump the compiled class bytes if required
+            Transformer.getTheTransformer().maybeDumpClass(externalName, classBytes);
+            // ensure the class is loaded
+            adapterClass = Transformer.getTheTransformer().loadHelperAdapter(helperClass, externalName, classBytes);
+        } catch(CompileException ce) {
+            throw ce;
+        } catch (Throwable th) {
+            if (compileToBytecode) {
+                throw new CompileException("Compiler.createHelperAdapter : exception creating compiled helper adapter for " + helperClass.getName(), th);
+            } else {
+                throw new CompileException("Compiler.createHelperAdapter : exception creating interpreted helper adapter for " + helperClass.getName(), th);
+            }
+        }
+
+        // stash for later reuse
+
+        if(compileToBytecode) {
+            compiledAdapterMap.put(helperClass, adapterClass);
+        } else {
+            interpretedAdapterMap.put(helperClass, adapterClass);
+        }
+
+        return adapterClass;
+    }
+
+    /**
+     * hashmap used to retrieve previously generated interpred adapters
+     *
+     * TODO strictly this should be a weak hash map so we don't hang on to adapters after the helper
+     * has been dropped. but to make that work we probably need to worry about dropping references to
+     * rules too.
+     */
+    private static HashMap<Class<?>, Class<?>> interpretedAdapterMap = new HashMap<Class<?>, Class<?>>();
+
+    /**
+     * hashmap used to retrieve previously generated compiled adapters
+     *
+     * TODO strictly this should be a weak hash map so we don't hang on to adapters after the helper
+     * has been dropped. but to make that work we probably need to worry about dropping references to
+     * rules too.
+     */
+    private static HashMap<Class<?>, Class<?>> compiledAdapterMap = new HashMap<Class<?>, Class<?>>();
+
+    private static byte[] compileBytes(Class helperClass, String helperName, String compiledHelperName, boolean compileToBytecode) throws Exception
+    {
+        ClassWriter cw = new ClassWriter(0);
+        FieldVisitor fv;
+        MethodVisitor mv;
+        AnnotationVisitor av0;
+        // create the class as a subclass of the rule helper class, appending Compiled to the front
+        // of the class name and a unique number to the end of the class helperName
+        // also ensure it implements the HelperAdapter interface
+        //
+        // public class foo.bar.Compiled_<helper>_<NNN> extends foo.bar.<helper> implements HelperAdapter
+
+        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, compiledHelperName, null, helperName, new String[] { "org/jboss/jbossts/orchestration/rule/helper/HelperAdapter" });
+        {
+        // we need a Hashmap field to hold the bindings
+        //
+        // private HashMap<String, Object> bindingMap;
+
+        fv = cw.visitField(ACC_PRIVATE, "bindingMap", "Ljava/util/HashMap;", "Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Object;>;", null);
+        fv.visitEnd();
+        }
+        {
+        // and another Hashmap field to hold the binding types
+        //
+        // private HashMap<String, Type> bindingTypeMap;
+
+        fv = cw.visitField(ACC_PRIVATE, "bindingTypeMap", "Ljava/util/HashMap;", "Ljava/util/HashMap<Ljava/lang/String;Lorg/jboss/jbossts/orchestration/rule/type/Type;>;", null);
+        fv.visitEnd();
+        }
+        {
+        // and a rule field to hold the rule
+        //
+        // private Rule rule;
+
+        fv = cw.visitField(ACC_PRIVATE, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;", "Lorg/jboss/jbossts/orchestration/rule/Rule;", null);
+        fv.visitEnd();
+        }
+        {
+        // we need a constructor which takes a Rule as argument
+        // if the helper implements a constructor which takes a Rule as argument then we invoke it
+        // otherwise we invoke the empty helper constructor
+
+        Constructor superConstructor = null;
+        try {
+            superConstructor = helperClass.getDeclaredConstructor(Rule.class);
+        } catch (NoSuchMethodException e) {
+            // hmm, ok see if there is an empty constructor
+        } catch (SecurityException e) {
+            throw new CompileException("Compiler.compileBytes : unable to access constructor for helper class " + helperClass.getCanonicalName());
+        }
+        boolean superWantsRule = (superConstructor != null);
+        if (!superWantsRule) {
+            try {
+                superConstructor = helperClass.getDeclaredConstructor();
+            } catch (NoSuchMethodException e) {
+                throw new CompileException("Compiler.compileBytes : no valid constructor found for helper class " + helperClass.getCanonicalName());
+            } catch (SecurityException e) {
+                throw new CompileException("Compiler.compileBytes : unable to access constructor for helper class " + helperClass.getCanonicalName());
+            }
+        }
+        //
+        //  public Compiled<helper>_<NNN>()Rule rule)
+        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Lorg/jboss/jbossts/orchestration/rule/Rule;)V", null, null);
+        mv.visitCode();
+        // super();
+        //
+        // or
+        //
+        // super(Rule);
+        if (superWantsRule) {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitMethodInsn(INVOKESPECIAL, helperName, "<init>", "(Lorg/jboss/jbossts/orchestration/rule/Rule;)V");
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, helperName, "<init>", "()V");
+        }
+        // bindingMap = new HashMap<String, Object);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitTypeInsn(NEW, "java/util/HashMap");
+        mv.visitInsn(DUP);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+        mv.visitFieldInsn(PUTFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        // bindingTypeMap = new HashMap<String, Type);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitTypeInsn(NEW, "java/util/HashMap");
+        mv.visitInsn(DUP);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+        mv.visitFieldInsn(PUTFIELD, compiledHelperName, "bindingTypeMap", "Ljava/util/HashMap;");
+        // this.rule = rule
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitFieldInsn(PUTFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+        // return;
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(3, 2);
+        mv.visitEnd();
+        }
+        {
+        // create the execute method
+        //
+        // public void execute(Bindings bindings, Object recipient, Object[] args) throws ExecuteException
+        mv = cw.visitMethod(ACC_PUBLIC, "execute", "(Lorg/jboss/jbossts/orchestration/rule/binding/Bindings;Ljava/lang/Object;[Ljava/lang/Object;)V", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+        mv.visitCode();
+        // if (Transformer.isVerbose())
+        mv.visitMethodInsn(INVOKESTATIC, "org/jboss/jbossts/orchestration/agent/Transformer", "isVerbose", "()Z");
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+        // then
+        // System.out.println(rule.getName() + " execute");
+        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+        mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+        mv.visitInsn(DUP);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Rule", "getName", "()Ljava/lang/String;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+        mv.visitLdcInsn(" execute()");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+        // end if
+        mv.visitLabel(l0);
+        // Iterator<Binding> iterator = bindings.iterator();
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Bindings", "iterator", "()Ljava/util/Iterator;");
+        mv.visitVarInsn(ASTORE, 4);
+        // while 
+        Label l1 = new Label();
+        mv.visitLabel(l1);
+        // iterator.hasNext()
+        mv.visitVarInsn(ALOAD, 4);
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
+        Label l2 = new Label();
+        mv.visitJumpInsn(IFEQ, l2);
+        // do
+        // binding = (Binding)iterator.next();
+        mv.visitVarInsn(ALOAD, 4);
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+        mv.visitTypeInsn(CHECKCAST, "org/jboss/jbossts/orchestration/rule/binding/Binding");
+        mv.visitVarInsn(ASTORE, 5);
+        // String name = binding.getName();
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "getName", "()Ljava/lang/String;");
+        mv.visitVarInsn(ASTORE, 6);
+        // Type type = binding.getType();
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "getType", "()Lorg/jboss/jbossts/orchestration/rule/type/Type;");
+        mv.visitVarInsn(ASTORE, 7);
+        // if (binding.isHelper())
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "isHelper", "()Z");
+        Label l3 = new Label();
+        mv.visitJumpInsn(IFEQ, l3);
+        // then
+        // bindingMap.put(name, this);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        // bindingTypeMap.put(name, type);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingTypeMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 7);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        Label l4 = new Label();
+        mv.visitJumpInsn(GOTO, l4);
+        // else if (binding.isRecipient())
+        mv.visitLabel(l3);
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "isRecipient", "()Z");
+        Label l5 = new Label();
+        mv.visitJumpInsn(IFEQ, l5);
+        // then
+        // bindingMap.put(name, recipient);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 2);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        // bindingTypeMap.put(name, type);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingTypeMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 7);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        mv.visitJumpInsn(GOTO, l4);
+        // else if (binding.isParam())
+        mv.visitLabel(l5);
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "isParam", "()Z");
+        mv.visitJumpInsn(IFEQ, l4);
+        // then
+        // bindingMap.put(name, args[binding.getIndex() - 1]);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 3);
+        mv.visitVarInsn(ALOAD, 5);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/binding/Binding", "getIndex", "()I");
+        mv.visitInsn(ICONST_1);
+        mv.visitInsn(ISUB);
+        mv.visitInsn(AALOAD);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        // bindingTypeMap.put(name, type);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingTypeMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 6);
+        mv.visitVarInsn(ALOAD, 7);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        // end if
+        mv.visitLabel(l4);
+        mv.visitJumpInsn(GOTO, l1);
+        // end do while
+        mv.visitLabel(l2);
+        // execute0()
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKEVIRTUAL, compiledHelperName, "execute0", "()V");
+        // return
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(5, 8);
+        mv.visitEnd();
+        }
+        {
+        // create the bindVariable method
+        //
+        // public void bindVariable(String name, Object value)
+        mv = cw.visitMethod(ACC_PUBLIC, "bindVariable", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);
+        mv.visitCode();
+        //  bindingMap.put(name, value);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitVarInsn(ALOAD, 2);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(POP);
+        // return
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(3, 3);
+        mv.visitEnd();
+        }
+        {
+        // create the getBinding method
+        //
+        // public Object getBinding(String name)
+        mv = cw.visitMethod(ACC_PUBLIC, "getBinding", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
+        mv.visitCode();
+        // {TOS} <== bindingMap.get(name);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+        // return {TOS}
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(2, 2);
+        mv.visitEnd();
+        }
+        {
+        // create the getName method
+        //
+        // public String getName()
+        mv = cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
+        mv.visitCode();
+        // {TOS} <== rule.getName()
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Rule", "getName", "()Ljava/lang/String;");
+        // return {TOS}
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(1, 1);
+        mv.visitEnd();
+        }
+        if (compileToBytecode) {
+            // we generate a single execute0 method if we want to run compiled and get
+            // the event, condiiton and action to insert the relevant bytecode to implement
+            // bind(), test() anf fire()
+
+            /*
+            StackHeights maxStackHeights = new StackHeights();
+            {
+            // create the execute0() method
+            //
+            // private void execute0()
+            mv = cw.visitMethod(ACC_PRIVATE, "execute0", "()V", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+            mv.visitCode();
+            // bind();
+            rule.getEvent().createHelperAdapter(mv, compiledHelperName, helperName, maxStackHeights);
+            // if (test())
+            rule.getCondition().createHelperAdapter(mv, compiledHelperName, helperName, maxStackHeights);
+            Label l0 = new Label();
+            mv.visitJumpInsn(IFEQ, l0);
+            // then
+            rule.getAction().createHelperAdapter(mv, compiledHelperName, helperName, maxStackHeights);
+            // fire();
+            // end if
+            mv.visitLabel(l0);
+            // return
+            mv.visitInsn(RETURN);
+            // need to specify correct Maxs values
+            mv.visitMaxs(maxStackHeights.stackCount, maxStackHeights.localCount);
+            mv.visitEnd();
+            }
+            */
+        } else {
+            // we generate the following methods if we want to run interpreted
+            {
+            // create the execute0() method
+            //
+            // private void execute0()
+            mv = cw.visitMethod(ACC_PRIVATE, "execute0", "()V", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+            mv.visitCode();
+            // bind();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "bind", "()V");
+            // if (test())
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "test", "()Z");
+            Label l0 = new Label();
+            mv.visitJumpInsn(IFEQ, l0);
+            // then
+            // fire();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "fire", "()V");
+            // end if
+            mv.visitLabel(l0);
+            // return
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(1, 1);
+            mv.visitEnd();
+            }
+            {
+            // create the bind method
+            //
+            // private void bind()
+            mv = cw.visitMethod(ACC_PRIVATE, "bind", "()V", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+            mv.visitCode();
+            // rule.getEvent().interpret(this);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Rule", "getEvent", "()Lorg/jboss/jbossts/orchestration/rule/Event;");
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Event", "interpret", "(Lorg/jboss/jbossts/orchestration/rule/helper/HelperAdapter;)V");
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(2, 1);
+            mv.visitEnd();
+            }
+            {
+            // create the test method
+            //
+            // private boolean test()
+            mv = cw.visitMethod(ACC_PRIVATE, "test", "()Z", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+            mv.visitCode();
+            // {TOS} <== rule.getCondition().interpret(this);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Rule", "getCondition", "()Lorg/jboss/jbossts/orchestration/rule/Condition;");
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Condition", "interpret", "(Lorg/jboss/jbossts/orchestration/rule/helper/HelperAdapter;)Z");
+            // return {TOS}
+            mv.visitInsn(IRETURN);
+            mv.visitMaxs(2, 1);
+            mv.visitEnd();
+            }
+            {
+            // create the fire method
+            //
+            // private void fire()
+            mv = cw.visitMethod(ACC_PRIVATE, "fire", "()V", null, new String[] { "org/jboss/jbossts/orchestration/rule/exception/ExecuteException" });
+            mv.visitCode();
+            // rule.getAction().interpret(this);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/jbossts/orchestration/rule/Rule;");
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Rule", "getAction", "()Lorg/jboss/jbossts/orchestration/rule/Action;");
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/jbossts/orchestration/rule/Action", "interpret", "(Lorg/jboss/jbossts/orchestration/rule/helper/HelperAdapter;)V");
+            // return
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(2, 1);
+            mv.visitEnd();
+            }
+        }
+
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/StackHeights.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/StackHeights.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/compiler/StackHeights.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -0,0 +1,67 @@
+package org.jboss.jbossts.orchestration.rule.compiler;
+
+import org.jboss.jbossts.orchestration.rule.exception.CompileException;
+
+/**
+ * container to track either the current or the maximum local variable count and stack height
+ * while generating compiled code for a rule
+ */
+public class StackHeights
+{
+    /**
+     * number of stack slots
+     */
+    public int stackCount;
+    /**
+     * number of local variable slots
+     */
+    int localCount;
+
+    /**
+     * create withinitial counts 0
+     */
+    public StackHeights()
+    {
+        stackCount = localCount = 0;
+    }
+
+    /**
+     * create a copy with the same coutns as the original
+     * @param toCopy
+     */
+    public StackHeights(StackHeights toCopy)
+    {
+        stackCount = toCopy.stackCount;
+        localCount = toCopy.localCount;
+    }
+
+    /**
+     * increment the stack count and return this to allow chaining
+     * @param increment the amount ot add to stackCount (can be negative)
+     * @return this
+     * @throws CompileException if the stack count goes negative
+     */
+    public StackHeights addStackCount(int increment) throws CompileException
+    {
+        stackCount += increment;
+        if (stackCount < 0) {
+            throw new CompileException("StackHeights.addStackCount : negative count for stack slots!");
+        }
+        return this;
+    }
+
+    /**
+     * increment the local count and return this to allow chaining
+     * @param increment the amount ot add to localCount (can be negative)
+     * @return this
+     * @throws CompileException if the local count goes negative
+     */
+    public StackHeights addLocalCount(int increment) throws CompileException
+    {
+        localCount += increment;
+        if (localCount < 0) {
+            throw new CompileException("StackHeights.addLocalCount : negative count for local variable slots!");
+        }
+        return this;
+    }
+};

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -50,7 +51,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper)
+    public Object interpret(HelperAdapter helper)
             throws ExecuteException
     {
         try {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.util.List;
@@ -90,7 +91,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         // evaluate the array expression then evaluate each index expression in turn and
         // dereference to access the array element
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -57,7 +58,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         try {
 // n.b. be careful with characters here
             Number value1 = (Number)getOperand(0).interpret(helper);

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanLiteral.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanLiteral.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanLiteral.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -65,7 +66,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         return value;
     }
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -70,7 +71,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    public Object interpret(HelperAdapter helper) throws ExecuteException
     {
         try {
             if (comparisonType.isNumeric()) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -65,7 +66,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    public Object interpret(HelperAdapter helper) throws ExecuteException
     {
         Boolean executeFirstBranch = (Boolean)getOperand(0).interpret(helper);
         if (executeFirstBranch) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -28,6 +28,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -102,7 +103,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    public Object interpret(HelperAdapter helper) throws ExecuteException
     {
         return helper.getBinding(name);
     }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -29,6 +29,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
 import org.jboss.jbossts.orchestration.rule.RuleElement;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 
 import java.io.StringWriter;
 
@@ -99,7 +100,7 @@
      * @return  the result of evaluation as an Object
      * @throws org.jboss.jbossts.orchestration.rule.exception.ExecuteException
      */
-    public abstract Object interpret(Rule.BasicHelper helper) throws ExecuteException;
+    public abstract Object interpret(HelperAdapter helper) throws ExecuteException;
 
     public abstract void writeTo(StringWriter stringWriter);
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -29,6 +29,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -156,7 +157,7 @@
         }
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    public Object interpret(HelperAdapter helper) throws ExecuteException
     {
         if (indirectStatic) {
             return owner.interpret(helper);

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -50,7 +51,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         Boolean value = (Boolean)getOperand(0).interpret(helper);
 
         if (oper == AND) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -29,6 +29,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 import java.util.List;
 import java.util.Iterator;
@@ -98,7 +99,7 @@
 
     public Type typeCheck(Type expected) throws TypeException {
         // if we have no recipient then we use the rule's helper as a target via a binding
-        // to $-1. this means  we can type check the call against methods of class Rule$Helper
+        // to $-1. this means  we can type check the call against methods of class Helper
         // without having to do any special case processing.
 
         TypeGroup typeGroup =  getTypeGroup();
@@ -147,7 +148,8 @@
         
         if (recipient == null) {
             if (rootType == null) {
-                Type ruleType = typeGroup.create("org.jboss.jbossts.orchestration.rule.Rule$Helper");
+                //Type ruleType = typeGroup.create("org.jboss.jbossts.orchestration.rule.helper.Helper");
+                Type ruleType = typeGroup.create(rule.getHelperClass().getCanonicalName());
                 recipient = new DollarExpression(rule, ruleType, token, -1);
 
                 rootType = recipient.typeCheck(Type.UNDEFINED);
@@ -228,7 +230,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         Object recipientValue = null;
         try {
             if (recipient != null) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MinusExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MinusExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MinusExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -47,7 +48,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         try {
             Number value = (Number)getOperand(0).interpret(helper);
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -47,7 +48,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         Boolean result = (Boolean) getOperand(0).interpret(helper);
         return !result;
     }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -62,7 +63,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         return value;
     }
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -61,7 +62,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         Object value1 = getOperand(0).interpret(helper);
         Object value2 = getOperand(1).interpret(helper);
         if (type == Type.S) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ReturnExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ReturnExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ReturnExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -29,6 +29,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.exception.EarlyReturnException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -105,7 +106,7 @@
      * @throws org.jboss.jbossts.orchestration.rule.exception.ExecuteException
      *
      */
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    public Object interpret(HelperAdapter helper) throws ExecuteException
     {
         // time to take an early bath -- the code compield into the trigger method should
         // catch this and return as appropriate

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -28,6 +28,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -85,7 +86,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         try {
             return field.get(null);
         } catch (ExecuteException e) {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -59,7 +60,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         return text;
     }
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -51,7 +52,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         Object value1 = getOperand(0).interpret(helper);
         Object value2 = getOperand(1).interpret(helper);
         String string1 = value1.toString();

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ThrowExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ThrowExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ThrowExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -29,6 +29,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.exception.ThrowException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -218,7 +219,7 @@
      * @throws org.jboss.jbossts.orchestration.rule.exception.ExecuteException
      *
      */
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         int l = arguments.size();
         int i;
         Object[] callArgs = new Object[l];

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -27,6 +27,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 /**
@@ -50,7 +51,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         try {
             Number value = (Number)getOperand(0).interpret(helper);
 

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -28,6 +28,7 @@
 import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.helper.HelperAdapter;
 import org.jboss.jbossts.orchestration.rule.grammar.ParseNode;
 
 import java.io.StringWriter;
@@ -89,7 +90,7 @@
         return type;
     }
 
-    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+    public Object interpret(HelperAdapter helper) throws ExecuteException {
         return helper.getBinding(name);
     }
 

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/Helper.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/Helper.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/Helper.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -0,0 +1,627 @@
+package org.jboss.jbossts.orchestration.rule.helper;
+
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.synchronization.CountDown;
+import org.jboss.jbossts.orchestration.synchronization.Counter;
+import org.jboss.jbossts.orchestration.synchronization.Waiter;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * This is the default helper class which is used to define builtin operations for rules.
+ * Methods provided on this class are automatically made available as builtin operations in
+ * expressions appearing in rule event bindings, conditions and actions. Although Helper
+ * methods are all instance methods the message recipient for the method call is implicit
+ * and does not appear in the builtin call. It does, however, appear in the runtime
+ * invocation, giving the builtin operation access to the helper and thence the rule being
+ * fired.
+ */
+public class Helper
+{
+    protected Rule rule;
+
+    protected Helper(Rule rule)
+    {
+        this.rule = rule;
+    }
+    // tracing support
+    /**
+     * builtin to print a message during rule execution. n.b. this always returns true which
+     * means it can be invoked during condition execution
+     * @param text the message to be printed as trace output
+     * @return true
+     */
+    public boolean debug(String text)
+    {
+        System.out.println("rule.debug{" + rule.getName() + "} : " + text);
+        return true;
+    }
+
+    // file based trace support
+    /**
+     * open a trace output stream identified by identifier to a file located in the current working
+     * directory using a unique generated name
+     * @param identifier an identifier used subsequently to identify the trace output stream
+     * @return true if new file and stream was created, false if a stream identified by identifier
+     * already existed or the identifer is null, "out" or "err"
+     */
+    public boolean openTrace(Object identifier)
+    {
+        return openTrace(identifier, null);
+    }
+
+    /**
+     * open a trace output stream identified by identifier to a file located in the current working
+     * directory using the given file name or a generated name if the supplied name is null
+     * @param identifier an identifier used subsequently to identify the trace output stream
+     * @return true if new file and stream was created, false if a stream identified by identifier
+     * already existed or if a file of the same name already exists or the identifer is null, "out"
+     * or "err"
+     */
+    public boolean openTrace(Object identifier, String fileName)
+    {
+        if (identifier == null) {
+            return false;
+        }
+
+        synchronized(traceMap) {
+            PrintStream stream = traceMap.get(identifier);
+            String name = fileName;
+            if (stream != null) {
+                return false;
+            }
+            if (fileName == null) {
+                name = nextFileName();
+            }
+            File file = new File(name);
+
+            if (file.exists() && !file.canWrite()) {
+                if (fileName == null) {
+                    // keep trying new names until we hit an unused one
+                    do {
+                        name = nextFileName();
+                        file = new File(name);
+                    } while (file.exists() && !file.canWrite());
+                } else {
+                    // can't open file as requested
+                    return false;
+                }
+            }
+                
+            FileOutputStream fos;
+
+            try {
+                if (file.exists()) {
+                    fos = new FileOutputStream(file, true);
+                } else {
+                    fos = new FileOutputStream(file, true);
+                }
+            } catch (FileNotFoundException e) {
+                // oops, just return false
+                return false;
+            }
+
+            PrintStream ps = new PrintStream(fos, true);
+
+            traceMap.put(identifier, ps);
+
+            return true;
+        }
+    }
+
+    /**
+     * close the trace output stream identified by identifier flushing any pending output
+     * @param identifier an identifier used subsequently to identify the trace output stream
+     * @return true if the stream was flushed and closed, false if no stream is identified by identifier
+     * or the identifer is null, "out" or "err"
+     */
+    public boolean closeTrace(Object identifier)
+    {
+        if (identifier == null ||
+                identifier.equals("out") ||
+                identifier.equals("err")) {
+            return false;
+        }
+
+        synchronized(traceMap) {
+            PrintStream ps = traceMap.get(identifier);
+            if (ps != null) {
+                // need to do the close while synchornized so we ensure an open cannot
+                // proceed until we have flushed all changes to disk
+                ps.close();
+                traceMap.put(identifier, null);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * write the supplied message to the trace stream identified by identifier, creating a new stream
+     * if none exists
+     * @param identifier an identifier used subsequently to identify the trace output stream
+     * @param message
+     * @return true
+     * @caveat if identifier is the string "out" or null the message will be written to System.out.
+     * if identifier is the string "err" the message will be written to System.err.
+     */
+    public boolean trace(Object identifier, String message)
+    {
+        synchronized(traceMap) {
+            PrintStream ps = traceMap.get(identifier);
+            if (ps == null) {
+                if (openTrace(identifier)) {
+                    ps = traceMap.get(identifier);
+                } else {
+                    ps = System.out;
+                }
+            }
+            ps.print(message);
+            ps.flush();
+        }
+        return true;
+    }
+
+    /**
+     * write the supplied message to the trace stream identified by identifier, creating a new stream
+     * if none exists, and append a new line
+     * @param identifier an identifier used subsequently to identify the trace output stream
+     * @param message
+     * @return true
+     * @caveat if identifier is the string "out" or null the message will be written to System.out.
+     * if identifier is the string "err" the message will be written to System.err.
+     */
+    public boolean traceln(Object identifier, String message)
+    {
+        synchronized(traceMap) {
+            PrintStream ps = traceMap.get(identifier);
+            if (ps == null) {
+                if (openTrace(identifier)) {
+                    ps = traceMap.get(identifier);
+                } else {
+                    ps = System.out;
+                }
+            }
+            ps.println(message);
+            ps.flush();
+        }
+        return true;
+    }
+
+    // flag support
+    /**
+     * set a flag keyed by the supplied object if it is not already set
+     * @param identifier the object identifying the relevant flag
+     * @return true if the flag was clear before this call otherwise false
+     */
+    public boolean flag(Object identifier)
+    {
+        synchronized (flagSet) {
+            return flagSet.add(identifier);
+        }
+    }
+
+    /**
+     * test the state of the flag keyed by the supplied object
+     * @param identifier the object identifying the relevant flag
+     * @return true if the flag is set otherwise false
+     */
+    public boolean flagged(Object identifier)
+    {
+        synchronized (flagSet) {
+            return flagSet.contains(identifier);
+        }
+    }
+
+    /**
+     * clear the flag keyed by the supplied object if it is not already clear
+     * @param identifier the object identifying the relevant flag
+     * @return true if the flag was clear before this call otherwise false
+     */
+    public boolean clear(Object identifier)
+    {
+        synchronized (flagSet) {
+            return flagSet.remove(identifier);
+        }
+    }
+
+    // countdown support
+    /**
+     * builtin to test test if a countdown has been installed
+     * @param identifier an object which uniquely identifies the countdown in question
+     * @return true if the countdown is currently installed
+     */
+    public boolean getCountDown(Object identifier)
+    {
+        synchronized (countDownMap) {
+            return (countDownMap.get(identifier) != null);
+        }
+    }
+
+    /**
+     * builtin to test add a countdown identified by a specific object and with the specified
+     * count. n.b. this builtin checks if a countdown identified by the supplied object is
+     * currently installed, returning false if so, otherwise atomically adds the countdown
+     * and returns true. This allows the builtin to be used safely in conditions where concurrent
+     * rule firings (including firings of multiple rules) might otherwise lead to a race condition.
+     * @param identifier an object which uniquely identifies the countdown in question
+     * @param count the number of times the countdown needs to be counted down before the
+     * countdown operation returns true. e.g. if count is supplied as 2 then the first two
+     * calls to @link{#countdown(Object)} will return false and the third call will return true.
+     * @return true if a new countdown is installed, false if one already exists.
+     */
+    public boolean addCountDown(Object identifier, int count)
+    {
+        synchronized (countDownMap) {
+            if (countDownMap.get(identifier) == null) {
+                countDownMap.put(identifier, new CountDown(count));
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * builtin to decrement the countdown identified by a specific object, uninstalling it and
+     * returning true only when the count is zero.
+     * @param identifier an object which uniquely identifies the countdown in question
+     * @return true if the countdown is installed and its count is zero, otherwise false
+     */
+    public boolean countDown(Object identifier)
+    {
+        synchronized (countDownMap) {
+            CountDown countDown = countDownMap.get(identifier);
+
+            if (countDown != null) {
+                boolean result = countDown.decrement();
+                if (result) {
+                    countDownMap.remove(identifier);
+                }
+                return result;
+            }
+        }
+
+        // we must only fire a decrement event once for a given counter
+
+        return false;
+    }
+
+    // wait/notify support
+    /**
+     * test if there are threads waiting for an event identified by the supplied object to
+     * be signalled
+     * @param identifier an object identifying the event to be signalled
+     * @return true if threads are waiting for the associated event to be signalled
+     */
+    public boolean waiting(Object identifier)
+    {
+        return (getWaiter(identifier, false) != null);
+    }
+    /**
+     * wait for another thread to signal an event with no timeout. see
+     * @link{#waitFor(Object, long)} for details and caveats regarding calling this builtin.
+     * @param identifier an object used to identify the signal that is to be waited on.
+     */
+    public void waitFor(Object identifier)
+    {
+        waitFor(identifier, 0);
+    }
+
+    /**
+     * wait for another thread to signal an event with a specific timeout or no timeout if zero
+     * is supplied as the second argument. this may be called in a rule event, condition or action.
+     * it will suspend the current thread pending signalling of the event at which point rule
+     * processing will either continue or abort depending upon the type of signal. if an exception
+     * is thrown it will be an instance of runtime exception which, in normal circumstances, will
+     * cause the thread to exit. The exception may not kill the thread f the trigger method or
+     * calling code contains a catch-all handler so care must be used to ensure that an abort of
+     * waiting threads has the desired effect. n.b. care must also be employed if the current
+     * thread is inside a synchronized block since there is a potential for the waitFor call to
+     * cause deadlock.
+     * @param identifier an object used to identify the signal that is to be waited on. n.b. the
+     * wait operation is not performed using synchronization on the supplied object as the rule
+     * system cannot safely release and reobtain locks on application data. this argument is used
+     * as a key to identify a synchronization object private to the rule system.
+     */
+    public void waitFor(Object identifier, long millisecs)
+    {
+        Waiter waiter = getWaiter(identifier, true);
+
+        waiter.waitFor(millisecs);
+    }
+
+    /**
+     * signal an event identified by the supplied object, causing all waiting threads to resume
+     * rule processing and clearing the event. if there are no threads waiting either because
+     * there has been no call to @link{#waitFor} or because some other thread has sent the signal
+     * then this call returns false, otherwise it returns true. This operation is atomic,
+     * allowing the builtin to be used in rule conditions.
+     * @param identifier an object used to identify the which waiting threads the signal should
+     * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
+     * this argument is used as a key to identify a synchronization object private to the rule
+     * system.
+     */
+    public boolean signalWake(Object identifier)
+    {
+        Waiter waiter = removeWaiter(identifier);
+
+        if (waiter != null) {
+            return waiter.signalWake();
+        }
+            
+        return false;
+    }
+
+    /**
+     * signal an event identified by the suppied object, causing all waiting threads to throw an
+     * exception and clearing the event. if there are no objects waiting, either because there has been
+     * no call to @link{#waitFor} or because some other thread has already sent the signal, then this
+     * call returns false, otherwise it returns true. This operation is atomic, allowing the builtin
+     * to be used safely in rule conditions.
+     * @param identifier an object used to identify the which waiting threads the signal should
+     * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
+     * this argument is used as a key to identify a synchronization object private to the rule
+     * system.
+     */
+    public boolean signalKill(Object identifier)
+    {
+        Waiter waiter = removeWaiter(identifier);
+
+        if (waiter != null) {
+            return waiter.signalKill();
+        }
+
+        return false;
+    }
+
+    /**
+     * delay execution of the current thread for a specified number of milliseconds
+     * @param millisecs how many milliseconds to delay for
+     */
+
+    public void delay(long millisecs)
+    {
+        try {
+            Thread.sleep(millisecs);
+        } catch (InterruptedException e) {
+            // ignore this
+        }
+    }
+
+    /**
+     * create a counter identified by the given object with count 0 as its initial count
+     * @param o an identifier used to refer to the counter in future
+     * @return true if a new counter was created and false if one already existed under the given identifier
+     */
+    public boolean createCounter(Object o)
+    {
+        return createCounter(o, 0);
+    }
+
+    /**
+     * create a counter identified by the given object with the supplied value as its iniital count
+     * @param o an identifier used to refer to the counter in future
+     * @param value the initial value for the counter
+     * @return true if a new counter was created and false if one already existed under the given identifier
+     */
+    public boolean createCounter(Object o, int value)
+    {
+        synchronized (counterMap) {
+            Counter counter = counterMap.get(o);
+            if  (counter != null) {
+                return false;
+            } else {
+                counterMap.put(o, new Counter(value));
+                return true;
+            }
+        }
+    }
+
+    /**
+     * delete a counter identified by the given object with count 0 as its initial count
+     * @param o the identifier for the coounter
+     * @return true if a counter was deleted and false if no counter existed under the given identifier
+     */
+    public boolean deleteCounter(Object o)
+    {
+        synchronized (counterMap) {
+            Counter counter = counterMap.get(o);
+            if  (counter != null) {
+                counterMap.put(o, null);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * read the value of the counter associated with given identifier, creating a new one with count zero
+     * if none exists
+     * @param o the identifier for the coounter
+     * @return the value of the counter
+     */
+    public int readCounter(Object o)
+    {
+        synchronized (counterMap) {
+            Counter counter = counterMap.get(o);
+            if (counter == null) {
+                counter = new Counter();
+                counterMap.put(o, counter);
+            }
+            return counter.count();
+        }
+    }
+
+    /**
+     * increment the value of the counter associated with given identifier, creating a new one with count zero
+     * if none exists
+     * @param o the identifier for the coounter
+     * @return the value of the counter after the increment
+     */
+    public int incrementCounter(Object o)
+    {
+        synchronized (counterMap) {
+            Counter counter = counterMap.get(o);
+            if (counter == null) {
+                counter = new Counter();
+                counterMap.put(o, counter);
+            }
+            return counter.increment();
+        }
+    }
+
+    /**
+     * decrement the value of the counter associated with given identifier, creating a new one with count zero
+     * if none exists
+     * @param o the identifier for the coounter
+     * @return the value of the counter after the decrement
+     */
+    public int decrementCounter(Object o)
+    {
+        synchronized (counterMap) {
+            Counter counter = counterMap.get(o);
+            if (counter == null) {
+                counter = new Counter();
+                counterMap.put(o, counter);
+            }
+            return counter.decrement();
+        }
+    }
+
+    /**
+     * cause the current thread to throw a runtime exception which will normally cause it to exit.
+     * The exception may not kill the thread if the trigger method or calling code contains a
+     * catch-all handler so care must be employed to ensure that a call to this builtin has the
+     * desired effect.
+     */
+    public void killThread()
+    {
+        throw new ExecuteException("rule " + rule.getName() + " : killing thread " + Thread.currentThread().getName());
+    }
+
+    /**
+     * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1
+     * is returned
+     */
+
+    public void killJVM()
+    {
+        killJVM(-1);
+    }
+
+    /**
+     * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1
+     * is returned
+     */
+
+    public void killJVM(int exitCode)
+    {
+        java.lang.Runtime.getRuntime().halt(-1);
+    }
+
+    /**
+     * return a unique name for the trigger point associated with this rule. n.b. a single rule may
+     * give rise to more than one trigger point if the rule applies to several methods with the same
+     * name or to several classes with the same (package unqualified) name, or even to several
+     * versions of the same compiled class loaded into distinct class loaders.
+     *
+     * @return a unique name for the trigger point from which this rule was invoked
+     */
+    public String toString()
+    {
+        return rule.getName();
+    }
+
+    /**
+     * lookup the waiter object used to target wait and signal requests associated with a
+     * specific identifying object
+     * @param object the identifer for the waiter
+     * @param createIfAbsent true if the waiter should be (atomically) inserted if it is not present
+     * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
+     */
+    private Waiter getWaiter(Object object, boolean createIfAbsent)
+    {
+        Waiter waiter;
+
+        synchronized(waitMap) {
+            waiter = waitMap.get(object);
+            if (waiter == null && createIfAbsent) {
+                waiter = new Waiter(object);
+                waitMap.put(object, waiter);
+            }
+        }
+
+        return waiter;
+    }
+
+    /**
+     * remove the waiter object used to target wait and signal requests associated with a
+     * specific identifying object
+     * @param object the identifer for the waiter
+     * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
+     */
+    private Waiter removeWaiter(Object object)
+    {
+        return waitMap.remove(object);
+    }
+
+    private static int nextFileIndex = 0;
+
+    private static synchronized int nextFileIndex()
+    {
+        return nextFileIndex++;
+    }
+
+    private String nextFileName()
+    {
+        StringWriter writer = new StringWriter();
+        String digits = Integer.toString(nextFileIndex());
+        int numDigits = digits.length();
+        int idx;
+
+        writer.write("trace");
+
+        // this pads up to 9 digits but we may get more if we open enough files!
+        for (idx = 9; idx > numDigits; idx--) {
+            writer.write('0');
+        }
+
+        writer.write(digits);
+
+        return writer.toString();
+    }
+    /**
+     * a hash map used to identify trace streams from their identifying objects
+     */
+    private static HashMap<Object, PrintStream> traceMap = new HashMap<Object, PrintStream>();
+
+    /**
+     * a set used to identify settings for boolean flags associated with arbitrary objects. if
+     * an object is in the set then the flag associated with the object is set (true) otherwise
+     * it is clear (false).
+     */
+    private static Set<Object> flagSet = new HashSet<Object>();
+
+    /**
+     * a hash map used to identify countdowns from their identifying objects
+     */
+    private static HashMap<Object, CountDown> countDownMap = new HashMap<Object, CountDown>();
+
+    /**
+     * a hash map used to identify counters from their identifying objects
+     */
+    private static HashMap<Object, Counter> counterMap = new HashMap<Object, Counter>();
+
+    /**
+     * a hash map used to identify waiters from their identifying objects
+     */
+    private static HashMap<Object, Waiter> waitMap = new HashMap<Object, Waiter>();
+
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/HelperAdapter.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/HelperAdapter.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/HelperAdapter.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -0,0 +1,25 @@
+package org.jboss.jbossts.orchestration.rule.helper;
+
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
+
+/**
+ * This interface defines the methods which need to be added to a helper class in order for it
+ * to plug in to the rule system. In the case of the default helper class, Helper, this interface
+ * is implemented by a pre-defined subclass, InterpretedHelper which interprets the rule parse
+ * tree. Given any user-supplied helper class the rule compiler can generate a HelperAdapter class
+ * which interprets the rule tree and invokes builtin methods using reflection. The compiler can
+ * also generate a HelperAdapter whose bind(), test() and fire() methods are compiled from bytecode
+ * derived from the parse trees of, respectively, the rule's event, condition and action. Bytecode
+ * compilation is applicable to rules which employ the default helper as well as rules which employ
+ * user-defined helpers.
+ */
+public interface HelperAdapter
+{
+    public void execute(Bindings bindings, Object recipient, Object[] args)
+            throws ExecuteException;
+    public void bindVariable(String name, Object value);
+    public Object getBinding(String name);
+    public String getName();
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/InterpretedHelper.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/InterpretedHelper.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/helper/InterpretedHelper.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -0,0 +1,129 @@
+package org.jboss.jbossts.orchestration.rule.helper;
+
+import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.binding.Binding;
+import org.jboss.jbossts.orchestration.agent.Transformer;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Implementation of RuleHelper which extends the functionality of the standard helper class,
+ * Helper, by adding the methods required to implement interface RuleHelper. It provides an
+ * implementation which executes rules by interpreting the rule tree. Any rule which employs the
+ * standard helper type checks built in method calls against class Helper. However, the interpreter
+ * assumes that the helper object implements InterpretedHelper.
+ *
+ * When a rule is compiled class Helper is extended with a generated class CompiledHelper<NNN>
+ * which also implements interface RuleHelper. The implementation of the execute method is generated
+ * by translating the parse tree to bytecode. Builtin calls are translated to calls of methods
+ * defined by class Helper.
+ *
+ * A rule can also specify its own helper class in order to provide its own set of builtin
+ * operations. The helper class does not implement interface RuleHelper. Instead the
+ * compilation process will generate a subclass of the user-defined helper class which
+ * provides an appropriate implementation for the RuleHelper methods, including an implementation
+ * of the execute method dreived from the rule parse tree. As in the default case, builtin calls
+ * are translated to calls of methods defined by the helper class. 
+ */
+public class InterpretedHelper extends Helper implements HelperAdapter
+{
+    protected HashMap<String, Object> bindingMap;
+    private HashMap<String, Type> bindingTypeMap;
+
+    public InterpretedHelper(Rule rule)
+    {
+        super(rule);
+        bindingMap = new HashMap<String, Object>();
+        bindingTypeMap = new HashMap<String, Type>();
+    }
+
+    /**
+     * install values into the bindings map and then call the execute0 method
+     * to actually execute the rule
+     * @param bindings
+     * @param recipient
+     * @param args
+     */
+    public void execute(Bindings bindings, Object recipient, Object[] args)
+            throws ExecuteException
+    {
+        if (Transformer.isVerbose()) {
+            System.out.println(rule.getName() + " execute");
+        }
+        Iterator<Binding> iterator = bindings.iterator();
+        while (iterator.hasNext()) {
+            Binding binding = iterator.next();
+            String name = binding.getName();
+            Type type = binding.getType();
+            if (binding.isHelper()) {
+                bindingMap.put(name, this);
+                bindingTypeMap.put(name, type);
+            } else if (binding.isRecipient()) {
+                bindingMap.put(name, recipient);
+                bindingTypeMap.put(name, type);
+            } else if (binding.isParam()) {
+                bindingMap.put(name, args[binding.getIndex() - 1]);
+                bindingTypeMap.put(name, type);
+            }
+        }
+
+        // now do the actual execution
+
+        execute0();
+    }
+
+    /**
+     * basic implementation of rule execution
+     *
+     * @throws ExecuteException
+     */
+        
+    protected void execute0()
+            throws ExecuteException
+    {
+        // System.out.println(rule.getName() + " execute0");
+        bind();
+        if (test()) {
+            fire();
+        }
+    }
+
+    public void bindVariable(String name, Object value)
+    {
+        bindingMap.put(name, value);
+    }
+
+    public Object getBinding(String name)
+    {
+        return bindingMap.get(name);
+    }
+
+    private void bind()
+            throws ExecuteException
+    {
+        // System.out.println(rule.getName() + " bind");
+        rule.getEvent().interpret(this);
+    }
+
+    private boolean test()
+            throws ExecuteException
+    {
+        // System.out.println(rule.getName() + " test");
+        return rule.getCondition().interpret(this);
+    }
+        
+    private void fire()
+            throws ExecuteException
+    {
+        // System.out.println(rule.getName() + " fire");
+        rule.getAction().interpret(this);
+    }
+
+    public String getName() {
+        return rule.getName();
+    }
+}

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/test/TestScript.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/test/TestScript.java	2009-03-13 13:59:32 UTC (rev 25634)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/test/TestScript.java	2009-03-13 14:04:26 UTC (rev 25635)
@@ -118,6 +118,7 @@
                 String[] lines = script.split("\n");
                 String targetClassName;
                 String targetMethodName;
+                String targetHelperName = null;
                 LocationType locationType = null;
                 Location targetLocation = null;
                 String text = "";
@@ -167,6 +168,16 @@
                         throw new ParseException("Rule is incomplete : " + script);
                     }
                 }
+                if (lines[idx].startsWith("HELPER ")) {
+                    targetHelperName = lines[idx].substring(7).trim();
+                    idx++;
+                    while (lines[idx].trim().equals("") || lines[idx].trim().startsWith("#")) {
+                        idx++;
+                        if (idx == len) {
+                            throw new ParseException("Rule is incomplete : " + script);
+                        }
+                    }
+                }
                 locationType = LocationType.type(lines[idx]);
                 if (locationType != null) {
                     String parameters = LocationType.parameterText(lines[idx]);
@@ -190,7 +201,15 @@
                     throw new ParseException("Missing ENDRULE : " + script);
                 }
 
-                Rule rule = Rule.create(ruleName, targetClassName, targetMethodName, targetLocation, text, loader);
+                Class targetHelperClass = null;
+                if (targetHelperName != null) {
+                    try {
+                        targetHelperClass = loader.loadClass(targetHelperName);
+                    } catch (ClassNotFoundException e) {
+                        System.out.println("org.jboss.jbossts.orchestration.agent.Transformer : unknown helper class " + targetHelperName + " for rule " + ruleName);
+                    }
+                }
+                Rule rule = Rule.create(ruleName, targetClassName, targetMethodName, targetHelperClass, targetLocation, text, loader);
                 System.err.println("TestScript: parsed rule " + rule.getName());
                 System.err.println(rule);
                 
@@ -283,7 +302,7 @@
             } catch (CompileException e) {
                 compileErrorCount++;
                 errorCount++;
-                System.err.println("TestScript: compile exception for rule " + " : " + ruleName + e);
+                System.err.println("TestScript: createHelperAdapter exception for rule " + " : " + ruleName + e);
                 e.printStackTrace(System.err);
             }
         }




More information about the jboss-svn-commits mailing list