[jboss-svn-commits] JBL Code SVN: r29154 - in labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman: rule and 1 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Sep 3 04:57:31 EDT 2009


Author: adinn
Date: 2009-09-03 04:57:31 -0400 (Thu, 03 Sep 2009)
New Revision: 29154

Added:
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transform.java
Modified:
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/rule/Rule.java
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/test/TestScript.java
Log:
cleaned up organization of code and added tracking of successful/failed transforms and compiles

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java	2009-09-02 19:42:36 UTC (rev 29153)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java	2009-09-03 08:57:31 UTC (rev 29154)
@@ -23,8 +23,16 @@
 */
 package org.jboss.byteman.agent;
 
+import org.jboss.byteman.rule.Rule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
 /**
- * information about a single rule derived from a rule script
+ * information about a single rule obtained from a rule script including any failed or successful transforms
+ * performed using the rule
  */
 
 public class RuleScript
@@ -35,19 +43,22 @@
     private String targetHelper;
     private Location targetLocation;
     private String ruleText;
-    int line;
-    String file;
+    private boolean deleted;
+    private int line;
+    private String file;
+    private List<Transform> transformed;
 
-    RuleScript(String name, String targetClass, String targetMethod, String targetHelper, Location targetLocation, String ruleText, int line, String file)
+    public RuleScript(String name, String targetClass, String targetMethod, String targetHelper, Location targetLocation, String ruleText, int line, String file)
     {
         this.name = name;
         this.targetClass = targetClass;
         this.targetMethod = targetMethod;
         this.targetHelper = targetHelper;
-        this.targetLocation = targetLocation;
+        this.targetLocation = (targetLocation != null ? targetLocation : Location.create(LocationType.ENTRY, ""));
         this.ruleText = ruleText;
         this.line = line;
         this.file = file;
+        this.transformed = null;
     }
 
     public String getName() {
@@ -83,4 +94,128 @@
     {
         return file;
     }
+
+    /**
+     * getter for list of transformed applied for this script. must be called synchronized on the script.
+     * @return te list of transforms
+     */
+    public List<Transform> getTransformed()
+    {
+        return transformed;
+    }
+
+    /**
+     * invoked by the retransformer code when a rule is redefined to inhibit further transformations via this script
+     */
+    public synchronized void setDeleted()
+    {
+        deleted = true;
+    }
+
+    /**
+     * record the fact that a trigger call has been successfully installed into bytecode associated with a specific
+     * class and loader and a corresponding rule instance been installed
+     * @param loader
+     * @param internalClassName
+     * @param rule
+     * @return
+     */
+    public synchronized boolean recordTransform(ClassLoader loader, String internalClassName, Rule rule)
+    {
+        return recordTransform(loader, internalClassName, rule, null);
+    }
+
+    /**
+     * record the fact that a trigger call has failed to install into bytecode associated with a specific
+     * class and loader
+     * @param loader the loader of the class being transformed
+     * @param internalClassName the internal name of the class being transformed
+     * @param rule the rule resulting from the parse of the rule text or null if a parse error occurred
+     * @param th throwable generated during the attempt to parse the rule text or inject code at the trigger point
+     * @return
+     */
+    public synchronized boolean recordTransform(ClassLoader loader, String internalClassName, Rule rule, Throwable th)
+    {
+        if (deleted) {
+            return false;
+        }
+
+        addTransform(new Transform(loader, internalClassName, rule, th));
+
+        return true;
+    }
+
+    private void addTransform(Transform transform)
+    {
+        if (transformed == null) {
+            transformed = new ArrayList<Transform>();
+        }
+
+        transformed.add(transform);
+    }
+
+    /**
+     * record the fact that a rule has been compiled wiht or without success
+     * @param triggerClass the name of the trigger class to which the rule is attached
+     * @param loader the classloader of the trigger class
+     * @param successful true if the rule compiled successfully and false if it suffered form parse,
+     * type or compile errors
+     */
+    public synchronized void recordCompile(String triggerClass, ClassLoader loader, boolean successful, String detail)
+    {
+        int count = transformed.size();
+        for (int i =  0; i < count; i++) {
+            Transform transform = transformed.get(i);
+            if (transform.getLoader() == loader) {
+                transform.setCompiled(successful, detail);
+            }
+        }
+    }
+
+    /**
+     * uninstall any rules associated with this script. this is called after marking the script as
+     * deleted and regenerating the methods for any associated transformed class to ensure that it does
+     * not cause a rule trigger call to fail.
+     */
+    public synchronized void purge()
+    {
+        int count = transformed.size();
+        for (int i =  0; i < count; i++) {
+            Transform transform = transformed.get(i);
+            Rule rule = transform.getRule();
+            if (rule != null) {
+                rule.purge();
+            }
+        }
+    }
+
+    public String toString()
+    {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter);
+        writeTo(writer);
+        writer.flush();
+        return stringWriter.toString();
+    }
+    
+    public void writeTo(PrintWriter writer)
+    {
+        writer.print("# File ");
+        writer.print(file);
+        writer.print(" line ");
+        writer.println(line);
+        writer.print("RULE ");
+        writer.println(name);
+        writer.print("CLASS ");
+        writer.println(targetClass);
+        writer.print("METHOD ");
+        writer.println(targetMethod);
+        if (targetHelper != null) {
+            writer.print("HELPER ");
+            writer.println(targetHelper);
+        }
+        writer.println(targetLocation.toString());
+        writer.println(ruleText);
+        writer.println("ENDRULE");
+    }
 }

Added: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transform.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transform.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transform.java	2009-09-03 08:57:31 UTC (rev 29154)
@@ -0,0 +1,112 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2009, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*
+* @authors Andrew Dinn
+*/
+package org.jboss.byteman.agent;
+
+import org.jboss.byteman.rule.Rule;
+
+import java.io.PrintWriter;
+
+/**
+ * record of a specific bytecode transformation applied by the byteman agent for a given trigger class
+ */
+public class Transform
+{
+    private ClassLoader loader;
+    private String internalClassName;
+    private Rule rule;
+    private Throwable throwable;
+    private boolean compiled;
+    private boolean successful;
+    private String detail;
+
+    public Transform(ClassLoader loader, String internalClassName, Rule rule) {
+        this(loader, internalClassName, rule, null);
+    }
+
+    public Transform(ClassLoader loader, String internalClassName, Rule rule, Throwable th) {
+        this.loader = loader;
+        this.internalClassName = internalClassName;
+        this.rule = rule;
+        this.compiled = false;
+        this.throwable = th;
+        this.successful = false;
+        this.detail = "";
+    }
+
+    public ClassLoader getLoader() {
+        return loader;
+    }
+
+    public String getInternalClassName() {
+        return internalClassName;
+    }
+
+    public Rule getRule() {
+        return rule;
+    }
+
+    public Throwable getThrowable() {
+        return throwable;
+    }
+
+    public String getDetail() {
+        return detail;
+    }
+
+    public void setCompiled(boolean successful, String detail) {
+        this.compiled = true;
+        this.successful = successful;
+        this.detail = detail;
+    }
+
+    public boolean isTransformed()
+    {
+        return (throwable == null);
+    }
+    
+    public boolean isCompiledOk() {
+        return compiled && successful;
+    }
+
+    public void writeTo(PrintWriter writer)
+    {
+        writer.print("Transformed in:\n");
+        writer.print("loader: ");
+        writer.println(loader);
+        writer.print("trigger class: ");
+        writer.println(internalClassName);
+        if (throwable != null) {
+            writer.print("threw ");
+            writer.println(throwable);
+            throwable.printStackTrace(writer);
+        } else if (compiled) {
+            if (successful) {
+                writer.println("compiled successfully");
+            } else {
+                writer.println("failed to compile");
+                writer.println(detail);
+            }
+        }
+    }
+}

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java	2009-09-02 19:42:36 UTC (rev 29153)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java	2009-09-03 08:57:31 UTC (rev 29154)
@@ -35,10 +35,7 @@
 import java.lang.instrument.IllegalClassFormatException;
 import java.lang.instrument.Instrumentation;
 import java.security.ProtectionDomain;
-import java.util.List;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.*;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.File;
@@ -73,107 +70,147 @@
         while (iter.hasNext()) {
             String scriptText = iter.next();
             String file = scriptPaths.get(scriptIdx);
-            if (scriptText != null) {
-                // split rules into separate lines
-                String[] lines = scriptText.split("\n");
-                List<String> rules = new ArrayList<String>();
-                String nextRule = "";
-                String sepr = "";
-                String name = null;
-                String targetClass = null;
-                String targetMethod = null;
-                String targetHelper = null;
-                LocationType locationType = null;
-                Location targetLocation = null;
-                int lineNumber = 0;
-                int startNumber = -1;
-                int maxLines = lines.length;
-                boolean inRule = false;
-                for (String line : lines) {
-                    lineNumber++;
-                    if (line.trim().startsWith("#")) {
-                        if (inRule) {
-                            // add a blank line in place of the comment so the line numbers
-                            // are reported consistently during parsing
-                            nextRule += sepr;
-                            sepr = "\n";
-                        } // else { // just drop comment line }
-                    } else if (line.startsWith("RULE ")) {
-                        inRule = true;
-                        name = line.substring(5).trim();
-                    } else if (!inRule) {
-                        if (!line.trim().equals("")) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : invalid text outside of RULE/ENDRULE " + "at line " + lineNumber + " in script " + file);
-                        }
-                    } else if (line.startsWith("CLASS ")) {
-                        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);
+            List<RuleScript> ruleScripts = processScripts(scriptText, file);
+            for (RuleScript ruleScript : ruleScripts) {
+                addScript(ruleScript);
+            }
+        }
+    }
+
+    private List<RuleScript> processScripts(String scriptText, String scriptFile) throws Exception
+    {
+        List<RuleScript> ruleScripts = new LinkedList<RuleScript>();
+
+        if (scriptText != null) {
+            // split rules into separate lines
+            String[] lines = scriptText.split("\n");
+            List<String> rules = new ArrayList<String>();
+            String nextRule = "";
+            String sepr = "";
+            String name = null;
+            String targetClass = null;
+            String targetMethod = null;
+            String targetHelper = null;
+            LocationType locationType = null;
+            Location targetLocation = null;
+            int lineNumber = 0;
+            int startNumber = -1;
+            int maxLines = lines.length;
+            boolean inRule = false;
+            for (String line : lines) {
+                line = line.trim();
+                lineNumber++;
+                if (line.startsWith("#")) {
+                    if (inRule) {
+                        // add a blank line in place of the comment so the line numbers
+                        // are reported consistently during parsing
+                        nextRule += sepr;
+                        sepr = "\n";
+                    } // else { // just drop comment line }
+                } else if (line.startsWith("RULE ")) {
+                    inRule = true;
+                    name = line.substring(5).trim();
+                    if (name.equals("")) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : RULE with no name at line " + lineNumber + " in script " + scriptFile);
+                    }
+                } else if (!inRule) {
+                    if (!line.equals("")) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : invalid text outside of RULE/ENDRULE " + "at line " + lineNumber + " in script " + scriptFile);
+                    }
+                } else if (line.startsWith("CLASS ")) {
+                    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);
+                    if (targetLocation == null) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : invalid target location at line " + lineNumber + " in script " + scriptFile);
+                    }
+                } else if (line.startsWith("ENDRULE")) {
+                    if (name == null) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : no matching RULE for ENDRULE at line " + lineNumber + " in script " + scriptFile);
+                    } else if (targetClass == null) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : no CLASS for RULE  " + name + " in script " + scriptFile);
+                    } else if (targetMethod == null) {
+                        throw new Exception("org.jboss.byteman.agent.Transformer : no METHOD for RULE  " + name + " in script " + scriptFile);
+                    } else {
                         if (targetLocation == null) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : invalid target location at line " + lineNumber + " in script " + scriptPaths.get(scriptIdx));
+                            targetLocation = Location.create(LocationType.ENTRY, "");
                         }
-                    } else if (line.startsWith("ENDRULE")) {
-                        if (name == null) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : no matching RULE for ENDRULE at line " + lineNumber + " in script " + scriptPaths.get(scriptIdx));
-                        } else if (targetClass == null) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : no CLASS for RULE  " + name + " in script " + scriptPaths.get(scriptIdx));
-                        } else if (targetMethod == null) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : no METHOD for RULE  " + name + " in script " + scriptPaths.get(scriptIdx));
-                        } else {
-                            List<RuleScript> ruleScripts = targetToScriptMap.get(targetClass);
-                            if (ruleScripts == null) {
-                                ruleScripts = new ArrayList<RuleScript>();
-                                targetToScriptMap.put(targetClass, ruleScripts);
-                            }
-                            if (targetLocation == null) {
-                                targetLocation = Location.create(LocationType.ENTRY, "");
-                            }
-                            RuleScript ruleScript = new RuleScript(name, targetClass, targetMethod, targetHelper, targetLocation, nextRule, startNumber, file);
-                            ruleScripts.add(ruleScript);
-                            if (isVerbose()) {
-                                System.out.println("RULE " + ruleScript.getName());
-                                System.out.println("CLASS " + ruleScript.getTargetClass());
-                                System.out.println("METHOD " + ruleScript.getTargetMethod());
-                                if (ruleScript.getTargetHelper() != null) {
-                                    System.out.println("HELPER " + ruleScript.getTargetHelper());
-                                }
-                                if (targetLocation != null) {
-                                    System.out.println(targetLocation);
-                                } else {
-                                    System.out.println("AT ENTRY");
-                                }
-                                System.out.println(ruleScript.getRuleText());
-                                System.out.println("ENDRULE");
-                            }
-                        }
-                        name = null;
-                        targetClass = null;
-                        targetMethod = null;
-                        targetLocation = null;
-                        nextRule = "";
-                        sepr = "";
-                        inRule = false;
-                        // reset start nuuber so we pick up the next rule text line
-                        startNumber = -1;
-                    } else if (lineNumber == maxLines && !nextRule.trim().equals("")) {
-                            throw new Exception("org.jboss.byteman.agent.Transformer : no matching ENDRULE for RULE " + name + " in script " + scriptPaths.get(scriptIdx));
-                    } else {
-                        if (startNumber < 0) {
-                            startNumber = lineNumber;
-                        }
-                        nextRule += sepr + line;
-                        sepr = "\n";
+                        ruleScripts.add(new RuleScript(name, targetClass, targetMethod, targetHelper, targetLocation, nextRule, startNumber, scriptFile));
                     }
+                    name = null;
+                    targetClass = null;
+                    targetMethod = null;
+                    targetLocation = null;
+                    nextRule = "";
+                    sepr = "";
+                    inRule = false;
+                    // reset start nuuber so we pick up the next rule text line
+                    startNumber = -1;
+                } else if (lineNumber == maxLines && !nextRule.trim().equals("")) {
+                    throw new Exception("org.jboss.byteman.agent.Transformer : no matching ENDRULE for RULE " + name + " in script " + scriptFile);
+                } else {
+                    // this is a line of rule text - see if it is the first one
+                    if (startNumber < 0) {
+                        startNumber = lineNumber;
+                    }
+                    nextRule += sepr + line;
+                    sepr = "\n";
                 }
             }
         }
+
+        return ruleScripts;
     }
 
+    private void indexScriptByTarget(RuleScript ruleScript)
+    {
+        String targetClass = ruleScript.getTargetClass();
+
+        List<RuleScript> ruleScripts;
+
+        synchronized (targetToScriptMap) {
+            ruleScripts = targetToScriptMap.get(targetClass);
+            if (ruleScripts == null) {
+                ruleScripts = new ArrayList<RuleScript>();
+                targetToScriptMap.put(targetClass, ruleScripts);
+            }
+        }
+
+        ruleScripts.add(ruleScript);
+    }
+
+    private void dumpScript(RuleScript ruleScript)
+    {
+        String file = ruleScript.getFile();
+        int line = ruleScript.getLine();
+        if (file != null) {
+            System.out.println("# " + file + " line " + line);
+        }
+        System.out.println("RULE " + ruleScript.getName());
+        System.out.println("CLASS " + ruleScript.getTargetClass());
+        System.out.println("METHOD " + ruleScript.getTargetMethod());
+        if (ruleScript.getTargetHelper() != null) {
+            System.out.println("HELPER " + ruleScript.getTargetHelper());
+        }
+        System.out.println(ruleScript.getTargetLocation());
+        System.out.println(ruleScript.getRuleText());
+        System.out.println("ENDRULE");
+    }
+
+    private void addScript(RuleScript ruleScript) throws Exception
+    {
+        indexScriptByTarget(ruleScript);
+
+        if (isVerbose()) {
+            dumpScript(ruleScript);
+        }
+    }
+    
     /**
      * The implementation of this method may transform the supplied class file and
      * return a new replacement class file.
@@ -238,14 +275,20 @@
             throws IllegalClassFormatException
     {
         byte[] newBuffer = classfileBuffer;
-        // we only transform certain classes -- in  particular, we exclude bootstrap classes whose loader is null
-        // and we exclude byteman classes
+        // we only transform certain classes -- we do allow bootstrap classes whose loader is null
+        // but we exclude byteman classes and java.lang classes
         String internalClassName = TypeHelper.internalizeClass(className);
 
-        if (loader == null || isBytemanClass(internalClassName) || !isTransformable(internalClassName)) {
+        if (isBytemanClass(internalClassName) || !isTransformable(internalClassName)) {
             return null;
         }
 
+        // TODO-- reconsider this as it is a bit dodgy as far as security is concerned
+
+        if (loader == null) {
+            loader = ClassLoader.getSystemClassLoader();
+        }
+
         // ok, we need to check whether there are any scripts associated with this class and if so
         // we will consider transforming the byte code
 
@@ -286,7 +329,7 @@
         if (newBuffer != classfileBuffer) {
             // see if we need to dump the transformed bytecode for checking
             if (dumpGeneratedClasses) {
-                dumpClass(internalClassName, newBuffer);
+                dumpClass(internalClassName, newBuffer, classfileBuffer);
             }
             return newBuffer;
         } else {
@@ -361,14 +404,12 @@
      */
     public static final String DUMP_GENERATED_CLASSES_DIR = BYTEMAN_PACKAGE_PREFIX + "dump.generated.classes.directory";
 
-    private byte[] transform(RuleScript ruleScript, ClassLoader loader, String className, Class classBeingRedefined, byte[] targetClassBytes)
+    protected byte[] transform(RuleScript ruleScript, ClassLoader loader, String className, Class classBeingRedefined, byte[] targetClassBytes)
     {
         final String handlerClass = ruleScript.getTargetClass();
         final String handlerMethod = ruleScript.getTargetMethod();
         final String helperName = ruleScript.getTargetHelper();
         final Location handlerLocation = ruleScript.getTargetLocation();
-        final int lineNumber = ruleScript.getLine();
-        final String file = ruleScript.getFile();
         Class helperClass = null;
         if (helperName != null) {
             try {
@@ -386,21 +427,24 @@
         final Rule rule;
         String ruleName = ruleScript.getName();
         try {
-            rule = Rule.create(ruleName, handlerClass, handlerMethod, helperClass, handlerLocation, ruleScript.getRuleText(), lineNumber, file, loader);
+            rule = Rule.create(ruleScript, helperClass, loader);
         } catch (ParseException pe) {
             System.out.println("org.jboss.byteman.agent.Transformer : error parsing rule " + ruleName + " : " + pe);
+            ruleScript.recordTransform(loader, className, null, pe);
             return targetClassBytes;
         } catch (TypeException te) {
             System.out.println("org.jboss.byteman.agent.Transformer : error checking rule " + ruleName + " : " + te);
+            ruleScript.recordTransform(loader, className, null, te);
             return targetClassBytes;
         } catch (Throwable th) {
             System.out.println("org.jboss.byteman.agent.Transformer : error processing rule " + ruleName + " : " + th);
+            ruleScript.recordTransform(loader, className, null, th);
             return targetClassBytes;
         }
         if (isVerbose()) {
             System.out.println(rule);
         }
-        
+
         // ok, we have a rule with a matchingclass and a candidiate method and location
         // we need to see if the class has a matching method and, if so, add a call to
         // execute the rule when we hit the relevant line
@@ -415,8 +459,9 @@
         try {
             cr.accept(checkAdapter, ClassReader.EXPAND_FRAMES);
         } catch (Throwable th) {
-            System.out.println("org.jboss.byteman.agent.Transformer : error applying rule " + rule.getName() + " to class " + className + th);
+            System.out.println("org.jboss.byteman.agent.Transformer : error applying rule " + rule.getName() + " to class " + className + " " + th);
             th.printStackTrace(System.out);
+            ruleScript.recordTransform(loader, className, rule, th);
             return targetClassBytes;
         }
         // only insert the rule trigger call if there is a suitable location in the target method
@@ -430,12 +475,17 @@
             try {
                 cr.accept(adapter, ClassReader.EXPAND_FRAMES);
             } catch (Throwable th) {
-                System.out.println("org.jboss.byteman.agent.Transformer : error compiling rule " + rule.getName() + " for class " + className + th);
+                System.out.println("org.jboss.byteman.agent.Transformer : error injecting trigger for rule " + rule.getName() + " into class " + className + " " +  th);
                 th.printStackTrace(System.out);
+                ruleScript.recordTransform(loader, className, rule, th);
                 return targetClassBytes;
             }
-            // hand back the transformed byte code
-            return cw.toByteArray();
+            // only return transformed code if ruleScript is still active
+
+            if (ruleScript.recordTransform(loader, className, rule)) {
+                // hand back the transformed byte code
+                return cw.toByteArray();
+            }
         }
 
         return targetClassBytes;
@@ -583,6 +633,11 @@
 
     private void dumpClass(String fullName, byte[] bytes)
     {
+        dumpClass(fullName, bytes, null);
+    }
+
+    private void dumpClass(String fullName, byte[] bytes, byte[] oldBytes)
+    {
         int dotIdx = fullName.lastIndexOf('.');
 
         String name = (dotIdx < 0 ? fullName : fullName.substring(dotIdx + 1));
@@ -592,16 +647,28 @@
             System.out.println("org.jboss.byteman.agent.Transformer : Cannot dump transformed bytes to directory " + dir + File.separator + prefix);
             return;
         }
-        name = dir + File.separator + name + ".class";
-        System.out.println("org.jboss.byteman.agent.Transformer : Saving transformed bytes to " + name);
+        String newname = dir + File.separator + name + ".class";
+        System.out.println("org.jboss.byteman.agent.Transformer : Saving transformed bytes to " + newname);
         try {
-            FileOutputStream fio = new FileOutputStream(name);
+            FileOutputStream fio = new FileOutputStream(newname);
             fio.write(bytes);
             fio.close();
         } catch (IOException ioe) {
-            System.out.println("Error saving transformed bytes to" + name);
+            System.out.println("Error saving transformed bytes to" + newname);
             ioe.printStackTrace(System.out);
         }
+        if (oldBytes != null) {
+            String oldname = dir + File.separator + name + "_orig.class";
+            System.out.println("org.jboss.byteman.agent.Transformer : Saving original bytes to " + oldname);
+            try {
+                FileOutputStream fio = new FileOutputStream(oldname);
+                fio.write(oldBytes);
+                fio.close();
+            } catch (IOException ioe) {
+                System.out.println("Error saving transformed bytes to" + oldname);
+                ioe.printStackTrace(System.out);
+            }
+        }
     }
 
     private boolean ensureDumpDirectory(String fileName)

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/rule/Rule.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/rule/Rule.java	2009-09-02 19:42:36 UTC (rev 29153)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/rule/Rule.java	2009-09-03 08:57:31 UTC (rev 29154)
@@ -37,6 +37,7 @@
 import org.jboss.byteman.agent.Location;
 import org.jboss.byteman.agent.LocationType;
 import org.jboss.byteman.agent.Transformer;
+import org.jboss.byteman.agent.RuleScript;
 import org.objectweb.asm.Opcodes;
 
 import org.jboss.byteman.rule.compiler.Compiler;
@@ -55,6 +56,10 @@
 public class Rule
 {
     /**
+     * the script defining this rule
+     */
+    private RuleScript ruleScript;
+    /**
      * the name of this rule supplied in the rule script
      */
     private String name;
@@ -155,53 +160,53 @@
 
     private Type returnType;
 
-    private Rule(String name, String targetClass, String targetMethod,Class<?> helperClass, Location targetLocation, String ruleSpec, int line, String file, ClassLoader loader)
+    /**
+     * the key under which this rule is indexed in the rule key map.
+     */
+
+    private String key;
+
+    private Rule(RuleScript ruleScript, Class<?> helperClass, ClassLoader loader)
             throws ParseException, TypeException, CompileException
     {
         ParseNode ruleTree;
 
-        this.name = name;
-        this.line = line;
-        this.file = file;
+        this.ruleScript = ruleScript;
+        this.helperClass = (helperClass != null ? helperClass : Helper.class);
+        this.loader = loader;
+
         typeGroup = new TypeGroup(loader);
         bindings = new Bindings();
-        if (ruleSpec != null) {
-            // ensure line numbers start at 1
-            String fullSpec = "\n" + ruleSpec;
-            try {
-                ECATokenLexer lexer = new ECATokenLexer(new StringReader(fullSpec));
-                lexer.setStartLine(line);
-                lexer.setFile(file);
-                ECAGrammarParser parser = new ECAGrammarParser(lexer);
-                parser.setFile(file);
-                Symbol parse = (debugParse ? parser.debug_parse() : parser.parse());
-                if (parser.getErrorCount() != 0) {
-                    throw new ParseException("org.jboss.byteman.rule.Rule : error parsing rule\n" + ruleSpec);
-                }
-                ruleTree = (ParseNode) parse.value;
-            } catch (ParseException pe) {
-                throw pe;
-            } catch (Exception e) {
-                throw new ParseException("org.jboss.byteman.rule.Rule : error parsing rule\n" + ruleSpec, e);
-            }
-            ParseNode eventTree = (ParseNode)ruleTree.getChild(0);
-            ParseNode conditionTree = (ParseNode)ruleTree.getChild(1);
-            ParseNode actionTree = (ParseNode)ruleTree.getChild(2);
-            event = Event.create(this, eventTree);
-            condition = Condition.create(this, conditionTree);
-            action = Action.create(this, actionTree);
-        }
         checked = false;
-        this.loader = loader;
-        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;
         triggerDescriptor = null;
         triggerAccess = 0;
         returnType = null;
+        try {
+            ECATokenLexer lexer = new ECATokenLexer(new StringReader(ruleScript.getRuleText()));
+            lexer.setStartLine(getLine());
+            lexer.setFile(getFile());
+            ECAGrammarParser parser = new ECAGrammarParser(lexer);
+            parser.setFile(file);
+            Symbol parse = (debugParse ? parser.debug_parse() : parser.parse());
+            if (parser.getErrorCount() != 0) {
+                throw new ParseException("org.jboss.byteman.rule.Rule : error parsing rule\n" + ruleScript.getRuleText());
+            }
+            ruleTree = (ParseNode) parse.value;
+        } catch (ParseException pe) {
+            throw pe;
+        } catch (Exception e) {
+            throw new ParseException("org.jboss.byteman.rule.Rule : error parsing rule\n" + ruleScript.getRuleText(), e);
+        }
+
+        ParseNode eventTree = (ParseNode)ruleTree.getChild(0);
+        ParseNode conditionTree = (ParseNode)ruleTree.getChild(1);
+        ParseNode actionTree = (ParseNode)ruleTree.getChild(2);
+
+        event = Event.create(this, eventTree);
+        condition = Condition.create(this, conditionTree);
+        action = Action.create(this, actionTree);
     }
 
     public TypeGroup getTypeGroup()
@@ -215,21 +220,39 @@
     }
 
     public String getName() {
-        return name;
+        return ruleScript.getName();
     }
 
     public String getTargetClass() {
-        return targetClass;
+        return ruleScript.getTargetClass();
     }
 
     public String getTargetMethod() {
-        return targetMethod;
+        return ruleScript.getTargetMethod();
     }
 
     public Location getTargetLocation() {
-        return targetLocation;
+        return ruleScript.getTargetLocation();
     }
 
+    /**
+     * retrieve the start line for the rule
+     * @return the start line for the rule
+     */
+    public int getLine()
+    {
+        return ruleScript.getLine();
+    }
+
+    /**
+     * retrieve the name of the file containing this rule
+     * @return the name of the file containing this rule
+     */
+    public String getFile()
+    {
+        return ruleScript.getFile();
+    }
+
     public Event getEvent()
     {
         return event;
@@ -261,10 +284,10 @@
         return loader;
     }
 
-    public static Rule create(String name, String targetClass, String targetMethod, Class<?> helperClass, Location targetLocation, String ruleSpec, int line, String file, ClassLoader loader)
+    public static Rule create(RuleScript ruleScript, Class<?> helperClass, ClassLoader loader)
             throws ParseException, TypeException, CompileException
     {
-            return new Rule(name, targetClass, targetMethod, helperClass, targetLocation, ruleSpec, line, file, loader);
+            return new Rule(ruleScript, helperClass, loader);
     }
 
     public void setEvent(String eventSpec) throws ParseException, TypeException
@@ -297,6 +320,38 @@
         triggerExceptions = exceptions;
     }
 
+    /**
+     * has this rule been typechecked and/or compiled
+     * @return true if this rule has been typechecked and/or compiled otherwise false
+     */
+    public boolean isChecked()
+    {
+        return checked;
+    }
+
+    /**
+     * has this rule failed to typecheck or compile
+     * @return true if this rule has failed to typecheck or compile otherwise false
+     */
+    public boolean isCheckFailed()
+    {
+        return checkFailed;
+    }
+
+    /**
+     * has this rule been typechecked and compiled without error.
+     * @return true if this rule has been typechecked and compiled without error otherwise false
+     */
+    public boolean isCheckedOk()
+    {
+        return (checked && !checkFailed);
+    }
+
+    /**
+     * typecheck and then compile this rule unless either action has been tried before
+     * @return true if the rule successfully type checks and then compiles under this call or a previous
+     * call or false if either operation has previously failed or fails under this call.
+     */
     private synchronized boolean ensureTypeCheckedCompiled()
     {
         if (checkFailed) {
@@ -304,26 +359,38 @@
         }
 
         if (!checked) {
+            String detail = "";
             try {
                 typeCheck();
                 compile();
+                checked = true;
             } catch (TypeException te) {
-                System.out.println("Rule.ensureTypeCheckedCompiled : error typechecking rule " + getName());
-                te.printStackTrace(System.out);
                 checkFailed = true;
-                return false;
+                StringWriter stringWriter = new StringWriter();
+                PrintWriter writer = new PrintWriter(stringWriter);
+                writer.println("Rule.ensureTypeCheckedCompiled : error type checking rule " + getName());
+                te.printStackTrace(writer);
+                detail = writer.toString();
+                System.out.println(detail);
             } catch (CompileException ce) {
-                System.out.println("Rule.ensureTypeCheckedCompiled : error compiling rule " + getName());
-                ce.printStackTrace(System.out);
                 checkFailed = true;
-                return false;
+                StringWriter stringWriter = new StringWriter();
+                PrintWriter writer = new PrintWriter(stringWriter);
+                writer.println("Rule.ensureTypeCheckedCompiled : error compiling rule " + getName());
+                ce.printStackTrace(writer);
             }
+
+            ruleScript.recordCompile(triggerClass, loader, !checkFailed, detail);
+            return !checkFailed;
         }
-        
+
         return true;
     }
 
-
+    /**
+     * type check this rule
+     * @throws TypeException if the ruele contains type errors
+     */
     public void typeCheck()
             throws TypeException
     {
@@ -344,9 +411,16 @@
         event.typeCheck(Type.VOID);
         condition.typeCheck(Type.Z);
         action.typeCheck(Type.VOID);
-        checked = true;
     }
 
+    /**
+     * install helper class used to execute this rule. this may involve generating a compiled helper class
+     * for the rule and, if compilation to bytecode is enabled, generating bytecode for a method of this class
+     * used to execute the rule binding, condition and action expressions. If the rule employ sthe default helper
+     * without enabling compilation to bytecode then no class need be generated. the installed helper class will
+     * be the predefined class InterpretedHelper.
+     * @throws CompileException if the rule cannot be compiled
+     */
     public void compile()
             throws CompileException
     {
@@ -375,7 +449,7 @@
             throws TypeException
     {
         Type type;
-        // add a binding for the rule so we can call builtin static methods
+        // add a binding for the helper so we can call builtin static methods
         type = typeGroup.create(helperClass.getName());
         Binding ruleBinding = bindings.lookup("$$");
         if (ruleBinding != null) {
@@ -434,6 +508,7 @@
             System.out.println("Rule.execute called for " + key);
         }
 
+        // should not happen -- even if the key is deleted because a rule is updated
         if (rule == null) {
             throw new ExecuteException("Rule.execute : unable to find rule with key " + key);
         }
@@ -512,26 +587,27 @@
     public String getKey()
     {
         String key = getName() + "_" + nextId();
+        this.key = key;
         ruleKeyMap.put(key, this);
         return key;
     }
 
     /**
-     * retrieve the start line for the ruel's parseable text
-     * @return the start line for the ruel's parseable text
+     * return the key under which this rule has been indexed in the rule key map
+     * @return
      */
-    public int getLine()
+    public String lookupKey()
     {
-        return line;
+        return key;
     }
 
+
     /**
-     * retrieve the name of the file containing this rule
-     * @return the name of the file containing this rule
+     * delete any reference to the rule from the rule map
      */
-    public String getFile()
+    public void purge()
     {
-        return file;
+        ruleKeyMap.remove(key);
     }
 
     /**
@@ -568,15 +644,15 @@
     {
         StringWriter stringWriter = new StringWriter();
         stringWriter.write("RULE ");
-        stringWriter.write(name);
+        stringWriter.write(getName());
         stringWriter.write("\n");
         stringWriter.write("CLASS ");
-        stringWriter.write(targetClass);
+        stringWriter.write(getTargetClass());
         stringWriter.write('\n');
         stringWriter.write("METHOD ");
-        stringWriter.write(targetMethod);
+        stringWriter.write(getTargetMethod());
         stringWriter.write('\n');
-        stringWriter.write(targetLocation.toString());
+        stringWriter.write(getTargetLocation().toString());
         stringWriter.write('\n');
         if (event != null) {
             event.writeTo(stringWriter);

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/test/TestScript.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/test/TestScript.java	2009-09-02 19:42:36 UTC (rev 29153)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/test/TestScript.java	2009-09-03 08:57:31 UTC (rev 29154)
@@ -34,6 +34,7 @@
 import org.jboss.byteman.rule.exception.CompileException;
 import org.jboss.byteman.agent.LocationType;
 import org.jboss.byteman.agent.Location;
+import org.jboss.byteman.agent.RuleScript;
 import org.jboss.byteman.agent.adapter.RuleCheckAdapter;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.ClassReader;
@@ -220,7 +221,8 @@
                         System.out.println("org.jboss.byteman.agent.Transformer : unknown helper class " + targetHelperName + " for rule " + ruleName);
                     }
                 }
-                Rule rule = Rule.create(ruleName, targetClassName, targetMethodName, targetHelperClass, targetLocation, text, baseline + lineNumber, file, loader);
+                RuleScript ruleScript = new RuleScript(ruleName, targetClassName, targetMethodName, targetHelperName, targetLocation, text, baseline + lineNumber, file);
+                Rule rule = Rule.create(ruleScript, targetHelperClass, loader);
                 System.err.println("TestScript: parsed rule " + rule.getName());
                 System.err.println(rule);
                 



More information about the jboss-svn-commits mailing list