You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2019/07/02 20:10:07 UTC

[jmeter] branch master updated: This implements "Provide ability to configure whether a new iteration is a new user or same user" (BUG 62861) (#470)

This is an automated email from the ASF dual-hosted git repository.

pmouawad pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new 629c643  This implements "Provide ability to configure whether a new iteration is a new user or same user" (BUG 62861) (#470)
629c643 is described below

commit 629c6430e17df7f7f75891d29e2129ab55a57d68
Author: Philippe M <pm...@users.noreply.github.com>
AuthorDate: Tue Jul 2 22:10:02 2019 +0200

    This implements "Provide ability to configure whether a new iteration is a new user or same user" (BUG 62861) (#470)
    
    * This fixes BUG 62861
    
    Thread Group: Provide ability to configure whether a new iteration is a
    new user or same user
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=62861
    
    Contribution by UbikLoadPack Team
    https://ubikloadpack.com
    @ubikloadpack
    
    * Run clean
    
    * Fix build error
    
    * Take into account @vsi remark
    
    * Rename getters/variables for better understanding
    Move method from ThreadGroup to AbstractThreadGroup
    
    * Remove added newline
    
    * Remove trailing whitespace
    
    * Make isSameUserOnNextIteration final as per @vlsi review
    
    * Remove unintended space as per @vlsi review
    
    * Fix :
    controlledByThreadGroup is not configured
    
    * Make Authentication Manager use Thread Group config
    
    * modify clear of cookiemanager and cachemanager
    
    * modify clear of cookiemanager and cachemanager
    
    * Use same logic
    
    * Add docs
    Add JUnit
    
    * Rename some vars and add spaces
---
 .travis.yml                                        |   2 +-
 .../apache/jmeter/resources/messages.properties    |   5 +
 .../apache/jmeter/resources/messages_fr.properties |   5 +
 .../apache/jmeter/threads/AbstractThreadGroup.java |  28 ++
 .../org/apache/jmeter/threads/JMeterThread.java    |  12 +-
 .../org/apache/jmeter/threads/JMeterVariables.java |   9 +
 .../org/apache/jmeter/threads/ThreadGroup.java     |  25 +-
 .../apache/jmeter/threads/gui/ThreadGroupGui.java  |  31 +-
 .../jmeter/protocol/http/control/AuthManager.java  |  17 +-
 .../jmeter/protocol/http/control/CacheManager.java |  14 +-
 .../protocol/http/control/CookieManager.java       |  14 +-
 .../apache/jmeter/protocol/http/gui/AuthPanel.java |  23 +-
 .../jmeter/protocol/http/gui/CacheManagerGui.java  |  27 +-
 .../jmeter/protocol/http/gui/CookiePanel.java      |  14 +
 .../jmeter/protocol/http/sampler/HTTPHC4Impl.java  |  12 +-
 .../control/TestAuthManagerThreadIteration.java    | 117 ++++++
 .../control/TestCacheManagerThreadIteration.java   | 427 +++++++++++++++++++++
 .../control/TestCookieManagerThreadIteration.java  | 182 +++++++++
 .../protocol/http/sampler/TestHTTPHC4Impl.java     |  61 +++
 xdocs/images/screenshots/webtest/threadgroup3.png  | Bin 0 -> 64355 bytes
 xdocs/images/screenshots/webtest/threadgroup4.png  | Bin 0 -> 25471 bytes
 xdocs/images/screenshots/webtest/threadgroup5.png  | Bin 0 -> 29946 bytes
 xdocs/images/screenshots/webtest/threadgroup6.png  | Bin 0 -> 21381 bytes
 xdocs/usermanual/build-web-test-plan.xml           |  21 +
 24 files changed, 1022 insertions(+), 24 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index d53e617..37ad1a7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ sudo: false
 language: java
 
 before_script:
-  - test "x$RUN_CHECKSTYLE" != 'x' || ant -Djava.awt.headless=true download_jars install
+  - test "x$RUN_CHECKSTYLE" != 'x' || ant -Djava.awt.headless=true clean download_jars install
   - test "x$RUN_CHECKSTYLE" != 'xtrue' || ant -Djava.awt.headless=true download_checkstyle
 
 before_install:
diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties
index 9a67475..63e93bf 100644
--- a/src/core/org/apache/jmeter/resources/messages.properties
+++ b/src/core/org/apache/jmeter/resources/messages.properties
@@ -137,6 +137,7 @@ attribute_field=Attribute\:
 attrs=Attributes
 auth_base_url=Base URL
 auth_manager_clear_per_iter=Clear auth on each iteration?
+auth_manager_clear_controlled_by_threadgroup=Use Thread Group configuration to control clearing
 auth_manager_options=Options
 auth_manager_title=HTTP Authorization Manager
 auths_stored=Authorizations Stored in the Authorization Manager
@@ -175,6 +176,7 @@ busy_testing=I'm busy testing, please stop the test before changing settings
 cache_manager_size=Max Number of elements in cache
 cache_manager_title=HTTP Cache Manager
 cache_session_id=Cache Session Id?
+cache_clear_controlled_by_threadgroup=Use Thread Group configuration to control cache clearing
 cancel=Cancel
 cancel_exit_to_save=There are test items that have not been saved. Do you wish to save before exiting?
 cancel_new_from_template=There are test items that have not been saved. Do you wish to save before creating a test plan from selected template?
@@ -229,6 +231,7 @@ cookie_manager_policy=Cookie Policy:
 cookie_manager_title=HTTP Cookie Manager
 cookie_options=Options
 cookies_stored=User-Defined Cookies
+cookie_clear_controlled_by_threadgroup=Use Thread Group configuration to control cookie clearing
 copy=Copy
 counter_config_title=Counter
 counter_per_user=Track counter independently for each user
@@ -1280,6 +1283,8 @@ thread_group_title=Thread Group
 thread_group_scheduler_warning=If Loop Count is not -1 or Forever, duration will be min(Duration, Loop Count * iteration duration)
 thread_properties=Thread Properties
 threadgroup=Thread Group
+threadgroup_same_user=Same user on each iteration
+threadgroup_different_user=Different User on Each Iteration
 throughput_control_bynumber_label=Total Executions
 throughput_control_bypercent_label=Percent Executions
 throughput_control_perthread_label=Per User
diff --git a/src/core/org/apache/jmeter/resources/messages_fr.properties b/src/core/org/apache/jmeter/resources/messages_fr.properties
index f7883c2..ec03294 100644
--- a/src/core/org/apache/jmeter/resources/messages_fr.properties
+++ b/src/core/org/apache/jmeter/resources/messages_fr.properties
@@ -132,6 +132,7 @@ attribute_field=Attribut \:
 attrs=Attributs
 auth_base_url=URL de base
 auth_manager_clear_per_iter=Réauthentifier à chaque itération ?
+auth_manager_clear_controlled_by_threadgroup=Utiliser la configuration du groupe de threads pour contrôler la Réauthentification
 auth_manager_options=Options
 auth_manager_title=Gestionnaire d'autorisation HTTP
 auths_stored=Autorisations stockées
@@ -170,6 +171,7 @@ busy_testing=Je suis occupé à tester, veuillez arrêter le test avant de chang
 cache_manager_size=Nombre maximum d'éléments dans le cache
 cache_manager_title=Gestionnaire de cache HTTP
 cache_session_id=Identifiant de session de cache ?
+cache_clear_controlled_by_threadgroup=Utiliser thread group pour contrôler l'effacement de cache
 cancel=Annuler
 cancel_exit_to_save=Il y a des éléments qui n'ont pas été sauvés. Voulez-vous enregistrer avant de sortir ?
 cancel_new_from_template=Il y a des éléments qui n'ont pas été sauvés. Voulez-vous enregistrer avant de charger le modèle ?
@@ -224,6 +226,7 @@ cookie_manager_policy=Politique des cookies \:
 cookie_manager_title=Gestionnaire de cookies HTTP
 cookie_options=Options
 cookies_stored=Cookies stockés
+cookie_clear_controlled_by_threadgroup=Utiliser thread group pour contrôler l'effacement des cookies
 copy=Copier
 counter_config_title=Compteur
 counter_per_user=Suivre le compteur indépendamment pour chaque unité de test
@@ -1269,6 +1272,8 @@ thread_group_title=Groupe d'unités
 thread_group_scheduler_warning=Si le nombre de boucles n'est pas -1 ou Infini, la durée sera min (Durée, Nombre d'itérations * durée de l'itération).
 thread_properties=Propriétés du groupe d'unités
 threadgroup=Groupe d'unités
+threadgroup_same_user=Même utilisateur à chaque itération
+threadgroup_different_user=Utilisateur différent à chaque itération
 throughput_control_bynumber_label=Exécutions totales
 throughput_control_bypercent_label=Pourcentage d'exécution
 throughput_control_perthread_label=Par utilisateur
diff --git a/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java b/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
index daefdb7..06ecc63 100644
--- a/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
+++ b/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
@@ -30,6 +30,7 @@ import org.apache.jmeter.engine.event.LoopIterationListener;
 import org.apache.jmeter.samplers.Sampler;
 import org.apache.jmeter.testelement.AbstractTestElement;
 import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.property.BooleanProperty;
 import org.apache.jmeter.testelement.property.IntegerProperty;
 import org.apache.jmeter.testelement.property.JMeterProperty;
 import org.apache.jmeter.testelement.property.TestElementProperty;
@@ -72,6 +73,10 @@ public abstract class AbstractThreadGroup extends AbstractTestElement
     public static final String NUM_THREADS = "ThreadGroup.num_threads";
 
     public static final String MAIN_CONTROLLER = "ThreadGroup.main_controller";
+        
+    /** The same user or different users */
+    public static final String IS_SAME_USER_ON_NEXT_ITERATION = "ThreadGroup.same_user_on_next_iteration";
+
 
     private final AtomicInteger numberOfThreads = new AtomicInteger(0); // Number of active threads in this group
 
@@ -299,4 +304,27 @@ public abstract class AbstractThreadGroup extends AbstractTestElement
     public void breakThreadLoop() {
         ((LoopController) getSamplerController()).breakLoop();
     }
+    
+    /**
+     * Set the kind of user
+     *
+     * @param isSameUserOnNextIteration
+     *            true is the same user on next iteration of loop
+     *            false is a different user on next iteration of loop
+     */
+    public void setIsSameUserOnNextIteration(boolean isSameUserOnNextIteration) {
+        setProperty(new BooleanProperty(IS_SAME_USER_ON_NEXT_ITERATION, isSameUserOnNextIteration));
+    }
+
+    /**
+     * Get kind of user:
+     * <ul>
+     *  <li>true means same user running multiple iterations</li>
+     *  <li>false means a different user for each iteration</li>
+     * </ul>
+     * @return the kind of user.
+     */
+    public boolean isSameUserOnNextIteration() {
+        return getPropertyAsBoolean(ThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION);
+    }
 }
diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java
index 03eb44d..2cec5e7 100644
--- a/src/core/org/apache/jmeter/threads/JMeterThread.java
+++ b/src/core/org/apache/jmeter/threads/JMeterThread.java
@@ -123,8 +123,10 @@ public class JMeterThread implements Runnable, Interruptible {
 
     private long endTime = 0;
 
-    private boolean scheduler = false;
+    private final boolean isSameUserOnNextIteration;
+
     // based on this scheduler is enabled or disabled
+    private boolean scheduler = false;
 
     // Gives access to parent thread threadGroup
     private AbstractThreadGroup threadGroup;
@@ -149,6 +151,10 @@ public class JMeterThread implements Runnable, Interruptible {
     private final ReentrantLock interruptLock = new ReentrantLock(); // ensure that interrupt cannot overlap with shutdown
 
     public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note) {
+        this(test, monitor, note, false);
+    }
+    
+    public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note,Boolean isSameUserOnNextIteration) {
         this.monitor = monitor;
         threadVars = new JMeterVariables();
         testTree = test;
@@ -162,6 +168,7 @@ public class JMeterThread implements Runnable, Interruptible {
         sampleMonitors = sampleMonitorSearcher.getSearchResults();
         notifier = note;
         running = true;
+        this.isSameUserOnNextIteration = isSameUserOnNextIteration;
     }
 
     public void setInitialContext(JMeterContext context) {
@@ -237,13 +244,11 @@ public class JMeterThread implements Runnable, Interruptible {
     public void setThreadName(String threadName) {
         this.threadName = threadName;
     }
-
     @Override
     public void run() {
         // threadContext is not thread-safe, so keep within thread
         JMeterContext threadContext = JMeterContextService.getContext();
         LoopIterationListener iterationListener = null;
-
         try {
             iterationListener = initRun(threadContext);
             while (running) {
@@ -687,6 +692,7 @@ public class JMeterThread implements Runnable, Interruptible {
      * @return the iteration listener
      */
     private IterationListener initRun(JMeterContext threadContext) {
+        threadVars.putObject(JMeterVariables.VAR_IS_SAME_USER_KEY, isSameUserOnNextIteration);
         threadContext.setVariables(threadVars);
         threadContext.setThreadNum(getThreadNum());
         threadContext.getVariables().put(LAST_SAMPLE_OK, TRUE);
diff --git a/src/core/org/apache/jmeter/threads/JMeterVariables.java b/src/core/org/apache/jmeter/threads/JMeterVariables.java
index 7a005ed..8180e48 100644
--- a/src/core/org/apache/jmeter/threads/JMeterVariables.java
+++ b/src/core/org/apache/jmeter/threads/JMeterVariables.java
@@ -44,6 +44,8 @@ public class JMeterVariables {
       "TESTSTART.MS", // $NON-NLS-1$
     };
 
+    static final String VAR_IS_SAME_USER_KEY = "__jmv_SAME_USER";
+
     /**
      * Constructor, that preloads the variables from the JMeter properties
      */
@@ -171,4 +173,11 @@ public class JMeterVariables {
     public Set<Entry<String, Object>> entrySet(){
         return Collections.unmodifiableMap(variables).entrySet();
     }
+
+    /**
+     * @return boolean true if user is the same on next iteration of Thread loop, false otherwise
+     */
+    public boolean isSameUserOnNextIteration() {
+        return Boolean.TRUE.equals(variables.get(VAR_IS_SAME_USER_KEY));
+    }
 }
diff --git a/src/core/org/apache/jmeter/threads/ThreadGroup.java b/src/core/org/apache/jmeter/threads/ThreadGroup.java
index 72c2d7a..6e70fe2 100644
--- a/src/core/org/apache/jmeter/threads/ThreadGroup.java
+++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java
@@ -69,7 +69,6 @@ public class ThreadGroup extends AbstractThreadGroup {
 
     /** Scheduler start delay, overrides start time */
     public static final String DELAY = "ThreadGroup.delay";
-
     //- JMX entries
 
     private transient Thread threadStarter;
@@ -216,6 +215,7 @@ public class ThreadGroup extends AbstractThreadGroup {
         this.threadGroupTree = threadGroupTree;
         int numThreads = getNumThreads();
         int rampUpPeriodInSeconds = getRampUp();
+        boolean isSameUserOnNextIteration = isSameUserOnNextIteration();
         delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant
         log.info("Starting thread group... number={} threads={} ramp-up={} delayedStart={}", groupNumber,
                 numThreads, rampUpPeriodInSeconds, delayedStartup);
@@ -236,11 +236,11 @@ public class ThreadGroup extends AbstractThreadGroup {
                     delayForNextThreadInMillis += perThreadDelayInMillis - timeElapsedToStartLastThread;
                 }
                 if (log.isDebugEnabled()) {
-                    log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis);
+                    log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis, Thread.currentThread().getId());
                 }
                 lastThreadStartInMillis = nowInMillis;
-                startNewThread(notifier, threadGroupTree, engine, threadNum, context, nowInMillis,
-                        Math.max(0, delayForNextThreadInMillis));
+                startNewThread(notifier, threadGroupTree, engine, threadNum, context, 
+                        nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUserOnNextIteration);
             }
         }
         log.info("Started thread group number {}", groupNumber);
@@ -255,11 +255,12 @@ public class ThreadGroup extends AbstractThreadGroup {
      * @param context {@link JMeterContext}
      * @param now Nom in milliseconds
      * @param delay int delay in milliseconds
+     * @param isSameUserOnNextIteration boolean indicating a next iteration will simulate a new or returning user
      * @return {@link JMeterThread} newly created
      */
     private JMeterThread startNewThread(ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine,
-            int threadNum, final JMeterContext context, long now, int delay) {
-        JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context);
+            int threadNum, final JMeterContext context, long now, int delay, Boolean isSameUserOnNextIteration) {
+        JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context, isSameUserOnNextIteration);
         scheduleThread(jmThread, now); // set start and end time
         jmThread.setInitialDelay(delay);
         Thread newThread = new Thread(jmThread, jmThread.getThreadName());
@@ -292,18 +293,19 @@ public class ThreadGroup extends AbstractThreadGroup {
      * @param engine {@link StandardJMeterEngine}
      * @param threadNumber int thread number
      * @param context {@link JMeterContext}
+     * @param isSameUserOnNextIteration Boolean 
      * @return {@link JMeterThread}
      */
     private JMeterThread makeThread(
             ListenerNotifier notifier, ListedHashTree threadGroupTree,
-            StandardJMeterEngine engine, int threadNumber,
-            JMeterContext context) { // N.B. Context needs to be fetched in the correct thread
+            StandardJMeterEngine engine, int threadNumber, 
+            JMeterContext context, Boolean isSameUserOnNextIteration) { // N.B. Context needs to be fetched in the correct thread
         boolean onErrorStopTest = getOnErrorStopTest();
         boolean onErrorStopTestNow = getOnErrorStopTestNow();
         boolean onErrorStopThread = getOnErrorStopThread();
         boolean onErrorStartNextLoop = getOnErrorStartNextLoop();
         String groupName = getName();
-        final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier);
+        final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier, isSameUserOnNextIteration);
         jmeterThread.setThreadNum(threadNumber);
         jmeterThread.setThreadGroup(this);
         jmeterThread.setInitialContext(context);
@@ -329,7 +331,7 @@ public class ThreadGroup extends AbstractThreadGroup {
             numThreads = getNumThreads();
             setNumThreads(numThreads + 1);
         }
-        newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay);
+        newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay, isSameUserOnNextIteration());
         JMeterContextService.addTotalThreads( 1 );
         log.info("Started new thread in group {}", groupNumber);
         return newJmThread;
@@ -597,6 +599,7 @@ public class ThreadGroup extends AbstractThreadGroup {
                 final int numThreads = getNumThreads();
                 final float rampUpOriginInMillis = (float) getRampUp() * 1000;
                 final long startTimeInMillis = System.currentTimeMillis();
+                final boolean isSameUserOnNextIteration = isSameUserOnNextIteration();
                 for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) {
                     if (threadNumber > 0) {
                         long elapsedInMillis = System.currentTimeMillis() - startTimeInMillis;
@@ -607,7 +610,7 @@ public class ThreadGroup extends AbstractThreadGroup {
                     if (usingScheduler && System.currentTimeMillis() > endtime) {
                         break; // no point continuing beyond the end time
                     }
-                    JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context);
+                    JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context, isSameUserOnNextIteration);
                     jmThread.setInitialDelay(0);   // Already waited
                     if (usingScheduler) {
                         jmThread.setScheduled(true);
diff --git a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java
index 1c05e8f..1010c3a 100644
--- a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java
+++ b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java
@@ -23,15 +23,18 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 
 import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
 import javax.swing.ImageIcon;
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.JRadioButton;
 import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import org.apache.jmeter.control.LoopController;
 import org.apache.jmeter.control.gui.LoopControlPanel;
+import org.apache.jmeter.gui.util.HorizontalPanel;
 import org.apache.jmeter.gui.util.VerticalPanel;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.testelement.property.BooleanProperty;
@@ -62,6 +65,10 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
 
     private JTextField delay; // Relative start-up time
 
+    private JRadioButton sameUserBox;
+
+    private JRadioButton differentUserBox;
+
     public ThreadGroupGui() {
         this(true);
     }
@@ -100,6 +107,7 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
         tg.setProperty(new BooleanProperty(ThreadGroup.SCHEDULER, scheduler.isSelected()));
         tg.setProperty(ThreadGroup.DURATION, duration.getText());
         tg.setProperty(ThreadGroup.DELAY, delay.getText());
+        tg.setProperty(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,sameUserBox.isSelected());
     }
 
     @Override
@@ -117,6 +125,12 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
 
         duration.setText(tg.getPropertyAsString(ThreadGroup.DURATION));
         delay.setText(tg.getPropertyAsString(ThreadGroup.DELAY));
+        final boolean isSameUser = tg.getPropertyAsBoolean(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION, false);
+        if (isSameUser){
+            sameUserBox.setSelected(true);
+        } else {
+            differentUserBox.setSelected(true);
+        }
     }
 
     @Override
@@ -193,6 +207,8 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
         scheduler.setSelected(false);
         delay.setText(""); // $NON-NLS-1$
         duration.setText(""); // $NON-NLS-1$
+        sameUserBox.setSelected(true);
+        differentUserBox.setSelected(false);
     }
 
    private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
@@ -228,7 +244,7 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
 
         // LOOP COUNT
         threadPropsPanel.add(createControllerPanel());
-
+        threadPropsPanel.add(createUserOptionsPanel());  
         if (showDelayedStart) {
             delayedStart = new JCheckBox(JMeterUtils.getResString("delayed_start")); // $NON-NLS-1$
             threadPropsPanel.add(delayedStart);
@@ -252,4 +268,17 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
         intgrationPanel.add(mainPanel);
         add(intgrationPanel, BorderLayout.CENTER);
     }
+   
+   private JPanel createUserOptionsPanel(){
+       ButtonGroup group = new ButtonGroup();
+       sameUserBox = new JRadioButton(JMeterUtils.getResString("threadgroup_same_user")); //$NON-NLS-1$
+       group.add(sameUserBox);
+       sameUserBox.setSelected(true);
+       differentUserBox = new JRadioButton(JMeterUtils.getResString("threadgroup_different_user")); //$NON-NLS-1$
+       group.add(differentUserBox);
+       JPanel optionsPanel = new HorizontalPanel();
+       optionsPanel.add(sameUserBox);
+       optionsPanel.add(differentUserBox);
+       return optionsPanel;
+   }
 }
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java
index 13ed6d3..8bc5148 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java
@@ -45,9 +45,12 @@ import org.apache.jmeter.engine.event.LoopIterationEvent;
 import org.apache.jmeter.protocol.http.util.HTTPConstants;
 import org.apache.jmeter.testelement.TestIterationListener;
 import org.apache.jmeter.testelement.TestStateListener;
+import org.apache.jmeter.testelement.property.BooleanProperty;
 import org.apache.jmeter.testelement.property.CollectionProperty;
 import org.apache.jmeter.testelement.property.JMeterProperty;
 import org.apache.jmeter.testelement.property.TestElementProperty;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.util.JOrphanUtils;
 import org.slf4j.Logger;
@@ -70,6 +73,8 @@ public class AuthManager extends ConfigTestElement implements TestStateListener,
 
     private static final String AUTH_LIST = "AuthManager.auth_list"; //$NON-NLS-1$
 
+    private static final String CONTROLLED_BY_THREADGROUP = "AuthManager.controlledByThreadGroup";// $NON-NLS-1$
+
     private static final String[] COLUMN_RESOURCE_NAMES = {
         "auth_base_url", //$NON-NLS-1$
         "username",      //$NON-NLS-1$
@@ -549,8 +554,18 @@ public class AuthManager extends ConfigTestElement implements TestStateListener,
     /** {@inheritDoc} */
     @Override
     public void testIterationStart(LoopIterationEvent event) {
-        if (getClearEachIteration()) {
+        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+        if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration())
+                || (!getControlledByThread() && getClearEachIteration())) {
             kerberosManager.clearSubjects();
         }
     }
+    
+    public boolean getControlledByThread() {
+        return getPropertyAsBoolean(CONTROLLED_BY_THREADGROUP);
+    }
+
+    public void setControlledByThread(boolean control) {
+        setProperty(new BooleanProperty(CONTROLLED_BY_THREADGROUP, control));
+    }
 }
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java
index a75a6f8..529e460 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java
@@ -49,6 +49,8 @@ import org.apache.jmeter.protocol.http.util.HTTPConstants;
 import org.apache.jmeter.testelement.TestIterationListener;
 import org.apache.jmeter.testelement.TestStateListener;
 import org.apache.jmeter.testelement.property.BooleanProperty;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
 import org.apache.jmeter.util.JMeterUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,6 +68,7 @@ public class CacheManager extends ConfigTestElement implements TestStateListener
     private static final int DEFAULT_MAX_SIZE = 5000;
     private static final long ONE_YEAR_MS = 365*24*60*60*1000L;
     private static final String[] CACHEABLE_METHODS = JMeterUtils.getPropDefault("cacheable_methods", "GET").split("[ ,]");
+    private static final String CONTROLLED_BY_THREAD = "CacheManager.controlledByThread";// $NON-NLS-1$
 
     static {
         if (log.isInfoEnabled()) {
@@ -99,6 +102,13 @@ public class CacheManager extends ConfigTestElement implements TestStateListener
         this.localCache = localCache;
         this.useExpires = useExpires;
     }
+    public boolean getControlledByThread() {
+        return getPropertyAsBoolean(CONTROLLED_BY_THREAD);
+    }
+
+    public void setControlledByThread(boolean control) {
+        setProperty(new BooleanProperty(CONTROLLED_BY_THREAD, control));
+    }
 
     /*
      * Holder for storing cache details.
@@ -622,7 +632,9 @@ public class CacheManager extends ConfigTestElement implements TestStateListener
 
     @Override
     public void testIterationStart(LoopIterationEvent event) {
-        if (getClearEachIteration()) {
+        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+        if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) 
+                || (!getControlledByThread() && getClearEachIteration())) {
             clearCache();
         }
         useExpires = getUseExpires(); // cache the value
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java
index 1dc80a4..189030a 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java
@@ -40,6 +40,8 @@ import org.apache.jmeter.testelement.property.CollectionProperty;
 import org.apache.jmeter.testelement.property.JMeterProperty;
 import org.apache.jmeter.testelement.property.PropertyIterator;
 import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.reflect.ClassTools;
 import org.apache.jorphan.util.JMeterException;
@@ -61,6 +63,7 @@ public class CookieManager extends ConfigTestElement implements TestStateListene
     private static final String COOKIES = "CookieManager.cookies";// $NON-NLS-1$
     private static final String POLICY = "CookieManager.policy"; //$NON-NLS-1$
     private static final String IMPLEMENTATION = "CookieManager.implementation"; //$NON-NLS-1$
+    private static final String CONTROLLED_BY_THREADGROUP = "CookieManager.controlledByThreadGroup";// $NON-NLS-1$
     //-- JMX tag values
 
     private static final String TAB = "\t"; //$NON-NLS-1$
@@ -149,6 +152,13 @@ public class CookieManager extends ConfigTestElement implements TestStateListene
     public void setClearEachIteration(boolean clear) {
         setProperty(new BooleanProperty(CLEAR, clear));
     }
+    public boolean getControlledByThread() {
+        return getPropertyAsBoolean(CONTROLLED_BY_THREADGROUP);
+    }
+
+    public void setControlledByThread(boolean control) {
+        setProperty(new BooleanProperty(CONTROLLED_BY_THREADGROUP, control));
+    }
 
     public String getImplementation() {
         return getPropertyAsString(IMPLEMENTATION, DEFAULT_IMPLEMENTATION);
@@ -428,7 +438,9 @@ public class CookieManager extends ConfigTestElement implements TestStateListene
     /** {@inheritDoc} */
     @Override
     public void testIterationStart(LoopIterationEvent event) {
-        if (getClearEachIteration()) {
+        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+        if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) 
+                || (!getControlledByThread() && getClearEachIteration())) {
             log.debug("Initialise cookies from pre-defined list");
             // No need to call clear
             setProperty(initialCookies.clone());
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java
index 1cf6aab..9b34541 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java
@@ -73,10 +73,14 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
 
     private static final String SAVE_COMMAND = "Save"; //$NON-NLS-1$
 
+    private static final String CONTROLLED_BY_THREADGROUP = "Controlled_By_ThreadGroup"; //$NON-NLS-1$
+
     private InnerTableModel tableModel;
 
     private JCheckBox clearEachIteration;
 
+    private JCheckBox controlledByThreadGroup;
+
     /**
      * A table to show the authentication information.
      */
@@ -118,6 +122,7 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
         authManager.clear();
         authManager.addTestElement((TestElement) tableModel.manager.clone());
         authManager.setClearEachIteration(clearEachIteration.isSelected());
+        authManager.setControlledByThread(controlledByThreadGroup.isSelected());
         configureTestElement(el);
     }
 
@@ -132,14 +137,17 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
         deleteButton.setEnabled(false);
         saveButton.setEnabled(false);
         clearEachIteration.setSelected(false);
+        controlledByThreadGroup.setSelected(false);
     }
 
     @Override
     public void configure(TestElement el) {
         super.configure(el);
         tableModel.manager.clear();
-        tableModel.manager.addTestElement((AuthManager) el.clone());
-        clearEachIteration.setSelected(((AuthManager) el).getClearEachIteration());
+        AuthManager authManager = (AuthManager) el; 
+        tableModel.manager.addTestElement((AuthManager) authManager.clone());
+        clearEachIteration.setSelected(authManager.getClearEachIteration());
+        controlledByThreadGroup.setSelected(authManager.getControlledByThread());
         checkButtonsStatus();
     }
 
@@ -166,7 +174,14 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
         optionsPane.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));
         clearEachIteration =
                 new JCheckBox(JMeterUtils.getResString("auth_manager_clear_per_iter"), false); //$NON-NLS-1$
+
+        controlledByThreadGroup = 
+                new JCheckBox(JMeterUtils.getResString("auth_manager_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$
+        controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP);
+        controlledByThreadGroup.addActionListener(this);
+
         optionsPane.add(clearEachIteration);
+        optionsPane.add(controlledByThreadGroup);
         northPanel.add(optionsPane);
         add(northPanel, BorderLayout.NORTH);
 
@@ -220,6 +235,10 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
     public void actionPerformed(ActionEvent e) {
         String action = e.getActionCommand();
 
+        if (action.equals(CONTROLLED_BY_THREADGROUP)) {
+            clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected());
+        }
+        
         if (action.equals(DELETE_COMMAND)) {
             deleteRows();
         }
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java
index 0c1c789..8f9ada6 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java
@@ -19,6 +19,8 @@
 package org.apache.jmeter.protocol.http.gui;
 
 import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
@@ -36,13 +38,15 @@ import org.apache.jorphan.gui.layout.VerticalLayout;
  * The GUI for the HTTP Cache Manager {@link CacheManager}
  */
 @GUIMenuSortOrder(4)
-public class CacheManagerGui extends AbstractConfigGui {
+public class CacheManagerGui extends AbstractConfigGui implements ActionListener {
 
     private static final long serialVersionUID = 240L;
+    private static final String CONTROLLED_BY_THREADGROUP = "controlled_By_ThreadGroup"; //$NON-NLS-1$
 
     private JCheckBox clearEachIteration;
     private JCheckBox useExpires;
     private JTextField maxCacheSize;
+    private JCheckBox controlledByThreadGroup;
 
     public CacheManagerGui() {
         init();
@@ -57,15 +61,18 @@ public class CacheManagerGui extends AbstractConfigGui {
     public void configure(TestElement element) {
         super.configure(element);
         final CacheManager cacheManager = (CacheManager)element;
-        clearEachIteration.setSelected(cacheManager.getClearEachIteration());
         useExpires.setSelected(cacheManager.getUseExpires());
         maxCacheSize.setText(Integer.toString(cacheManager.getMaxSize()));
+        controlledByThreadGroup.setSelected(cacheManager.getControlledByThread());
+        clearEachIteration.setSelected(cacheManager.getClearEachIteration());
     }
 
     @Override
     public TestElement createTestElement() {
         CacheManager element = new CacheManager();
         modifyTestElement(element);
+        controlledByThreadGroup.setSelected(element.getControlledByThread());
+        clearEachIteration.setEnabled(!element.getControlledByThread());
         return element;
     }
 
@@ -75,6 +82,7 @@ public class CacheManagerGui extends AbstractConfigGui {
         final CacheManager cacheManager = (CacheManager)element;
         cacheManager.setClearEachIteration(clearEachIteration.isSelected());
         cacheManager.setUseExpires(useExpires.isSelected());
+        cacheManager.setControlledByThread(controlledByThreadGroup.isSelected());
         try {
             cacheManager.setMaxSize(Integer.parseInt(maxCacheSize.getText()));
         } catch (NumberFormatException ignored) {
@@ -91,6 +99,7 @@ public class CacheManagerGui extends AbstractConfigGui {
         clearEachIteration.setSelected(false);
         useExpires.setSelected(true);
         maxCacheSize.setText(""); //$NON-NLS-1$
+        controlledByThreadGroup.setSelected(false);
     }
 
     /**
@@ -102,12 +111,19 @@ public class CacheManagerGui extends AbstractConfigGui {
         setBorder(makeBorder());
 
         clearEachIteration = new JCheckBox(JMeterUtils.getResString("clear_cache_per_iter"), false); // $NON-NLS-1$
+        
+        controlledByThreadGroup = 
+                new JCheckBox(JMeterUtils.getResString("cache_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$
+        controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP);
+        controlledByThreadGroup.addActionListener(this);
+        
         useExpires = new JCheckBox(JMeterUtils.getResString("use_expires"), false); // $NON-NLS-1$
 
         JPanel northPanel = new JPanel();
         northPanel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));
         northPanel.add(makeTitlePanel());
         northPanel.add(clearEachIteration);
+        northPanel.add(controlledByThreadGroup);
         northPanel.add(useExpires);
 
         JLabel label = new JLabel(JMeterUtils.getResString("cache_manager_size")); //$NON-NLS-1$
@@ -122,4 +138,11 @@ public class CacheManagerGui extends AbstractConfigGui {
         add(northPanel, BorderLayout.NORTH);
     }
 
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        String action = e.getActionCommand();
+        if (action.equals(CONTROLLED_BY_THREADGROUP)) {
+            clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected());
+        }
+    }
 }
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java
index 61f5d6f..4f77a11 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java
@@ -69,6 +69,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
     private static final String DELETE_COMMAND = "Delete"; //$NON-NLS-1$
     private static final String LOAD_COMMAND = "Load"; //$NON-NLS-1$
     private static final String SAVE_COMMAND = "Save"; //$NON-NLS-1$
+    private static final String CONTROLLED_BY_THREADGROUP = "Controlled_By_ThreadGroup"; //$NON-NLS-1$
     //--
 
     private static final String DEFAULT_POLICY = HC4CookieHandler.DEFAULT_POLICY_NAME;
@@ -90,6 +91,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
     private JTable cookieTable;
     private PowerTableModel tableModel;
     private JCheckBox clearEachIteration;
+    private JCheckBox controlledByThreadGroup;
     private JButton addButton;
     private JButton deleteButton;
     private JButton loadButton;
@@ -108,6 +110,9 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
     @Override
     public void actionPerformed(ActionEvent e) {
         String action = e.getActionCommand();
+        if (action.equals(CONTROLLED_BY_THREADGROUP)) {
+          clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected());
+        }
 
         if (action.equals(DELETE_COMMAND)) {
             if (tableModel.getRowCount() > 0) {
@@ -204,6 +209,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
                 cookieManager.add(cookie);
             }
             cookieManager.setClearEachIteration(clearEachIteration.isSelected());
+            cookieManager.setControlledByThread(controlledByThreadGroup.isSelected());
             cookieManager.setCookiePolicy(policy.getText());
         }
     }
@@ -217,6 +223,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
 
         tableModel.clearData();
         clearEachIteration.setSelected(false);
+        controlledByThreadGroup.setSelected(false);
         policy.setText(DEFAULT_POLICY);
         configureButtonsState();
     }
@@ -265,6 +272,8 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
         clearEachIteration.setSelected(cookieManager.getClearEachIteration());
         // must set policy after setting handler (which may change the policy)
         policy.setText(cookieManager.getPolicy());
+        controlledByThreadGroup.setSelected(cookieManager.getControlledByThread());
+        clearEachIteration.setEnabled(!cookieManager.getControlledByThread());
     }
 
     /**
@@ -274,6 +283,10 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
         tableModel = new PowerTableModel(COLUMN_RESOURCE_NAMES, columnClasses);
         clearEachIteration =
             new JCheckBox(JMeterUtils.getResString("clear_cookies_per_iter"), false); //$NON-NLS-1$
+        controlledByThreadGroup = 
+                new JCheckBox(JMeterUtils.getResString("cookie_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$
+        controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP);
+        controlledByThreadGroup.addActionListener(this);
         policy = new JLabeledChoice(
                 JMeterUtils.getResString("cookie_manager_policy"), //$NON-NLS-1$
                 new HC4CookieHandler().getPolicies());
@@ -288,6 +301,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
                 JMeterUtils.getResString("cookie_options"))); // $NON-NLS-1$
         optionsPane.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));
         optionsPane.add(clearEachIteration);
+        optionsPane.add(controlledByThreadGroup);
         JPanel policyTypePane = new JPanel();
         policyTypePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
         policyTypePane.add(policy);
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
index aad06a9..f93728f 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
@@ -1790,7 +1790,17 @@ public class HTTPHC4Impl extends HTTPHCAbstractImpl {
         log.debug("notifyFirstSampleAfterLoopRestart called "
                 + "with config(httpclient.reset_state_on_thread_group_iteration={})",
                 Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION));
-        resetStateOnThreadGroupIteration.set(Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION));
+        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+        if (jMeterVariables.isSameUserOnNextIteration()) {
+            log.debug("Thread Group is configured to simulate a returning visitor on each iteration, ignoring property value {}", 
+                    RESET_STATE_ON_THREAD_GROUP_ITERATION);
+            resetStateOnThreadGroupIteration.set(false);
+        } else {
+            log.debug("Thread Group is configured to simulate a new visitor on each iteration, using property value {}", 
+                    RESET_STATE_ON_THREAD_GROUP_ITERATION);
+            resetStateOnThreadGroupIteration.set(Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION));
+        }
+        log.debug("Thread state will be reset ?: {}", RESET_STATE_ON_THREAD_GROUP_ITERATION);
     }
 
     @Override
diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java
new file mode 100644
index 0000000..4282a2d
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java
@@ -0,0 +1,117 @@
+/*
+ * 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.protocol.http.control;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.security.auth.Subject;
+
+import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestAuthManagerThreadIteration {
+    private JMeterContext jmctx;
+    private JMeterVariables jmvars;
+    private static final String SAME_USER = "__jmv_SAME_USER";
+    private final ConcurrentMap<String, Future<Subject>> subjects = new ConcurrentHashMap<>();
+
+    public KerberosManager initKerberosManager() throws IllegalAccessException, NoSuchFieldException {
+        KerberosManager kerberosManager = new KerberosManager();
+        Future<Subject> future = Executors.newSingleThreadExecutor().submit(new Callable<Subject>() {
+            @Override
+            public Subject call() throws Exception {
+                return new Subject();
+            }
+        });
+        subjects.put("test", future);
+        Field privateField = kerberosManager.getClass().getDeclaredField("subjects");
+        privateField.setAccessible(true);
+        privateField.set(kerberosManager, subjects);
+        return kerberosManager;
+    }
+    @Before
+    public void setUp() {
+        jmctx = JMeterContextService.getContext();
+        jmvars = new JMeterVariables();
+    }
+
+    @Test
+    public void testJmeterVariableAuthorizationWhenThreadIterationIsADifferentUser()
+            throws IllegalAccessException, NoSuchFieldException {
+        //Test button clear after each Iteration
+        KerberosManager kerberosManager=initKerberosManager();
+        AuthManager authManager = new AuthManager();
+        Field authPrivateField = authManager.getClass().getDeclaredField("kerberosManager");
+        authPrivateField.setAccessible(true);
+        authPrivateField.set(authManager, kerberosManager);
+        assertNotNull("Before the iteration, the AuthManager shouldn't be cleared",subjects.get("test"));
+        authManager.setControlledByThread(false);
+        authManager.setClearEachIteration(true);
+        authManager.testIterationStart(null);
+        assertNull("After the iteration, the AuthManager should be cleared",subjects.get("test"));
+        //Test button controlled by Thread
+        kerberosManager=initKerberosManager();
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        authManager.setThreadContext(jmctx);
+        authPrivateField.set(authManager, kerberosManager);
+        assertNotNull("Before the iteration, the AuthManager shouldn't be cleared",subjects.get("test"));
+        authManager.setControlledByThread(true);
+        authManager.setClearEachIteration(false);
+        authManager.testIterationStart(null);
+        assertNull("After the iteration, the AuthManager should be cleared",subjects.get("test"));
+    }
+
+    @Test
+    public void testJmeterVariableAuthorizationWhenThreadIterationIsASameUser()
+            throws IllegalAccessException, NoSuchFieldException {
+        // Test button clear after each Iteration
+        KerberosManager kerberosManager = initKerberosManager();
+        AuthManager authManager = new AuthManager();
+        Field authPrivateField = authManager.getClass().getDeclaredField("kerberosManager");
+        authPrivateField.setAccessible(true);
+        authPrivateField.set(authManager, kerberosManager);
+        assertNotNull("Before the iteration, the AuthManager shouldn't be cleared", subjects.get("test"));
+        authManager.setControlledByThread(false);
+        authManager.setClearEachIteration(false);
+        authManager.testIterationStart(null);
+        assertNotNull("After the iteration, the AuthManager shouldn't be cleared", subjects.get("test"));
+        // Test button controlled by Thread
+        kerberosManager = initKerberosManager();
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        authManager.setThreadContext(jmctx);
+        authPrivateField.set(authManager, kerberosManager);
+        assertNotNull("Before the iteration, the AuthManager shouldn't be cleared", subjects.get("test"));
+        authManager.setControlledByThread(true);
+        authManager.setClearEachIteration(false);
+        authManager.testIterationStart(null);
+        assertNotNull("After the iteration, the AuthManager shouldn't be cleared", subjects.get("test"));
+    }
+}
diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java
new file mode 100644
index 0000000..d434416f
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java
@@ -0,0 +1,427 @@
+/*
+ * 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.protocol.http.control;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.message.AbstractHttpMessage;
+import org.apache.http.message.BasicHeader;
+import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
+import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
+import org.apache.jmeter.protocol.http.util.HTTPConstants;
+import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test {@link CacheManager} that uses HTTPHC4Impl
+ */
+public class TestCacheManagerThreadIteration {
+    private JMeterContext jmctx;
+    private JMeterVariables jmvars;
+    private static final String SAME_USER="__jmv_SAME_USER";
+    protected static final String LOCAL_HOST = "http://localhost/";
+    protected static final String EXPECTED_ETAG = "0xCAFEBABEDEADBEEF";
+    protected static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+    protected CacheManager cacheManager;
+    protected String currentTimeInGMT;
+    protected String vary = null;
+    protected URL url;
+    protected HTTPSampleResult sampleResultOK;
+
+    private class HttpResponseStub extends AbstractHttpMessage implements HttpResponse {
+        private org.apache.http.Header lastModifiedHeader;
+        private org.apache.http.Header etagHeader;
+        private String expires;
+        private String cacheControl;
+        private org.apache.http.Header dateHeader;
+        private List<org.apache.http.Header> headers;
+
+        public HttpResponseStub() {
+            this.headers = new ArrayList<>();
+            this.lastModifiedHeader = new BasicHeader(HTTPConstants.LAST_MODIFIED, currentTimeInGMT);
+            this.dateHeader = new BasicHeader(HTTPConstants.DATE, currentTimeInGMT);
+            this.etagHeader = new BasicHeader(HTTPConstants.ETAG, EXPECTED_ETAG);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see org.apache.http.message.AbstractHttpMessage#getAllHeaders()
+         */
+        @Override
+        public org.apache.http.Header[] getAllHeaders() {
+            return headers.toArray(new org.apache.http.Header[headers.size()]);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * org.apache.http.message.AbstractHttpMessage#addHeader(org.apache.http.Header)
+         */
+        @Override
+        public void addHeader(org.apache.http.Header header) {
+            headers.add(header);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * org.apache.http.message.AbstractHttpMessage#getFirstHeader(java.lang.String)
+         */
+        @Override
+        public Header getFirstHeader(String headerName) {
+            Header[] headers = getHeaders(headerName);
+            if (headers.length > 0) {
+                return headers[0];
+            }
+            return null;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * org.apache.http.message.AbstractHttpMessage#getLastHeader(java.lang.String)
+         */
+        @Override
+        public Header getLastHeader(String headerName) {
+            Header[] headers = getHeaders(headerName);
+            if (headers.length > 0) {
+                return headers[headers.length - 1];
+            }
+            return null;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see org.apache.http.message.AbstractHttpMessage#getHeaders(java.lang.String)
+         */
+        @Override
+        public Header[] getHeaders(String headerName) {
+            org.apache.http.Header header = null;
+            if (HTTPConstants.LAST_MODIFIED.equals(headerName)) {
+                header = this.lastModifiedHeader;
+            } else if (HTTPConstants.ETAG.equals(headerName)) {
+                header = this.etagHeader;
+            } else if (HTTPConstants.EXPIRES.equals(headerName)) {
+                header = expires == null ? null : new BasicHeader(HTTPConstants.EXPIRES, expires);
+            } else if (HTTPConstants.CACHE_CONTROL.equals(headerName)) {
+                header = cacheControl == null ? null : new BasicHeader(HTTPConstants.CACHE_CONTROL, cacheControl);
+            } else if (HTTPConstants.DATE.equals(headerName)) {
+                header = this.dateHeader;
+            } else if (HTTPConstants.VARY.equals(headerName)) {
+                header = vary == null ? null : new BasicHeader(HTTPConstants.VARY, vary);
+            }
+            if (header != null) {
+                return new org.apache.http.Header[] { header };
+            } else {
+                return super.getHeaders(headerName);
+            }
+        }
+
+        @Override
+        public ProtocolVersion getProtocolVersion() {
+            return null;
+        }
+
+        @Override
+        public StatusLine getStatusLine() {
+            return null;
+        }
+
+        @Override
+        public void setStatusLine(StatusLine statusline) {
+        }
+
+        @Override
+        public void setStatusLine(ProtocolVersion ver, int code) {
+        }
+
+        @Override
+        public void setStatusLine(ProtocolVersion ver, int code, String reason) {
+        }
+
+        @Override
+        public void setStatusCode(int code) throws IllegalStateException {
+        }
+
+        @Override
+        public void setReasonPhrase(String reason) throws IllegalStateException {
+        }
+
+        @Override
+        public HttpEntity getEntity() {
+            return null;
+        }
+
+        @Override
+        public void setEntity(HttpEntity entity) {
+        }
+
+        @Override
+        public Locale getLocale() {
+            return null;
+        }
+
+        @Override
+        public void setLocale(Locale loc) {
+        }
+    }
+
+    private class HttpPostStub extends HttpPost {
+        HttpPostStub() {
+        }
+
+        @Override
+        public java.net.URI getURI() {
+            try {
+                return url.toURI();
+            } catch (URISyntaxException e) {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    protected String makeDate(Date d) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
+        simpleDateFormat.setTimeZone(GMT);
+        return simpleDateFormat.format(d);
+    }
+
+    protected HTTPSampleResult getSampleResultWithSpecifiedResponseCode(String code) {
+        HTTPSampleResult sampleResult = new HTTPSampleResult();
+        sampleResult.setResponseCode(code);
+        sampleResult.setHTTPMethod("GET");
+        sampleResult.setURL(url);
+        return sampleResult;
+    }
+
+    private HttpRequestBase httpMethod;
+    private HttpResponse httpResponse;
+
+    @Before
+    public void setUp() throws Exception {
+        this.cacheManager = new CacheManager();
+        this.currentTimeInGMT = makeDate(new Date());
+        this.url = new URL(LOCAL_HOST);
+        this.sampleResultOK = getSampleResultWithSpecifiedResponseCode("200");
+        this.httpMethod = new HttpPostStub();
+        this.httpResponse = new HttpResponseStub();
+        this.httpMethod.setURI(this.url.toURI());
+        jmctx = JMeterContextService.getContext();
+        jmvars = new JMeterVariables();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        this.url = null;
+        this.httpMethod = null;
+        this.httpResponse = null;
+        this.cacheManager =  new CacheManager();
+        this.currentTimeInGMT = null;
+        this.sampleResultOK = null;
+    }
+
+    protected void setExpires(String expires) {
+        ((HttpResponseStub) httpResponse).expires = expires;
+    }
+
+    protected void setCacheControl(String cacheControl) {
+        ((HttpResponseStub) httpResponse).cacheControl = cacheControl;
+    }
+
+    protected void setLastModified(String lastModified) {
+        ((HttpResponseStub) httpResponse).lastModifiedHeader = new BasicHeader(HTTPConstants.LAST_MODIFIED,
+                lastModified);
+    }
+
+    protected void cacheResult(HTTPSampleResult result) {
+        this.cacheManager.saveDetails(httpResponse, result);
+    }
+
+    protected void addRequestHeader(String requestHeader, String value) {
+        this.httpMethod.addHeader(new BasicHeader(requestHeader, value));
+    }
+
+    protected void setRequestHeaders() {
+        this.cacheManager.setHeaders(this.url, this.httpMethod);
+    }
+
+    private Map<String, CacheManager.CacheEntry> getThreadCache() throws Exception {
+        Field threadLocalfield = CacheManager.class.getDeclaredField("threadCache");
+        threadLocalfield.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        ThreadLocal<Map<String, CacheManager.CacheEntry>> threadLocal = (ThreadLocal<Map<String, CacheManager.CacheEntry>>) threadLocalfield
+                .get(this.cacheManager);
+        return threadLocal.get();
+    }
+
+    protected CacheManager.CacheEntry getThreadCacheEntry(String url) throws Exception {
+        return getThreadCache().get(url);
+    }
+    @Test
+    public void testCacheControlCleared() throws Exception {
+        this.cacheManager.setUseExpires(true);
+        this.cacheManager.testIterationStart(null);
+        assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        Header[] headers = new Header[1];
+        assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers));
+        long start = System.currentTimeMillis();
+        setExpires(makeDate(new Date(start)));
+        setCacheControl("public, max-age=1");
+        cacheResult(sampleResultOK);
+        assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers));
+        this.cacheManager.setClearEachIteration(true);
+        this.cacheManager.testIterationStart(null);
+        assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers));
+    }
+
+    @Test
+    public void testJmeterVariableCacheWhenThreadIterationIsANewUser() {
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        cacheManager.setControlledByThread(true);
+        sampler.setCacheManager(cacheManager);
+        sampler.setThreadContext(jmctx);
+        boolean res = (boolean) cacheManager.getThreadContext().getVariables().getObject(SAME_USER);
+        assertTrue("When test different user on the different iternation, the cache should be cleared", res);
+    }
+
+    @Test
+    public void testJmeterVariableWhenThreadIterationIsSameUser() {
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        cacheManager.setControlledByThread(true);
+        sampler.setCacheManager(cacheManager);
+        sampler.setThreadContext(jmctx);
+        boolean res = (boolean) cacheManager.getThreadContext().getVariables().getObject(SAME_USER);
+        assertFalse("When test different user on the different iternation, the cache shouldn't be cleared", res);
+    }
+
+    @Test
+    public void testCacheManagerWhenThreadIterationIsANewUser() throws Exception {
+        //Controlled by ThreadGroup
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        this.cacheManager.setUseExpires(true);
+        this.cacheManager.testIterationStart(null);
+        assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        Header[] headers = new Header[1];
+        assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers));
+        long start = System.currentTimeMillis();
+        setExpires(makeDate(new Date(start)));
+        setCacheControl("public, max-age=1");
+        cacheResult(sampleResultOK);
+        this.cacheManager.setThreadContext(jmctx);
+        this.cacheManager.setControlledByThread(true);
+        assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers));
+        this.cacheManager.testIterationStart(null);
+        assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers));
+        
+        //Controlled by cacheManager
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        this.cacheManager.setThreadContext(jmctx);
+        start = System.currentTimeMillis();
+        setExpires(makeDate(new Date(start)));
+        setCacheControl("public, max-age=1");
+        cacheResult(sampleResultOK);
+        assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers));
+        this.cacheManager.setControlledByThread(false);
+        this.cacheManager.setClearEachIteration(true);
+        this.cacheManager.testIterationStart(null);
+        assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers));
+    }
+
+    @Test
+    public void testCacheManagerWhenThreadIterationIsSameUser() throws Exception {
+        // Controlled by ThreadGroup
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        this.cacheManager.setUseExpires(true);
+        this.cacheManager.testIterationStart(null);
+        assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST));
+        Header[] headers = new Header[1];
+        assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers));
+        long start = System.currentTimeMillis();
+        setExpires(makeDate(new Date(start)));
+        setCacheControl("public, max-age=1");
+        cacheResult(sampleResultOK);
+        this.cacheManager.setThreadContext(jmctx);
+        this.cacheManager.setControlledByThread(true);
+        assertNotNull("Before iteration, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("Before iteration, should find valid entry", this.cacheManager.inCache(url, headers));
+        this.cacheManager.testIterationStart(null);
+        assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("After iteration, should find valid entry", this.cacheManager.inCache(url, headers));
+        // Controlled by cacheManager
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        this.cacheManager.setThreadContext(jmctx);
+        start = System.currentTimeMillis();
+        setExpires(makeDate(new Date(start)));
+        setCacheControl("public, max-age=1");
+        cacheResult(sampleResultOK);
+        assertNotNull("Before iteration, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("Before iteration, should find valid entry", this.cacheManager.inCache(url, headers));
+        this.cacheManager.setControlledByThread(false);
+        this.cacheManager.setClearEachIteration(false);
+        this.cacheManager.testIterationStart(null);
+        assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST));
+        assertTrue("After iteration, should find valid entry", this.cacheManager.inCache(url, headers));
+    }
+
+}
diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java
new file mode 100644
index 0000000..20c0586
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java
@@ -0,0 +1,182 @@
+/*
+ * 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.protocol.http.control;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+
+import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
+import org.apache.jmeter.testelement.property.CollectionProperty;
+import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestCookieManagerThreadIteration {
+    private JMeterContext jmctx;
+    private JMeterVariables jmvars;
+    private static final String SAME_USER = "__jmv_SAME_USER";
+    private static final String DYNAMIC_COOKIE = "dynamic_cookie_added_by_user";
+    private static final String STATIC_COOKIE = "static_cookie";
+
+    @Before
+    public void setUp() {
+        jmctx = JMeterContextService.getContext();
+        jmvars = new JMeterVariables();
+    }
+
+    @Test
+    public void testJmeterVariableCookieWhenThreadIterationIsANewUser() {
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        CookieManager cookieManager = new CookieManager();
+        cookieManager.setControlledByThread(true);
+        sampler.setCookieManager(cookieManager);
+        sampler.setThreadContext(jmctx);
+        boolean res = (boolean) cookieManager.getThreadContext().getVariables().getObject(SAME_USER);
+        assertTrue("When test different users on the different iternations, the cookie should be cleared", res);
+    }
+
+    @Test
+    public void testJmeterVariableCookieWhenThreadIterationIsSameUser() {
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        CookieManager cookieManager = new CookieManager();
+        cookieManager.setControlledByThread(true);
+        sampler.setCookieManager(cookieManager);
+        sampler.setThreadContext(jmctx);
+        boolean res = (boolean) cookieManager.getThreadContext().getVariables().getObject(SAME_USER);
+        assertFalse("When test same user on the different iternations, the cookie shouldn't be cleared", res);
+    }
+
+    @Test
+    public void testCookieManagerWhenThreadIterationIsNewUser() throws NoSuchFieldException, IllegalAccessException {
+        // Controlled by Thread Group
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        CookieManager cookieManagerDynamic = new CookieManager();
+        cookieManagerDynamic.setThreadContext(jmctx);
+        cookieManagerDynamic.getCookies().clear();
+        cookieManagerDynamic.testStarted();
+        Cookie cookieDynamic = new Cookie();
+        cookieDynamic.setName(DYNAMIC_COOKIE);
+        cookieManagerDynamic.getCookies().addItem(cookieDynamic);
+        cookieManagerDynamic.setControlledByThread(true);
+        Field privateStringField = CookieManager.class.getDeclaredField("initialCookies");
+        privateStringField.setAccessible(true);
+        CookieManager cookieManagerStatic = new CookieManager();
+        Cookie cookieStatic = new Cookie();
+        cookieStatic.setName(STATIC_COOKIE);
+        cookieManagerStatic.getCookies().addItem(cookieStatic);
+        CollectionProperty initialCookies = cookieManagerStatic.getCookies();
+        privateStringField.set(cookieManagerDynamic, initialCookies);
+        assertTrue("Before the iteration,the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        cookieManagerDynamic.testIterationStart(null);
+        assertEquals("After the iteration, the value of cookie should be the initial cookies", STATIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("After the iteration, the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        // Controlled by CookieManager
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        cookieManagerDynamic.setThreadContext(jmctx);
+        cookieManagerDynamic.getCookies().clear();
+        cookieManagerDynamic.testStarted();
+        cookieDynamic.setName(DYNAMIC_COOKIE);
+        cookieManagerDynamic.getCookies().addItem(cookieDynamic);
+        cookieManagerDynamic.setClearEachIteration(true);
+        cookieManagerDynamic.setControlledByThread(false);
+        cookieManagerStatic.getCookies().clear();
+        cookieManagerStatic.getCookies().addItem(cookieStatic);
+        initialCookies = cookieManagerStatic.getCookies();
+        privateStringField.set(cookieManagerDynamic, initialCookies);
+        assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("Before the iteration,the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        cookieManagerDynamic.testIterationStart(null);
+        assertEquals("After the iteration, the value of cookie should be the initial cookies", STATIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("After the iteration, the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+    }
+
+    @Test
+    public void testCookieManagerWhenThreadIterationIsSameUser() throws NoSuchFieldException, IllegalAccessException {
+        // Controlled by Thread Group
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        CookieManager cookieManagerDynamic = new CookieManager();
+        cookieManagerDynamic.setThreadContext(jmctx);
+        cookieManagerDynamic.getCookies().clear();
+        cookieManagerDynamic.testStarted();
+        Cookie cookieDynamic = new Cookie();
+        cookieDynamic.setName(DYNAMIC_COOKIE);
+        cookieManagerDynamic.getCookies().addItem(cookieDynamic);
+        cookieManagerDynamic.setControlledByThread(true);
+        Field privateStringField = CookieManager.class.getDeclaredField("initialCookies");
+        privateStringField.setAccessible(true);
+        CookieManager cookieManagerStatic = new CookieManager();
+        Cookie cookieStatic = new Cookie();
+        cookieStatic.setName(STATIC_COOKIE);
+        cookieManagerStatic.getCookies().addItem(cookieStatic);
+        CollectionProperty initialCookies = cookieManagerStatic.getCookies();
+        privateStringField.set(cookieManagerDynamic, initialCookies);
+        assertTrue("Before the iteration,the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        cookieManagerDynamic.testIterationStart(null);
+        assertEquals("After the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("After the iteration, the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        
+        // Controlled by CookieManager
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        cookieManagerDynamic.setControlledByThread(false);
+        cookieManagerDynamic.getCookies().clear();
+        cookieManagerDynamic.testStarted();
+        cookieDynamic.setName(DYNAMIC_COOKIE);
+        cookieManagerDynamic.getCookies().addItem(cookieDynamic);
+        cookieManagerDynamic.setClearEachIteration(false);
+        cookieManagerStatic.getCookies().clear();
+        cookieStatic.setName(STATIC_COOKIE);
+        privateStringField.set(cookieManagerDynamic, initialCookies);
+        assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("Before the iteration,the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+        cookieManagerDynamic.testIterationStart(null);
+        assertEquals("After the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE,
+                cookieManagerDynamic.getCookies().get(0).getName());
+        assertTrue("After the iteration, the quantity of cookies should be 1",
+                cookieManagerDynamic.getCookies().size() == 1);
+    }
+}
diff --git a/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java b/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java
new file mode 100644
index 0000000..c63ee04
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java
@@ -0,0 +1,61 @@
+/*
+ * 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.protocol.http.sampler;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
+import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContextService;
+import org.apache.jmeter.threads.JMeterVariables;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestHTTPHC4Impl {
+    private JMeterContext jmctx;
+    private JMeterVariables jmvars;
+    private static final String SAME_USER = "__jmv_SAME_USER";
+
+    @Before
+    public void setUp() {
+        jmctx = JMeterContextService.getContext();
+        jmvars = new JMeterVariables();
+    }
+
+    @Test
+    public void testNotifyFirstSampleAfterLoopRestartWhenThreadIterationIsSameUser() {
+        jmvars.putObject(SAME_USER, true);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        sampler.setThreadContext(jmctx);
+        HTTPHC4Impl hc = new HTTPHC4Impl(sampler);
+        hc.notifyFirstSampleAfterLoopRestart();
+        assertFalse("User is the same, the state shouldn't be reset", HTTPHC4Impl.resetStateOnThreadGroupIteration.get());
+    }
+
+    @Test
+    public void testNotifyFirstSampleAfterLoopRestartWhenThreadIterationIsANewUser() {
+        jmvars.putObject(SAME_USER, false);
+        jmctx.setVariables(jmvars);
+        HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
+        sampler.setThreadContext(jmctx);
+        HTTPHC4Impl hc = new HTTPHC4Impl(sampler);
+        hc.notifyFirstSampleAfterLoopRestart();
+        assertTrue("Users are different, the state should be reset", HTTPHC4Impl.resetStateOnThreadGroupIteration.get());
+    }
+}
diff --git a/xdocs/images/screenshots/webtest/threadgroup3.png b/xdocs/images/screenshots/webtest/threadgroup3.png
new file mode 100644
index 0000000..ed6c8d4
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup3.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup4.png b/xdocs/images/screenshots/webtest/threadgroup4.png
new file mode 100644
index 0000000..56f16ff
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup4.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup5.png b/xdocs/images/screenshots/webtest/threadgroup5.png
new file mode 100644
index 0000000..72bec09
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup5.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup6.png b/xdocs/images/screenshots/webtest/threadgroup6.png
new file mode 100644
index 0000000..74ff0a6
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup6.png differ
diff --git a/xdocs/usermanual/build-web-test-plan.xml b/xdocs/usermanual/build-web-test-plan.xml
index 84f8e1c..6afe4ab 100644
--- a/xdocs/usermanual/build-web-test-plan.xml
+++ b/xdocs/usermanual/build-web-test-plan.xml
@@ -227,5 +227,26 @@ Figure &sect-num;.8. Sample HTTP login request</figure>
 
 </section>
 
+<section name="&sect-num;.7 choose the same user or different users" anchor="choose_users">
+<p>
+When creating a Test Plan, on each Thread Group iteration, we can choose to simulate the same user running multiple iterations, 
+or different users running one iteration.
+You can configure this behaviour on Thread Group element, and have HTTP Cache Manager, HTTP Cookie Manager, HTTP Authorization Manager
+controlled by this setting. 
+</p>
+<figure image="webtest/threadgroup3.png">
+Figure &sect-num;.9. Choose the same user or different users</figure>
+<p>
+You can choose to clear the cookies/cache content/authorization in the CookieManager/CacheManager/Authorization Manager,
+ or choose to be controlled by the Thread Group.
+</p>
+<figure image="webtest/threadgroup4.png">
+Figure &sect-num;.10. Use Thread Group to control CookieManager</figure>
+<figure image="webtest/threadgroup5.png">
+Figure &sect-num;.11. Use Thread Group to control CacheManager</figure>
+<figure image="webtest/threadgroup6.png">
+Figure &sect-num;.12. Use Thread Group to control Authorization Manager</figure>
+</section>
+
 </body>
 </document>