Thanks for reporting your problem and thank you also for your praise for Byteman. I'm glad you have found it useful.
Unfortunately, I'm not sure exactly what problem you are reporting:
Are you suggesting that the behaviour has changed between 3.0.3 and 3.0.4?
Or are you saying that the API is not doing what you expect given how it is described in the documentation?
There are unit tests in the Byteman agent tree which test callerMatches whenever a Byteman release is built. So, if the behaviour started to go wrong in 3.0.4 then this should have caught it. Of course, that's no proof that all is ok. The failure may depend upon what rule is being injected into what code or, indeed, 3.0.3 might also have been wrong :-)
Could you provide me with a test case showing the failing behaviour and explain what you think should happen? A simple Java program and a rule script would make things a lot clearer.
Yes, that was a bit quick.
Here is a quick test project. I had a look at your test cases but I was not able to make one that would fit in the src/test/java, extend Test etc...
Running my test, I have the same result with 3.0.3 and 3.0.4 (the only two versions I installed so far).
What I originally meant is that I was expecting to be able to sense which caller triggered the call to my helper method through a simple call to Helper#callerEquals
Instead I need to use the method callerEquals ( method, startFrame, frameCount).
I'm not ruling out that I'd be misusing or misunderstanding the API. .
TestCallerEquals.zip 1.4 KB
Ah ok, I think this is just the lack of a complete and clear explanation in the javadoc. In other words the Byteman code is doing what it is meant to do not what you expect it to do and it is my fault that you have been misled :-) The javadoc for the basic flavour of this method says this:
/** * test whether the name of the method which called the the trigger method matches the supplied name * by calling callerEquals(name, false) * @param name the name to match * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name)
The trigger method is the one into which the rule code was injected. So, if your rule says this
RULE use callerEquals CLASS MyClass METHOD myMethod AT . . . IF callerEquals("myOtherMethod") . . .
then the rule condition will only be true when myMethod is called from a method named myOtherMethod. It doesn't matter whether you use callerEquals in your rule as above or call it from your own helper implementation. The implementation looks up the stack to find the point where the rule code was injected and starts counting from there. The trigger method is frame zero and the caller of the trigger method is frame 1. callerEquals("myOtherMethod") is simply a convenience method which gets translated to callerCheck("myOtherMethod", false, false, 1, 1) i.e. it says start searching from frame 1 and only test 1 frame using just the method name, no class or package.
I think the confusion arises in the javadoc for the variant which accepts a starting frame index.
/** * test whether the name of method which called the the trigger method matches the supplied name * by calling callerEquals(name, includeClass, false, startFrame, frameCount) * @param name the name to match * @param includeClass true if the check should include the * class name false if not * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, int startFrame, int frameCount)
This indicates that startFrame needs to be >= 0. That's correct. If you provide 0 as the value for startFrame then the checks will include frame 0 i.e. the trigger method frame. However, if you omit startFrame it defaults to 1 i.e. the trigger method is not normally checked. This is actually described in the Programmer's Guide:
startFrame defaults to 1 which identifies the stack frame for the caller of the trigger method (0 can be used to identify the trigger method itself). framecount also defaults to 1 which means that when startFrame and frameCount are defaulted the call only checks the frame for the caller of the trigger method.
So, yes, I am afraid that when your helper wants to identify the trigger method it will need to provide startFrame = 0 and frameCount = 1. The convenience APIs are implemented to be most useful when they are called from rule code as that is the common case. I hope that explains the current behaviour clearly. I think a small edit of the javadoc might be of use to help make this clear for others.
Thanks for posting this as it is very helpful for me and others to find out where the documentation is unclear. Please let me know if you have any other issues using Byteman. I'm always glad to hear people's war stories, especially when they also say nice things about Byteman :-)
It's all clear now.
It seems I forgot for a minute that callerEquals() was meant to be used inside the ECA rule scripts (i.e. caller is caller of the trigger).
When I'm subclassing Helper and need to sense the trigger, it makes complete sense to go one stack frame lower and use 0 explicitly.
So it's all clear now and the system is consistent. Which is quite a relief because changing this behaviour would have potentially broken a lot of existing uses.
Anyway I'm progressing.
For your info, I'm using byteman to build a Mock server "learner".
The user story is as follows.
- you need to test/develop in lab an adapter that adapts with one or more remote systems.
- for your continuous integration (e.g. maven surefire run) you want to unit test things (in true TDD bet practice).
- But of course not everyone who will run your POM will have the test servers available. So you need one or more mock servers in your test architecture.
- But a mock can be tricky to build for some protocols (e.g. CORBA).
- So sometimes you can replace a class in an API when this class is the only one that interfaces with the remote system.
- You just have to substitute the public methods at run time with your mock implementation.
- This is, as you have already guessed, where byteman can help. But that's not the end of it.
- Because you're still left with the possibly arduous task of mimicking the original class behaviour at least for a few use cases.
- Here byteman can help a second time.
- You can develop a byteman helper that will record the behaviour of the real class for in the real situation.
- This can yield a configuration file which can then be fed into the "mock" helper.
That explains why I needed to the callerEquals call inside my helper: I'm building my recorder and I need to understand which trigger with which argument yielded which return value/object.
Thanks for your patience and clear explanations.
Alain Pannetier wrote:
For your info, I'm using byteman to build a Mock server "learner".
The user story is as follows.. . .
Hmm, that's a very interesting and quite novel (to me at least) use of Byteman. Thanks very much for posting the details and good luck with the rest of the implementation.