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 :-) > * 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) for minThreads, maxThreads, how would you do it ? > 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())); > > >>>> + > > >>>> + } > > >>>> + > > >>>> > > >>> > > > > > -- Cordialement. Philippe Mouawad.
