Byteman advanced tutorial

After we have learnt the basics of ByteMan we can now dive into a more advanced tutorial which shows how to use method positional parameters and advanced Byteman rules to intercept JMS messages.

In this example we will show how we can interecept a JMS message received by an MDB so that we can perform some extra actions on it, without changing the actual MDB code.

Here's a basic rule named "JMS Message Rule" which is bound to the javax.jms.MessageListener interface ( by the way you can bind rules both to Classes and Interfaces, do you remember?).

The rule is bound on the onMessage Method and uses an Helper class (com.sample.Utility) passing two parameters (the MessageListener and the Message)


RULE JMS Message Rule

INTERFACE javax.jms.MessageListener

METHOD void onMessage(javax.jms.Message)
HELPER com.sample.Utility
BIND

          messageListener:MessageListener = $0;
          message:Message = $1

IF true

DO readMessage ($0,$1)

ENDRULE

 Notice the usage of the positional parameters ($0, $1) which are used to bind the actual Objects (MessageListener and Message) so that they can be used into the DO readMessage call, although for the purpose of this example, we will just need using the Message parameter.

The Utility class can be used to execute any kind of processing on the JMS message, for example check the content and send a warning message to the system.


public class Utility   {

public void readMessage(MessageListener listener, Message message) throws Throwable {
       
       TextMessage txt = (TextMessage)message;
         try {

            String strMessage = txt.getText();

            // Execute actions
            System.out.println("Message received "+strMessage);
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   }
}

So far we have seen how to use the Utility's readMessage helper class. One way to improve helper class is extending org.jboss.byteman.rule.helper.Helper class so that we can use also all other available utility methods in the base class. For example:


public class Utility extends  org.jboss.byteman.rule.helper.Helper {  
   protected Utility(Rule rule) {
        super(rule);
 
    }

public void readMessage(MessageListener listener, Message message) throws Throwable {
       setTriggering(false);
       
       TextMessage txt = (TextMessage)message;
         try {
            System.out.println(txt.getText());
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   }

}

 

 Notice that we have to provide a constructor for our helper which calls the constructor for the default helper. This is because, whenever a rule is triggered a helper instance is created to handle the rule trigger and, if necessary, to fill field calls with builtin methods. The instance is thrown away after the rule has been executed but it used for all operations during execution. So, the helper provides a context for each firing.

Once that we are extending Byteman rules, we can create more complex constructs like this one, which checks that the MDB that is firing the rule belongs to the com.sample.MyMDB class:

 


RULE JMS Message Rule

INTERFACE javax.jms.MessageListener

METHOD void onMessage(javax.jms.Message)
HELPER com.sample.Utility
BIND

          messageListener:MessageListener = $0;

          message:Message = $1

IF callerCheck("com.sample.MyMDB.*.onMessage", true, true, true, 0, 1)

DO readMessage ($0,$1)

ENDRULE

In this example, we are using the callerCheck method of the Helper class which can be used to check whether the name of any of the selected methods in the stack which called the trigger method matches the supplied regular expression:


public boolean   callerMatches(String regExp, boolean includeClass, boolean includePackage, int startFrame, int frameCount)

 

regExp: matches an expression which will be matched against the name of the method which called the trigger method
includeClass: true if the match should be against the class qualified method name
includePackage: true if the match should be against the package and class qualified method name.
startFrame: identifies the first frame which frame which should be considered.
frameCount: counts the frames which should be checked starting from the first caller.

Follow us on Twitter