[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