How to write JMeter tests for your GWT-RPC servlet

I wrote this post more than a year ago and apparently never finished it and published it.  So I’m just publishing it as is.  It may not be done, but it may be useful to someone…

I work on a pretty sizable GWT project that we would like to be able to load test on a regular basis using JMeter.  The client uses GWT-RPC for communication to the server. GWT-RPC is just a servlet that uses the HTTP protocol.  It’s pretty easy to inspect the payload of a request packet and replay that with JMeter, but there are a few problems with that approach.  The first is that the encoding of the request and response are unique and somewhat opaque.  It’s not that easy to create tests with fuzzed data.  I found this post on the GWT-RPC protocol, and it was helpful, but it also told me that wasn’t the way I wanted to go.  The second problem is that the GWT-RPC protocol includes hash values that reflect the state of the code when it was compiled.  It seems that I could create some recorded tests, and they would work, until I modified the code such that the hash changed, at which point I would have to update all of my requests.

While looking for possible approaches I came across this post that describes a method of creating JMeter tests for GWT-RPC using Java for the client.  The post is light on specifics, but it pointed me in the right direction.  I’m happy with what I ended up with.  This post describes in detail how to make it work.

1) Make sure you have a GWT project

For this howto I’m using the basic project that’s created by default.  Specifically I used the Maven GWT plugin to generate the project using an archetype.

2) Create a project for the JMeter tests written in Java

You’re going to end up with two projects for the JMeter tests.  I like to create them as modules of a parent.  To make things easy I’m also going to make the GWT project a module so all three build together with one command.  I do this by starting with a project directory with just a pom.  The pom describes the parent and has packaging “pom”.

Here’s my initial parent pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>jmeter-demo</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <name>jmeter-demo</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <modules>
  </modules>

</project>

Now inside of the directory with the parent pom I create a new quickstart project.  Maven will automatically create the project as a module of the parent.

mvn archetype:generate -DgroupId=com.example -DartifactId=jmeter-demo-java -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

3) Build a JAR from your GWT project

I’m not 100% sure this is the only solution, but the JMeter Java project is going to depend on your GWT project as a Java component.  My thinking is that means I need to tell Maven to package it as a jar.  You can do this one of two ways: 1) you can split the GWT project in two, one that generates a jar and another that consumes the jar and generates a war; 2) you can configure your GWT project to build both a jar and a war.  The first solution is more the maven way, but I didn’t want to go through that effort on our big project, so instead I added a profile that would build a jar.  Here’s the xml I added:

	<profiles>
		<profile>
			<id>jar</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-jar-plugin</artifactId>
						<executions>
							<execution>
								<id>make-a-jar</id>
								<phase>compile</phase>
								<goals>
									<goal>jar</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-install-plugin</artifactId>
						<executions>
							<execution>
								<phase>install</phase>
								<goals>
									<goal>install-file</goal>
								</goals>
								<configuration>
									<packaging>jar</packaging>
									<artifactId>${project.artifactId}</artifactId>
									<groupId>${project.groupId}</groupId>
									<version>${project.version}</version>
									<file>
										${project.build.directory}/${project.build.finalName}.jar
			                </file>
								</configuration>
							</execution>
						</executions>
					</plugin>
					<plugin>
						<groupId>org.apache.maven.plugins</groupId>
						<artifactId>maven-deploy-plugin</artifactId>
						<executions>
							<execution>
								<phase>deploy</phase>
								<goals>
									<goal>deploy-file</goal>
								</goals>
								<configuration>
									<packaging>jar</packaging>
									<generatePom>true</generatePom>
									<url>${project.distributionManagement.repository.url}</url>
									<artifactId>${project.artifactId}</artifactId>
									<groupId>${project.groupId}</groupId>
									<version>${project.version}</version>
									<file>${project.build.directory}/${project.artifactId}-${project.version}.jar</file>
								</configuration>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>

Now when I run mvn install I will get a jar of the GWT project installed in my local repo, not just the war.  And now I can add a dependency on the GWT project in my JMeter Java project:

    <dependency>
      <groupId>com.example</groupId>
      <artifactId>gwt-demo</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

4) Add GWT SyncProxy to the JMeter Java project

GWT SyncProxy is a project that lets you create GWT-RPC clients in Java that run in Java.  The intent of the project is really to make it easy to write tests against GWT-RPC servers.  And it does a good job.

However, it’s not a project that exists in a central Maven repository, so we have to do something to make it available.  There are a few ways to tackle this problem – I’m going to do it with an in-project repository.  This means I need to add this xml to my JMeter Java project’s pom.xml:

	<repositories>
		<repository>
			<id>lib</id>
			<name>lib</name>
			<releases>
				<enabled>true</enabled>
				<checksumPolicy>ignore</checksumPolicy>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
			<url>file://${project.basedir}/lib</url>
		</repository>
	</repositories>

I add a dependency to the project to the gwtsyncproxy jar, making up most of the information like groupId and artifactId (I also need Google’s gdata jar as gwtsyncproxy depends on it):

		<dependency>
			<groupId>com.gdevelop</groupId>
			<artifactId>gwt-syncproxy</artifactId>
			<version>0.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.google</groupId>
			<artifactId>gdata</artifactId>
			<version>1.0</version>
		</dependency>

I copy the jars into correct locations under ${project.basedir}/lib:

gwt-syncproxy.jar -> lib/com/gdevelop/gwt-syncproxy/0.3.1/gwt-syncproxy-0.3.1.jar
gdata.jar -> lib/com/google/gdata/1.0/gdata-1.0.jar

But wait, where do I get those jars?

You can grab the jars from my demo project on github, along with a full working example of this thing.  Or you can use Google to go find them.

5) Write some Java code

We have to add one more dependency to the JMeter Java project – for JMeter itself:


			org.apache.jmeter
			ApacheJMeter_java
			2.6

And now we create a Java class that extends org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient, creates a sync proxy on the GreetingService, and calls the one method as part of a test, returning an org.apache.jmeter.samplers.SampleResult.

package com.example;

import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;

import com.example.client.GreetingService;
import com.gdevelop.gwt.syncrpc.SyncProxy;

public class DemoSampler extends AbstractJavaSamplerClient {

	   private GreetingService svc = null;

	   @Override
	   public void setupTest( JavaSamplerContext context )
	   {
	      super.setupTest ( context );

	      try
	      {
	         svc = (GreetingService) SyncProxy.newProxyInstance ( GreetingService.class, "http://localhost:8888/GwtDemo/", "greet" );
	      }
	      catch ( Exception e )
	      {
	         throw new RuntimeException ( e );
	      }
	   }

	public SampleResult runTest(JavaSamplerContext arg0) {
	      SampleResult result =  new SampleResult ();
	      result.sampleStart();

	      String greeting = svc.greetServer( "Test" );

	      result.setSuccessful ( true );
	      result.setSampleLabel ( greeting );
	      result.sampleEnd();
	      return result;
	}

}

If you want to do a quick test you can add a main method to the DemoSampler:

	public static void main(String[] args) {
		DemoSampler test = new DemoSampler();
		JavaSamplerContext ctx = new JavaSamplerContext(new Arguments());
		test.setupTest(ctx);
		SampleResult result = test.runTest(ctx);
		System.out.println((result.isSuccessful() ? "SUCCESS: " : "FAILURE: ")
				+ result.getSampleLabel());
	}

Get the GWT project running by executing “mvn gwt:run” in the GWT project directory, then run DemoSampler as a Java application (I do that in Eclipse).
This is the result I get:

SUCCESS: Hello, Test!

I am running jetty-6.1.x.

It looks like you are using:
Java/1.6.0_24

6) Create a project for the XML JMeter tests

From this point you’re pretty much going to follow my post on How to run JMeter tests in Maven, with a few modifications:

  1. You’ll need to add the JMeter Java project as a dependency on the jmeter plugin in the test project pom.xml.
  2. In order to be able to use your Java sampler from the JMeter GUI you need the GUI to recognize your jar as an extension.  The way I know how to do this is to copy the jar into the JMeter lib/ext directory.  You will also need to make all of the jars that the sampler jar is dependent on available to the GUI.  You can either copy them all to the JMeter lib directory or you can use maven to make an uber jar that contains all dependencies.  I actually didn’t like either of these solutions.  I forked the jmeter plugin and added a goal that would open the gui for me.
  3. When you create the test case add a Java Request sampler and select your sample from the drop down box.

That’s it.  Write some load tests.  Run them automatically in Jenkins. Have fun.

Advertisements