I have got the following code:
Collection<String> errors = ...;
try (InputStream stream = My.class.getResourceAsStream(resource)) {
// do stuff
}
catch(IOException ex) {
errors.add("Fail");
}
I'm trying with Byteman Junit Runner to trigger an IOException when the (valid) input stream I give is supposedly closed:
@RunWith(BMUnitRunner.class)
public class MyTest {
private My my = new My();
@BMRule(
name = "force_read_error",
targetClass = "java.io.InputStream",
targetMethod = "close()",
action = "throw new IOException(\"bazinga\")"
)
@Test
public void catches_read_error() throws IOException {
Collection<String> errors = my.foo("/valid-resource-in-classpath");
assertThat(errors).containsExactly("Fail");
}
}
My test fails: errors is always empty, which means the Byteman rule obviously isn't executed (it's well loaded by the agent, so I don't understand what's going on).
How can I trigger an IOException on close method called via try-with-resources?
Your rule ist not firing, because the class of the stream object received when calling
InputStream stream = My.class.getResourceAsStream(resource)
is not a "java.io.InputStream" class. It is a class extending "java.io.InputStream", most likely a "BufferedInputStream".
To tell byteman to "trigger rule for any class extending java.io.InputStream", you need to put a '^' before the class name:
targetClass = "^java.io.InputStream"
This change might have the unwanted side effect, that the rule gets triggered also when other objects extending "java.io.InputStream" get closed. To prevent this from happening, a condition should be added to the rule to only get triggered, when the caller matches the "foo" method of the "My" class. Byteman has a helper method for that called "callerMatches" (Please see also the advanced tutorial)
A working condition for your case would be:
condition = "callerMatches(\".+My.foo\",true,true)"
The complete Byteman rule definition as BMRule annotation should look like:
@BMRule(
name = "force_read_error",
targetClass = "^java.io.InputStream",
targetMethod = "close()",
condition = "callerMatches(\".+My.foo\",true,true)",
action = "throw new java.io.IOException(\"bazinga\")"
)