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