Tutorial - Making a JMeter Visualizer
Of the component types, visualizers require greater depth in Swing than something like controllers, functions or samplers. You can find the full source for the distribution graph in “components/org/apache/jmeter/visualizers/”. The distribution graph visualizer is divided into two classes.
- DistributionGraphVisualizer – visualizer which Jmeter instantiates
- DistributionGraph – Jcomponent which draws the actual graph
The easiest way to write a visualizer is to do the following:
- extend org.apache.jmeter.visualizers.gui.AbstractVisualizer
- implement any additional interfaces need for call back and event notification. For example, the DistributionGraphVisualizer implements the following interfaces:
AbstractVisualizer provides some common functionality, which most visualizers like “graph results” use. The common functionality provided by the abstract class includes:
- configure test elements – This means it create a new ResultCollector, sets the file and sets the error log
- create the stock menu
- update the test element when changes are made
- create a file panel for the log file
- create the title panel
In some cases, you may not want to display the menu for the file textbox. In that case, you can override the “init()” method. Here is the implementation for DistributionGraphVisualizer.
The first thing the init method does is create a new BorderLayout. Depending on how you want to layout the widgets, you may want to use a different layout manager. Keep in mind using different layout managers is for experts.
The second thing the init method does is create a border. If you want to increase or decrease the border, change the four integer values. Each integer value represents pixels. If you want your visualizer to have no border, skip lines 9 and 11. Line 15 calls “createGraphPanel,” which is responsible for configuring and adding the DistributionGraph to the visualizer.
At line 6, the graph component is added to the graph panel. The constructor is where a new instance of DistributionGraph is created.
The constructor of DistributionGraphVisualizer is responsible for creating the model and the graph. Every time a new result is complete, the engine passes the result to all the listeners by calling add(SampleResult res). The visualizer passes the new SampleResult to the model.
In the case of the DistributionGraphVisualizer, the “add” method doesn't acutally update the graph. Instead, it calls “updateGui” in line four.
Lines 3 to 4 are suppose to resize the graph, if the user resizes the window or drags the divider. Line 6 updates the panel containing the graph. Line 7 triggers the update of the DistributionGraph. Before we cover writing graphcs, there are couple of important methods visualizer must implement.
The label resource retrieves the name of the visualizer from the properties file. The file is located in “core/org/apache/jmeter/resources”. It's best not to hardcode the name of the visualizer. Message.properties file is organized alphabetically, so adding a new entry is easy.
Every component in Jmeter should implement logic for “clear()” method. If this isn't done, the component will not clear the UI or model when the user tries to clear the last results and run a new test. If clear is not implemented, it can result in a memory leak.
The last method visualizers should implement is “getPrintableComponent()”. The method is responsible for returning the Jcomponent that can be saved or printed. This feature was recently added so that users can save a screen capture of any given visualizer.
Visualizers should implement GraphListener. This is done to make it simpler to add new Sample instances to listeners. As a general rule, if the a custom graph does not plot every single sample, it does not need to implement the interface.
The important method in the interface is “updateGui(Sample s)”. From DistributionGraphVisualizer, we see it calls graph.repaint() to refresh the graph. In most cases, the implementation of updateGui(Sample s) should do just that.
Visualizers generally do not need to implement this interface. The interface is used with combo boxes, checkbox and lists. If your visualizer uses one of these and needs to know when it has been updated, the visualizer will need to implement the interface. For an example of how to implement the interface, please look at GraphVisualizer.
Writing Custom Graphs
For those new to Swing and haven't written custom Jcomponents yet, I would suggest getting a book on Swing and get a good feel for how Swing widgets work. This tutorial will not attempt to explain basic Swing concepts and assumes the reader is already familiar with the Swing API and MVC (Model View Controller) design pattern. From the constructor of DistributionGraphVisualizer, we see a new instance of DistributionGraph is created with an instance of the model.
The implementation of “setModel” method is straight forward.
Notice the method calls “repaint” after it sets the model. If “repaint” isn't called, it can cause the GUI to not draw the graph. Once the test starts, the graph would redraw, so calling “repaint” isn't critical.
The other important aspect of updating the widget is placing the call to drawSample within a synchronized block. If drawSample wasn't synchronized, Jmeter would throw a ConcurrentModificationException at runtime. Depending on the test plan, there may be a dozen or more threads adding results to the model. The synchronized block does not affect the accuracy of each individual request and time measurement, but it does affect Jmeter's ability to generate large loads. As the number of threads in a test plan increases, the likelihood a thread will have to wait until the graph is done redrawing before starting a new request increases. Here is the implementation of drawSample.
In general, the rendering of the graph should be fairly quick and shouldn't be a bottleneck. As a general rule, it is a good idea to profile custom plugins. The only way to make sure a visualizer isn't a bottleneck is to run it with a tool like Borland OptimizeIt. A good way to test a plugin is to create a simple test plan and run it. The heap and garbage collection behavior should be regular and predictable.