Return-Path: X-Original-To: apmail-jmeter-dev-archive@minotaur.apache.org Delivered-To: apmail-jmeter-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D925E177F9 for ; Sun, 23 Nov 2014 11:29:07 +0000 (UTC) Received: (qmail 76053 invoked by uid 500); 23 Nov 2014 11:29:07 -0000 Delivered-To: apmail-jmeter-dev-archive@jmeter.apache.org Received: (qmail 76019 invoked by uid 500); 23 Nov 2014 11:29:07 -0000 Mailing-List: contact dev-help@jmeter.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jmeter.apache.org Delivered-To: mailing list dev@jmeter.apache.org Received: (qmail 76007 invoked by uid 99); 23 Nov 2014 11:29:07 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 23 Nov 2014 11:29:07 +0000 X-ASF-Spam-Status: No, hits=1.5 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of philippe.mouawad@gmail.com designates 209.85.213.169 as permitted sender) Received: from [209.85.213.169] (HELO mail-ig0-f169.google.com) (209.85.213.169) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 23 Nov 2014 11:28:59 +0000 Received: by mail-ig0-f169.google.com with SMTP id hl2so3757157igb.4 for ; Sun, 23 Nov 2014 03:27:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type; bh=QbLDjurbrzEift52ndfrSq3WR/++XbrylX/sy0BJP70=; b=QTYiz3l3YgLhPrTeLBgmRD31DAW0mgF8YMmB4uF86NttffoTXLqWVBwlZCia7YypP7 1oR4IALLnu5igWzZkrVafmbKz3ZTmjjIeUt6vMtaHdJ6F6KC0IY6Oq80pzwkJVXTaKKa 4X2NxZzVLprLyA+swlTBAqXVMjUFRrphO5TI+6l/a92vZZYp5ezDBobYTlJhwCaX/3do s0MHY2A1FXzuljXoETTFLj469E5ooRmSXlIWkcYi2gtKMSHSdS8Y/uNbhNOSTJGtG5zW CwX+6RGMaHgLkfF+RTBQNf4exmKzpTXEU+HTTvTmrA+prPFzR4BG02+XlJpqRF/UtZt6 PAzQ== MIME-Version: 1.0 X-Received: by 10.107.165.19 with SMTP id o19mr13198242ioe.1.1416742073507; Sun, 23 Nov 2014 03:27:53 -0800 (PST) Received: by 10.42.102.131 with HTTP; Sun, 23 Nov 2014 03:27:53 -0800 (PST) In-Reply-To: <5471B8E8.3060706@internetallee.de> References: <20141122153639.360D6238899C@eris.apache.org> <5470D42C.7090605@internetallee.de> <5471B8E8.3060706@internetallee.de> Date: Sun, 23 Nov 2014 12:27:53 +0100 Message-ID: Subject: Re: svn commit: r1641081 - in /jmeter/trunk: ./ bin/ res/maven/ src/components/org/apache/jmeter/visualizers/backend/ src/core/org/apache/jmeter/resources/ src/core/org/apache/jmeter/samplers/ src/core/org/apache/jmeter/save/ xdocs/ xdocs/usermanual/ From: Philippe Mouawad To: "dev@jmeter.apache.org" Content-Type: multipart/alternative; boundary=001a1141f1d207a541050884f808 X-Virus-Checked: Checked by ClamAV on apache.org --001a1141f1d207a541050884f808 Content-Type: text/plain; charset=ISO-8859-1 On Sunday, November 23, 2014, Felix Schumacher < felix.schumacher@internetallee.de> 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. > > Great. I will add documentation on Influxdb/grafana setup. I suppose you will be doing the same for collectd. I think we also need to make resistance tests( few hours of run), aggressive tests and check that obtained graphs matchs existing graphs. My first tests look ok for now. > Regards > Felix > >> >> Regards >> Philippe >> >> On Sat, Nov 22, 2014 at 7:21 PM, Felix Schumacher < >> felix.schumacher@internetallee.de> 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 pmouawad@apache.org: >>> >>> 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 @@ >>>> >>>> >>>> >>>> + >>>> >>>> + >>>> >>>> >>>> >>>> @@ -438,8 +440,10 @@ >>>> >>>> >>>> >>>> + >>>> >>>> - >>>> + >>>> + >>>> >>>> >>>> >>>> @@ -2909,7 +2913,9 @@ run JMeter unless all the JMeter jars ar >>>> >>>> >>>> >>>> + >>>> >>>> + >>>> >>>> >>>> >>>> >>>> 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 @@ >>>> >>>> >>>> >>>> + >>>> >>>> + >>>> >>>> >>>> >>>> >>>> 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. >>>> 2.1.1 >>>> 3.3.2 >>>> 1.2 >>>> + 3.3 >>>> 3.3 >>>> + 2.2 >>>> 2.1.6 >>>> 2.1>>> version> >>>> 1.0>>> version> >>>> @@ -181,11 +183,21 @@ under the License. >>>> ${commons-logging.version} >>>> >>>> >>>> + commons-math3 >>>> + commons-math3 >>>> + ${commons-math3.version} >>>> + >>>> + >>>> commons-net >>>> commons-net >>>> ${commons-net.version} >>>> >>>> >>>> + commons-pool2 >>>> + commons-pool2 >>>> + ${commons-pool2.version} >>>> + >>>> + >>>> dnsjava >>>> dnsjava >>>> ${dnsjava.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). >>>> + *

>>>> + * 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 metricsPerSampler >>>> = >>>> new ConcurrentHashMap(); >>>> + >>>> + /* 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 >>>> >>>> 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 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 TEAR_DOWN_SET = new >>>> HashSet(); >>>> + >>>> + /** >>>> + * 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 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(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 queue; >>>> + private final BackendListenerContext context; >>>> + private final BackendListenerClient backendListenerClient; >>>> + private Worker(Class javaClass, BackendListenerClient >>>> backendListenerClient, Arguments arguments, BlockingQueue >>>> 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 l = new ArrayList(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 BackendListenerClient. >>>> >>>> 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 >>>> 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). >>>> + *

>>>> + * 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). >>>> + *

>>>> + * 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. >>>> + *

>>>> + * 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. >>>> + *

>>>> + * 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 >>>> + * SampleResult object. SampleResult has >>>> many >>>> + * fields which can be used. At a minimum, the test should use >>>> + * SampleResult.sampleStart and >>>> + * SampleResult.sampleEndto set the time that the >>>> test >>>> >>>> use {@link..} instead of ..? >>> >>> + * 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 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 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 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 BackendListenerGui 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 possibleClasses = new ArrayList(); >>>> + >>>> + 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 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 set = new HashSet(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. --001a1141f1d207a541050884f808--