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="&sect-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>
>
>
>