Regards
Felix
Regards
Philippe
On Sunday, November 23, 2014, Felix Schumacher <
[email protected]> wrote:
Hi Phillipe,
Am 22.11.2014 um 19:29 schrieb Philippe Mouawad:
Hi Felix,
As I said in thread, I commited code and will improve, feel free
to
fix
javadocs issues on your side.
I will review your comment.
I have spent many hours if not days on this code and I am aware
it is
not
yet fully completed (although tests are promising) but my aim
when
commiting it was to have feedback and help to finish it.
I did not mean to offend you. I can clearly see, that you put a
lot of
effort
in this listener and I will surely try to integrate jmeter into
our
collectd server.
Regards
Felix
Regards
Philippe
On Sat, Nov 22, 2014 at 7:21 PM, Felix Schumacher <
[email protected]> wrote:
Hello Philippe,
I have hidden a few comments inside the cited code.
They are mostly around javadoc and naming things.
Am 22.11.2014 um 16:36 schrieb [email protected]:
Author: pmouawad
Date: Sat Nov 22 15:36:37 2014
New Revision: 1641081
URL: http://svn.apache.org/r1641081
Log:
Bug 55932 - Create a Async BackendListener to allow easy plug
of new
listener (Graphite, JDBC, Console,...)
Bugzilla Id: 55932
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/
AbstractBackendListenerClient.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListener.java
(with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerClient.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerContext.java
(with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerGui.java (with props)
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/SamplerMetric.java
(with props)
Modified:
jmeter/trunk/bin/saveservice.properties
jmeter/trunk/build.properties
jmeter/trunk/build.xml
jmeter/trunk/eclipse.classpath
jmeter/trunk/res/maven/ApacheJMeter_parent.pom
jmeter/trunk/src/core/org/apache/jmeter/resources/
messages.properties
jmeter/trunk/src/core/org/apache/jmeter/resources/
messages_fr.properties
jmeter/trunk/src/core/org/apache/jmeter/samplers/
SampleResult.java
jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java
jmeter/trunk/xdocs/changes.xml
jmeter/trunk/xdocs/usermanual/component_reference.xml
Modified: jmeter/trunk/bin/saveservice.properties
URL:
http://svn.apache.org/viewvc/jmeter/trunk/bin/saveservice.
properties?rev=1641081&r1=1641080&r2=1641081&view=diff
============================================================
==================
--- jmeter/trunk/bin/saveservice.properties (original)
+++ jmeter/trunk/bin/saveservice.properties Sat Nov 22
15:36:37 2014
@@ -53,7 +53,8 @@ _file_version=$Revision$
# 2.5 = 2.10
# 2.6 = 2.11
# 2.7 = 2.12
-_version=2.7
+# 2.8 = 2.13
+_version=2.8
#
#
# Character set encoding used to read and write JMeter XML
files
and
CSV results
@@ -78,6 +79,8 @@ AssertionVisualizer=org.apache.jmeter.vi
AuthManager=org.apache.jmeter.protocol.http.control.AuthManager
Authorization=org.apache.jmeter.protocol.http.control.Authorization
AuthPanel=org.apache.jmeter.protocol.http.gui.AuthPanel
+BackendListener=org.apache.jmeter.visualizers.backend.BackendListener
+BackendListenerGui=org.apache.jmeter.visualizers.
backend.BackendListenerGui
BarChart=org.apache.jmeter.testelement.BarChart
BarChartGui=org.apache.jmeter.report.gui.BarChartGui
BeanShellAssertion=org.apache.jmeter.assertions.BeanShellAssertion
Modified: jmeter/trunk/build.properties
URL:
http://svn.apache.org/viewvc/jmeter/trunk/build.properties?
rev=1641081&r1=1641080&r2=1641081&view=diff
============================================================
==================
--- jmeter/trunk/build.properties (original)
+++ jmeter/trunk/build.properties Sat Nov 22 15:36:37 2014
@@ -118,11 +118,21 @@ commons-logging.loc = ${maven2.r
#commons-logging.md5 =
E2C390FE739B2550A218262B28F290CE
commons-logging.md5 =
040b4b4d8eac886f6b4a2a3bd2f31b00
+commons-math3.version = 3.3
+commons-math3.jar =
commons-math3-${commons-math3.
version}.jar
+commons-math3.loc = ${maven2.repo}/org/apache/
commons/commons-math3/${commons-math3.version}
+commons-math3.md5 =
87346cf2772dc2becf106c45e0f63863
+
commons-net.version = 3.3
commons-net.jar =
commons-net-${commons-net.version}.jar
commons-net.loc = ${maven2.repo}/commons-net/
commons-net/${commons-net.version}
commons-net.md5 =
c077ca61598e9c21f43f8b6488fbbee9
+commons-pool2.version = 2.2
+commons-pool2.jar =
commons-pool2-${commons-pool2.
version}.jar
+commons-pool2.loc = ${maven2.repo}/org/apache/
commons/commons-pool2/${commons-pool2.version}
+commons-pool2.md5 =
51b56c92883812c56fbeb339866ce2df
+
# dnsjava for DNSCacheManager
dnsjava.version = 2.1.6
dnsjava.jar =
dnsjava-${dnsjava.version}.jar
Modified: jmeter/trunk/build.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/build.xml?rev=
1641081&r1=1641080&r2=1641081&view=diff
============================================================
==================
--- jmeter/trunk/build.xml (original)
+++ jmeter/trunk/build.xml Sat Nov 22 15:36:37 2014
@@ -365,7 +365,9 @@
<include name="${lib.dir}/${commons-jexl2.jar}"/>
<include name="${lib.dir}/${commons-lang3.jar}"/>
<include name="${lib.dir}/${commons-logging.jar}"/>
+ <include name="${lib.dir}/${commons-math3}"/>
<include name="${lib.dir}/${commons-net.jar}"/>
+ <include name="${lib.dir}/${commons-pool2.jar}"/>
<include name="${lib.dir}/${dnsjava.jar}"/>
<include
name="${lib.dir}/${excalibur-datasource.jar}"/>
<include
name="${lib.dir}/${excalibur-instrument.jar}"/>
@@ -438,8 +440,10 @@
<pathelement
location="${lib.dir}/${commons-jexl2.jar}"/>
<pathelement
location="${lib.dir}/${commons-lang3.jar}"/>
<pathelement
location="${lib.dir}/${commons-logging.jar}"/>
+ <pathelement location="${lib.dir}/${commons-math3.jar}"/>
<pathelement location="${lib.dir}/${commons-net.jar}"/>
- <pathelement location="${lib.dir}/${dnsjava.jar}"/>
+ <pathelement
location="${lib.dir}/${commons-pool2.jar}"/>
+ <pathelement location="${lib.dir}/${dnsjava.jar}"/>
<pathelement
location="${lib.dir}/${excalibur-datasource.jar}"/>
<pathelement
location="${lib.dir}/${excalibur-instrument.jar}"/>
<pathelement
location="${lib.dir}/${excalibur-logger.jar}"/>
@@ -2909,7 +2913,9 @@ run JMeter unless all the JMeter jars ar
<process_jarfile jarname="commons-jexl2"/>
<process_jarfile jarname="commons-lang3"/>
<process_jarfile jarname="commons-logging"/>
+ <process_jarfile jarname="commons-math3"/>
<process_jarfile jarname="commons-net"/>
+ <process_jarfile jarname="commons-pool2"/>
<process_jarfile jarname="dnsjava"/>
<process_jarfile jarname="excalibur-datasource"/>
<process_jarfile jarname="excalibur-instrument"/>
Modified: jmeter/trunk/eclipse.classpath
URL: http://svn.apache.org/viewvc/jmeter/trunk/eclipse.
classpath?rev=1641081&r1=1641080&r2=1641081&view=diff
============================================================
==================
--- jmeter/trunk/eclipse.classpath (original)
+++ jmeter/trunk/eclipse.classpath Sat Nov 22 15:36:37 2014
@@ -55,7 +55,9 @@
<classpathentry kind="lib"
path="lib/commons-jexl-2.1.1.jar"/>
<classpathentry kind="lib"
path="lib/commons-lang3-3.3.2.
jar"/>
<classpathentry kind="lib"
path="lib/commons-logging-1.2.
jar"/>
+ <classpathentry kind="lib"
path="lib/commons-math3-3.3.jar"/>
<classpathentry kind="lib"
path="lib/commons-net-3.3.jar"/>
+ <classpathentry kind="lib"
path="lib/commons-pool2-2.2.jar"/>
<classpathentry kind="lib"
path="lib/dnsjava-2.1.6.jar"/>
<classpathentry kind="lib" path="lib/excalibur-
datasource-2.1.jar"/>
<classpathentry kind="lib" path="lib/excalibur-
instrument-1.0.jar"/>
Modified: jmeter/trunk/res/maven/ApacheJMeter_parent.pom
URL: http://svn.apache.org/viewvc/jmeter/trunk/res/maven/
ApacheJMeter_parent.pom?rev=1641081&r1=1641080&r2=1641081&view=diff
============================================================
==================
--- jmeter/trunk/res/maven/ApacheJMeter_parent.pom (original)
+++ jmeter/trunk/res/maven/ApacheJMeter_parent.pom Sat Nov 22
15:36:37
2014
@@ -66,7 +66,9 @@ under the License.
<commons-jexl2.version>2.1.1</commons-jexl2.version>
<commons-lang3.version>3.3.2</commons-lang3.version>
<commons-logging.version>1.2</commons-logging.version>
+ <commons-math3.version>3.3</commons-math3.version>
<commons-net.version>3.3</commons-net.version>
+ <commons-pool2.version>2.2</commons-pool2.version>
<dnsjava.version>2.1.6</dnsjava.version>
<excalibur-datasource.version>2.1</excalibur-datasource.
version>
<excalibur-instrument.version>1.0</excalibur-instrument.
version>
@@ -181,11 +183,21 @@ under the License.
<version>${commons-logging.version}</version>
</dependency>
<dependency>
+ <groupId>commons-math3</groupId>
+ <artifactId>commons-math3</artifactId>
+ <version>${commons-math3.version}</version>
+ </dependency>
+ <dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<dependency>
+ <groupId>commons-pool2</groupId>
+ <artifactId>commons-pool2</artifactId>
+ <version>${commons-pool2.version}</version>
+ </dependency>
+ <dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<version>${dnsjava.version}</version>
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/
AbstractBackendListenerClient.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/
org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.
java?rev=1641081&view=auto
============================================================
==================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/
AbstractBackendListenerClient.java (added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/
AbstractBackendListenerClient.java Sat Nov 22 15:36:37 2014
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or
more
+ * contributor license agreements. See the NOTICE file
distributed
with
+ * this work for additional information regarding copyright
ownership.
+ * The ASF licenses this file to You under the Apache
License,
Version
2.0
+ * (the "License"); you may not use this file except in
compliance
with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
implied.
+ * See the License for the specific language governing
permissions
and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.visualizers.backend;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * An abstract implementation of the BackendListenerClient
interface.
This
+ * implementation provides default implementations of most of
the
methods in the
+ * interface, as well as some convenience methods, in order
to
simplify
+ * development of BackendListenerClient implementations.
+ *
+ * While it may be necessary to make changes to the
BackendListenerClient interface
+ * from time to time (therefore requiring changes to any
implementations
of this
+ * interface), we intend to make this abstract class provide
reasonable
+ * implementations of any new methods so that subclasses do
not
necessarily need
+ * to be updated for new versions. Therefore, when creating a
new
+ * BackendListenerClient implementation, developers are
encouraged
to
subclass this
+ * abstract class rather than implementing the
BackendListenerClient
interface
+ * directly. Implementing BackendListenerClient directly will
continue
to be
+ * supported for cases where extending this class is not
possible
(for
example,
+ * when the client class is already a subclass of some other
class).
+ * <p>
+ * The handleSampleResult() method of BackendListenerClient
does
not
have a default
+ * implementation here, so subclasses must define at least
this
method.
It may
+ * be useful to override other methods as well.
+ *
+ * @see BackendListener#sampleOccurred(org.apache.
jmeter.samplers.SampleEvent)
+ * @since 2.13
+ */
+public abstract class AbstractBackendListenerClient
implements
BackendListenerClient {
+
+ private static final Logger log = LoggingManager.
getLoggerForClass();
In classes further down the logger is stored in variables
named
LOG and
LOGGER, should we use one name?
In this class we have a getter for the logger in other classes
not.
Why?
+
+ private ConcurrentHashMap<String, SamplerMetric>
metricsPerSampler
=
new ConcurrentHashMap<String, SamplerMetric>();
+
+ /* Implements
BackendListenerClient.setupTest(JavaSamplerContext)
*/
+ @Override
+ public void setupTest(BackendListenerContext context)
throws
Exception {
+ log.debug(getClass().getName() + ": setupTest");
+ }
+
+ /* Implements BackendListenerClient.teardownTest(
JavaSamplerContext)
*/
+ @Override
+ public void teardownTest(BackendListenerContext context)
throws
Exception {
+ log.debug(getClass().getName() + ": teardownTest");
+ metricsPerSampler.clear();
+ }
+
+ /* Implements
BackendListenerClient.getDefaultParameters() */
+ @Override
+ public Arguments getDefaultParameters() {
+ return null;
+ }
+
+ /**
+ * Get a Logger instance which can be used by subclasses
to log
information.
+ *
+ * @return a Logger instance which can be used for
logging
+ */
+ protected Logger getLogger() {
+ return log;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.
backend.BackendListenerClient#
createSampleResult(org.apache.jmeter.samplers.SampleResult)
+ */
+ @Override
+ public SampleResult
createSampleResult(BackendListenerContext
context, SampleResult result) {
+ SampleResult sampleResult = (SampleResult)
result.clone();
+ return sampleResult;
+ }
+
+ /**
+ *
+ * @param sampleLabel
+ * @return SamplerMetric
No description of the method and the parameters?
+ */
+ protected SamplerMetric getSamplerMetric(String
sampleLabel) {
+ SamplerMetric samplerMetric = metricsPerSampler.get(
sampleLabel);
+ if(samplerMetric == null) {
+ samplerMetric = new SamplerMetric();
+ SamplerMetric oldValue =
metricsPerSampler.putIfAbsent(
sampleLabel,
samplerMetric);
+ if(oldValue != null ){
+ samplerMetric = oldValue;
+ }
+ }
+ return samplerMetric;
+ }
+
+ /**
+ *
+ * @return Map<String, SamplerMetric>
No description of the method and usage of forbidden
characters :)
there
are still more than 800 warnings in the javadoc to prune, so
don't
introduce new ones, please.
+ */
+ protected Map<String, SamplerMetric>
getMetricsPerSampler() {
+ return metricsPerSampler;
+ }
+
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/AbstractBackendListenerClient.java
------------------------------------------------------------
------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListener.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/
org/apache/jmeter/visualizers/backend/BackendListener.java?
rev=1641081&view=auto
============================================================
==================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListener.java
(added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListener.java
Sat Nov 22 15:36:37 2014
@@ -0,0 +1,448 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or
more
+ * contributor license agreements. See the NOTICE file
distributed
with
+ * this work for additional information regarding copyright
ownership.
+ * The ASF licenses this file to You under the Apache
License,
Version
2.0
+ * (the "License"); you may not use this file except in
compliance
with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
implied.
+ * See the License for the specific language governing
permissions
and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.visualizers.backend;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.locks.LockSupport;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.engine.util.NoThreadClone;
+import
org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
+import org.apache.jmeter.samplers.Remoteable;
+import org.apache.jmeter.samplers.SampleEvent;
+import org.apache.jmeter.samplers.SampleListener;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.testelement.AbstractTestElement;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.TestStateListener;
+import
org.apache.jmeter.testelement.property.TestElementProperty;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * Async Listener that delegates SampleResult handling to
implementations of {@link BackendListenerClient}
+ * @since 2.13
+ */
+public class BackendListener extends AbstractTestElement
+ implements Serializable, SampleListener,
TestStateListener,
NoThreadClone, Remoteable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID =
8184103677832024335L;
+
+ private static final Logger log = LoggingManager.
getLoggerForClass();
See naming comment of log from above
+
+ /**
+ * Set used to register instances which implement
teardownTest.
+ * This is used so that the BackendListenerClient can be
notified
when the test ends.
+ */
+ private static final Set<BackendListener> TEAR_DOWN_SET =
new
HashSet<BackendListener>();
+
+ /**
+ * Property key representing the classname of the
BackendListenerClient to user.
+ */
+ public static final String CLASSNAME = "classname";
+
+ /**
+ * Queue size
+ */
+ public static final String QUEUE_SIZE = "QUEUE_SIZE";
+
+ /**
+ * Property key representing the arguments for the
BackendListenerClient.
+ */
+ public static final String ARGUMENTS = "arguments";
+
+ /**
+ * The BackendListenerClient class used by this sampler.
+ * Created by testStarted; copied to cloned instances.
+ */
+ private Class<?> javaClass;
Could probably named clientClass instead of javaClass, since
we
already
know it is a java class.
+
+ /**
+ * If true, the BackendListenerClient class implements
teardownTest.
+ * Created by testStarted; copied to cloned instances.
+ */
+ private boolean isToBeRegistered;
+
+ /**
+ * The BackendListenerClient instance
+ */
+ private transient BackendListenerClient
backendListenerClient =
null;
+
+ /**
+ * The JavaSamplerContext instance used by this sampler
to hold
information
BackendListenerContext?
+ * related to the test run, such as the parameters
specified
for
the
sampler
+ * client.
+ */
+ private transient BackendListenerContext context = null;
+
+ private static final int DEFAULT_QUEUE_SIZE = 5000;
+
+ private transient BlockingQueue<SampleResult> queue; //
created by
server in readResolve method
+
+ private transient long queueWaits; // how many times we
had to
wait
to queue a sample
+
+ private transient long queueWaitTime; // how long we had
to
wait
(nanoSeconds)
+
+ // Create unique object as marker for end of queue
+ private transient static final SampleResult FINAL_EVENT =
new
SampleResult();
+
+ /**
+ * Create a BackendListener.
+ */
+ public BackendListener() {
+ setArguments(new Arguments());
+ }
+
+ /*
+ * Ensure that the required class variables are cloned,
+ * as this is not currently done by the
super-implementation.
+ */
+ @Override
+ public Object clone() {
+ BackendListener clone = (BackendListener)
super.clone();
+ clone.javaClass = this.javaClass;
+ clone.isToBeRegistered = this.isToBeRegistered;
+ return clone;
+ }
+
+ private void initClass() {
+ String name = getClassname().trim();
+ try {
+ javaClass = Class.forName(name, false,
Thread.currentThread().getContextClassLoader());
+ Method method =
javaClass.getMethod("teardownTest", new
Class[]{BackendListenerContext.class});
+ isToBeRegistered =
!method.getDeclaringClass().equals(
AbstractBackendListenerClient.class);
+ log.info("Created class: " + name + ". Uses
teardownTest:
"
+ isToBeRegistered);
+ } catch (Exception e) {
+ log.error(whoAmI() + "\tException initialising: "
+
name,
e);
+ }
+ }
+
+ /**
+ * Retrieves reference to BackendListenerClient.
+ *
+ * Convience method used to check for null reference
without
actually
+ * creating a BackendListenerClient
+ *
+ * @return reference to BackendListenerClient NOTUSED
private
BackendListenerClient
+ * retrieveJavaClient() { return javaClient; }
+ */
Javadoc for non-existant method?
+
+ /**
+ * Generate a String identifier of this instance for
debugging
purposes.
+ *
+ * @return a String identifier for this sampler instance
+ */
+ private String whoAmI() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Thread.currentThread().getName());
+ sb.append("@");
+ sb.append(Integer.toHexString(hashCode()));
+ sb.append("-");
+ sb.append(getName());
+ return sb.toString();
+ }
+
+ // TestStateListener implementation
+ /* Implements TestStateListener.testStarted() */
+ @Override
+ public void testStarted() {
+ testStarted("");
+ }
+
+ /* Implements TestStateListener.testStarted(String) */
+ @Override
+ public void testStarted(String host) {
+ log.debug(whoAmI() + "\ttestStarted(" + host + ")");
Maybe use isDebugEnabled to guard whoAmI() call?
+ queue = new
ArrayBlockingQueue<SampleResult>(getQueueSize());
+ initClass();
+ queueWaits=0L;
+ queueWaitTime=0L;
+ log.info(getName()+":Starting worker with
class:"+javaClass +"
and queue capacity:"+getQueueSize());
+
+ backendListenerClient =
createBackendListenerClientImp
l(javaClass);
+ context = new BackendListenerContext((
Arguments)getArguments().
clone());
+ if(isToBeRegistered) {
space after if and before (?
+ TEAR_DOWN_SET.add(this);
+ }
+ try {
+ backendListenerClient.setupTest(context);
+ } catch (Exception e) {
+ throw new java.lang.IllegalStateException("Failed
calling
setupTest", e);
+ }
+
+ Worker worker = new Worker(javaClass,
backendListenerClient,
(Arguments) getArguments().clone(), queue);
+ worker.setDaemon(true);
+ worker.start();
Don't we want to stop worker after we're done with one test?
+ log.info(getName()+":Started worker with
class:"+javaClass);
Spaces after :?
+
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.jmeter.samplers.SampleListener#sampleOccurred(
org.apache.jmeter.samplers.SampleEvent)
+ */
+ @Override
+ public void sampleOccurred(SampleEvent e) {
Longer name then 'e'? I expect e to be an exception, not an
event.
+ Arguments args = getArguments();
+ context = new BackendListenerContext(args);
+
+ SampleResult sr = backendListenerClient.
createSampleResult(context,
e.getResult());
+ try {
+ if (!queue.offer(sr)){ // we failed to add the
element
first
time
+ queueWaits++;
+ long t1 = System.nanoTime();
+ queue.put(sr);
+ long t2 = System.nanoTime();
+ queueWaitTime += t2-t1;
Will sampleOccurred be called concurrently? If so, than
queueWaitTime
+=
will not be correct.
+ }
+ } catch (Exception err) {
+ log.error("sampleOccurred, failed to queue the
sample",
err);
+ }
+ }
+
+ private static final class Worker extends Thread {
+
+ private final BlockingQueue<SampleResult> queue;
+ private final BackendListenerContext context;
+ private final BackendListenerClient
backendListenerClient;
+ private Worker(Class<?> javaClass,
BackendListenerClient
backendListenerClient, Arguments arguments,
BlockingQueue<SampleResult>
q){
Same naming argument as above. clientclass instead of
javaClass?
+ queue = q;
+ // Allow BackendListenerClient implementations to
get
access
to test element name
+ arguments.addArgument(TestElement.NAME,
getName());
+ context = new BackendListenerContext(arguments);
+ this.backendListenerClient =
backendListenerClient;
+ }
+
+
+ @Override
+ public void run() {
+ boolean isDebugEnabled = log.isDebugEnabled();
+ List<SampleResult> l = new
ArrayList<SampleResult>(queue.
size());
samples instead of l?
+ try {
+ boolean eof = false;
endOfLoop?
+ while (!eof) {
+ if(isDebugEnabled) {
+ log.debug("Thread:"+Thread.
currentThread().getName()+"
taking SampleResult from queue:"+queue.size());
+ }
+ SampleResult e = queue.take();
Could be named result, or sample instead of e
+ if(isDebugEnabled) {
+ log.debug("Thread:"+Thread.
currentThread().getName()+"
took SampleResult:"+e+", isFinal:" + (e==FINAL_EVENT));
+ }
+ while (!(eof = (e == FINAL_EVENT)) && e
!=
null ) {
// try to process as many as possible
+ l.add(e);
+ if(isDebugEnabled) {
+ log.debug("Thread:"+Thread.
currentThread().getName()+"
polling from queue:"+queue.size());
+ }
+ e = queue.poll(); // returns null if
nothing on
queue currently
+ if(isDebugEnabled) {
+ log.debug("Thread:"+Thread.
currentThread().getName()+"
took from queue:"+e+", isFinal:" + (e==FINAL_EVENT));
+ }
+ }
+ if(isDebugEnabled) {
+ log.debug("Thread:"+Thread.
currentThread().getName()+
+ " exiting with FINAL
EVENT:"+(e ==
FINAL_EVENT)
+ +", null:" + (e==null));
+ }
+ int size = l.size();
No need for a temporary variable.
+ if (size > 0) {
+
backendListenerClient.handleSampleResults(l,
context);
+ l.clear();
+ }
+ if(!eof) {
+ LockSupport.parkNanos(100);
+ }
+ }
+ } catch (InterruptedException e) {
+ // NOOP
+ }
+ // We may have been interrupted
+ int size = l.size();
+ if (size > 0) {
+ backendListenerClient.handleSampleResults(l,
context);
+ l.clear();
+ }
Same code as a few lines above, could be factored out into a
method
handleSamples(l, context)
+ log.info("Worker ended");
+ }
+ }
+
+
+ /**
+ * Returns reference to
<code>BackendListenerClient</code>.
Could use a {@link...}
+ *
+ *
+ * @return BackendListenerClient reference.
+ */
+ static BackendListenerClient
createBackendListenerClientImp
l(Class<?>
javaClass) {
+ if (javaClass == null) { // failed to initialise the
class
+ return new ErrorBackendListenerClient();
+ }
+ BackendListenerClient client;
+ try {
+ client = (BackendListenerClient)
javaClass.newInstance();
+ } catch (Exception e) {
+ log.error("Exception creating: " + javaClass, e);
+ client = new ErrorBackendListenerClient();
+ }
+ return client;
I would return newInstance() in try Block and return new
Error.. in
catch
Block. javaClass -> clientClass
+ }
+
+ /**
+ * Method called at the end of the test. This is called
only
on one
instance
+ * of BackendListener. This method will loop through all
of the
other
+ * BackendListenerClients which have been registered
(automatically
in the
+ * constructor) and notify them that the test has ended,
allowing
the
+ * BackendListenerClients to cleanup.
+ */
+ @Override
+ public void testEnded() {
+ try {
+ queue.put(FINAL_EVENT);
+ } catch (Exception ex) {
+ log.warn("testEnded() with
exception:"+ex.getMessage(),
ex);
+ }
+ if (queueWaits > 0) {
+ log.warn("QueueWaits: "+queueWaits+";
QueueWaitTime:
"+queueWaitTime+" (nanoseconds), you may need to increase
queue
capacity,
see property 'backend_queue_capacity'");
+ }
+ synchronized (TEAR_DOWN_SET) {
+ for (BackendListener backendListener :
TEAR_DOWN_SET) {
+ BackendListenerClient client =
backendListener.
backendListenerClient;
+ if (client != null) {
+ try {
+
client.teardownTest(backendListener.context);
+ } catch (Exception e) {
+ throw new java.lang.
IllegalStateException("Failed
calling teardownTest", e);
If we throw an exception here, we will not try every client.
+ }
+ }
+ }
+ TEAR_DOWN_SET.clear();
+ }
+ }
+
+ /* Implements TestStateListener.testEnded(String) */
+ @Override
+ public void testEnded(String host) {
+ testEnded();
+ }
+
+ /**
+ * A {@link BackendListenerClient} implementation used
for
error
handling. If an
+ * error occurs while creating the real
BackendListenerClient
object, it is
+ * replaced with an instance of this class. Each time a
sample
occurs with
+ * this class, the result is marked as a failure so the
user
can
see
that
+ * the test failed.
+ */
+ static class ErrorBackendListenerClient extends
AbstractBackendListenerClient {
+ /**
+ * Return SampleResult with data on error.
+ *
+ * @see
BackendListenerClient#runTest(JavaSamplerContext)
+ */
+ @Override
+ public void handleSampleResults(List<SampleResult>
sampleResults, BackendListenerContext context) {
+
log.warn("ErrorBackendListenerClient#handleSampleResult
called, noop");
+ Thread.yield();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.jmeter.samplers.SampleListener#sampleStarted(
org.apache.jmeter.samplers.SampleEvent)
+ */
+ @Override
+ public void sampleStarted(SampleEvent e) {
+ // NOOP
+
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.jmeter.samplers.SampleListener#sampleStopped(
org.apache.jmeter.samplers.SampleEvent)
+ */
+ @Override
+ public void sampleStopped(SampleEvent e) {
+ // NOOP
+
+ }
+
+ /**
+ * Set the arguments (parameters) for the
BackendListenerClient to
be executed
+ * with.
+ *
+ * @param args
+ * the new arguments. These replace any
existing
arguments.
+ */
+ public void setArguments(Arguments args) {
+ setProperty(new TestElementProperty(ARGUMENTS,
args));
+ }
+
+ /**
+ * Get the arguments (parameters) for the
BackendListenerClient to
be executed
+ * with.
+ *
+ * @return the arguments
+ */
+ public Arguments getArguments() {
+ return (Arguments)
getProperty(ARGUMENTS).getObjectValue();
+ }
+
+ /**
+ * Sets the Classname of the BackendListenerClient object
+ *
+ * @param classname
+ * the new Classname value
+ */
+ public void setClassname(String classname) {
+ setProperty(CLASSNAME, classname);
+ }
+
+ /**
+ * Gets the Classname of the BackendListenerClient object
+ *
+ * @return the Classname value
+ */
+ public String getClassname() {
+ return getPropertyAsString(CLASSNAME);
+ }
+
+ /**
+ * Sets the queue size
+ *
+ * @param queueSize
+ *
+ */
+ public void setQueueSize(int queueSize) {
+ setProperty(QUEUE_SIZE, queueSize,
DEFAULT_QUEUE_SIZE);
+ }
+
+ /**
+ * Gets the queue size
+ *
+ * @return int queueSize
+ */
+ public int getQueueSize() {
+ return getPropertyAsInt(QUEUE_SIZE,
DEFAULT_QUEUE_SIZE);
+ }
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListener.java
------------------------------------------------------------
------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerClient.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/
org/apache/jmeter/visualizers/backend/BackendListenerClient.
java?rev=1641081&view=auto
============================================================
==================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerClient.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerClient.java Sat Nov 22 15:36:37 2014
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or
more
+ * contributor license agreements. See the NOTICE file
distributed
with
+ * this work for additional information regarding copyright
ownership.
+ * The ASF licenses this file to You under the Apache
License,
Version
2.0
+ * (the "License"); you may not use this file except in
compliance
with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
implied.
+ * See the License for the specific language governing
permissions
and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.visualizers.backend;
+
+import java.util.List;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.samplers.SampleResult;
+
+/**
+ * This interface defines the interactions between the
BackendListener
and external
+ * Java programs which can be executed by JMeter. Any Java
class
which
wants to
+ * be executed as a JMeter test must implement this interface
(either
directly
+ * or indirectly through AbstractBackendListenerClient).
+ * <p>
+ * JMeter will create one instance of a BackendListenerClient
implementation for
+ * each user/thread in the test. Additional instances may be
created
for
+ * internal use by JMeter (for example, to find out what
parameters are
+ * supported by the client).
+ * <p>
+ * When the test is started, setupTest() will be called on
each
thread's
+ * BackendListenerClient instance to initialize the client.
Then
handleSampleResult() will be
+ * called for each SampleResult notification. Finally,
teardownTest()
will be called
+ * to allow the client to do any necessary clean-up.
+ * <p>
+ * The JMeter BackendListener GUI allows a list of parameters
to be
defined for the
+ * test. These are passed to the various test methods through
the
+ * {@link BackendListenerContext}. A list of default
parameters
can be
defined
+ * through the getDefaultParameters() method. These
parameters and
any
default
+ * values associated with them will be shown in the GUI.
Users can
add
other
+ * parameters as well.
+ * <p>
+ * When possible, Listeners should extend {@link
AbstractBackendListenerClient
+ * AbstractBackendListenerClient} rather than implementing
BackendListenerClient
+ * directly. This should protect your tests from future
changes to
the
+ * interface. While it may be necessary to make changes to
the
BackendListenerClient
+ * interface from time to time (therefore requiring changes
to any
+ * implementations of this interface), we intend to make this
abstract
class
+ * provide reasonable default implementations of any new
methods so
that
+ * subclasses do not necessarily need to be updated for new
versions.
+ * Implementing BackendListenerClient directly will continue
to be
supported for
+ * cases where extending this class is not possible (for
example,
when
the
+ * client class is already a subclass of some other class).
+ *
+ * @since 2.13
+ */
+public interface BackendListenerClient {
+ /**
+ * Do any initialization required by this client. It is
generally
+ * recommended to do any initialization such as getting
parameter
values in
+ * the setupTest method rather than the runTest method in
order to
add as
+ * little overhead as possible to the test.
+ *
+ * @param context
+ * the context to run with. This provides
access to
+ * initialization parameters.
+ */
+ void setupTest(BackendListenerContext context) throws
Exception;
+
+ /**
+ * Perform a single sample for each iteration. This
method
returns
a
+ * <code>SampleResult</code> object.
<code>SampleResult</code>
has
many
+ * fields which can be used. At a minimum, the test
should use
+ * <code>SampleResult.sampleStart</code> and
+ * <code>SampleResult.sampleEnd</code>to set the time
that the
test
use {@link..} instead of <code>..?
+ * required to execute. It is also a good idea to set the
sampleLabel and
+ * the successful flag.
+ *
+ * @see
org.apache.jmeter.samplers.SampleResult#sampleStart()
+ * @see
org.apache.jmeter.samplers.SampleResult#sampleEnd()
+ * @see
org.apache.jmeter.samplers.SampleResult#setSuccessful(
boolean)
+ * @see
org.apache.jmeter.samplers.SampleResult#setSampleLabel(
String)
+ *
+ * @param context
+ * the context to run with. This provides
access to
+ * initialization parameters.
+ *
+ */
+ void handleSampleResults(List<SampleResult>
sampleResults,
BackendListenerContext context);
+
+ /**
+ * Do any clean-up required by this test at the end of a
test
run.
+ *
+ * @param context
+ * the context to run with. This provides
access to
+ * initialization parameters.
+ */
+ void teardownTest(BackendListenerContext context) throws
Exception;
+
+ /**
+ * Provide a list of parameters which this test supports.
Any
parameter
+ * names and associated values returned by this method
will
appear
in the
+ * GUI by default so the user doesn't have to remember
the
exact
names. The
+ * user can add other parameters which are not listed
here. If
this
method
+ * returns null then no parameters will be listed. If the
value for
some
+ * parameter is null then that parameter will be listed
in the
GUI
with an
+ * empty value.
+ *
+ * @return a specification of the parameters used by this
test
which
should
+ * be listed in the GUI, or null if no parameters
should be
listed.
+ */
+ Arguments getDefaultParameters();
+
+ /**
+ *
+ * @param context
+ * @param result
+ * @return
+ */
+ SampleResult createSampleResult(
+ BackendListenerContext context, SampleResult
result);
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerClient.java
------------------------------------------------------------
------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/
BackendListenerContext.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/
org/apache/jmeter/visualizers/backend/BackendListenerContext.java?
rev=1641081&view=auto
============================================================
==================
---
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/
BackendListenerContext.java
(added)
+++
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/
BackendListenerContext.java
Sat Nov 22 15:36:37 2014
@@ -0,0 +1,237 @@
+/*
+
+ * Licensed to the Apache Software Foundation (ASF) under one
or
more
+ * contributor license agreements. See the NOTICE file
distributed
with
+ * this work for additional information regarding copyright
ownership.
+ * The ASF licenses this file to You under the Apache
License,
Version
2.0
+ * (the "License"); you may not use this file except in
compliance
with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
implied.
+ * See the License for the specific language governing
permissions
and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.visualizers.backend;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+/**
+ * BackendListenerContext is used to provide context
information
to a
+ * BackendListenerClient implementation. This currently
consists
of the
+ * initialization parameters which were specified in the GUI.
+ * @since 2.13
+ */
+public class BackendListenerContext {
+ /*
+ * Implementation notes:
+ *
+ * All of the methods in this class are currently
read-only. If
update
+ * methods are included in the future, they should be
defined
so
that a
+ * single instance of BackendListenerContext can be
associated
with
each thread.
+ * Therefore, no synchronization should be needed. The
same
instance
should
+ * be used for the call to setupTest, all calls to
runTest,
and the
call to
+ * teardownTest.
+ */
+
+ /** Logging */
+ private static final Logger log = LoggingManager.
getLoggerForClass();
See naming comments for logger above
+
+ /**
+ * Map containing the initialization parameters for the
BackendListenerClient.
+ */
+ private final Map<String, String> params;
+
+ /**
+ *
+ * @param args
+ * the initialization parameters.
+ */
+ public BackendListenerContext(Arguments args) {
+ this.params = args.getArgumentsAsMap();
+ }
+
+ /**
+ * Determine whether or not a value has been specified
for the
parameter
+ * with this name.
+ *
+ * @param name
+ * the name of the parameter to test
+ * @return true if the parameter value has been
specified,
false
otherwise.
+ */
+ public boolean containsParameter(String name) {
hasParameter instead of containsParameter?
+ return params.containsKey(name);
+ }
+
+ /**
+ * Get an iterator of the parameter names. Each entry in
the
Iterator is a
+ * String.
+ *
+ * @return an Iterator of Strings listing the names of
the
parameters which
+ * have been specified for this test.
+ */
+ public Iterator<String> getParameterNamesIterator() {
+ return params.keySet().iterator();
+ }
+
+ /**
+ * Get the value of a specific parameter as a String, or
null
if
the
value
+ * was not specified.
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @return the value of the parameter, or null if the
value
was not
+ * specified
+ */
+ public String getParameter(String name) {
+ return getParameter(name, null);
+ }
+
+ /**
+ * Get the value of a specified parameter as a String, or
return
the
+ * specified default value if the value was not
specified.
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @param defaultValue
+ * the default value to return if the value of
this
parameter was
+ * not specified
+ * @return the value of the parameter, or the default
value if
the
parameter
+ * was not specified
+ */
+ public String getParameter(String name, String
defaultValue) {
+ if (params == null || !params.containsKey(name)) {
+ return defaultValue;
+ }
+ return params.get(name);
+ }
+
+ /**
+ * Get the value of a specified parameter as an integer.
An
exception will
+ * be thrown if the parameter is not specified or if it
is not
an
integer.
+ * The value may be specified in decimal, hexadecimal, or
octal, as
defined
+ * by Integer.decode().
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @return the value of the parameter
+ *
+ * @throws NumberFormatException
+ * if the parameter is not specified or is
not an
integer
+ *
+ * @see java.lang.Integer#decode(java.lang.String)
+ */
+ public int getIntParameter(String name) throws
NumberFormatException
{
+ if (params == null || !params.containsKey(name)) {
+ throw new NumberFormatException("No value for
parameter
named '" + name + "'.");
I would expect an IllegalArgumentException, if no parameter
of that
name
is found
+ }
+
+ return Integer.decode(params.get(name)).intValue();
+ }
+
+ /**
+ * Get the value of a specified parameter as an integer,
or
return
the
+ * specified default value if the value was not specified
or
is not
an
+ * integer. A warning will be logged if the value is not
an
integer.
The
+ * value may be specified in decimal, hexadecimal, or
octal, as
defined by
+ * Integer.decode().
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @param defaultValue
+ * the default value to return if the value of
this
parameter was
+ * not specified
+ * @return the value of the parameter, or the default
value if
the
parameter
+ * was not specified
+ *
+ * @see java.lang.Integer#decode(java.lang.String)
+ */
+ public int getIntParameter(String name, int defaultValue)
{
+ if (params == null || !params.containsKey(name)) {
+ return defaultValue;
+ }
+
+ try {
+ return
Integer.decode(params.get(name)).intValue();
+ } catch (NumberFormatException e) {
+ log.warn("Value for parameter '" + name + "' not
an
integer:
'" + params.get(name) + "'. Using default: '"
+ + defaultValue + "'.", e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the value of a specified parameter as a long. An
exception
will be
+ * thrown if the parameter is not specified or if it is
not a
long.
The
+ * value may be specified in decimal, hexadecimal, or
octal, as
defined by
+ * Long.decode().
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @return the value of the parameter
+ *
+ * @throws NumberFormatException
+ * if the parameter is not specified or is
not a
long
+ *
+ * @see Long#decode(String)
+ */
+ public long getLongParameter(String name) throws
NumberFormatException {
+ if (params == null || !params.containsKey(name)) {
+ throw new NumberFormatException("No value for
parameter
named '" + name + "'.");
+ }
+
+ return Long.decode(params.get(name)).longValue();
+ }
+
+ /**
+ * Get the value of a specified parameter as along, or
return
the
specified
+ * default value if the value was not specified or is not
a
long. A
warning
+ * will be logged if the value is not a long. The value
may be
specified in
+ * decimal, hexadecimal, or octal, as defined by
Long.decode().
+ *
+ * @param name
+ * the name of the parameter whose value
should be
retrieved
+ * @param defaultValue
+ * the default value to return if the value of
this
parameter was
+ * not specified
+ * @return the value of the parameter, or the default
value if
the
parameter
+ * was not specified
+ *
+ * @see Long#decode(String)
+ */
+ public long getLongParameter(String name, long
defaultValue) {
+ if (params == null || !params.containsKey(name)) {
+ return defaultValue;
+ }
+ try {
+ return Long.decode(params.get(name)).longValue();
+ } catch (NumberFormatException e) {
+ log.warn("Value for parameter '" + name + "' not
a
long: '"
+ params.get(name) + "'. Using default: '"
+ + defaultValue + "'.", e);
+ return defaultValue;
+ }
+ }
+
+ /**
+ *
+ * @param name
+ * @param defaultValue
+ * @return
No javadoc? Again three warnings more :)
+ */
+ public boolean getBooleanParameter(String name, boolean
defaultValue) {
+ if (params == null || !params.containsKey(name)) {
+ return defaultValue;
+ }
+ return Boolean.valueOf(params.get(name));
+ }
+}
Propchange:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerContext.java
------------------------------------------------------------
------------------
svn:mime-type = text/plain
Added:
jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerGui.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/
org/apache/jmeter/visualizers/backend/BackendListenerGui.
java?rev=1641081&view=auto
============================================================
==================
--- jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerGui.java (added)
+++ jmeter/trunk/src/components/org/apache/jmeter/visualizers/
backend/BackendListenerGui.java Sat Nov 22 15:36:37 2014
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or
more
+ * contributor license agreements. See the NOTICE file
distributed
with
+ * this work for additional information regarding copyright
ownership.
+ * The ASF licenses this file to You under the Apache
License,
Version
2.0
+ * (the "License"); you may not use this file except in
compliance
with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
implied.
+ * See the License for the specific language governing
permissions
and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.visualizers.backend;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.ComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.apache.jmeter.config.Argument;
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.config.gui.ArgumentsPanel;
+import org.apache.jmeter.gui.util.HorizontalPanel;
+import org.apache.jmeter.testelement.TestElement;
+import
org.apache.jmeter.testelement.property.PropertyIterator;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.visualizers.gui.AbstractListenerGui;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.log.Logger;
+
+/**
+ * The <code>BackendListenerGui</code> class provides the
user
interface for the
+ * {@link BackendListener} object.
+ * @since 2.13
+ */
+public class BackendListenerGui extends AbstractListenerGui
implements
ActionListener {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID =
4331668988576438604L;
+
+ /** Logging */
+ private static final Logger log = LoggingManager.
getLoggerForClass();
+
+ /** A combo box allowing the user to choose a backend
class. */
+ private JComboBox classnameCombo;
+
+ /**
+ * A field allowing the user to specify the size of Queue
+ */
+ private JTextField queueSize;
+
+ /** A panel allowing the user to set arguments for this
test.
*/
+ private ArgumentsPanel argsPanel;
+
+ /**
+ * Create a new BackendListenerGui as a standalone
component.
+ */
+ public BackendListenerGui() {
+ super();
+ init();
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLabelResource() {
+ return "backend_listener"; // $NON-NLS-1$
+ }
+
+ /**
+ * Initialize the GUI components and layout.
+ */
+ private void init() {// called from ctor, so must not be
overridable
+ setLayout(new BorderLayout(0, 5));
+
+ setBorder(makeBorder());
+ add(makeTitlePanel(), BorderLayout.NORTH);
+
+ JPanel classnameRequestPanel = new JPanel(new
BorderLayout(0,
5));
+ classnameRequestPanel.add(createClassnamePanel(),
BorderLayout.NORTH);
+ classnameRequestPanel.add(createParameterPanel(),
BorderLayout.CENTER);
+
+ add(classnameRequestPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * Create a panel with GUI components allowing the user
to
select a
test
+ * class.
+ *
+ * @return a panel containing the relevant components
+ */
+ private JPanel createClassnamePanel() {
+ List<String> possibleClasses = new
ArrayList<String>();
+
+ try {
+ // Find all the classes which implement the
BackendListenerClient
+ // interface.
+ possibleClasses =
ClassFinder.findClassesThatExtend(
JMeterUtils.getSearchPaths(),
+ new Class[] { BackendListenerClient.class
});
+
+ // Remove the BackendListener class from the list
since it
only
ErrorBackendListener
+ // implements the interface for error conditions.
+
+
possibleClasses.remove(BackendListener.class.getName()
+
"$ErrorBackendListenerClient");
+ } catch (Exception e) {
+ log.debug("Exception getting interfaces.", e);
+ }
+
+ JLabel label = new
JLabel(JMeterUtils.getResString("backend_
listener_classname"));
// $NON-NLS-1$
+
+ classnameCombo = new
JComboBox(possibleClasses.toArray());
+ classnameCombo.addActionListener(this);
+ classnameCombo.setEditable(false);
+ label.setLabelFor(classnameCombo);
+
+ HorizontalPanel classNamePanel = new
HorizontalPanel();
+ classNamePanel.add(label);
+ classNamePanel.add(classnameCombo);
+
+ queueSize = new JTextField("", 5);
+ queueSize.setName("Queue Size"); //$NON-NLS-1$
+ JLabel queueSizeLabel = new JLabel(JMeterUtils.
getResString("backend_listener_queue_size")); // $NON-NLS-1$
+ queueSizeLabel.setLabelFor(queueSize);
+ HorizontalPanel queueSizePanel = new
HorizontalPanel();
+ queueSizePanel.add(queueSizeLabel,
BorderLayout.WEST);
+ queueSizePanel.add(queueSize);
+
+ JPanel panel = new JPanel(new BorderLayout(0, 5));
+ panel.add(classNamePanel, BorderLayout.NORTH);
+ panel.add(queueSizePanel, BorderLayout.CENTER);
+ return panel;
+ }
+
+ /**
+ * Handle action events for this component. This method
currently
handles
+ * events for the classname combo box.
+ *
+ * @param evt
I would spend the few extra characters to make it event
instead of
evt
+ * the ActionEvent to be handled
+ */
+ @Override
+ public void actionPerformed(ActionEvent evt) {
+ if (evt.getSource() == classnameCombo) {
+ String className = ((String) classnameCombo.
getSelectedItem()).trim();
+ try {
+ BackendListenerClient client =
(BackendListenerClient)
Class.forName(className, true,
+ Thread.currentThread().
getContextClassLoader()).
newInstance();
+
+ Arguments currArgs = new Arguments();
+ argsPanel.modifyTestElement(currArgs);
+ Map<String, String> currArgsMap =
currArgs.getArgumentsAsMap();
+
+ Arguments newArgs = new Arguments();
+ Arguments testParams = null;
+ try {
+ testParams =
client.getDefaultParameters();
+ } catch (AbstractMethodError e) {
+ log.warn("BackendListenerClient doesn't
implement
"
+ + "getDefaultParameters. Default
parameters
won't "
+ + "be shown. Please update your
client
class: " + className);
+ }
+
+ if (testParams != null) {
+ PropertyIterator i =
testParams.getArguments().
iterator();
I would try a for loop instead of explicitly using an
iterator
+ while (i.hasNext()) {
+ Argument arg = (Argument)
i.next().getObjectValue();
+ String name = arg.getName();
+ String value = arg.getValue();
+
+ // If a user has set parameters in
one
test,
and
then
+ // selects a different test which
supports
the
same
+ // parameters, those parameters
should
have the
same
+ // values that they did in the
original
test.
+ if (currArgsMap.containsKey(name)) {
+ String newVal =
currArgsMap.get(name);
+ if (newVal != null &&
newVal.length()
0)
{
+ value = newVal;
+ }
+ }
+ newArgs.addArgument(name, value);
+ }
+ }
+
+ argsPanel.configure(newArgs);
+ } catch (Exception e) {
+ log.error("Error getting argument list for "
+
className, e);
+ }
+ }
+ }
+
+ /**
+ * Create a panel containing components allowing the user
to
provide
+ * arguments to be passed to the test class instance.
+ *
+ * @return a panel containing the relevant components
+ */
+ private JPanel createParameterPanel() {
+ argsPanel = new ArgumentsPanel(JMeterUtils.
getResString("backend_listener_paramtable")); // $NON-NLS-1$
+ return argsPanel;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void configure(TestElement config) {
+ super.configure(config);
+
+ argsPanel.configure((Arguments) config.getProperty(
BackendListener.ARGUMENTS).getObjectValue());
+
+ String className = config.getPropertyAsString(
BackendListener.CLASSNAME);
+ if(checkContainsClassName(classnameCombo.getModel(),
className)) {
+ classnameCombo.setSelectedItem(className);
+ } else {
+ log.error("Error setting class:'"+className+"' in
BackendListener: "+getName()+
+ ", check for a missing jar in your jmeter
'search_paths' and 'plugin_dependency_paths' properties");
+ }
+ queueSize.setText(Integer.toString(((BackendListener)
config).getQueueSize()));
+ }
+
+ /**
+ * Check combo contains className
+ * @param model ComboBoxModel
+ * @param className String class name
+ * @return boolean
explain "boolean" or the other params a bit more?
+ */
+ private static final boolean
checkContainsClassName(ComboBoxModel
model, String className) {
+ int size = model.getSize();
+ Set<String> set = new HashSet<String>(size);
+ for (int i = 0; i < size; i++) {
+ set.add((String)model.getElementAt(i));
+ }
+ return set.contains(className);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TestElement createTestElement() {
+ BackendListener config = new BackendListener();
+ modifyTestElement(config);
+ return config;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void modifyTestElement(TestElement config) {
+ configureTestElement(config);
+ BackendListener backendListener = (BackendListener)
config;
+ backendListener.setArguments((Arguments)
argsPanel.createTestElement());
+
backendListener.setClassname(String.valueOf(classnameCombo.
getSelectedItem()));
+
backendListener.setQueueSize(Integer.parseInt(queueSize.
getText()));
+
+ }
+