Previous Topic: Java Agent ExtensionsNext Topic: Adding Tags to Transaction Frames


Extending the Agent

You can customize the agent behavior by writing Java classes that implement the com.itko.lisa.remote.transactions.interceptors.IInterceptor interface or extend the com.itko.lisa.remote.transactions.interceptors.AbstractInterceptor class. For more information, see the JavaDocs in the doc folder of your installation directory.

public interface IInterceptor {
    /**
     * Whether this custom interceptor is currently disabled
     */
    public boolean isDisabled();
 
    /**
     * Returns whether the interception should return (true) or proceed (false)
     */
    public boolean block(boolean wayIn, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret);
 
    /**
     * Called after method entry to possibly modify the current frame based on interceptor logic.
     */
    public boolean preProcess(TransactionFrame frame, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret);
 
    /**
     * Called before method exit to possibly modify the current frame based on interceptor logic
     */
    public boolean postProcess(TransactionFrame frame, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret);
}

If you want to stop data from being captured, overwrite the block() method. This technique is similar to adding the exclude directive to the rules.xml file, but provides more flexibility.

If you want the agent to perform logic immediately before it captures a method, overwrite the preProcess() method.

If you want the agent to perform logic immediately after it captures a method, overwrite the postProcess() method.

These methods are automatically invoked for all classes and methods that have been intercepted or tracked. To intercept a method or track a class, you can specify it using the documented syntax in the rules.xml file. You can also programmatically specify the interception in the constructor of the extension class. The advantage of the latter approach is that the extension is self-contained.

The first argument to the block() method is boolean wayIn. The block() method is called twice per method: once on entry, once on exit. When the entry call is made, the value of the wayIn argument is true. When the exit call is made, the value of the wayIn argument is false.

The following table describes the arguments that are common to the block(), preProcess(), and postProcess() methods:

Argument

Description

Object src

The object that the method is being called on.

String spec

The class or interface name that was specified to instrument the API.

String clazz

The name of the class that defines the intercepted method.

String method

The name of the intercepted method.

String signature

The signature (in JVM format) of the intercepted method.

Object[] args

The arguments being passed to the intercepted method.

Object ret

The return value of the intercepted method. When the wayIn argument is true, the return value is null.

The difference between the src, spec, and clazz arguments can be explained with an example.

Assume that you have the following interface and class definitions:

public interface A {
     void m();
}
 
public class B implements A {
     void m() {}
}
 
public class C extends B {
}

If the rules specify intercept("A", "m", "*") and the code calls C.m(), then the following information is true:

 

Deployment

To deploy an extension, compile it and package it in a JAR file with a manifest containing the following entry:

Agent-Extension: extension class name 

When you drop this JAR in the Agent JAR directory, the agent automatically picks it up. If you add the file after the agent has started, a hot load mechanism helps to ensure that the file is applied. If you update the extension JAR as the agent is running, the JAR classes are reloaded dynamically. Dynamic reloading makes it easy and fast to test your extension code without restarting the server.

The extension JARs get loaded by a classloader that can see all the classes that are used in the extension class. You can compile your extension source against LisaAgent.jar and all container JARs that define classes you want to use. You do not need to use reflection in your extension.

 

Examples

The following example uses the block() method to prevent the agent from capturing any method whose thread name starts with Event Sink Thread Pool.

public class MyInterceptor extends AbstractInterceptor {
 
   /** Returns true if this call should not be intercepted */
   public boolean block(boolean wayIn, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret) {
      if (Thread.currentThread().getName().startsWith("Event Sink Thread Pool")) {
         return true;
      }
      return super.block(wayIn, src, spec, clazz, method, signature, args, ret);
   }
 
   ...
 
}

The following example uses the postProcess() method to capture data for the Response row in the Transactions window. This example calls the setResponse() method of the com.itko.lisa.remote.transactions.TransactionFrame class. For more information, see the JavaDocs in the doc folder of your installation directory.

public class MyInterceptor extends AbstractInterceptor {
 
   ...
 
   public boolean postProcess(TransactionFrame frame, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret) {
      if (clazz.equals("com.itko.lisa.training.NotCaptured") && method.equals("doRequest")) {
         frame.setResponse((String) ret);
      }
      return super.postProcess(frame, src, spec, clazz, method, signature, args, ret);
   }

The following example shows how to print the XML contents of a WebMethods com.wm.data.IData object as it gets invoked in a flow of Integration Server. The agent already supports WebMethods, so an extension is not necessary for it.

package com.itko.lisa.ext;
 
 import java.io.ByteArrayOutputStream;
 import com.itko.lisa.remote.transactions.interceptors.AbstractInterceptor;
 import com.itko.lisa.remote.transactions.TransactionFrame;
 import com.wm.app.b2b.server.ServiceManager;
 import com.wm.app.b2b.server.BaseService;
 import com.wm.data.IData;
 import com.wm.util.coder.IDataXMLCoder;
 
 public class IDataInterceptor extends AbstractInterceptor {
 
 public IDataInterceptor() {
     super.intercept("com.wm.app.b2b.server.ServiceManager", "invoke", "(Lcom/wm/app/b2b/server/BaseService;Lcom/wm/data/IData;Z)Lcom/wm/data/IData;");
 }
 
 public boolean preProcess(TransactionFrame frame, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret) {
     if (ServiceManager.class.getName().equals(clazz) && "invoke".equals(method)) {
         doCustomLogic((BaseService) args\[0\], (IData) args\[1\], false);
     }
 
     return super.preProcess(frame, src, spec, clazz, method, signature, args, ret);
 }
 
 public boolean postProcess(TransactionFrame frame, Object src, String spec, String clazz, String method, String signature, Object[] args, Object ret) {
     if (ServiceManager.class.getName().equals(clazz) && "invoke".equals(method)) {
         doCustomLogic((BaseService) args\[0\], (IData) ret, true);
     }
 
     return super.postProcess(frame, src, spec, clazz, method, signature, args, ret);
 }
 
 private void doCustomLogic(BaseService flow, IData p, boolean output) {
     ByteArrayOutputStream baos = new ByteArrayOutputStream(63);
 
     try {
         new IDataXMLCoder().encode(baos, p);
     } catch (IOException e) {
         e.printStackTrace();
     }
 
     System.out.println("Flow: " + flow.getNSName());
     System.out.println((output ? "Output" : " Input") + "Pipeline: " + baos);
 
 }
 }

To build this extension yourself, compile this code against LisaAgent.jar, wm-isclient.jar, and wm-isserver.jar then JAR the class file with a manifest containing the following line:

Agent-Extension: com.itko.lisa.ext.IDataInterceptor