anthonygauthier/jmeter-elasticsearch-backend-listener

Exception for Java-based tests

chbndrhnns opened this issue · 15 comments

My expectation was that I can specify the required paramters (es.host, es.index) and use your backend listener in a Java-based test script. As I end up with NumberFormatException doing it that way and succeeding when configuring the test via the JMeter GUI, I assume that maybe getDefaultParameters() is not getting called in the Java case?

Here is the exception:

java.lang.IllegalStateException: Failed calling setupTest
	at org.apache.jmeter.visualizers.backend.BackendListener.testStarted(BackendListener.java:339)
	at org.apache.jmeter.visualizers.backend.BackendListener.testStarted(BackendListener.java:292)
	at org.apache.jmeter.engine.StandardJMeterEngine.notifyTestListenersOfStart(StandardJMeterEngine.java:206)
	at org.apache.jmeter.engine.StandardJMeterEngine.run(StandardJMeterEngine.java:381)
	at pannet.jmeter.PerformanceTest.execute(PerformanceTest.java:148)
	at step_definitions.JMeter2.userCreateSoakRequests(JMeter2.java:259)
[...]
Caused by: java.lang.NumberFormatException: null
	at java.lang.Integer.parseInt(Integer.java:542)
	at java.lang.Integer.parseInt(Integer.java:615)
	at io.github.delirius325.jmeter.backendlistener.elasticsearch.ElasticsearchBackendClient.setupTest(ElasticsearchBackendClient.java:81)
	... 47 more

This is my setup code:

 private BackendListener getBackendListener() {
        backendListener = new BackendListener();
        backendListener.setProperty(BackendListener.TEST_CLASS, BackendListener.class.getName());
        backendListener.setProperty(BackendListener.GUI_CLASS, BackendListenerGui.class.getName());

        Arguments listenerArgs = new Arguments();
        listenerArgs.addArgument("es.host", "localhost", "=");
        listenerArgs.addArgument("es.index", "poc-reporting-elk.perf", "=");
        backendListener.setArguments(listenerArgs);
        backendListener
                .setClassname("io.github.delirius325.jmeter.backendlistener.elasticsearch.ElasticsearchBackendClient");

        return backendListener;
    }

After trying to put all arguments in Java, I still see the exception and cannot get it to work. Is there something else I am missing here?

Indeed there seems to be an issue with getDefaultParameter() in that case as your stack trace is returning an error on line 81 of the ElasticSearchBackendClient class. That specific line does the following: this.bulkSize = Integer.parseInt(context.getParameter(ES_BULK_SIZE)); and throws a null exception.

I'll make a SNAPSHOT release with an explicit call to getDefaultParameters() in the setupTest() method.

@chbndrhnns SNAPSHOT is currently being deployed to maven-central repository. If you don't want to wait for it to be available on Maven. Please add the following JAR to your lib/ext folder (while deleting the old version).

I see that you are calling getDefaultParameters() now but you are not using the returned values...

So I found out that I get it working if i really specify ALL possible parameters like this:

    private BackendListener getBackendListener() {
        backendListener = new BackendListener();
        backendListener.setProperty(BackendListener.TEST_CLASS, BackendListener.class.getName());
        backendListener.setProperty(BackendListener.GUI_CLASS, BackendListenerGui.class.getName());

        Arguments listenerArgs = new Arguments();
        listenerArgs.addArgument("es.scheme", "http", "=");
        listenerArgs.addArgument("es.host", "localhost");
        listenerArgs.addArgument("es.port", "9200", "=");
        listenerArgs.addArgument("es.index", "poc-reporting-elk.perf", "=");
        listenerArgs.addArgument("es.timestamp", "yyyy-MM-dd'T'HH:mm:ss.SSSZZ", "=");
        listenerArgs.addArgument("es.bulk.size", "100", "=");
        listenerArgs.addArgument("es.timeout.ms", "200", "=");
        listenerArgs.addArgument("es.sample.filter", "", "=");
        listenerArgs.addArgument("es.test.mode", "info", "=");
        listenerArgs.addArgument("es.xpack.user", "abc", "=");
        listenerArgs.addArgument("es.xpack.password", "abc", "=");
        listenerArgs.addArgument("es.parse.req.headers", "false", "=");
        listenerArgs.addArgument("es.parse.res.headers", "false", "=");
        listenerArgs.addArgument("es.aws.endpoint", "", "=");
        listenerArgs.addArgument("es.aws.region", "", "=");
        backendListener.setArguments(listenerArgs);
        backendListener
                .setClassname("io.github.delirius325.jmeter.backendlistener.elasticsearch.ElasticsearchBackendClient");

        return backendListener;
    }

@chbndrhnns Oh yes, my bad I tried to push it out in a hurry 😆 ! Let me actually use the values of getDefaultParameters.

@chbndrhnns I have uploaded a new JAR this time it is recreating the context (since the context parameters is a private Map and cannot be explicitly reassigned) by doing this: context = new BackendListenerContext(getDefaultParameters());.

Tell me if it works!

https://github.com/delirius325/jmeter-elasticsearch-backend-listener/releases/tag/2.6.3-SNAPSHOT

Hey, with that snapshot, I get

Caused by: java.lang.IllegalArgumentException: Host name may not be empty
	at org.apache.http.util.Args.containsNoBlanks(Args.java:84)
	at org.apache.http.HttpHost.<init>(HttpHost.java:80)
	at io.github.delirius325.jmeter.backendlistener.elasticsearch.ElasticsearchBackendClient.setupTest(ElasticsearchBackendClient.java:87)

I assume this is because the existing context is overwritten. Shouldn't it be done like this?
First, create default context.
Second, overwrite the parameters that are passed in from the outside

It seems like I am not able to set any parameters now.

Yeah, I understand what you mean.. And frankly, I see it that way as well. But I can't seem to find a solution to access and change/add the parameters that are coming from an external source. As I mentioned in an earlier comment, the parameters in context are stored in a private Map which I cannot explicitly change as there is no setParameters() method.

Wouldn't it be easier if I provided a static method in my plugin, say getDefaultValues(), which would return an Arguments and you could then modify it as you see fit?

I am looking at the influxdb code to understand what they did.

Without fully understanding it as of now, can specify the parameters I want and the rest is taken from the default but the test does not just fail.

Implementing this static method would work but it would make it harder to switch between the influxdb and elasticsearch backend listener as there way of usage differs then.

They did

@Override
    public Arguments getDefaultParameters() {
        Arguments arguments = new Arguments();
        DEFAULT_ARGS.forEach(arguments::addArgument);
        return arguments;
    }

DEFAULT_ARGS being a private static final Map<String, String> DEFAULT_ARGS I'll implement a similar structure in the next SNAPSHOT

Hey @chbndrhnns,

So I just switched the parameters declaration to the style they're using in the InfluxDB Backend Listener, let me know if that helps!

https://github.com/delirius325/jmeter-elasticsearch-backend-listener/releases/download/2.6.3-SNAPSHOT/jmeter.backendlistener.elasticsearch-2.6.3-SNAPSHOT.jar

Thanks,

Hey, there is still a NullPointerException:

Caused by: java.lang.NullPointerException
	at io.github.delirius325.jmeter.backendlistener.elasticsearch.ElasticsearchBackendClient.setupTest(ElasticsearchBackendClient.java:90)
	... 47 more

I see that context.getParameter() returns null if a parameter is not specified which is the base issue here: I would like to be able to leave out parameters I do not need :)

I am having issues understanding how the new way of adding the parameters is causing a NullExceptionPointer. I am basically looping through the static parameters and adding them all to the listener.

private static final Map<String, String> DEFAULT_ARGS = new LinkedHashMap<>();
    static {
        DEFAULT_ARGS.put(ES_SCHEME, "http");
        DEFAULT_ARGS.put(ES_HOST, null);
        DEFAULT_ARGS.put(ES_PORT, "9200");
        DEFAULT_ARGS.put(ES_INDEX, null);
        DEFAULT_ARGS.put(ES_TIMESTAMP, "yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
        DEFAULT_ARGS.put(ES_BULK_SIZE, "100");
        DEFAULT_ARGS.put(ES_TIMEOUT_MS, Long.toString(DEFAULT_TIMEOUT_MS));
        DEFAULT_ARGS.put(ES_SAMPLE_FILTER, null);
        DEFAULT_ARGS.put(ES_TEST_MODE, "info");
        DEFAULT_ARGS.put(ES_AUTH_USER, "");
        DEFAULT_ARGS.put(ES_AUTH_PWD, "");
        DEFAULT_ARGS.put(ES_PARSE_REQ_HEADERS, "false");
        DEFAULT_ARGS.put(ES_PARSE_RES_HEADERS, "false");
        DEFAULT_ARGS.put(ES_AWS_ENDPOINT,  "");
        DEFAULT_ARGS.put(ES_AWS_REGION, "");
    }

@Override
    public Arguments getDefaultParameters() {
        Arguments arguments = new Arguments();
        DEFAULT_ARGS.forEach(arguments::addArgument);
        return arguments;
    }

It's as if making a JMeter test-plan from code isn't calling the same methods...
Going through the InfluxDB listener and I noticed that my BackendClient was missing a constructor. Not sure if that's the cause of the issue or not, but could be a possible factor. Still investigating.

Taking a look at https://jmeter.apache.org/api/org/apache/jmeter/visualizers/backend/BackendListenerContext.html#getParameter-java.lang.String-java.lang.String- indicates that the desired default parameter needs to be passed to the getParameter call or null is returned otherwise, or am I wrong?

That is correct 😄, which is why I'm trying to fix the issue where the default parameters are not automatically set when creating a test plan from code (as you're doing).