[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