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 2016/12/30 17:59:35 UTC
svn commit: r1776612 - in /jmeter/trunk:
src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
src/core/org/apache/jmeter/threads/JMeterThread.java
src/core/org/apache/jmeter/threads/ThreadGroup.java xdocs/changes.xml
Author: pmouawad
Date: Fri Dec 30 17:59:35 2016
New Revision: 1776612
URL: http://svn.apache.org/viewvc?rev=1776612&view=rev
Log:
Bug 60530 - Add API to create JMeter threads while test is running
Based on a contribution by Logan Mauzaize & Maxime Chassagneux
Bugzilla Id: 60530
Modified:
jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java
jmeter/trunk/src/core/org/apache/jmeter/threads/ThreadGroup.java
jmeter/trunk/xdocs/changes.xml
Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java?rev=1776612&r1=1776611&r2=1776612&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java Fri Dec 30 17:59:35 2016
@@ -245,17 +245,54 @@ public abstract class AbstractThreadGrou
return getPropertyAsString(AbstractThreadGroup.ON_SAMPLE_ERROR).equalsIgnoreCase(ON_SAMPLE_ERROR_STOPTEST_NOW);
}
+ /**
+ * Hard or graceful stop depending on now flag
+ * @param threadName String thread name
+ * @param now if true interrupt {@link Thread}
+ * @return boolean true if stop succeeded
+ */
public abstract boolean stopThread(String threadName, boolean now);
+ /**
+ * @return int number of active threads
+ */
public abstract int numberOfActiveThreads();
+ /**
+ * Start the {@link ThreadGroup}
+ * @param groupCount group number
+ * @param notifier {@link ListenerNotifier}
+ * @param threadGroupTree {@link ListedHashTree}
+ * @param engine {@link StandardJMeterEngine}
+ */
public abstract void start(int groupCount, ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine);
+ /**
+ * Add a new {@link JMeterThread} to this {@link ThreadGroup} for engine
+ * @param delay Delay in milliseconds
+ * @param engine {@link StandardJMeterEngine}
+ * @return {@link JMeterThread}
+ */
+ public abstract JMeterThread addNewThread(int delay, StandardJMeterEngine engine);
+
+ /**
+ * @return true if threads were correctly stopped
+ */
public abstract boolean verifyThreadsStopped();
+ /**
+ * Wait for all Group Threads to stop after a graceful stop
+ */
public abstract void waitThreadsStopped();
+ /**
+ * Ask threads to stop gracefully
+ */
public abstract void tellThreadsToStop();
+ /**
+ * This immediately stop threads of Group by interrupting them
+ * It differs from {@link AbstractThreadGroup#tellThreadsToStop()} by being a hard stop
+ */
public abstract void stop();
}
Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java?rev=1776612&r1=1776611&r2=1776612&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java Fri Dec 30 17:59:35 2016
@@ -50,6 +50,7 @@ import org.apache.jmeter.timers.Timer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.HashTreeTraverser;
+import org.apache.jorphan.collections.ListedHashTree;
import org.apache.jorphan.collections.SearchByClass;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JMeterError;
@@ -675,6 +676,10 @@ public class JMeterThread implements Run
return threadName;
}
+ /**
+ * Set running flag to false which will interrupt JMeterThread on next flag test.
+ * This is a clean shutdown.
+ */
public void stop() { // Called by StandardJMeterEngine, TestAction and AccessLogSampler
running = false;
log.info("Stopping: " + threadName);
@@ -977,4 +982,18 @@ public class JMeterThread implements Run
this.threadGroup = group;
}
+ /**
+ * @return {@link ListedHashTree}
+ */
+ public ListedHashTree getTestTree() {
+ return (ListedHashTree) testTree;
+ }
+
+ /**
+ * @return {@link ListenerNotifier}
+ */
+ public ListenerNotifier getNotifier() {
+ return notifier;
+ }
+
}
Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/ThreadGroup.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/ThreadGroup.java?rev=1776612&r1=1776611&r2=1776612&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/ThreadGroup.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/ThreadGroup.java Fri Dec 30 17:59:35 2016
@@ -42,7 +42,7 @@ import org.apache.log.Logger;
* This class is intended to be ThreadSafe.
*/
public class ThreadGroup extends AbstractThreadGroup {
- private static final long serialVersionUID = 280L;
+ private static final long serialVersionUID = 281L;
private static final Logger log = LoggingManager.getLoggerForClass();
@@ -83,6 +83,8 @@ public class ThreadGroup extends Abstrac
// List of active threads
private final Map<JMeterThread, Thread> allThreads = new ConcurrentHashMap<>();
+
+ private final Object addThreadLock = new Object();
/**
* Is test (still) running?
@@ -90,11 +92,26 @@ public class ThreadGroup extends Abstrac
private volatile boolean running = false;
/**
+ * Thread Group number
+ */
+ private int groupNumber;
+
+ /**
* Are we using delayed startup?
*/
private boolean delayedStartup;
/**
+ * Thread safe class
+ */
+ private ListenerNotifier notifier;
+
+ /**
+ * This property will be cloned
+ */
+ private ListedHashTree threadGroupTree;
+
+ /**
* No-arg constructor.
*/
public ThreadGroup() {
@@ -222,6 +239,7 @@ public class ThreadGroup extends Abstrac
* This will schedule the time for the JMeterThread.
*
* @param thread JMeterThread
+ * @param now in milliseconds
*/
private void scheduleThread(JMeterThread thread, long now) {
@@ -257,39 +275,58 @@ public class ThreadGroup extends Abstrac
}
@Override
- public void start(int groupCount, ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine) {
- running = true;
+ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine) {
+ this.running = true;
+ this.groupNumber = groupNum;
+ this.notifier = notifier;
+ this.threadGroupTree = threadGroupTree;
int numThreads = getNumThreads();
int rampUpPeriodInSeconds = getRampUp();
float perThreadDelayInMillis = (float) (rampUpPeriodInSeconds * 1000) / (float) getNumThreads();
delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant
- log.info("Starting thread group number " + groupCount
+ log.info("Starting thread group number " + groupNumber
+ " threads " + numThreads
+ " ramp-up " + rampUpPeriodInSeconds
+ " perThread " + perThreadDelayInMillis
+ " delayedStart=" + delayedStartup);
if (delayedStartup) {
- threadStarter = new Thread(new ThreadStarter(groupCount, notifier, threadGroupTree, engine), getName()+"-ThreadStarter");
+ threadStarter = new Thread(new ThreadStarter(notifier, threadGroupTree, engine), getName()+"-ThreadStarter");
threadStarter.setDaemon(true);
threadStarter.start();
// N.B. we don't wait for the thread to complete, as that would prevent parallel TGs
} else {
long now = System.currentTimeMillis(); // needs to be same time for all threads in the group
final JMeterContext context = JMeterContextService.getContext();
- for (int i = 0; running && i < numThreads; i++) {
- JMeterThread jmThread = makeThread(groupCount, notifier, threadGroupTree, engine, i, context);
- scheduleThread(jmThread, now); // set start and end time
- jmThread.setInitialDelay((int)(i * perThreadDelayInMillis));
- Thread newThread = new Thread(jmThread, jmThread.getThreadName());
- registerStartedThread(jmThread, newThread);
- newThread.start();
+ for (int threadNum = 0; running && threadNum < numThreads; threadNum++) {
+ startNewThread(notifier, threadGroupTree, engine, threadNum, context, now, (int)(threadNum * perThreadDelayInMillis));
}
}
- log.info("Started thread group number "+groupCount);
+ log.info("Started thread group number "+groupNumber);
}
/**
+ * Start a new {@link JMeterThread} and registers it
+ * @param notifier {@link ListenerNotifier}
+ * @param threadGroupTree {@link ListedHashTree}
+ * @param engine {@link StandardJMeterEngine}
+ * @param threadNum Thread number
+ * @param context {@link JMeterContext}
+ * @param now Nom in milliseconds
+ * @param delay int delay in milliseconds
+ * @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);
+ scheduleThread(jmThread, now); // set start and end time
+ jmThread.setInitialDelay(delay);
+ Thread newThread = new Thread(jmThread, jmThread.getThreadName());
+ registerStartedThread(jmThread, newThread);
+ newThread.start();
+ return jmThread;
+ }
+ /**
* Register Thread when it starts
* @param jMeterThread {@link JMeterThread}
* @param newThread Thread
@@ -298,9 +335,18 @@ public class ThreadGroup extends Abstrac
allThreads.put(jMeterThread, newThread);
}
- private JMeterThread makeThread(int groupCount,
+ /**
+ * Create {@link JMeterThread} cloning threadGroupTree
+ * @param notifier {@link ListenerNotifier}
+ * @param threadGroupTree {@link ListedHashTree}
+ * @param engine {@link StandardJMeterEngine}
+ * @param threadNumber int thread number
+ * @param context {@link JMeterContext}
+ * @return {@link JMeterThread}
+ */
+ private JMeterThread makeThread(
ListenerNotifier notifier, ListedHashTree threadGroupTree,
- StandardJMeterEngine engine, int i,
+ StandardJMeterEngine engine, int threadNumber,
JMeterContext context) { // N.B. Context needs to be fetched in the correct thread
boolean onErrorStopTest = getOnErrorStopTest();
boolean onErrorStopTestNow = getOnErrorStopTestNow();
@@ -308,10 +354,10 @@ public class ThreadGroup extends Abstrac
boolean onErrorStartNextLoop = getOnErrorStartNextLoop();
String groupName = getName();
final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier);
- jmeterThread.setThreadNum(i);
+ jmeterThread.setThreadNum(threadNumber);
jmeterThread.setThreadGroup(this);
jmeterThread.setInitialContext(context);
- final String threadName = groupName + " " + (groupCount) + "-" + (i + 1);
+ final String threadName = groupName + " " + groupNumber + "-" + (threadNumber + 1);
jmeterThread.setThreadName(threadName);
jmeterThread.setEngine(engine);
jmeterThread.setOnErrorStopTest(onErrorStopTest);
@@ -321,6 +367,22 @@ public class ThreadGroup extends Abstrac
return jmeterThread;
}
+ @Override
+ public JMeterThread addNewThread(int delay, StandardJMeterEngine engine) {
+ long now = System.currentTimeMillis();
+ JMeterContext context = JMeterContextService.getContext();
+ JMeterThread newJmThread;
+ int numThreads;
+ synchronized (addThreadLock) {
+ numThreads = getNumThreads();
+ setNumThreads(numThreads + 1);
+ }
+ newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay);
+ JMeterContextService.addTotalThreads( 1 );
+ log.info("Started new thread in group " + groupNumber );
+ return newJmThread;
+ }
+
/**
* Stop thread called threadName:
* <ol>
@@ -345,15 +407,16 @@ public class ThreadGroup extends Abstrac
}
/**
- * @param thrd JMeterThread
- * @param t Thread
- * @param interrupt Interrup thread or not
- */
- private void stopThread(JMeterThread thrd, Thread t, boolean interrupt) {
- thrd.stop();
- thrd.interrupt(); // interrupt sampler if possible
- if (interrupt && t != null) { // Bug 49734
- t.interrupt(); // also interrupt JVM thread
+ * Hard Stop JMeterThread thrd and interrupt JVM Thread if interrupt is true
+ * @param jmeterThread {@link JMeterThread}
+ * @param jvmThread {@link Thread}
+ * @param interrupt Interrupt thread or not
+ */
+ private void stopThread(JMeterThread jmeterThread, Thread jvmThread, boolean interrupt) {
+ jmeterThread.stop();
+ jmeterThread.interrupt(); // interrupt sampler if possible
+ if (interrupt && jvmThread != null) { // Bug 49734
+ jvmThread.interrupt(); // also interrupt JVM thread
}
}
@@ -485,12 +548,20 @@ public class ThreadGroup extends Abstrac
}
}
+ /**
+ * @param tree {@link ListedHashTree}
+ * @return a clone of tree
+ */
private ListedHashTree cloneTree(ListedHashTree tree) {
TreeCloner cloner = new TreeCloner(true);
tree.traverse(cloner);
return cloner.getClonedTree();
}
+ /**
+ * Pause ms milliseconds
+ * @param ms long milliseconds
+ */
private void pause(long ms){
try {
TimeUnit.MILLISECONDS.sleep(ms);
@@ -504,15 +575,13 @@ public class ThreadGroup extends Abstrac
*/
class ThreadStarter implements Runnable {
- private final int groupCount;
private final ListenerNotifier notifier;
private final ListedHashTree threadGroupTree;
private final StandardJMeterEngine engine;
private final JMeterContext context;
- public ThreadStarter(int groupCount, ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine) {
+ public ThreadStarter(ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine) {
super();
- this.groupCount = groupCount;
this.notifier = notifier;
this.threadGroupTree = threadGroupTree;
this.engine = engine;
@@ -577,14 +646,14 @@ public class ThreadGroup extends Abstrac
}
final int numThreads = getNumThreads();
final int perThreadDelayInMillis = Math.round((float) (getRampUp() * 1000) / (float) numThreads);
- for (int i = 0; running && i < numThreads; i++) {
- if (i > 0) {
+ for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) {
+ if (threadNumber > 0) {
pause(perThreadDelayInMillis); // ramp-up delay (except first)
}
if (usingScheduler && System.currentTimeMillis() > endtime) {
break; // no point continuing beyond the end time
}
- JMeterThread jmThread = makeThread(groupCount, notifier, threadGroupTree, engine, i, context);
+ JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context);
jmThread.setInitialDelay(0); // Already waited
if (usingScheduler) {
jmThread.setScheduled(true);
Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1776612&r1=1776611&r2=1776612&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Fri Dec 30 17:59:35 2016
@@ -140,6 +140,7 @@ Fill in some detail.
<h3>General</h3>
<ul>
<li><bug>54525</bug>Search Feature : Enhance it with ability to replace</li>
+ <li><bug>60530</bug>Add API to create JMeter threads while test is running. Based on a contribution by Logan Mauzaize and Maxime Chassagneux</li>
</ul>
<ch_section>Non-functional changes</ch_section>
@@ -208,6 +209,8 @@ Fill in some detail.
<li>(gavin at 16degrees.com.au)</li>
<li>Thomas Schapitz (ts-nospam12 at online.de)</li>
<li>Murdecai777 (https://github.com/Murdecai777)</li>
+<li>Logan Mauzaize (https://github.com/loganmzz)</li>
+<li>Maxime Chassagneux (https://github.com/max3163)</li>
</ul>
<p>We also thank bug reporters who helped us improve JMeter. <br/>
For this release we want to give special thanks to the following reporters for the clear reports and tests made after our fixes:</p>