Am 30.11.2014 um 21:32 schrieb Philippe Mouawad:
On Sun, Nov 30, 2014 at 10:19 AM, Felix Schumacher <
[email protected]> wrote:


Am 29. November 2014 21:12:00 MEZ, schrieb Philippe Mouawad <
[email protected]>:
Hi Felix

My answer inline.
Regards

On Sat, Nov 29, 2014 at 11:51 AM, Felix Schumacher <
[email protected]> wrote:

Hello Philippe,

Am Sonntag, den 23.11.2014, 12:22 +0100 schrieb Philippe Mouawad:
Thanks a lot for your review which pointed to a synchronisation
issue
that
I fixed, good catch!

I think I took all your notes into account, let me know if I forgot
something.
in SamplerMetric you have used a sliding window for the statistics.
* Should we make the length of the window configurable?

Yes but it's a new property and we have a lot :-)
But the size of the window depends on the number of samples per time slot.
Perhaps it could be dynamically sized?

You mean it needs to be increased if there is a big number of requests in
time slot for 1 sample ?
Please clarfify so that I am sure to understand. For now I made it
configurable through a property.
Right, if you have only a few requests per second but report the stats for that window every second, you will get values from other time slots too. On the other hand, if you have a few hundred requests per second, you might not even report the correct values for the time slot.

Thus I thought it could be made more dynamic, but if it is configurable, that might be enough. I don't think many will actually change it. Would you?


* Should min/max and minThreads/maxThreads be limited to the sliding
window, also? At least min and max would be simple to do.

Dev team opinion is welcome, maybe it would be better
- min would be percentile(0)
- max would be percentile(100)
The stats field has methods to get the minimum and the maximum values. I
would take those.

OK , done

for minThreads, maxThreads, how would you do it ?
Create another statistical field for the number of threads. Then we could
provide correct answers for max/min thread numbers plus
mean/average/percentile.

For this one, I don't see in fact the use case, as number of threads will
not vary a lot so in what way do you think it is incorrect ?
Maybe my question is stupid as I don't see currently the problem but I may
be wrong. Can you clarify ?
Thx
If every value I get presented is part of a windowed look into the data, I would be surprised to find, that one value is actually computed using all data available.

The values could vary for example in ramp up time and they will again vary at the end of the test run. (They do at least in my tests)

But if we are not using a windowed representation the max threads will never go down and the min threads will probably represent the first time slot for the whole test.

Does that make sense?

Felix


Regards
Felix


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()));
+
+    }
+






Reply via email to