ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Java Extreme Programming Cookbook

Cooking with Java XP, Part 3

Related Reading

Java Extreme Programming Cookbook
By Eric M. Burke, Brian M. Coyner

by Eric M. Burke and Brian M. Coyner

Editor's note: In the previous excerpt from Java Extreme Programming Cookbook we showed you how to create a mock implementation of an event listener interface, and how to avoid duplicated validation logic in your tests. This week we offer a sampling from Chapter 8 ("JUnitPerf") on creating a load test, and one from Chapter 9 ("XDoclet") on executing a custom template.

8.4 Creating a LoadTest

Problem

You need to make sure that code executes correctly under varying load conditions, such as a large number of concurrent users.

Solution

Decorate an existing JUnit Test with a JUnitPerf LoadTest.

Discussion

A JUnitPerf LoadTest decorates an existing JUnit test to simulate a given number of concurrent users, in which each user may execute the test one or more times. By default, each simulated user executes the test once. For more flexibility, a load test may use a com.clarkware.junitperf.Timer to ramp up the number of concurrent users during test execution. JUnitPerf provides a ConstantTimer and RandomTimer to simulate delays between user requests. By default all threads are started at the same time by constructing a ConstantTimer with a delay of zero milliseconds.

TIP: If you need to simulate unique user information, each test must randomly choose a different user ID (for example). This can be accomplished using JUnit's setUp() method.

Here is an example that constructs a LoadTest with 100 simultaneous users:

public static Test suite(  ) {
    Test testCase = new TestSearchModel("testAsynchronousSearch");
    Test loadTest = new LoadTest(testCase, 100);
    TestSuite suite = new TestSuite(  );
    suite.addTest(loadTest);
    return suite;
}

Here is an example that constructs a LoadTest with 100 simultaneous users, in which each user executes the test 10 times:

public static Test suite(  ) {
    Test testCase = new TestSearchModel("testAsynchronousSearch");
    Test loadTest = new LoadTest(testCase, 100, 10);
    TestSuite suite = new TestSuite(  );
    suite.addTest(loadTest);
    return suite;
}

And here is an example that constructs a LoadTest with 100 users, in which each user executes the test 10 times, and each user starts at a random interval:

public static Test suite(  ) {
    Test testCase = new TestSearchModel("testAsynchronousSearch");
    Timer timer = new RandomTimer(1000, 500);
    Test loadTest = new LoadTest(testCase, 100, 10, timer);
    TestSuite suite = new TestSuite(  );
    suite.addTest(loadTest);
    return suite;
}

The Timer interface defines a single method, getDelay( ), that returns the time in milliseconds-to-wait until the next thread starts executing. The example above constructs a RandomTimer with a delay of 1,000 milliseconds (1 second), with a variation of 500 milliseconds (half a second). This means that a new user is added every one to one and a half seconds.

NOTE: Be careful when creating timers that wait long periods of time between starting new threads. The longer the wait period, the longer it takes for the test to complete, which may or may not be desirable. If you need to test this type of behavior, you may want to set up a suite of tests that run automatically (perhaps at night).

There are commercial tools available for this type of performance test, but typically they are hard to use. JUnitPerf is simple and elegant, and any developer that knows how to write a JUnit test can sit down and write complex performance tests.

Example 8-2 shows how to create a JUnitPerf load test. The use of the public static Test suite( ) method proves invaluable for integrating JUnitPerf tests into an Ant buildfile.

Example 8-2. JUnitPerf LoadTest

package com.oreilly.javaxp.junitperf;

import junit.framework.Test;
import junit.framework.TestSuite;
import com.clarkware.junitperf.TimedTest;

public class TestPerfSearchModel {

    public static Test suite(  ) {
        Test testCase = new TestSearchModel("testAsynchronousSearch");
        Test loadTest = new LoadTest(testCase, 
                                     100, 
                                     new RandomTimer(1000, 500));
        TestSuite suite = new TestSuite(  );
        suite.addTest(loadTest);
        return suite;
    }

    public static void main(String args[]) {
        junit.textui.TestRunner.run(suite(  ));
    }
}

9.8 Creating and Executing a Custom Template

Problem

You want to write and execute a custom XDoclet template file (.xdt).

Solution

Create an .xdt file that contains the necessary XDoclet template tags to generate the desired output. Finally, update your Ant buildfile to execute the template subtask via the xdoclet.DocletTask task.

Discussion

Using XDoclet to generate EJB files is fairly straightforward because the templates are already written. The challenge occurs when you want to create a custom template to generate a specific file. Knowing where to look up information on custom templates and deciphering that information can be a difficult task.

XDoclet templates are at the core of XDoclet's extensibility, and XDoclet provides a plethora of built-in template tags for us to use. Template tags control how and what information is generated. Template tags are broken down into two categories, block and content.

Block

Block tags are used for iterating and performing logic, which is synonymous with for loops and if statements. The snippet below shows how to iterate through all classes using the built-in Class template tag:

<XDtClass:forAllClasses>    
</XDtClass:forAllClasses>

The next snippet shows how to check if a method contains a specific @ tag using the built-in Method template tag. In this example we are checking for "deprecated":

<XDtMethod:ifHasMethodTag tagName="deprecated">    
</XDtMethod:ifHasMethodTag>

Content

Content tags are used for outputting information. These tags are synonymous with getter methods that return a string. Content tags never contain nested information. The snippet below shows how to output the current method name using the built-in Method template tag.

<XDtMethod:methodName/>

A template tag is very similar to an XML tag. The first part of the tag represents the namespace. For example, XDtMethod is a namespace. The part directly after the namespace represents the tag name. For example, methodName is a tag name. By convention all namespaces begin with "XDt". This prefix is not directly part of the namespace and is stripped off by XDoclet when the template is being parsed.

Now, let's delve into creating a custom template. Example 9-9 shows a custom template used to generate a code-deprecation report. The custom template uses template tags defined by XDoclet. The first step is to create a new template file and add it to your project. This recipe creates a new template file called deprecation-report.xdt and places it in the resources directory. This template generates a report of all classes and methods marked as deprecated. Take note that the tagName attribute omits the @ character.

Example 9-9. deprecation-report.xdt template file

Deprecated Classes and Methods
------------------------------
<XDtClass:forAllClasses>
  +++ <XDtClass:fullClassName/>
  <XDtClass:ifHasClassTag tagName="deprecated">
      WARNING: This class is deprecated.
      NOTE: <XDtClass:classTagValue tagName="deprecated"/>
  </XDtClass:ifHasClassTag>

  <XDtClass:ifDoesntHaveClassTag tagName="deprecated">
      DEPRECATED METHODS
      ------------------
    <XDtMethod:forAllMethods>
      <XDtMethod:ifHasMethodTag tagName="deprecated">
        METHOD: <XDtMethod:methodName/>(<XDtParameter:parameterList/>)
          NOTE: <XDtMethod:methodTagValue tagName="deprecated"/>
      </XDtMethod:ifHasMethodTag>
    </XDtMethod:forAllMethods>
  </XDtClass:ifDoesntHaveClassTag>
</XDtClass:forAllClasses>

Example 9-10 shows an updated Ant buildfile that executes the new template.

Example 9-10. Executing a custom template

<target name="deprecation.report"
    description="Generates a Deprecation Report."
    depends="prepare">

  <taskdef name="deprecateddoclet" classname="xdoclet.DocletTask">
    <classpath>
      <pathelement path="${env.XDOCLET_HOME}/lib/xdoclet.jar"/>
      <pathelement path="${env.XDOCLET_HOME}/lib/xjavadoc.jar"/>
      <pathelement location="${dir.lib}/commons-logging-1.0.jar"/>
    </classpath>
  </taskdef>

  <deprecateddoclet
      destdir="${dir.build}">

    <fileset dir="${dir.src}">
      <include name="**/deprecation/"/>
    </fileset>

    <template
        templateFile="${dir.build}/${deprecation.template}"
        destinationFile="deprecation-report.txt"/>
  </deprecateddoclet>
</target>

The first step is to set up a task definition called deprecateddoclet for the class xdoclet.DocletTask. The DocletTask is the base class for all Ant XDoclet tasks. This class can be used directly to execute a custom template file when a subclass is not required.

The fileset specifies which files should be included or excluded from the generation process.

Finally, the template subtask specifies which template file to use and the name of the file to generate. This subtask is used to apply generic templates (in this example it is the template file deprecation-report.xdt) to produce any type of text output.

Here is what an example report might look like:

Deprecated Classes and Methods
------------------------------

  +++ com.oreilly.javaxp.xdoclet.deprecation.Employee

      DEPRECATED METHODS
      ------------------
        METHOD: getEmployeeId(  )
          NOTE: use {@link #getId}.
        METHOD: setEmployeeId(long)
          NOTE: use {@link #setId}.

  +++ com.oreilly.javaxp.xdoclet.deprecation.Person
      WARNING: This class is deprecated.
      NOTE: No replacement for this class.

See Also

The tags used in this recipe only scratch the surface of what XDoclet provides. For a complete listing of all XDoclet template tags, see http://xdoclet.sourceforge.net/1.2beta/templates/index.html.

Eric M. Burke is an O'Reilly author and a principal software engineer with Object Computing, Inc. in St. Louis, MO.

Brian M. Coyner is coauthor of the Java Extreme Programming Cookbook and a Senior Software Engineer with Object Computing, Inc. in St. Louis, Missouri.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.