From 9be8566a56c574a2f77255c9c70c570824c64ded Mon Sep 17 00:00:00 2001
From: Sebastian Bazley
Date: Sat, 5 Jul 2008 16:48:57 +0000
Subject: [PATCH] Fix NPE when using nested Transaction Controllers with parent
samples
git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/trunk@674210 13f79535-47bb-0310-9956-ffa450edef68
Former-commit-id: e5f3ecb5c78ce85c340ff79cde8478c825c464c7
---
.../apache/jmeter/threads/JMeterThread.java | 221 ++++++++++--------
xdocs/changes.xml | 4 +-
2 files changed, 126 insertions(+), 99 deletions(-)
diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java
index 4a88344489..314272d33c 100644
--- a/src/core/org/apache/jmeter/threads/JMeterThread.java
+++ b/src/core/org/apache/jmeter/threads/JMeterThread.java
@@ -237,104 +237,7 @@ public class JMeterThread implements Runnable, Serializable {
while (running) {
Sampler sam;
while (running && (sam = controller.next()) != null) {
- try {
- threadContext.setCurrentSampler(sam);
-
- // Check if we are running a transaction
- TransactionSampler transactionSampler = null;
- if(sam instanceof TransactionSampler) {
- transactionSampler = (TransactionSampler) sam;
- }
- // Find the package for the transaction
- SamplePackage transactionPack = null;
- if(transactionSampler != null) {
- transactionPack = compiler.configureTransactionSampler(transactionSampler);
-
- // Check if the transaction is done
- if(transactionSampler.isTransactionDone()) {
- // Get the transaction sample result
- SampleResult transactionResult = transactionSampler.getTransactionResult();
- transactionResult.setThreadName(threadName);
- transactionResult.setGroupThreads(threadGroup.getNumberOfThreads());
- transactionResult.setAllThreads(JMeterContextService.getNumberOfThreads());
-
- // Check assertions for the transaction sample
- checkAssertions(transactionPack.getAssertions(), transactionResult);
- // Notify listeners with the transaction sample result
- notifyListeners(transactionPack.getSampleListeners(), transactionResult);
- compiler.done(transactionPack);
- // Transaction is done, we do not have a sampler to sample
- sam = null;
- }
- else {
- // It is the sub sampler of the transaction that will be sampled
- sam = transactionSampler.getSubSampler();
- }
- }
-
- // Check if we have a sampler to sample
- if(sam != null) {
- // Get the sampler ready to sample
- SamplePackage pack = compiler.configureSampler(sam);
-
- // Hack: save the package for any transaction controllers
- threadVars.putObject(PACKAGE_OBJECT, pack);
-
- delay(pack.getTimers());
- Sampler sampler = pack.getSampler();
- sampler.setThreadContext(threadContext);
- sampler.setThreadName(threadName);
- TestBeanHelper.prepare(sampler);
-
- // Perform the actual sample
- SampleResult result = sampler.sample(null);
- // TODO: remove this useless Entry parameter
-
- // If we got any results, then perform processing on the result
- if (result != null) {
- result.setGroupThreads(threadGroup.getNumberOfThreads());
- result.setAllThreads(JMeterContextService.getNumberOfThreads());
- result.setThreadName(threadName);
- threadContext.setPreviousResult(result);
- runPostProcessors(pack.getPostProcessors());
- checkAssertions(pack.getAssertions(), result);
- // Do not send subsamples to listeners which receive the transaction sample
- List sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler);
- notifyListeners(sampleListeners, result);
- compiler.done(pack);
- // Add the result as subsample of transaction if we are in a transaction
- if(transactionSampler != null) {
- transactionSampler.addSubSamplerResult(result);
- }
-
- // Check if thread or test should be stopped
- if (result.isStopThread() || (!result.isSuccessful() && onErrorStopThread)) {
- stopThread();
- }
- if (result.isStopTest() || (!result.isSuccessful() && onErrorStopTest)) {
- stopTest();
- }
- } else {
- compiler.done(pack); // Finish up
- }
- }
- if (scheduler) {
- // checks the scheduler to stop the iteration
- stopScheduler();
- }
- } catch (JMeterStopTestException e) {
- log.info("Stopping Test: " + e.toString());
- stopTest();
- } catch (JMeterStopThreadException e) {
- log.info("Stopping Thread: " + e.toString());
- stopThread();
- } catch (Exception e) {
- if (sam != null) {
- log.error("Error while processing sampler '"+sam.getName()+"' :", e);
- } else {
- log.error("", e);
- }
- }
+ process_sampler(sam, null);
}
if (controller.isDone()) {
running = false;
@@ -360,6 +263,128 @@ public class JMeterThread implements Runnable, Serializable {
threadFinished();
}
}
+
+ /**
+ * Process the current sampler, handling transaction samplers.
+ *
+ * @param current sampler
+ * @param parent sampler
+ * @return SampleResult if a transaction was processed
+ */
+ private SampleResult process_sampler(Sampler current, Sampler parent) {
+ SampleResult transactionResult = null;
+ try {
+ threadContext.setCurrentSampler(current);
+
+ // Check if we are running a transaction
+ TransactionSampler transactionSampler = null;
+ if(current instanceof TransactionSampler) {
+ transactionSampler = (TransactionSampler) current;
+ }
+ // Find the package for the transaction
+ SamplePackage transactionPack = null;
+ if(transactionSampler != null) {
+ transactionPack = compiler.configureTransactionSampler(transactionSampler);
+
+ // Check if the transaction is done
+ if(transactionSampler.isTransactionDone()) {
+ // Get the transaction sample result
+ transactionResult = transactionSampler.getTransactionResult();
+ transactionResult.setThreadName(threadName);
+ transactionResult.setGroupThreads(threadGroup.getNumberOfThreads());
+ transactionResult.setAllThreads(JMeterContextService.getNumberOfThreads());
+
+ // Check assertions for the transaction sample
+ checkAssertions(transactionPack.getAssertions(), transactionResult);
+ // Notify listeners with the transaction sample result
+ if (!(parent instanceof TransactionSampler)){
+ notifyListeners(transactionPack.getSampleListeners(), transactionResult);
+ }
+ compiler.done(transactionPack);
+ // Transaction is done, we do not have a sampler to sample
+ current = null;
+ }
+ else {
+ Sampler prev = current;
+ // It is the sub sampler of the transaction that will be sampled
+ current = transactionSampler.getSubSampler();
+ if (current instanceof TransactionSampler){
+ SampleResult res = process_sampler(current, prev);// recursive call
+ threadContext.setCurrentSampler(prev);
+ current=null;
+ if (res!=null){
+ transactionSampler.addSubSamplerResult(res);
+ }
+ }
+ }
+ }
+
+ // Check if we have a sampler to sample
+ if(current != null) {
+ // Get the sampler ready to sample
+ SamplePackage pack = compiler.configureSampler(current);
+
+ // Hack: save the package for any transaction controllers
+ threadVars.putObject(PACKAGE_OBJECT, pack);
+
+ delay(pack.getTimers());
+ Sampler sampler = pack.getSampler();
+ sampler.setThreadContext(threadContext);
+ sampler.setThreadName(threadName);
+ TestBeanHelper.prepare(sampler);
+
+ // Perform the actual sample
+ SampleResult
+ result = sampler.sample(null);
+ // TODO: remove this useless Entry parameter
+
+ // If we got any results, then perform processing on the result
+ if (result != null) {
+ result.setGroupThreads(threadGroup.getNumberOfThreads());
+ result.setAllThreads(JMeterContextService.getNumberOfThreads());
+ result.setThreadName(threadName);
+ threadContext.setPreviousResult(result);
+ runPostProcessors(pack.getPostProcessors());
+ checkAssertions(pack.getAssertions(), result);
+ // Do not send subsamples to listeners which receive the transaction sample
+ List sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler);
+ notifyListeners(sampleListeners, result);
+ compiler.done(pack);
+ // Add the result as subsample of transaction if we are in a transaction
+ if(transactionSampler != null) {
+ transactionSampler.addSubSamplerResult(result);
+ }
+
+ // Check if thread or test should be stopped
+ if (result.isStopThread() || (!result.isSuccessful() && onErrorStopThread)) {
+ stopThread();
+ }
+ if (result.isStopTest() || (!result.isSuccessful() && onErrorStopTest)) {
+ stopTest();
+ }
+ } else {
+ compiler.done(pack); // Finish up
+ }
+ }
+ if (scheduler) {
+ // checks the scheduler to stop the iteration
+ stopScheduler();
+ }
+ } catch (JMeterStopTestException e) {
+ log.info("Stopping Test: " + e.toString());
+ stopTest();
+ } catch (JMeterStopThreadException e) {
+ log.info("Stopping Thread: " + e.toString());
+ stopThread();
+ } catch (Exception e) {
+ if (current != null) {
+ log.error("Error while processing sampler '"+current.getName()+"' :", e);
+ } else {
+ log.error("", e);
+ }
+ }
+ return transactionResult;
+ }
/**
* Get the SampleListeners for the sampler. Listeners who receive transaction sample
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 6cd33e7605..3c72e84633 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -63,6 +63,7 @@ but otherwise its behaviour is not consistent (or clearly specified).
The menu item Options / Choose Language does not change all the displayed text to the new language.
To override the default local language, set the JMeter property "language" before starting JMeter.
+
Incompatible changes
The test element "Save Results to a file" is now shown as a Listener.
@@ -77,7 +78,8 @@ It does not affect test plans or test behaviour.
- The "prev" and "sampler" objects are now defined for BSF test elements
- Prompt to overwrite an existing file when first saving a new test plan
- The test element "Save Results to a file" is now shown as a Listener
-- Correct TestBeans to show the correct popup menu for Listeners
+- Amend TestBeans to show the correct popup menu for Listeners
+- Fix NPE when using nested Transaction Controllers with parent samples
Improvements