Lines 21-34
Link Here
|
21 |
import java.io.PrintWriter; |
21 |
import java.io.PrintWriter; |
22 |
import java.io.StringWriter; |
22 |
import java.io.StringWriter; |
23 |
import java.util.ArrayList; |
23 |
import java.util.ArrayList; |
|
|
24 |
import java.util.Collections; |
24 |
import java.util.Date; |
25 |
import java.util.Date; |
25 |
import java.util.Iterator; |
26 |
import java.util.Iterator; |
26 |
import java.util.LinkedList; |
27 |
import java.util.LinkedList; |
27 |
import java.util.List; |
28 |
import java.util.List; |
28 |
import java.util.Map; |
|
|
29 |
import java.util.Map.Entry; |
30 |
import java.util.Properties; |
29 |
import java.util.Properties; |
31 |
import java.util.concurrent.ConcurrentHashMap; |
|
|
32 |
|
30 |
|
33 |
import org.apache.jmeter.JMeter; |
31 |
import org.apache.jmeter.JMeter; |
34 |
import org.apache.jmeter.testbeans.TestBean; |
32 |
import org.apache.jmeter.testbeans.TestBean; |
Lines 36-49
Link Here
|
36 |
import org.apache.jmeter.testelement.TestElement; |
34 |
import org.apache.jmeter.testelement.TestElement; |
37 |
import org.apache.jmeter.testelement.TestListener; |
35 |
import org.apache.jmeter.testelement.TestListener; |
38 |
import org.apache.jmeter.testelement.TestPlan; |
36 |
import org.apache.jmeter.testelement.TestPlan; |
|
|
37 |
import org.apache.jmeter.threads.AbstractThreadGroup; |
39 |
import org.apache.jmeter.threads.JMeterContextService; |
38 |
import org.apache.jmeter.threads.JMeterContextService; |
40 |
import org.apache.jmeter.threads.JMeterThread; |
39 |
import org.apache.jmeter.threads.JMeterThread; |
41 |
import org.apache.jmeter.threads.JMeterThreadMonitor; |
40 |
import org.apache.jmeter.threads.JMeterThreadMonitor; |
42 |
import org.apache.jmeter.threads.ListenerNotifier; |
41 |
import org.apache.jmeter.threads.ListenerNotifier; |
43 |
import org.apache.jmeter.threads.TestCompiler; |
|
|
44 |
import org.apache.jmeter.threads.AbstractThreadGroup; |
45 |
import org.apache.jmeter.threads.SetupThreadGroup; |
46 |
import org.apache.jmeter.threads.PostThreadGroup; |
42 |
import org.apache.jmeter.threads.PostThreadGroup; |
|
|
43 |
import org.apache.jmeter.threads.SetupThreadGroup; |
44 |
import org.apache.jmeter.threads.TestCompiler; |
47 |
import org.apache.jmeter.util.JMeterUtils; |
45 |
import org.apache.jmeter.util.JMeterUtils; |
48 |
import org.apache.jorphan.collections.HashTree; |
46 |
import org.apache.jorphan.collections.HashTree; |
49 |
import org.apache.jorphan.collections.ListedHashTree; |
47 |
import org.apache.jorphan.collections.ListedHashTree; |
Lines 58-65
Link Here
|
58 |
public class StandardJMeterEngine implements JMeterEngine, JMeterThreadMonitor, Runnable { |
56 |
public class StandardJMeterEngine implements JMeterEngine, JMeterThreadMonitor, Runnable { |
59 |
private static final Logger log = LoggingManager.getLoggerForClass(); |
57 |
private static final Logger log = LoggingManager.getLoggerForClass(); |
60 |
|
58 |
|
61 |
private static final long WAIT_TO_DIE = JMeterUtils.getPropDefault("jmeterengine.threadstop.wait", 5 * 1000); // 5 seconds |
|
|
62 |
|
63 |
// Should we exit at end of the test? (only applies to server, because host is non-null) |
59 |
// Should we exit at end of the test? (only applies to server, because host is non-null) |
64 |
private static final boolean exitAfterTest = |
60 |
private static final boolean exitAfterTest = |
65 |
JMeterUtils.getPropDefault("server.exitaftertest", false); // $NON-NLS-1$ |
61 |
JMeterUtils.getPropDefault("server.exitaftertest", false); // $NON-NLS-1$ |
Lines 92-101
Link Here
|
92 |
|
88 |
|
93 |
/** Whether to call System.exit(1) if threads won't stop */ |
89 |
/** Whether to call System.exit(1) if threads won't stop */ |
94 |
private static final boolean SYSTEM_EXIT_ON_STOP_FAIL = JMeterUtils.getPropDefault("jmeterengine.stopfail.system.exit", true); |
90 |
private static final boolean SYSTEM_EXIT_ON_STOP_FAIL = JMeterUtils.getPropDefault("jmeterengine.stopfail.system.exit", true); |
95 |
|
91 |
|
96 |
/** JMeterThread => its JVM thread */ |
|
|
97 |
private final Map<JMeterThread, Thread> allThreads; |
98 |
|
99 |
/** Flag to show whether test is running. Set to false to stop creating more threads. */ |
92 |
/** Flag to show whether test is running. Set to false to stop creating more threads. */ |
100 |
private volatile boolean running = false; |
93 |
private volatile boolean running = false; |
101 |
|
94 |
|
Lines 111-116
Link Here
|
111 |
|
104 |
|
112 |
private final String host; |
105 |
private final String host; |
113 |
|
106 |
|
|
|
107 |
private List<AbstractThreadGroup> groups = Collections.synchronizedList(new ArrayList<AbstractThreadGroup>()); |
108 |
|
114 |
public static void stopEngineNow() { |
109 |
public static void stopEngineNow() { |
115 |
if (engine != null) {// May be null if called from Unit test |
110 |
if (engine != null) {// May be null if called from Unit test |
116 |
engine.stopTest(true); |
111 |
engine.stopTest(true); |
Lines 139-160
Link Here
|
139 |
if (engine == null) { |
134 |
if (engine == null) { |
140 |
return false;// e.g. not yet started |
135 |
return false;// e.g. not yet started |
141 |
} |
136 |
} |
|
|
137 |
boolean wasStopped = false; |
142 |
// ConcurrentHashMap does not need synch. here |
138 |
// ConcurrentHashMap does not need synch. here |
143 |
for(Entry<JMeterThread, Thread> entry : engine.allThreads.entrySet()){ |
139 |
for (AbstractThreadGroup threadGroup : engine.groups) { |
144 |
JMeterThread thrd = entry.getKey(); |
140 |
wasStopped = wasStopped || threadGroup.stopThread(threadName, now); |
145 |
if (thrd.getThreadName().equals(threadName)){ |
|
|
146 |
thrd.stop(); |
147 |
thrd.interrupt(); |
148 |
if (now) { |
149 |
Thread t = entry.getValue(); |
150 |
if (t != null) { |
151 |
t.interrupt(); |
152 |
} |
153 |
} |
154 |
return true; |
155 |
} |
156 |
} |
141 |
} |
157 |
return false; |
142 |
return wasStopped; |
158 |
} |
143 |
} |
159 |
|
144 |
|
160 |
// End of code to allow engine to be controlled remotely |
145 |
// End of code to allow engine to be controlled remotely |
Lines 165-171
Link Here
|
165 |
|
150 |
|
166 |
public StandardJMeterEngine(String host) { |
151 |
public StandardJMeterEngine(String host) { |
167 |
this.host = host; |
152 |
this.host = host; |
168 |
this.allThreads = new ConcurrentHashMap<JMeterThread, Thread>(); |
|
|
169 |
// Hack to allow external control |
153 |
// Hack to allow external control |
170 |
engine = this; |
154 |
engine = this; |
171 |
} |
155 |
} |
Lines 268-274
Link Here
|
268 |
// Called by JMeter thread when it finishes |
252 |
// Called by JMeter thread when it finishes |
269 |
public synchronized void threadFinished(JMeterThread thread) { |
253 |
public synchronized void threadFinished(JMeterThread thread) { |
270 |
log.info("Ending thread " + thread.getThreadName()); |
254 |
log.info("Ending thread " + thread.getThreadName()); |
271 |
allThreads.remove(thread); |
255 |
for (AbstractThreadGroup threadGroup : groups) { |
|
|
256 |
threadGroup.threadFinished(thread); |
257 |
} |
272 |
} |
258 |
} |
273 |
|
259 |
|
274 |
public synchronized void stopTest() { |
260 |
public synchronized void stopTest() { |
Lines 292-298
Link Here
|
292 |
engine = null; |
278 |
engine = null; |
293 |
if (now) { |
279 |
if (now) { |
294 |
tellThreadsToStop(); |
280 |
tellThreadsToStop(); |
295 |
pause(10 * allThreads.size()); |
281 |
pause(10 * countStillActiveThreads()); |
296 |
boolean stopped = verifyThreadsStopped(); |
282 |
boolean stopped = verifyThreadsStopped(); |
297 |
if (!stopped) { // we totally failed to stop the test |
283 |
if (!stopped) { // we totally failed to stop the test |
298 |
if (JMeter.isNonGUI()) { |
284 |
if (JMeter.isNonGUI()) { |
Lines 381-389
Link Here
|
381 |
String groupName = startThreadGroup(group, groupCount, setupSearcher, testLevelElements, notifier); |
367 |
String groupName = startThreadGroup(group, groupCount, setupSearcher, testLevelElements, notifier); |
382 |
if (serialized && setupIter.hasNext()) { |
368 |
if (serialized && setupIter.hasNext()) { |
383 |
log.info("Waiting for setup thread group: "+groupName+" to finish before starting next setup group"); |
369 |
log.info("Waiting for setup thread group: "+groupName+" to finish before starting next setup group"); |
384 |
while (running && allThreads.size() > 0) { |
370 |
pauseWhileRemainingThreads(); |
385 |
pause(1000); |
|
|
386 |
} |
387 |
} |
371 |
} |
388 |
} |
372 |
} |
389 |
log.info("Waiting for all setup thread groups To Exit"); |
373 |
log.info("Waiting for all setup thread groups To Exit"); |
Lines 414-422
Link Here
|
414 |
String groupName=startThreadGroup(group, groupCount, searcher, testLevelElements, notifier); |
398 |
String groupName=startThreadGroup(group, groupCount, searcher, testLevelElements, notifier); |
415 |
if (serialized && iter.hasNext()) { |
399 |
if (serialized && iter.hasNext()) { |
416 |
log.info("Waiting for thread group: "+groupName+" to finish before starting next group"); |
400 |
log.info("Waiting for thread group: "+groupName+" to finish before starting next group"); |
417 |
while (running && allThreads.size() > 0) { |
401 |
pauseWhileRemainingThreads(); |
418 |
pause(1000); |
|
|
419 |
} |
420 |
} |
402 |
} |
421 |
} // end of thread groups |
403 |
} // end of thread groups |
422 |
if (groupCount == 0){ // No TGs found |
404 |
if (groupCount == 0){ // No TGs found |
Lines 443-451
Link Here
|
443 |
String groupName = startThreadGroup(group, groupCount, postSearcher, testLevelElements, notifier); |
425 |
String groupName = startThreadGroup(group, groupCount, postSearcher, testLevelElements, notifier); |
444 |
if (serialized && postIter.hasNext()) { |
426 |
if (serialized && postIter.hasNext()) { |
445 |
log.info("Waiting for post thread group: "+groupName+" to finish before starting next post group"); |
427 |
log.info("Waiting for post thread group: "+groupName+" to finish before starting next post group"); |
446 |
while (running && allThreads.size() > 0) { |
428 |
pauseWhileRemainingThreads(); |
447 |
pause(1000); |
|
|
448 |
} |
449 |
} |
429 |
} |
450 |
} |
430 |
} |
451 |
waitThreadsStopped(); // wait for Post threads to stop |
431 |
waitThreadsStopped(); // wait for Post threads to stop |
Lines 454-459
Link Here
|
454 |
notifyTestListenersOfEnd(testListenersSave); |
434 |
notifyTestListenersOfEnd(testListenersSave); |
455 |
} |
435 |
} |
456 |
|
436 |
|
|
|
437 |
/** |
438 |
* Loop with 1s pause while thread groups have threads running |
439 |
*/ |
440 |
private void pauseWhileRemainingThreads() { |
441 |
while (running && groupsHaveThreads()) { |
442 |
pause(1000); |
443 |
} |
444 |
} |
445 |
|
446 |
/** |
447 |
* @return true if remaining threads |
448 |
*/ |
449 |
private boolean groupsHaveThreads() { |
450 |
return countStillActiveThreads()>0; |
451 |
} |
452 |
|
453 |
/** |
454 |
* @return total of active threads in all Thread Groups |
455 |
*/ |
456 |
private int countStillActiveThreads() { |
457 |
int reminingThreads= 0; |
458 |
for (AbstractThreadGroup threadGroup : groups) { |
459 |
reminingThreads += threadGroup.numberOfActiveThreads(); |
460 |
} |
461 |
return reminingThreads; |
462 |
} |
463 |
|
457 |
private String startThreadGroup(AbstractThreadGroup group, int groupCount, SearchByClass<?> searcher, List<?> testLevelElements, ListenerNotifier notifier) |
464 |
private String startThreadGroup(AbstractThreadGroup group, int groupCount, SearchByClass<?> searcher, List<?> testLevelElements, ListenerNotifier notifier) |
458 |
{ |
465 |
{ |
459 |
int numThreads = group.getNumThreads(); |
466 |
int numThreads = group.getNumThreads(); |
Lines 478-483
Link Here
|
478 |
} |
485 |
} |
479 |
ListedHashTree threadGroupTree = (ListedHashTree) searcher.getSubTree(group); |
486 |
ListedHashTree threadGroupTree = (ListedHashTree) searcher.getSubTree(group); |
480 |
threadGroupTree.add(group, testLevelElements); |
487 |
threadGroupTree.add(group, testLevelElements); |
|
|
488 |
|
489 |
JMeterThread[] jmThreads = |
490 |
new JMeterThread[numThreads]; |
481 |
for (int i = 0; running && i < numThreads; i++) { |
491 |
for (int i = 0; running && i < numThreads; i++) { |
482 |
final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier); |
492 |
final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier); |
483 |
jmeterThread.setThreadNum(i); |
493 |
jmeterThread.setThreadNum(i); |
Lines 493-537
Link Here
|
493 |
|
503 |
|
494 |
group.scheduleThread(jmeterThread); |
504 |
group.scheduleThread(jmeterThread); |
495 |
|
505 |
|
496 |
Thread newThread = new Thread(jmeterThread); |
506 |
jmThreads[i] = jmeterThread; |
497 |
newThread.setName(threadName); |
|
|
498 |
allThreads.put(jmeterThread, newThread); |
499 |
newThread.start(); |
500 |
} // end of thread startup for this thread group |
507 |
} // end of thread startup for this thread group |
|
|
508 |
group.setJMeterThreads(jmThreads); |
509 |
group.start(); |
510 |
groups.add(group); |
501 |
return groupName; |
511 |
return groupName; |
502 |
} |
512 |
} |
503 |
|
513 |
|
|
|
514 |
/** |
515 |
* @return boolean true if all threads of all Threead Groups stopped |
516 |
*/ |
504 |
private boolean verifyThreadsStopped() { |
517 |
private boolean verifyThreadsStopped() { |
505 |
boolean stoppedAll = true; |
518 |
boolean stoppedAll = true; |
506 |
// ConcurrentHashMap does not need synch. here |
519 |
// ConcurrentHashMap does not need synch. here |
507 |
for (Thread t : allThreads.values()) { |
520 |
for (AbstractThreadGroup threadGroup : groups) { |
508 |
if (t != null) { |
521 |
stoppedAll = stoppedAll && threadGroup.verifyThreadsStopped(); |
509 |
if (t.isAlive()) { |
|
|
510 |
try { |
511 |
t.join(WAIT_TO_DIE); |
512 |
} catch (InterruptedException e) { |
513 |
} |
514 |
if (t.isAlive()) { |
515 |
stoppedAll = false; |
516 |
log.warn("Thread won't exit: " + t.getName()); |
517 |
} |
518 |
} |
519 |
} |
520 |
} |
522 |
} |
521 |
return stoppedAll; |
523 |
return stoppedAll; |
522 |
} |
524 |
} |
523 |
|
525 |
|
|
|
526 |
/** |
527 |
* Wait for Group Threads to stop |
528 |
*/ |
524 |
private void waitThreadsStopped() { |
529 |
private void waitThreadsStopped() { |
525 |
// ConcurrentHashMap does not need synch. here |
530 |
// ConcurrentHashMap does not need synch. here |
526 |
for (Thread t : allThreads.values()) { |
531 |
for (AbstractThreadGroup threadGroup : groups) { |
527 |
if (t != null) { |
532 |
threadGroup.waitThreadsStopped(); |
528 |
while (t.isAlive()) { |
|
|
529 |
try { |
530 |
t.join(WAIT_TO_DIE); |
531 |
} catch (InterruptedException e) { |
532 |
} |
533 |
} |
534 |
} |
535 |
} |
533 |
} |
536 |
} |
534 |
} |
537 |
|
535 |
|
Lines 545-558
Link Here
|
545 |
*/ |
543 |
*/ |
546 |
private void tellThreadsToStop() { |
544 |
private void tellThreadsToStop() { |
547 |
// ConcurrentHashMap does not need protecting |
545 |
// ConcurrentHashMap does not need protecting |
548 |
for (Entry<JMeterThread, Thread> entry : allThreads.entrySet()) { |
546 |
for (AbstractThreadGroup threadGroup : groups) { |
549 |
JMeterThread item = entry.getKey(); |
547 |
threadGroup.tellThreadsToStop(); |
550 |
item.stop(); // set stop flag |
|
|
551 |
item.interrupt(); // interrupt sampler if possible |
552 |
Thread t = entry.getValue(); |
553 |
if (t != null ) { // Bug 49734 |
554 |
t.interrupt(); // also interrupt JVM thread |
555 |
} |
556 |
} |
548 |
} |
557 |
} |
549 |
} |
558 |
|
550 |
|
Lines 570-577
Link Here
|
570 |
*/ |
562 |
*/ |
571 |
private void stopAllThreads() { |
563 |
private void stopAllThreads() { |
572 |
// ConcurrentHashMap does not need synch. here |
564 |
// ConcurrentHashMap does not need synch. here |
573 |
for (JMeterThread item : allThreads.keySet()) { |
565 |
for (AbstractThreadGroup threadGroup : groups) { |
574 |
item.stop(); |
566 |
threadGroup.stop(); |
575 |
} |
567 |
} |
576 |
} |
568 |
} |
577 |
|
569 |
|