Sebastian is a Computer Engineer and Technical Lead at Abstracta with more than 8 years of experience in the field. During this time he has been working on many Performance Engineering projects. In the last few years, Sebastian has contributed to JMeter's community developing plugins to support new protocols like RTE, HTTP 2.0, HLS, and more. He has knowledge in several Performance Testing tools like JMeter, Gatling, Taurus, APM, and other related tools.

Become a JMeter and Continuous Testing Pro

Start Learning
Slack

Test Your Website Performance NOW! |

arrowPlease enter a URL with http(s)
Mar 08 2021

How to Develop a JMeter Plugin: Intro & Best Practices

Over the last few years, my team and I have been working with Blazemeter Labs on developing plugins for JMeter. We aim to extend several of its functionalities and the supported protocols, to better serve the open source load testing community.

In the beginning, it proved to be hard work. This was due to two main challenges: the first was obtaining sufficient knowledge of the protocols that we wanted to add support for. This  involved a lot of research. We had to learn how they work and if there was already any available tool that we could use as a basis for the implementation. The second challenge was getting familiar with JMeter's own implementation and how we could extend it. Because, even though JMeter was designed to be extended, there is a lack of documentation that explains how to do so. 

Along the way, we learned a lot. We enjoy the process, and especially the finish line - when a plugin is ready to be used by the developer community. So, we decided to share what we’ve learned and create a tutorial for building a JMeter plugin.

Every protocol has its own particularities. So in this article, we will not focus on individual ones, but rather how to get started with the JMeter plugin development process. This post is aimed for anyone who needs to implement a feature or functionality to cover a certain need in their workflow. We’ll start with an explanation of how to start developing your own plugins for JMeter, and then we’ll share some of the best practices and tips that we’ve learned along the way.

We are going to describe how to start the development from scratch. We’ll show how to create the project, define the structure and define the build process. Then, we are going to take you through all the steps you should follow for the implementation, explaining the classes and methods of JMeter that we need to extend. Finally, we are going to review how to share your plugin with the community and what are, in our opinion, the main considerations to take when implementing a new plugin.  

First Steps: Setting Up

The Plugin We’re Developing

To describe how to develop a plugin, we’re going to implement a simplified version of Dummy sampler. This plugin will simply allow us to define certain values that we will obtain in the result and the time it will take to respond.

Creating the Project

The first thing to do before starting to implement the plugin is to create the project and define the structure you are going to use in your implementation. 

For the project, we recommend creating a maven project. This simplifies the management of the dependencies and the build process. We use the convention defined by maven for naming the groupId, artifactId and version, as well as for the package naming. 

In our case it will be:

  • groupId: com.blazemeter.jmeter
  • artifactId: jmeter-bzm-dummy
  • version: 1.0-SNAPSHOT
  • package: com.blazemeter.jmeter.dummy

 

After creating our project, we should have a package structure like this: 

 

 

and the following configuration in our pom file: 

<?xml version="1.0" encoding="UTF-8"?>
<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.blazemeter.jmeter</groupId>
 <artifactId>jmeter-bzm-dummy</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>

 <properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 </properties>

 <build>
   <plugins>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>3.8.1</version>
       <configuration>
         <source>1.8</source>
         <target>1.8</target>
       </configuration>
     </plugin>
   </plugins>
 </build>

 <dependencies>
   <dependency>
     <groupId>org.apache.jmeter</groupId>
     <artifactId>ApacheJMeter_core</artifactId>
     <version>5.0</version>
     <scope>provided</scope>
   </dependency>
   <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.7.26</version>
     <scope>provided</scope>
   </dependency>
 </dependencies>

</project>

 

This is a standard configuration of a project that will generate a jar. You can see we added the info of the project and then the maven-compiler-plugin. 

Adding Dependencies

After that you can see in the code above that we added the basic dependencies that we are going to use to develop our plugin. JMeter has several libraries available, but in this case we’re going to use only the core library.

We are using version 5.0 of JMeter. This is so our developed plugin is compatible with all JMeter versions since 5.0. But if you prefer using a different version you can do it. For example, if you want to develop a plugin compatible with JMeter 4.0, you only have to change the version. 

Additionally, we added the slf4j-api since we are going to use it for logging purposes. 

Now we have everything we need, and we can start developing the plugin!

Building Your Own JMeter Sampler

Developing plugins is made of two different parts. Each part is a class, based on JMeter’s contracts that we should implement. The first class is responsible for implementing the GUI,  and it is where we have to define fields needed for the user to interact with our plugins. The second class will contain the logic that our plugin should execute. This class will also be responsible for the serialization of the data. Let’s see how to do it:

Developing the Plugin GUI

We are going to start with implementing the class corresponding to the GUI. This class must be strictly stateless, which means it should not have any reference to the test element. This restriction is due to JMeter reusing this class for different test elements, to optimize the resources usage. If you want to know more about it, you can read the official JMeter guide here.

We are going to follow the same package structure used by JMeter. As you can see in the following image, JMeter has a package for each kind of test element (assertions, config, samplers, etc.) and a gui package inside each one. 

 

 

So, as we are implementing a sampler, we are going to create a package with the sampler name. Our gui package will be created inside, and we will add our first class, which will be DummySamplerGui

JMeter provides us with several abstract classes, to make the implementation of the GUI easier. There are classes for all JMeter supported components, but, as previously mentioned, we are implementing a sampler, so we are going to extend the class AbstractSamplerGui and override the methods I will describe in the following paragraph to define how to interact with our GUI.

In our example, we  will create a simple GUI, which you can see in the following image: 

 

 

Let's explain how we got there, step by step.

1. First, we are going to create a constructor that contains the Swing code to implement the GUI. The method makeTitlePanel() is used to create the panel with the name and the comment, and createDummySamplerPanel is where we place our custom panel. Due to the fact that we are not going to focus on Swing, we are not going to get into the details of this method. But at the end of this post, you can find the link to the repo where the example’s complete code is. 

The constructor:

private final JTextField responseTime = new JTextField();
private final JTextField label = new JTextField();
private final JTextField responseCode = new JTextField();
private final JCheckBox success = new JCheckBox("Success", true);

public DummySamplerGui() {
 setLayout(new BorderLayout());
 setBorder(makeBorder());
 add(makeTitlePanel(), BorderLayout.NORTH);
 add(createDummySamplerPanel(), BorderLayout.CENTER);
}

 

Then, we have to override some of JMeter’s methods. Let’s get into it.

 

2. The first two methods we have to override are getLabelResource and getStaticLabel. These methods define the label of the sampler, which we can see in the GUI of the sampler and in the JMeter components drop-down list.

 

@Override
public String getLabelResource() {
 return "Dummy Sampler";
}

@Override
public String getStaticLabel() {
 return getLabelResource();
}

 

3. The method createTestElement is responsible for creating a new instance of our DummySampler. So after creating the instance, we have to update the GUI and then return this instance.

 

@Override
public TestElement createTestElement() {
 DummySampler dummySampler = new DummySampler();
 configureTestElement(dummySampler);
 return dummySampler;
}

 

As I previously commented, JMeter reuses the same GUI for all test elements. So now we have to implement the two methods that support this. These are  modifyTestElement and configure. These two methods are in a sort of way opposite and complementary.  

4. modifyTestElement sends all the information from the GUI to the test element. The first thing we should always do is call the method of the super configureTestElement to set required JMeter alongside the name, the comment, and the state of the sampler. Then, cast the TestElement to our DummySampler and set all the values.

5. The second method is basically the opposite of modifyTestElement. In this case, we are going to load the fields of the GUI with the values of a test element. The logic is pretty much similar. In this case, we have to call the configure method from the super one, and then we have to cast the test element and set the values.

 

@Override
public void modifyTestElement(TestElement element) {
 super.configureTestElement(element);
 if (element instanceof DummySampler) {
   DummySampler dummySampler = (DummySampler) element;
   dummySampler.setLabel(label.getText());
   dummySampler.setResponseCode(responseCode.getText());
   dummySampler.setSuccessful(success.isSelected());
   try {
     int responseTime = Integer.parseInt(this.responseTime.getText());
     dummySampler.setResponseTime(responseTime);
   } catch (NumberFormatException e) {
     LOG.error("Response time must be Integer", e);
   }
 }
}

 

@Override
public void configure(TestElement element) {
 super.configure(element);
 if (element instanceof DummySampler) {
   DummySampler dummySampler = (DummySampler) element;
   label.setText(dummySampler.getLabel());
   responseCode.setText(dummySampler.getResponseCode());
   responseTime.setText(dummySampler.getResponseTime().toString());
   success.setSelected(dummySampler.getSuccessful());
 }
}

 

6. Finally, we have the method that clears the GUI and leaves it with the default values. This is usually used when creating new test elements. Like in the previous methods, we have to call the clear of the super:

@Override
public void clearGui() {
 super.clearGui();
 label.setText("");
 responseTime.setText("1000");
 responseCode.setText("");
 success.setSelected(true);
}

 

Developing the Plugin Test Element

We now have the GUI of our plugin implemented, but this is only the first part. So, let's implement the test element. JMeter provides us with some classes we can extend, similar to what we used in the GUI. In this case, we are going to extend the class AbstractSampler.

1. Let’s add a new class to our sampler package. In this case the class will be DummySampler, and it will extend AbstractSampler. After that, we have to extend the method sample and create all the getters and setters of the test element. So, let’s get to work!

2. We are going to start with the most basic: the field's definition, getters and setters. The first thing we can see in the following code is the definition of some constants that are used as JMeter property names. These properties are used to save and load the test element when saving and loading a test plan to and from the .jmx file.

Then we can see the getters and the setters. In these methods, we use functions provided by JMeter to access corresponding properties. setProperty is generic, but there are several getters depending on the type of the property:

private static final String RESPONSE_TIME_PROPERTY = "Dummy.responseTime";
private static final String LABEL = "Dummy.label";
private static final String RESPONSE_CODE = "Dummy.responseCode";
private static final String SUCCESS = "Dummy.Success";

public void setResponseTime(int responseTime) {
 setProperty(RESPONSE_TIME_PROPERTY, responseTime);
}

public Integer getResponseTime() {
 return getPropertyAsInt(RESPONSE_TIME_PROPERTY, 1000);
}

public void setLabel(String label) {
 setProperty(LABEL, label);
}

public String getLabel() {
 return getPropertyAsString(LABEL, Strings.EMPTY);
}

public void setResponseCode(String responseCode) {
 setProperty(RESPONSE_CODE, responseCode);
}

public String getResponseCode() {
 return getPropertyAsString(RESPONSE_CODE, "200");
}

public void setSuccessful(boolean success) {
 setProperty(SUCCESS, success);
}

public boolean getSuccessful() {
 return getPropertyAsBoolean(SUCCESS, true);
}

 

3. Last but not least, the sample method will be executed when the sample occurs, to interact with a service to be tested and sampled. This is where we can add any particular logic that we need to interact with a service. For example, a DB, Web Server, Message Broker, etc. In our case the logic is very simple, but it could be much more complex.

As you can notice, the first thing is creating an instance of SampleResult. This is the class used by JMeter to store all the measures of a particular sample, which you can later save to a log file or visualize with a listener (like View Results Tree or Summary Report). In our case, it’s a dummy sampler, so all the measures are loaded with the values that we set in the GUI. But, in the case of HTTP for example, the response code will be the one received by the server.  

The SampleResult has a lot of fields that could be populated, and it also has a method to measure the response time. When the method sampleStart is called, the current timestamp will be stored. When the sampleEnd method is triggered, the difference between the stored timestamp and the current timestamp will be stored in the SampleResult.

Finally, after everything is executed, our sample method has to return the SampleResult with all the relevant information that we collected from the recording.

As you can see we put all our code in a try catch block. This is because in case of any exception, we should manage it and set the result as failure that notifies why it failed.

We can also see that we set the current thread to interrupted. This is because when we get an interrupted exception, we have to let JMeter manage this behaviour in the proper way.

  

public SampleResult sample(Entry entry) {
 SampleResult result = new SampleResult();
 result.setSampleLabel(getLabel());
 result.setResponseCode(getResponseCode());
 result.setSuccessful(getSuccessful());
 result.sampleStart();
 try {
   Thread.sleep(getResponseTime());
 } catch (InterruptedException e) {
   result.setSuccessful(false);
   result.setResponseMessage(e.getMessage());
   LOG.error("Error while sleep", e);
   Thread.currentThread().interrupt();
 }
 result.sampleEnd();
 return result;
}

  

 

Congratulations! Your plugin is now developed and ready for sharing.

Sharing Your Plugin

So now that you have developed your own plugin, what can you do with it? You can probably guess my answer already, but yes... If you put so much effort into developing something awesome, why not share it with the community? 

The open-source project, JMeter Plugins (independent of JMeter), serves as a basic tool that we recommend for every performance tester since it allows plugin developers to share their plugins. 

To share your development, you have to send a pull request to the plugins repo updating this file with the description of your plugin. In this link you can find the format of the description. 

Plugin Development Best Practices

We’ve reached the end of the implementation of our dummy plugin! However, for larger projects, there are some considerations to keep in mind: 

  • First, as in any development project, create unit tests for the greatest amount of code possible, to increase the quality of your plugin.
  • This example project is very small. But when you are developing a plugin that provides support for a new protocol, it will probably contain a large amount of code. So, make sure to have a good organizational structure in place. For example, in general, the sample method should call the logic from a core class instead of having all the logic inside the method.
  • If you are going to invest so much time in developing a new JMeter feature, the best thing you can do  is to release if for free and allow anybody to use it and improve it. So, try to define an open source licence. Be careful if you are using an external dependency, in this case you have to respect that licence.
  • Define which Java and JMeter versions you are going to support, and in every release execute tests over all those versions.
  • As we are developing a performance tool, we have to be sure that the performance of this tool is optimized. The users who are going to use it need the guarantee that when an issue is detected, it is in the application under test and not in the plugin. So, in order to achieve that, execute performance testing while taking the threads management and the memory usage into special consideration.
  • We also recommend documenting the plugin extensively to simplify its usage to the users. 
  • Finally, when our plugin is in production, be very careful when modifying the model. If you have to make a big change, try to make it backwards compatible. Avoid users not being able to load previously generated JMXs with newer versions of the plugin.

 

That was a short introduction with a very basic example of how to implement your own plugin following the best practices that we consider important.  

If you want to see the code of this example, you can find it in this GitHub repository.

Thank you for reading. To see the plugins we’ve developed, you can visit BlazeMeter labs.

   
arrowPlease enter a URL with http(s)

Interested in writing for our Blog?Send us a pitch!