You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jmeter.apache.org by sebb <se...@gmail.com> on 2014/07/24 12:20:26 UTC
Re: svn commit: r1611689 - in /jmeter/trunk: src/components/org/apache/jmeter/control/
src/components/org/apache/jmeter/control/gui/ src/core/org/apache/jmeter/resources/
xdocs/ xdocs/usermanual/
On 18 July 2014 17:13, <pm...@apache.org> wrote:
> Author: pmouawad
> Date: Fri Jul 18 16:13:18 2014
> New Revision: 1611689
>
> URL: http://svn.apache.org/r1611689
> Log:
> Bug 56728 - New Critical Section Controller to serialize blocks of a Test
> Bugzilla Id: 56728
>
> Added:
> jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java (with props)
> jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java (with props)
> Modified:
> jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
> jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
> jmeter/trunk/xdocs/changes.xml
> jmeter/trunk/xdocs/usermanual/component_reference.xml
>
> Added: jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java
> URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java?rev=1611689&view=auto
> ==============================================================================
> --- jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java (added)
> +++ jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java Fri Jul 18 16:13:18 2014
> @@ -0,0 +1,201 @@
> +/*
> + * 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.control;
> +
> +import java.util.concurrent.ConcurrentHashMap;
> +import java.util.concurrent.locks.ReentrantLock;
> +
> +import org.apache.commons.lang3.StringUtils;
> +import org.apache.jmeter.samplers.Sampler;
> +import org.apache.jmeter.testelement.TestStateListener;
> +import org.apache.jmeter.testelement.ThreadListener;
> +import org.apache.jmeter.testelement.property.StringProperty;
> +import org.apache.jorphan.logging.LoggingManager;
> +import org.apache.log.Logger;
> +
> +/**
> + * This is a Critical Section Controller; it will execute the set of statements
> + * (samplers/controllers, etc) under named lock.
> + * <p>
> + * In a programming world - this is equivalent of :
> + *
> + * <pre>
> + * try {
> + * named_lock.lock();
> + * statements ....
> + * } finally {
> + * named_lock.unlock();
> + * }
> + * </pre>
> + *
> + * In JMeter you may have :
> + *
> + * <pre>
> + * Thread-Group (set to loop a number of times or indefinitely,
> + * ... Samplers ... (e.g. Counter )
> + * ... Other Controllers ....
> + * ... CriticalSectionController ( lock name like "foobar" )
> + * ... statements to perform when lock acquired
> + * ...
> + * ... Other Controllers /Samplers }
> + * </pre>
> + *
> + * @since 2.12
> + */
> +public class CriticalSectionController extends GenericController implements
> + ThreadListener, TestStateListener {
> +
> + /**
> + *
> + */
> + private static final long serialVersionUID = 4362876132435968088L;
> +
> + private static final Logger logger = LoggingManager.getLoggerForClass();
> +
> + private static final String LOCK_NAME = "CriticalSectionController.lockName"; //$NON-NLS-1$
> +
> + private static final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<String, ReentrantLock>();
s/lockMap/LOCK_MAP/ ?
This is a static constant, so should normally be upper case.
> +
> + private transient ReentrantLock currentLock;
Does this need to be volatile/or synch?
Is it accessed by multiple threads?
The reInitialize() method suggests that multiple threads do access the
same variable.
> +
> + /**
> + * constructor
> + */
> + public CriticalSectionController() {
> + super();
> + }
> +
> + /**
> + * constructor
> + */
> + public CriticalSectionController(String name) {
> + super();
> + this.setName(name);
> + }
> +
> + /**
> + * Condition Accessor - this is gonna be any string value
Javadoc does not match code/methof name
> + */
> + public void setLockName(String name) {
> + setProperty(new StringProperty(LOCK_NAME, name));
> + }
> +
> + /**
> + * Function for autocreate and get lock
Javadoc is misleading.
> + *
> + * @return named lock
> + */
> + private ReentrantLock getLock() {
> + ReentrantLock lock = lockMap.get(getLockName());
Should fetch the lock name once ... i.e. cache the string in a local variable.
> + ReentrantLock prev = null;
> + if (lock != null) {
> + return lock;
> + }
> + lock = new ReentrantLock();
> + prev = lockMap.putIfAbsent(getLockName(), lock);
Re-use the cached lock name here.
Apart from reducing the work needed, it's vital that the same string
is used here.
> + return prev == null ? lock : prev;
> + }
> +
> + /**
> + * Lock name
> + */
> + public String getLockName() {
> + return getPropertyAsString(LOCK_NAME);
> + }
> +
> + /**
> + * @see org.apache.jmeter.control.Controller#next()
> + */
> + @Override
> + public Sampler next() {
> + if (StringUtils.isEmpty(getLockName())) {
> + logger.warn("Empty lock name in Critical Section Controller:"
> + + getName());
> + return super.next();
> + }
> + if (isFirst()) {
> + // Take the lock for first child element
> + long startTime = System.currentTimeMillis();
> + if (this.currentLock == null) {
> + this.currentLock = getLock();
> + }
> + this.currentLock.lock();
> + long endTime = System.currentTimeMillis();
> + if (logger.isDebugEnabled()) {
> + logger.debug(Thread.currentThread().getName()
> + + " acquired lock:'" + getLockName()
> + + "' in Critical Section Controller " + getName()
> + + " in:" + (endTime - startTime) + " ms");
> + }
> + }
> + return super.next();
> + }
> +
> + /**
> + * Called after execution of last child of the controller We release lock
> + *
> + * @see org.apache.jmeter.control.GenericController#reInitialize()
> + */
> + @Override
> + protected void reInitialize() {
> + if (this.currentLock != null) {
> + if (currentLock.isHeldByCurrentThread()) {
> + this.currentLock.unlock();
> + }
> + this.currentLock = null;
> + }
> + super.reInitialize();
> + }
> +
> + @Override
> + public void threadStarted() {
> + this.currentLock = null;
> + }
> +
> + @Override
> + public void threadFinished() {
> + if (this.currentLock != null
> + && this.currentLock.isHeldByCurrentThread()) {
> + logger.warn("Lock " + getLockName() + " not released in:"
> + + getName() + ", releasing in threadFinished");
> + this.currentLock.unlock();
> + }
> + this.currentLock = null;
> + }
> +
> + @Override
> + public void testStarted() {
> + // NOOP
> + }
> +
> + @Override
> + public void testStarted(String host) {
> + // NOOP
> + }
> +
> + @Override
> + public void testEnded() {
> + lockMap.clear();
> + }
> +
> + @Override
> + public void testEnded(String host) {
> + testEnded();
> + }
> +}
>
> Propchange: jmeter/trunk/src/components/org/apache/jmeter/control/CriticalSectionController.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java
> URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java?rev=1611689&view=auto
> ==============================================================================
> --- jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java (added)
> +++ jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java Fri Jul 18 16:13:18 2014
> @@ -0,0 +1,183 @@
> +/*
> + * 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.control.gui;
> +
> +import java.awt.BorderLayout;
> +
> +import javax.swing.Box;
> +import javax.swing.JLabel;
> +import javax.swing.JPanel;
> +import javax.swing.JTextField;
> +
> +import org.apache.jmeter.control.CriticalSectionController;
> +import org.apache.jmeter.testelement.TestElement;
> +import org.apache.jmeter.util.JMeterUtils;
> +
> +/**
> + * The user interface for a controller which specifies that its subcomponents
> + * should be executed while a condition holds. This component can be used
> + * standalone or embedded into some other component.
> + *
> + * @since 2.12
> + */
> +public class CriticalSectionControllerGui extends AbstractControllerGui {
> +
> + /**
> + *
> + */
> + private static final long serialVersionUID = 7177285850634344095L;
> +
> + /**
> + * A field allowing the user to specify the number of times the controller
> + * should loop.
Incorrect Javadoc
> + */
> + private JTextField tfLockName;
> +
> + /**
> + * Boolean indicating whether or not this component should display its name.
> + * If true, this is a standalone component. If false, this component is
> + * intended to be used as a subpanel for another component.
> + */
> + private boolean displayName = true;
> +
> + /**
> + * Create a new LoopControlPanel as a standalone component.
Incorrect Javadoc
> + */
> + public CriticalSectionControllerGui() {
> + this(true);
> + }
> +
> + /**
> + * Create a new IfControllerPanel as either a standalone or an embedded
> + * component.
Incorrect Javadoc
> + *
> + * @param displayName
> + * indicates whether or not this component should display its
> + * name. If true, this is a standalone component. If false, this
> + * component is intended to be used as a subpanel for another
> + * component.
> + */
> + public CriticalSectionControllerGui(boolean displayName) {
> + this.displayName = displayName;
> + init();
> + }
> +
> + /**
> + * A newly created component can be initialized with the contents of a Test
> + * Element object by calling this method. The component is responsible for
> + * querying the Test Element object for the relevant information to display
> + * in its GUI.
> + *
> + * @param element
> + * the TestElement to configure
> + */
> + @Override
> + public void configure(TestElement element) {
> + super.configure(element);
> + if (element instanceof CriticalSectionController) {
> + CriticalSectionController controller = (CriticalSectionController) element;
> + tfLockName.setText(controller.getLockName());
> + }
> +
> + }
> +
> + /**
> + * Implements JMeterGUIComponent.createTestElement()
> + */
> + @Override
> + public TestElement createTestElement() {
> + CriticalSectionController controller = new CriticalSectionController();
> + modifyTestElement(controller);
> + return controller;
> + }
> +
> + /**
> + * Implements JMeterGUIComponent.modifyTestElement(TestElement)
> + */
> + @Override
> + public void modifyTestElement(TestElement controller) {
> + configureTestElement(controller);
> + if (controller instanceof CriticalSectionController) {
> + CriticalSectionController csController = (CriticalSectionController) controller;
> + csController.setLockName(tfLockName.getText());
> + }
> + }
> +
> + /**
> + * Implements JMeterGUIComponent.clearGui
> + */
> + @Override
> + public void clearGui() {
> + super.clearGui();
> + tfLockName.setText("global_lock"); // $NON-NLS-1$
> + }
> +
> + @Override
> + public String getLabelResource() {
> + return "critical_section_controller_title"; // $NON-NLS-1$
> + }
> +
> + /**
> + * Initialize the GUI components and layout for this component.
> + */
> + private void init() {
> + // Standalone
> + if (displayName) {
> + setLayout(new BorderLayout(0, 5));
> + setBorder(makeBorder());
> + add(makeTitlePanel(), BorderLayout.NORTH);
> +
> + JPanel mainPanel = new JPanel(new BorderLayout());
> + mainPanel.add(createConditionPanel(), BorderLayout.NORTH);
> + add(mainPanel, BorderLayout.CENTER);
> +
> + } else {
> + // Embedded
> + setLayout(new BorderLayout());
> + add(createConditionPanel(), BorderLayout.NORTH);
> + }
> + }
> +
> + /**
> + * Create a GUI panel containing the condition.
> + *
> + * @return a GUI panel containing the condition components
> + */
> + private JPanel createConditionPanel() {
> + JPanel conditionPanel = new JPanel(new BorderLayout(5, 0));
> +
> + // Condition LABEL
> + JLabel conditionLabel = new JLabel(
> + JMeterUtils.getResString("critical_section_controller_label")); // $NON-NLS-1$
> + conditionPanel.add(conditionLabel, BorderLayout.WEST);
> +
> + // TEXT FIELD
> + tfLockName = new JTextField(""); // $NON-NLS-1$
> + conditionLabel.setLabelFor(tfLockName);
> + conditionPanel.add(tfLockName, BorderLayout.CENTER);
> +
> + conditionPanel
> + .add(Box.createHorizontalStrut(conditionLabel
> + .getPreferredSize().width
> + + tfLockName.getPreferredSize().width),
> + BorderLayout.NORTH);
> +
> + return conditionPanel;
> + }
> +}
>
> Propchange: jmeter/trunk/src/components/org/apache/jmeter/control/gui/CriticalSectionControllerGui.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
> URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties?rev=1611689&r1=1611688&r2=1611689&view=diff
> ==============================================================================
> --- jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties (original)
> +++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties Fri Jul 18 16:13:18 2014
> @@ -209,6 +209,8 @@ counter_config_title=Counter
> counter_per_user=Track counter independently for each user
> counter_reset_per_tg_iteration=Reset counter on each Thread Group Iteration
> countlim=Size limit
> +critical_section_controller_label=Lock name
> +critical_section_controller_title=Critical Section Controller
> csvread_file_file_name=CSV file to get values from | *alias
> cut=Cut
> cut_paste_function=Copy and paste function string
>
> Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
> URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties?rev=1611689&r1=1611688&r2=1611689&view=diff
> ==============================================================================
> --- jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties (original)
> +++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties Fri Jul 18 16:13:18 2014
> @@ -197,6 +197,8 @@ counter_config_title=Compteur
> counter_per_user=Suivre le compteur ind\u00E9pendamment pour chaque unit\u00E9 de test
> counter_reset_per_tg_iteration=R\u00E9initialiser le compteur \u00E0 chaque it\u00E9ration du groupe d'unit\u00E9s
> countlim=Limiter le nombre d'\u00E9l\u00E9ments retourn\u00E9s \u00E0
> +critical_section_controller_label=Nom du verrou
> +critical_section_controller_title=Contr\u00F4leur section critique
> cssjquery_attribute=Attribut
> cssjquery_impl=Impl\u00E9mentation CSS/JQuery\:
> cssjquery_render_no_text=Les donn\u00E9es de r\u00E9ponse ne sont pas du texte.
>
> Modified: jmeter/trunk/xdocs/changes.xml
> URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1611689&r1=1611688&r2=1611689&view=diff
> ==============================================================================
> --- jmeter/trunk/xdocs/changes.xml (original)
> +++ jmeter/trunk/xdocs/changes.xml Fri Jul 18 16:13:18 2014
> @@ -1,4 +1,4 @@
> -<?xml version="1.0"?>
> +Crti<?xml version="1.0"?>
> <!--
> Licensed to the Apache Software Foundation (ASF) under one or more
> contributor license agreements. See the NOTICE file distributed with
> @@ -201,6 +201,7 @@ A workaround is to use a Java 7 update 4
>
> <h3>Controllers</h3>
> <ul>
> +<li><bugzilla>56728</bugzilla> - New Critical Section Controller to serialize blocks of a Test. Based partly on a patch contributed by Mikhail Epikhin(epihin-m at yandex.ru)</li>
> </ul>
>
> <h3>Listeners</h3>
>
> Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
> URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1611689&r1=1611688&r2=1611689&view=diff
> ==============================================================================
> --- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
> +++ jmeter/trunk/xdocs/usermanual/component_reference.xml Fri Jul 18 16:13:18 2014
> @@ -2518,6 +2518,17 @@ be saved under the Recording Controller.
>
> </component>
>
> +<component name="Critical Section Controller" index="§-num;.2.17" width="420" height="79" screenshot="logic-controller/critical-section-controller.png">
> +<description>
> +<p>The Critical Section Controller ensures that its children elements (samplers/controllers, etc) will be executed
> +by only one thread as a named lock will be taken before executing children of controller.</p>
> +
> +</description>
> +<properties>
> + <property name="Lock Name" required="Yes">Lock that will be taken by controller, ensure you use different lock names for unrelated sections</property>
> +</properties>
> +
> +</component>
>
> <a href="#">^</a>
>
>
>