This tutorial introduces the Byteman framework which is part of the JBoss Ecosystem, showing how it can be used to increase your productivity.
What is Byteman?
Byteman is a tracing, debugging and testing tool which you can use in any Java application. You might wonder why do we need to learn another tool for tracing and debugging applications ?
Let me tell you with a simple example. Supposing that you are hitting a NPE in your Web application. The first and most obvious fix would be either starting a remote debugging session (which may not be always possible) or add some logs in the critical points. In most cases you will resort to modify your code, deploy you app and, once fixed the issue, disable or remove the logs.
Here’s what you can do with Byteman: inject a rule into your code –without any change in your code– in which you can directly refer to app/runtime data & types. In other words you can write a simple rule which says:
WHEN YOU ENTER THE Dummy SERVLET
THEN DUMP THE HTTPREQUEST
The rule will inject dynamically the code in your application (in this case into the application server) and execute the rule. Once you don’t need it any more, just tell to the JVM that you don’t need anymore firing ByteMan rules.
A rule file is built-on the following skeleton:
RULE <rule name> CLASS <class name> METHOD <method name> BIND <bindings> IF <condition> DO <actions> ENDRULE
The RULE keyword identifies the rule name (Rule names do not have to be unique but it obviously helps when debugging rule scripts if they clearly identify the rule).
The CLASS can identify a class either with or without the package qualification.
The METHOD name can identify a method with or without an argument list or return type.
The BIND specification ca be used to bind application variable into rule variables which can subsequently be referenced in the rule body.
The IF section can be ovbiously used to check Rule conditions
The DO section is the Rule action which can be a rule expression, return values or throw action
Setting up
Now let’s get our hands dirty. Firstly Download Byteman. Next, unpack it in a folder.
Then, export the root folder in the variable BYTEMAN_HOME. For example:
export BYTEMAN_HOME=/home/jboss/byteman-download-4.0.18
In order to start Byteman you need to provide the path to Byteman libs to the -javaagent option, specifying as well the path to the rule file. For example on windows you would code (You need to define BYTEMAN_HOME as system environment variable):
java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:example.btm com.sample.BytemanTest
On Linux
java -javaagent:${BYTEMAN_HOME}/lib/byteman.jar=script:example.btm com.sample.BytemnTest
Testing Byteman
Let’s try an HelloWorld example:
package com.sample; public class SimpleTest { public static void main(String[] args) { System.out.println("Entering main"); } }
And here’s a simple rule, name it example.btm:
RULE trace main entry CLASS com.sample.SimpleTest METHOD main AT ENTRY IF true DO traceln("Byteman detected you are entering main") ENDRULE
As you might guess, here we are defining a rule named “trace main entry” which is fired on main method of the class com.sample.SimpleTest, as soon as the method is entered. In all conditions (IF true) a trace line is printed on the console.
To run the SimpleTest Class with Byteman Rule, add the javaagent option pointing to the byteman.jar and the Rule File:
java -javaagent:$BYTEMAN_HOME/lib/byteman.jar=script:example.btm com.sample.SimpleTest
On a Windows machine:
java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:example.btm com.sample.SimpleTest
The expected outcome is:
Byteman detected you are entering main Entering main
Here’s the Eclipse launch configuration for it.
Running Byteman with WildFly
Now let’s see how you can run a Byteman rule when WildFly triggers the execution of some classes. At first, you need to activate Byteman on the JVM startup options (for example on WildFly just add this to standalone.conf.bat)
JAVA_OPTS="$JAVA_OPTS -javaagent:/$BYTEMAN_HOME/lib/byteman.jar=script:example.btm"
The above configuration expects to find the file example.btm in the same folder where you have started WildFly (typically $JBOSS_HOME/bin)
Then, let’s write an example.btm rule file (as stated in the startup options) which will dump on the console the com.sample.ByteServlet’s query String:
RULE trace Servlet CLASS com.sample.ByteServlet METHOD doGet AT ENTRY BIND request = $request; IF true DO traceln ("Query String " + request.getQueryString()) ENDRULE
As you can see, this simple rule does one more step compared with the first rule: it binds the doGet request parameter into the Rule’s request variable. In the rule action the request’s query string is printed.
Define any com.sample.ByteServlet Servlet and invoke it by passing some parameters (/ByteServlet?param1=boo¶m2=foo )
As you can see, as soon as the Servlet is invoked you should see the Query String printed on the Console.
A more complex Rule
Now, let’s write a slightly more complex rule:
RULE trace Servlet CLASS com.sample.ByteServlet METHOD doGet HELPER com.sample.Utility AT ENTRY BIND request = $request; IF true DO trace ($request) ENDRULE
This rule includes an HELPER class which means that all the rules in the script will use this class in the Rule actions.
A rule’s helper class is used to resolve built-in calls like the trace() method of the com.sample.Utility class, which merely does some request snooping:
package com.sample; import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; public class Utility { public void trace(HttpServletRequest request) { System.out.println("Called trace!"); Enumeration e = request.getSession().getAttributeNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); System.out.println(name + ": " + request.getSession().getAttribute(name)); } System.out.println("Protocol: " + request.getProtocol()); System.out.println("Scheme: " + request.getScheme()); System.out.println("Server Port: " + request.getServerPort()); System.out.println("Remote Addr: " + request.getRemoteAddr()); System.out.println("Remote Host: " + request.getRemoteHost()); System.out.println("Content Length: " + request.getContentLength()); System.out.close(); } }
In this example, as soon as the doGet of the ByteServlet is invoked, the Uility’s trace method is invoked, which receives as parameter the HttpServletRerquest. All this, has been done without one single change into the ByteServlet (!). Cool isn’t it ?