General Java Agent

Agent is a generic solution to greatly simplify the agent programming (see java.lang.instrument package description for more information about agents in general).

The main idea is to have a totally separated environment for running agents. This means that the agent uses its own namespace (i.e. class loader) for its classes. With Agent a programmer can avoid .jar file version conflicts. That's why the Agent configuration file has its own classpath element(s). Another advantage is that the XML configuration file is always similar for different agents. And yet one more advantage is that the programmer does not need to care about the manifest attributes mentioned in java.lang.instrument package description. They are already handled for the programmer.

Using Agent

Agent is specified by using Java -javaagent switch like this:

-javaagent:agent-jarpath=path-to-xml-config-file

For example:

-javaagent:/users/me/agent/target/agent-1.0.0.jar=/users/me/agent/agent-config.xml

Configuration file

The configuration file is an XML file and has the <agent> as its root element. <agent> has the following childs:

  • <variable>, this is an optional element for simplifying the configuration
  • <delegate>, this is a mandatory element to define the agent delegate
  • <classpath>, this is a mandatory element and has at minimum of one (1) <entry> child element.
  • <filter>, this is an optional element and can have zero (0) <include> and/or <exclude> child elements. Filters are regular expressions patterns to include or exclude classes to be instrumented.
  • <configuration>, which is an optional element is used to configure agent delegate. See /agent/configuration element

So, in general a configuration XML file looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <variable />
    <variable />
    ...
    <delegate />
    <classpath>
        <entry />
        <entry />
        ...
    </classpath>
    <filter>
        <include />
        <include />
        <exclude />
        <exclude />
        ...    
    </filter>    

    <configuration>
        <!--
            This can be text, a predefined structure or
            programmer's own structure
        -->
    </configuration>
</agent>

/agent/variable element

The /agent/variable element is optional and it is supposed to be used to simplify the configuration file. Variables can be anywhere under agent element (i.e. they need not to be in the beginning of the configuration file).

The /agent/variable element must have (only) name attribute which is used as a reference in other parts of the configuration file. The name reference is replaced by the value of the /agent/variable element. The variable is referenced with the following pattern:

${VARIABLE}

where:

  • VARIABLE is the name attribute of the /agent/variable element

Here is a simple example where every ${repo-path} variable reference is replaced with /users/me/.m2/repository string:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <variable name="repo-path">/users/me/.m2/repository</variable>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
        <entry>${repo-path}/asm/asm/3.1/asm-3.1.jar</entry>
        <entry>${repo-path}/asm/asm-commons/3.1/asm-commons-3.1.jar</entry>
        <entry>${repo-path}/asm/asm-util/3.1/asm-util-3.1.jar</entry>
        <entry>${repo-path}/asm/asm-tree/3.1/asm-tree-3.1.jar</entry>
    </classpath>
    <configuration>...</configuration>
</agent>

Variables can be used more creatively if there is a need for that. This example produces exactly the same result than the example above but the use of variables are more complex:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <variable name="a">repo</variable>
    <variable name="b">path</variable>
    <variable name="c">ju</variable>
    <variable name="juuri">roo</variable>
    <variable name="${${c}uri}t">users</variable>
    <variable name="${a}-${b}">/${root}/me/.m2/repository</variable>
    <variable name="asm-package">asm</variable>
    <variable name="${asm-package}-version">3.1</variable>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
        <entry>${repo-path}/${asm-package}/asm/${asm-version}/asm-${asm-version}.jar</entry>
        <entry>${repo-path}/${asm-package}/asm-commons/${asm-version}/asm-commons-${asm-version}.jar</entry>
        <entry>${repo-path}/${asm-package}/asm-util/${asm-version}/asm-util-${asm-version}.jar</entry>
        <entry>${repo-path}/${asm-package}/asm-tree/${asm-version}/asm-tree-${asm-version}.jar</entry>
    </classpath>
    <configuration>...</configuration>
</agent>

/agent/delegate element

The /agent/delegate element is mandatory and its value is the name of the delegate class as a fully qualified name (e.g. com.hapiware.asm.TimeMachineAgentDelegate).

The agent delegate class must have the following method (with the exact signature):

<pre>
    public static void premain(
        java.util.regex.Pattern[] includePatterns,
        java.util.regex.Pattern[] excludePatterns,
        Object config,
        Instrumentation instrumentation
    )
</pre>

where:

  • java.util.regex.Pattern[] includePatterns has a list of regular expression patterns to be used to include classes for instrumentation. See /agent/filter
  • java.util.regex.Pattern[] excludePatterns has a list of regular expression patterns to be used to set classes not to be instrumented. See /agent/filter
  • Object config is the configuration object based on the /agent/configuration element.
  • Instrumentation instrumentation has services to provide the instrumentation.

This static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method can do all the same things as defined for static void premain(String, Instrumentation) method in the java.lang.instrument package description.

/agent/classpath element

The /agent/classpath element is mandatory and is used to define the classpath for the agent delegate class. This means that there is no need to put any of the used libraries for the agent delegate class in to your environment classpath.

The /agent/classpath element must have at least one <entry> child element but can have several. The only required classpath entry is the delegate agent (.jar file) itself. However, usually there are other classpath entries for the libraries needed by the delegate agent. Here is an example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.agent.TimeMachineAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/timemachine-delegate-1.0.0.jar</entry>
        <entry>/usr/local/asm-3.1/lib/all/all-asm-3.1.jar</entry>
    </classpath>
    <configuration>...</configuration>
</agent>

/agent/filter element

The /agent/filter is optional and is used to filter classes to be instrumented.

The /agent/filter element can have several include and/or exclude elements but can have also none of them. Here is an example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
    </classpath>
    <filter>
        <include>^com/hapiware/.*f[oi]x/.+</include>
        <include>^com/mysoft/.+</include>
        <exclude>^com/hapiware/.+/CreateCalculationForm</exclude>
    </filter>
    <configuration>...</configuration>
</agent>

<include> element

<include> element can be used for matching the possible candidates for instrumentation. If none is defined then one pattern containing ".+" is assumed as a default value. <include> element is a normal Java regular expression.

Notice that the class names are presented in the internal form of fully qualified class
names as defined in The Java Virtual Machine Specification (e.g. "java/util/List"). So, when you create <include> and <exclude> elements, remember that package names are separated with slash (/) instead of period (.).

<exclude> element

<exclude> can be used to ensure that the instrumentation is not done for some classes. <exclude> element is a normal Java regular expression.

Notice that the class names are presented in the internal form of fully qualified class
names as defined in The Java Virtual Machine Specification (e.g. "java/util/List"). So, when you create <include> and <exclude> elements, remember that package names are separated with slash (/) instead of period (.).

/agent/configuration/ element

The /agent/configuration/ element is optional and has all the necessary configuration information for the agent delegate class. The exact structure can depend on the programmer but there are some predefined structures as well. The configuration object is delivered to the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument.

All the possible options for configuration object creation are:

  1. null
  2. String
  3. List<String>
  4. Map<String, String>
  5. User defined configuration object

null

If the /agent/configuration/ element is not defined at all then null is delivered to the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument. For example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
    </classpath>
</agent>

which sends null to the MyAgentDelegate.premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument.

String

If the /agent/configuration/ element has only pure text (i.e. String), the text string is delivered to the delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument. For example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
    </classpath>
    <configuration>Show me!</configuration>
</agent>

which sends "Show me!" to the MyAgentDelegate.premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument.

List<String>

If the /agent/configuration/ element has <item> child elements without an attribute then the List<String> is created which is in turn delivered to the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument. For example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
    </classpath>
    <configuration>
        <item>One</item>
        <item>Two</item>
        <item>Three</item>
    </configuration>
</agent>

which sends List<String> {"One", "Two", "Three"} to the MyAgentDelegate.premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument.

Map<String, String>

If the /agent/configuration/ element has <item> child elements with a key attribute then the Map<String, String> is created which is in turn delivered to the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument. For example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.test.MyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/my-delegate-1.0.0.jar</entry>
    </classpath>
    <configuration>
        <item key="1">One</item>
        <item key="2">Two</item>
        <item key="3">Three</item>
    </configuration>
</agent>

which sends Map<String, String> {{"1", "One"}, {"2", "Two"}, {"3", "Three"}} to the MyAgentDelegate.premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument.

User defined configuration object

If the /agent/configuration/ element has the custom child element defined, then public static Object unmarshall(org.w3c.dom.Element configElement) method must be defined to the agent delegate class in addition to premain() method. The unmarshall() method is called with the /agent/configuration/custom element as an argument. The system then delivers the returned Object directly to the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method as an Object argument. This approach makes it possible to create different configuration structures for the agent delegate's static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method very flexibly.

public static Object unmarshall(org.w3c.dom.Element configElement) is assumed to return a programmer's own configuration object. Here is an example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.agent.FancyAgentDelegate</delegate>
    <classpath>
        <entry>/users/me/agent/target/fancy-delegate-1.0.0.jar</entry>
        <entry>/usr/local/asm-3.1/lib/all/all-asm-3.1.jar</entry>
    </classpath>
    <filter>
        <include>^com/hapiware/.*f[oi]x/.+</include>
        <include>^com/mysoft/.+</include>
        <exclude>^com/hapiware/.+/CreateCalculationForm</exclude>
    </filter>
    <configuration>
        <custom>
            <message>Hello World!</message>
            <date>2010-3-13</date>
        </custom>
    </configuration>
</agent>

This assumes that com.hapiware.asm.FancyAgentDelegate class has public static Object unmarshall(org.w3c.dom.Element configElement) method defined to handle <message> and <date> elements from the <configuration/custom> element. It is also assumed that the Object the unmarshall() method returns can be properly handled (and type casted) in the static void premain(java.util.regex.Pattern[], java.util.regex.Pattern[], Object, Instrumentation) method.

Requirements

  • Java 5 or later

Download

Download from Java Agent Tools page.

License

MIT License

Copyright (c) 2010 Hapi, http://www.hapiware.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License