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();
+    
+    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
+     */
+    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>
+     */
+    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();
+
+    /**
+     * 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;
+
+    /**
+     * 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
+     * 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; }
+     */
+
+    /**
+     * 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 + ")");
+        queue = new ArrayBlockingQueue<SampleResult>(getQueueSize()); 
+        initClass();
+        queueWaits=0L;
+        queueWaitTime=0L;
+        log.info(getName()+":Starting worker with class:"+javaClass +" and 
queue capacity:"+getQueueSize());
+
+        backendListenerClient = createBackendListenerClientImpl(javaClass);
+        context = new 
BackendListenerContext((Arguments)getArguments().clone());
+        if(isToBeRegistered) {
+            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();
+        log.info(getName()+":Started  worker with class:"+javaClass);
+        
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.jmeter.samplers.SampleListener#sampleOccurred(org.apache.jmeter.samplers.SampleEvent)
+     */
+    @Override
+    public void sampleOccurred(SampleEvent e) {
+        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;
+            }
+        } 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){
+            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());
+            try {
+                boolean eof = false;
+                while (!eof) {
+                    if(isDebugEnabled) {
+                        log.debug("Thread:"+Thread.currentThread().getName()+" 
taking SampleResult from queue:"+queue.size());
+                    }
+                    SampleResult e = queue.take();
+                    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();
+                    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();
+            }
+            log.info("Worker ended");
+        }
+    }
+    
+
+    /**
+     * Returns reference to <code>BackendListenerClient</code>.
+     *
+     *
+     * @return BackendListenerClient reference.
+     */
+    static BackendListenerClient createBackendListenerClientImpl(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;
+    }
+
+    /**
+     * 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);
+                    }
+                }
+            }
+            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
+     * 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();
+
+    /**
+     * 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) {
+        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 + "'.");
+        }
+
+        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
+     */
+    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
+            // 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
+     *            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();
+                    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
+     */
+    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()));
+        
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.jmeter.gui.AbstractJMeterGuiComponent#clearGui()
+     */
+    @Override
+    public void clearGui() {
+        super.clearGui();
+        argsPanel.clearGui();
+        classnameCombo.setSelectedIndex(0);
+        queueSize.setText("");
+    }
+}

Propchange: 
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/BackendListenerGui.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java?rev=1641081&view=auto
==============================================================================
--- 
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
 (added)
+++ 
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
 Sat Nov 22 15:36:37 2014
@@ -0,0 +1,138 @@
+/*
+ * 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 org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.apache.jmeter.samplers.SampleResult;
+
+/**
+ * Sampler metric
+ * @since 2.13
+ */
+public class SamplerMetric {
+    // Limit to sliding window of 100 values 
+    private DescriptiveStatistics stats = new DescriptiveStatistics(100);
+    private int success;
+    private int failure;
+    private long maxTime=0L;
+    private long minTime=Long.MAX_VALUE;
+    private int maxActiveThreads = 0;
+    private int minActiveThreads = Integer.MAX_VALUE;
+    /**
+     * 
+     */
+    public SamplerMetric() {
+    }
+
+    /**
+     * 
+     * @param result SampleResult
+     */
+    public synchronized void add(SampleResult result) {
+        if(result.isSuccessful()) {
+            success++;
+        } else {
+            failure++;
+        }
+        long time = result.getTime();
+        int activeThreads = result.getAllThreads();
+        maxTime = Math.max(time, maxTime);
+        minTime = Math.min(time, minTime); 
+        maxActiveThreads = Math.max(maxActiveThreads, activeThreads);
+        minActiveThreads = Math.min(minActiveThreads, activeThreads);
+        if(result.isSuccessful()) {
+            // Should we also compute KO , all response time ?
+            // only take successful requests for time computing
+            stats.addValue(time);
+        }
+    }
+    
+    /**
+     * Reset metric except for percentile related datas
+     */
+    public synchronized void resetForTimeInterval() {
+        // We don't clear stats as it will slide as per my understanding of 
+        // http://commons.apache.org/proper/commons-math/userguide/stat.html
+        success = 0;
+        failure = 0;
+        maxTime=0L;
+        minTime=Long.MAX_VALUE;
+        maxActiveThreads = 0;
+        minActiveThreads = Integer.MAX_VALUE;
+    }
+
+    /**
+     * @return mean
+     */
+    public double getMean() {
+        return stats.getMean();
+    }
+    
+    /**
+     * @param percentile
+     * @return
+     */
+    public double getPercentile(double percentile) {
+        return stats.getPercentile(percentile);
+    }
+
+    /**
+     * 
+     * @return total request
+     */
+    public int getTotal() {
+        return success+failure;
+    }
+    
+    /**
+     * @return the success
+     */
+    public int getSuccess() {
+        return success;
+    }
+
+    /**
+     * @return the failure
+     */
+    public int getFailure() {
+        return failure;
+    }
+
+    /**
+     * @return the maxTime
+     */
+    public long getMaxTime() {
+        return maxTime;
+    }
+
+    /**
+     * @return the minTime
+     */
+    public long getMinTime() {
+        return minTime;
+    }
+
+    public int getMaxActiveThreads() {
+        return maxActiveThreads;
+    }
+
+    public int getMinActiveThreads() {
+        return minActiveThreads;
+    }
+}

Propchange: 
jmeter/trunk/src/components/org/apache/jmeter/visualizers/backend/SamplerMetric.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties 
(original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties Sat 
Nov 22 15:36:37 2014
@@ -129,6 +129,10 @@ auth_manager_title=HTTP Authorization Ma
 auths_stored=Authorizations Stored in the Authorization Manager
 average=Average
 average_bytes=Avg. Bytes
+backend_listener=Backend Listener
+backend_listener_classname=Backend Listener implementation
+backend_listener_paramtable=Parameters
+backend_listener_queue_size=Async Queue size
 bind=Thread Bind
 bouncy_castle_unavailable_message=The jars for bouncy castle are unavailable, 
please add them to your classpath.
 browse=Browse...

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties 
(original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties 
Sat Nov 22 15:36:37 2014
@@ -123,6 +123,10 @@ auth_manager_title=Gestionnaire d'autori
 auths_stored=Autorisations stock\u00E9es
 average=Moyenne
 average_bytes=Moy. octets
+backend_listener=R\u00E9cepteur asynchrone
+backend_listener_classname=Implémentation du r\u00E9cepteur asynchrone
+backend_listener_paramtable=Param\u00E8tres
+backend_listener_queue_size=Taille de la queue
 bind=Connexion de l'unit\u00E9
 bouncy_castle_unavailable_message=Les jars de bouncycastle sont indisponibles, 
ajoutez les au classpath.
 browse=Parcourir...

Modified: jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java 
(original)
+++ jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java Sat Nov 
22 15:36:37 2014
@@ -42,7 +42,7 @@ import org.apache.log.Logger;
  * sample of an entry.
  *
  */
-public class SampleResult implements Serializable {
+public class SampleResult implements Serializable, Cloneable {
 
     private static final long serialVersionUID = 241L;
 
@@ -1349,4 +1349,13 @@ public class SampleResult implements Ser
     public void cleanAfterSample() {
         this.responseDataAsString = null;
     }
+
+    @Override
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("This should not happen");
+        }
+    }
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java Sat Nov 22 
15:36:37 2014
@@ -176,7 +176,7 @@ public class SaveService {
     
     // Must match _version property value in saveservice.properties
     // used to ensure saveservice.properties and SaveService are updated 
simultaneously
-    private static final String PROPVERSION = "2.7";// Expected version 
$NON-NLS-1$
+    private static final String PROPVERSION = "2.8";// Expected version 
$NON-NLS-1$
 
     // Internal information only
     private static String fileVersion = ""; // read from 
saveservice.properties file// $NON-NLS-1$

Modified: jmeter/trunk/xdocs/changes.xml
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml (original)
+++ jmeter/trunk/xdocs/changes.xml Sat Nov 22 15:36:37 2014
@@ -188,6 +188,7 @@ See  <bugzilla>56357</bugzilla> for deta
 
 <h3>Listeners</h3>
 <ul>
+<li><bugzilla>55932</bugzilla> - Create a Async BackendListener to allow easy 
plug of new listener (Graphite, JDBC, Console,...)</li>
 </ul>
 
 <h3>Timers, Assertions, Config, Pre- &amp; Post-Processors</h3>

Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1641081&r1=1641080&r2=1641081&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Sat Nov 22 15:36:37 
2014
@@ -3380,6 +3380,19 @@ The Comparison Assertion Visualizer show
  </properties>
 </component>
 
+<component name="Backend Listener" index="&sect-num;.3.21"  width="777" 
height="266" screenshot="backend_listener.png">
+<description>
+The backend listener is an Asynchronous listener that enables you to plug 
custom implementations of <a 
href="../api/org/apache/jmeter/visualizers/backend/BackendListenerClient.html">BackendListenerClient</a>.
+By default, a Graphite implementation is provided.
+</description>
+ <properties>
+ <property name="Name" required="Yes">Descriptive name for this element that 
is shown in the tree.</property>
+ <property name="Backend Listener implementation" required="Yes">Class of the 
BackendListenerClient implementation.</property>
+ <property name="Async Queue size" required="Yes">Size of the queue that holds 
the SampleResults while they are processed asynchronously.</property>
+ <property name="Parameters" required="Yes">Parameters of the 
BackendListenerClient implementation.</property>
+ </properties>
+</component>
+
 <a href="#">^</a>
 
 </section>


Reply via email to