Field Init Agent Delegate
Requirements

FieldInitAgentDelegate requires:

  1. com.hapiware.agent.Agent. For more information see General Java Agent.
  2. ASM 3.0 or newer. For more information see http://asm.ow2.org/.

FieldInitAgentDelegate can be used to set initial values for member variables without touching the source code of the target class. This is useful for testing purpouses where some specific initial values may be required instead of the default values. FieldInitAgentDelegate is specified in the agent configuration XML file using /agent/delegate element. For example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.asm.fieldinit.FieldInitAgentDelegate</delegate>
    ...
</agent>

Configuring the field initialisation agent

The field initialisation agent is configured using /agent/configuration/custom element with several elements defining the initialised fields. The configuration XML file looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
    <agent>
        <!--
            variable, delegate, classpath and filter elements as described in the documentation
            of the general Java agent.
        -->
        ...
        <configuration>
            <custom>
                <target-class type="class.name.to.be.Initialised">
                    <target-field name="_fieldName">
                        <initialiser type="class.to.be.Instantiated"> <!-- Can be primitive type also -->
                            <argument type="primitive-type">value</argument>
                            <argument type="class.with.string.Constructor">2</argument>
                            ...
                        </initialiser>
                    </target-field>

                    <target-field name="_date2">
                        <initialiser type="java.util.Date">
                            <argument type="java.lang.String">Sat, 12 Aug 1995 13:30:00 GMT</argument>
                        </initialiser>
                    </target-field>

                    <target-field name="_fieldName2">
                        <initialiser type="primitive-type">
                            <!-- For primitive fields the argument type cannot be defined. -->
                            <argument>314</argument>
                        </initialiser>
                    </target-field>

                    <target-field name="_fieldName3">
                        <initialiser
                            type="class.to.be.Instantiated"
                            factory-class="package.name.FactoryClass"
                            method="staticFactoryMethod"
                        />
                    </target-field>

                    ...
                </target-class>

                <target-class...> ... </target-class>

                <target-class...> ... </target-class>
                ...
            </custom>
        </configuration>
</agent>

In general the configuration XML file defines which classes and which fields of those classes are to be set to configured values or instantiated objects. There are three ways to instantiate an object for setting a field:
  1. Setting a primitive value
  2. Defining the constructor of the initialiser class
  3. Using the factory method

The general structure is that a single target-class element can have several target-field elements which always have a single initialiser element. initialiser elements can have zero to unlimited number of argument elements depending on the case.

Setting a primitive value

Setting a primitive value to a field is simple. Just define a target-class element and target-field element for each of the primitive type fields you want to initialise. In the case of the primitive type target-field can (and must) have only a single argument element which cannot have any attributes (i.e. when initialiser[@type] attribute is a primitive type). Here is an example:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.asm.fieldinit.FieldInitAgentDelegate</delegate>
        <classpath>
        <entry>${project.build.directory}/field-init-agent-delegate-1.0.0.jar</entry>
        <entry>${user.home}/.m2/repository/asm/asm-all/3.3/asm-all-3.3.jar</entry>
    </classpath>
    <filter>
            <include>^com/hapiware/asm/fieldinit/.+</include>
    </filter>
        <configuration>
            <custom>
                <target-class type="com.hapiware.asm.fieldinit.Container">
                    <target-field name="_i">
                        <initialiser type="int">
                            <argument>314</argument>
                        </initialiser>
                    </target-field>
                </target-class>
            </custom>
        </configuration>
</agent>

Defining the constructor

Defining an object using initialiser element does not differ much from defining a primitive value. The major difference is that initialiser can take multiple argument elements. argument elements are used to define a correct constructor for the initialiser class. In this case argument elements must have a type attribute to describe the type of the argument. type attribute can be:

  1. a primitive type (i.e. int, double, boolean, etc.)
  2. any class that accepts a single java.lang.String as a constructor argument like java.lang.String (well, that's obvious), java.lang.Integer and all the other wrapper classes

There are cases when this is not quite enough. For example org.joda.time.DateTime class accecpts a string as constructor parameter but in reality org.joda.time.DateTime does not have a defined constructor for java.lang.String. Instead Joda time uses a generic constructor which accepts a single java.lang.Object as a parameter. To handle this situation argument element accepts cast-to attribute to define a correct constructor. So, if cast-to attribute is defined then it is used to determine the correct constructor call, otherwise type attribute is used.

Here is an example of defining different kind of constructors:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.asm.fieldinit.FieldInitAgentDelegate</delegate>
        <classpath>
        <entry>${project.build.directory}/field-init-agent-delegate-1.0.0.jar</entry>
        <entry>${user.home}/.m2/repository/asm/asm-all/3.3/asm-all-3.3.jar</entry>
    </classpath>
    <filter>
            <include>^com/hapiware/asm/fieldinit/.+</include>
    </filter>
        <configuration>
            <custom>
                <target-class type="com.hapiware.asm.fieldinit.Container">
                    <target-field name="_date">
                        <initialiser type="java.util.Date">
                            <argument type="int">111</argument>
                            <argument type="int">2</argument>
                            <argument type="int">16</argument>
                        </initialiser>
                    </target-field>
                    <target-field name="_date2">
                        <initialiser type="java.util.Date">
                            <argument type="java.lang.String">Sat, 12 Aug 1995 13:30:00 GMT</argument>
                        </initialiser>
                    </target-field>
                    <target-field name="_address">
                        <initialiser type="com.hapiware.asm.fieldinit.Address">
                            <argument type="java.lang.String" cast-to="java.lang.Object">Street,12345,City</argument>
                        </initialiser>
                    </target-field>
                </target-class>
            </custom>
        </configuration>
</agent>

Using the factory method

Factory method is to be used when the creation of the initialisation object cannot be done with just using the initialiser element. Creating an object with a factory method is very simple. This is done by defining a target-class element and target-field element for each of the fields you want to initialise. Each target-field accepts a single initialser element which needs the following attributes:

  • type which is the type of the field to be initialised
  • factory-class a class which contains the static factory method
  • method a static method which creates the object to be used for the field initialisation. Notice also that the factory method must not take any arguments.

initialiser element does not accept any argument elements when factories are used.

NOTICE!
Remember to add your factory class to the classpath of the target JVM (i.e. to that JVM where you define -javaagent switch). Here is an example of using a factory method:

<?xml version="1.0" encoding="UTF-8" ?>
<agent>
    <delegate>com.hapiware.asm.fieldinit.FieldInitAgentDelegate</delegate>
        <classpath>
        <entry>${project.build.directory}/field-init-agent-delegate-1.0.0.jar</entry>
        <entry>${user.home}/.m2/repository/asm/asm-all/3.3/asm-all-3.3.jar</entry>
    </classpath>
    <filter>
            <include>^com/hapiware/asm/fieldinit/.+</include>
    </filter>
        <configuration>
            <custom>
                <target-class type="com.hapiware.asm.fieldinit.Container">
                    <target-field name="_address">
                        <initialiser
                            type="com.hapiware.asm.fieldinit.Address"
                            factory-class="com.hapiware.asm.fieldinit.Factory"
                            method="createAddress"
                        />
                    </target-field>
                </target-class>
            </custom>
        </configuration>
</agent>

Configuration elements

Here is a list of configuration elements and their attributes:

  1. target-class is a main level element which defines the class to be initialised
    • type the class name (e.g. com.hapiware.asm.fieldinit.Container)
  2. target-field defines the field to be initialised
    • name the name of the field (e.g. _address)
  3. initialiser defines an initialiser for the defined target-field (see Defining the constructor)
    • type the type of object to be instantiated (e.g. java.lang.Date)
    • factory-class the factory class (e.g. om.hapiware.asm.fieldinit.Factory)
    • method a static factory method name (e.g. createDate)
  4. argument defines the arguments and possibly types for the constructor defined in initialiser (see Defining the constructor)
    • type a primitive type or a class which is instantiated using a constructor which takes a single string argument (e.g. int or java.lang.String)
    • cast-to optinal class to define a proper constructor

Download

Download from Java Agent Tools page.

License

MIT License

Copyright (c) 2011 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