Hi Boky,
Apologies for the delayed response. You were shelved temporarily but not forgotten. ANyway, I now have a complete answer as to what is going on with a few very interesting wrinkles :-)
Ok, so first thing is that I found the reason why the rule was not being loaded. The problem is in class ScriptRepository which maintains the internal database of loaded rules. The problem arises in method matchTarget when trying to check whether a newly installed method applies to an already loaded class:
private boolean matchTarget(String name, Class<?> clazz, boolean isInterface, boolean isOverride) {
Map<String, List<RuleScript>> index = (isInterface ? targetInterfaceIndex : targetClassIndex);
synchronized (index) {
List<RuleScript> ruleScripts = index.get(name);
if (ruleScripts != null) {
for (RuleScript ruleScript: ruleScripts) {
if (isOverride && !ruleScript.isOverride()) {
continue;
}
String methodName = ruleScript.getTargetMethod();
int signaturePos = methodName.indexOf("(");
if (signaturePos > 0) {
methodName = methodName.substring(0, signaturePos).trim();
}
if (methodName == "<init>" || methodName == "<clinit>") {
// every class has some sort of constructor so accept it
return true;
}
. . .
You have probably spotted the error . . . that comparison against "<init>" or "<clinit>" uses == when it ought to use .equals().
What amazes me is that this has not shown up before but it is entirely possible that the == comparison might work in some cases but not others.
I have raised a JIRA for this which will be fixed in Byteman 2.0.0
https://issues.jboss.org/browse/BYTEMAN-181
So, next I ran your test with the == replaced by .equals expecting it to work.However, I found that there are a couple of other problems.
Fisrtly, you rules need to supply two Strings as arguments in the throw new URISyntaxException() expression. The class does not have a constructor taking one String. So, for example, your script rule should have the following DO clause
DO throw new java.net.URISyntaxException($1, "bad bad URI!")
Same applies for the inline code in the BMRule annotations.
The relevant constructor is declared as follows
public URISyntaxException(String input, String reason);
The first argument should be the URL supplied as argument to the URL constructor, $1.
I applied that fix to all the inline rules and scripts and then ran into the next problem which is . . . interesting. Essentially, your rule has broken all URI creation. Thta's a fairly dangerousthing to inject into the JVM and, in fact, it causes a problemfor Byteman itself. When the test finishes the Byteman test runner code tries to open a socket to the agent listener in order to ask it to unload the rule. This fails because somewhere below the call to Socket.<init> the implementation tries to create a URL -- auto-snookered!
A fix would be to add a condition like the following to your rules
RULE throw URI construction exception
CLASS java.net.URI
METHOD <init>(String)
AT ENTRY
IF callerMatches("Simple.*", true)
DO throw new java.net.URISyntaxException("bad bad URI!")
ENDRULE
The built-in callerMatches can be used to ensure that there is a frame in the call stack with a method name taht matches the specifiedregular expression. There are many overloaded versions of this method. If you just pass the regular expression argument it looks at the direct caller frame and the bare method name. If you add a second argument 'true' it includes the class name when doing the match.Another 'true' means include the package name. You canalso provide a frame count or a start frame and frame count. Anyway, this version says ony trigger the rule if the class qualified method name of the immediate caller starts wirth prefix Simple. This means an exception will be thrown from your app class Simple and from your test methods in classes SimpleTest and SimplePreloadTest but they wil not be thrown from any other classes. I modifedthe ruels accordingly and the test run successfully tocompletion. For example, here is the BMRule annotation for one of the test classes
@BMRule(
name = "throw URI exception",
targetClass = "java.net.URI",
targetMethod = "<init>(String)",
condition= "callerMatches(\"Simple.*\", true)",
action = "throw new java.net.URISyntaxException($1, \"bad bad URI!\")"
)
@Test(expectedExceptions = URISyntaxException.class)
public void testDirectBMRuleOKURI() throws URISyntaxException {
new java.net.URI("http://www.google.com/");
}
Finally, there is one other thing you might want to add to your pom. If environment variable BYTEMAN_HOME is set then the BMUnit package uses this to locate the agent jar (byteman.jar) rather than finding it in the classpath. This is probably the wrong way round i.e. it shoudl be found first via the classpath and then, failing that, via BYTEMAN_HOME. I will correct this in the 2.0.0 release. Until then it is a good idea to set this variable to an empty string in the surefire config.
<environmentVariables>
<property>
<name>BYTEMAN_HOME</name>
<value></value>
</property>
</environmentVariables>