Class Agent


  • public class Agent
    extends Object
    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 value can contain variable references which will be resolved in this order :

    • Variable defined previously
    • System property
    • Environment variable
    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):

                    public static void premain(
                            java.util.regex.Pattern[] includePatterns,
                            java.util.regex.Pattern[] excludePatterns,
                            Object config,
                            Instrumentation instrumentation
                    )
     
    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.
    Author:
    hapi
    See Also:
    java.lang.instrument
    • Constructor Detail

      • Agent

        public Agent()
    • Method Detail

      • premain

        public static void premain​(String agentArgs,
                                   Instrumentation instrumentation)
        This method is called before the main method call right after the JVM initialisation.

        Notice that this method follows the fail fast idiom and thus throws a runtime exception if there is something wrong in the configuration file.

        Parameters:
        agentArgs - Same string which was given to -javaagent as options (see the class description).
        instrumentation - See java.lang.instrument.Instrumentation
        Throws:
        com.hapiware.agent.Agent.ConfigurationError - If there is something wrong with the configuration file.
        See Also:
        java.lang.instrument