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:
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.
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.
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.
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.
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.
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:
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:
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.
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.
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.