diff --git a/build.xml b/build.xml
index 125a34102d9..f56285ed000 100644
--- a/build.xml
+++ b/build.xml
@@ -555,7 +555,7 @@
JMeter version
This is overridden for formal releases.
-->
-
+
diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties
index 8c486d78ca2..5298ed470ce 100644
--- a/src/core/org/apache/jmeter/resources/messages.properties
+++ b/src/core/org/apache/jmeter/resources/messages.properties
@@ -499,6 +499,8 @@ jms_testing_title=Messaging Request
jms_text_area=Text Message or Object Message serialized to XML by XStream
jms_text_message=Text Message
jms_timeout=Timeout (ms)
+jms_error_reconnect_on_codes=Reconnect on error codes (regex)
+jms_error_pause_between=Pause between errors (ms)
jms_topic=Destination
jms_use_auth=Use Authorization?
jms_use_file=From file
diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java
index 9e28e2e9c88..e68370b0453 100644
--- a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java
+++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSPublisherGui.java
@@ -90,6 +90,9 @@ public class JMSPublisherGui extends AbstractSamplerGui implements ChangeListene
private final JLabeledTextField expiration = new JLabeledTextField(JMeterUtils.getResString("jms_expiration"),10); //$NON-NLS-1$
+ private final JLabeledTextField jmsErrorReconnectOnCodes =
+ new JLabeledTextField(JMeterUtils.getResString("jms_error_reconnect_on_codes")); // $NON-NLS-1$
+
private final JLabeledTextField priority = new JLabeledTextField(JMeterUtils.getResString("jms_priority"),1); //$NON-NLS-1$
private final JCheckBox useAuth = new JCheckBox(JMeterUtils.getResString("jms_use_auth"), false); //$NON-NLS-1$
@@ -169,6 +172,7 @@ private void setupSamplerProperties(final PublisherSampler sampler) {
sampler.setConnectionFactory(jndiConnFac.getText());
sampler.setDestination(jmsDestination.getText());
sampler.setExpiration(expiration.getText());
+ sampler.setReconnectionErrorCodes(jmsErrorReconnectOnCodes.getText());
sampler.setPriority(priority.getText());
sampler.setUsername(jmsUser.getText());
sampler.setPassword(jmsPwd.getText());
@@ -204,6 +208,7 @@ private void init() { // WARNING: called from ctor so must not be overridden (i.
mainPanel.add(createDestinationPane());
mainPanel.add(createAuthPane());
mainPanel.add(createPriorityAndExpiration());
+ mainPanel.add(jmsErrorReconnectOnCodes);
mainPanel.add(iterations);
jmsPropertiesPanel = new JMSPropertiesPanel(); //$NON-NLS-1$
@@ -236,6 +241,7 @@ public void clearGui(){
jndiConnFac.setText(""); // $NON-NLS-1$
jmsDestination.setText(""); // $NON-NLS-1$
expiration.setText(""); // $NON-NLS-1$
+ jmsErrorReconnectOnCodes.setText("");
priority.setText(""); // $NON-NLS-1$
jmsUser.setText(""); // $NON-NLS-1$
jmsPwd.setText(""); // $NON-NLS-1$
@@ -277,6 +283,7 @@ public void configure(TestElement el) {
msgChoice.setText(sampler.getMessageChoice());
iterations.setText(sampler.getIterations());
expiration.setText(sampler.getExpiration());
+ jmsErrorReconnectOnCodes.setText(sampler.getReconnectionErrorCodes());
priority.setText(sampler.getPriority());
useAuth.setSelected(sampler.isUseAuth());
jmsUser.setEnabled(useAuth.isSelected());
diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSSubscriberGui.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSSubscriberGui.java
index a86ad212d06..a21322ed481 100644
--- a/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSSubscriberGui.java
+++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/control/gui/JMSSubscriberGui.java
@@ -87,6 +87,12 @@ public class JMSSubscriberGui extends AbstractSamplerGui implements ChangeListen
private final JLabeledTextField timeout =
new JLabeledTextField(JMeterUtils.getResString("jms_timeout")); //$NON-NLS-1$
+ private final JLabeledTextField jmsErrorPauseBetween =
+ new JLabeledTextField(JMeterUtils.getResString("jms_error_pause_between")); // $NON-NLS-1$
+
+ private final JLabeledTextField jmsErrorReconnectOnCodes =
+ new JLabeledTextField(JMeterUtils.getResString("jms_error_reconnect_on_codes")); // $NON-NLS-1$
+
private final JLabeledTextField separator =
new JLabeledTextField(JMeterUtils.getResString("jms_separator")); //$NON-NLS-1$
@@ -159,6 +165,8 @@ public void modifyTestElement(TestElement s) {
sampler.setClientChoice(clientChoice.getText());
sampler.setStopBetweenSamples(stopBetweenSamples.isSelected());
sampler.setTimeout(timeout.getText());
+ sampler.setReconnectionErrorCodes(jmsErrorReconnectOnCodes.getText());
+ sampler.setPauseBetweenErrors(jmsErrorPauseBetween.getText());
sampler.setDestinationStatic(destSetup.getText().equals(DEST_SETUP_STATIC));
sampler.setSeparator(separator.getText());
}
@@ -201,6 +209,9 @@ private void init() { // WARNING: called from ctor so must not be overridden (i.
mainPanel.add(choice);
mainPanel.add(separator);
+ mainPanel.add(jmsErrorReconnectOnCodes);
+ mainPanel.add(jmsErrorPauseBetween);
+
useProperties.addChangeListener(this);
useAuth.addChangeListener(this);
}
@@ -232,6 +243,8 @@ public void configure(TestElement el) {
timeout.setText(sampler.getTimeout());
separator.setText(sampler.getSeparator());
destSetup.setText(sampler.isDestinationStatic() ? DEST_SETUP_STATIC : DEST_SETUP_DYNAMIC);
+ jmsErrorReconnectOnCodes.setText(sampler.getReconnectionErrorCodes());
+ jmsErrorPauseBetween.setText(sampler.getPauseBetweenErrors());
}
@Override
@@ -257,6 +270,8 @@ public void clearGui(){
clientChoice.setText(RECEIVE_RSC);
stopBetweenSamples.setSelected(false);
destSetup.setText(DEST_SETUP_STATIC);
+ jmsErrorReconnectOnCodes.setText("");
+ jmsErrorPauseBetween.setText("");
}
/**
diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/BaseJMSSampler.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/BaseJMSSampler.java
index 1409445a377..8fff2562cc5 100644
--- a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/BaseJMSSampler.java
+++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/BaseJMSSampler.java
@@ -18,12 +18,15 @@
package org.apache.jmeter.protocol.jms.sampler;
import java.util.Date;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
+import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
@@ -71,6 +74,10 @@ public abstract class BaseJMSSampler extends AbstractSampler {
private static final String DESTINATION_STATIC = "jms.destination_static"; // $NON-NLS-1$
private static final boolean DESTINATION_STATIC_DEFAULT = true; // default to maintain compatibility
+ /** Property name for regex of error codes which force reconnection **/
+ private static final String ERROR_RECONNECT_ON_CODES = "jms_error_reconnect_on_codes"; // $NON-NLS-1$
+ private Predicate isReconnectErrorCode = e -> false;
+
//-- End of JMX file attribute names
// See BUG 45460. We need to keep the resource in order to interpret existing files
@@ -376,4 +383,25 @@ public static String getMessageHeaders(Message message) {
return new String(response);
}
+
+ public String getReconnectionErrorCodes() {
+ return getPropertyAsString(ERROR_RECONNECT_ON_CODES);
+ }
+
+ public void setReconnectionErrorCodes(String reconnectionErrorCodes) {
+ setProperty(ERROR_RECONNECT_ON_CODES, reconnectionErrorCodes);
+ }
+
+ public Predicate getIsReconnectErrorCode() {
+ return isReconnectErrorCode;
+ }
+
+ public void setIsReconnectErrorCode() {
+ String regex = StringUtils.trimToEmpty(getReconnectionErrorCodes());
+ if (regex.isEmpty()) {
+ isReconnectErrorCode = e -> false;
+ } else {
+ isReconnectErrorCode = Pattern.compile(regex).asPredicate();
+ }
+ }
}
diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java
index 53725446b83..8c391948835 100644
--- a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java
+++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/PublisherSampler.java
@@ -21,12 +21,15 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.io.Serializable;
+import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
@@ -139,6 +142,7 @@ public void testStarted() {
*
*/
private void initClient() throws JMSException, NamingException {
+ setIsReconnectErrorCode();
publisher = new Publisher(getUseJNDIPropertiesAsBoolean(), getJNDIInitialContextFactory(),
getProviderUrl(), getConnectionFactory(), getDestination(), isUseAuth(), getUsername(),
getPassword(), isDestinationStatic());
@@ -161,11 +165,8 @@ public SampleResult sample() {
if (publisher == null) {
try {
initClient();
- } catch (JMSException e) {
- result.setResponseMessage(e.toString());
- return result;
- } catch (NamingException e) {
- result.setResponseMessage(e.toString());
+ } catch (JMSException | NamingException e) {
+ handleError(result, e, false);
return result;
}
}
@@ -209,14 +210,37 @@ public SampleResult sample() {
result.setSamplerData(buffer.toString());
result.setSampleCount(loop);
result.setRequestHeaders(propBuffer.toString());
+ } catch (JMSException e) {
+ handleError(result, e, true);
} catch (Exception e) {
- result.setResponseMessage(e.toString());
+ handleError(result, e, false);
} finally {
result.sampleEnd();
}
return result;
}
+ private void handleError(SampleResult result, Exception e, boolean checkForReconnect) {
+ result.setSuccessful(false);
+ result.setResponseMessage(e.toString());
+
+ if (e instanceof JMSException) {
+ JMSException jms = (JMSException)e;
+
+ String errorCode = Optional.ofNullable(jms.getErrorCode()).orElse("");
+ if (checkForReconnect && publisher != null && getIsReconnectErrorCode().test(errorCode)) {
+ publisher.close();
+ publisher = null;
+ }
+
+ result.setResponseCode(errorCode);
+ }
+
+ StringWriter writer = new StringWriter();
+ e.printStackTrace(new PrintWriter(writer));
+ result.setResponseData(writer.toString(), "UTF-8");
+ }
+
private Map getMapContent() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Map m = new HashMap<>();
String text = getMessageContent();
diff --git a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/SubscriberSampler.java b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/SubscriberSampler.java
index da026349e9f..21a58a18671 100644
--- a/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/SubscriberSampler.java
+++ b/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/SubscriberSampler.java
@@ -18,6 +18,7 @@
package org.apache.jmeter.protocol.jms.sampler;
import java.util.Enumeration;
+import java.util.Optional;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
@@ -92,6 +93,8 @@ public class SubscriberSampler extends BaseJMSSampler implements Interruptible,
private static final String STOP_BETWEEN = "jms.stop_between_samples"; // $NON-NLS-1$
private static final String SEPARATOR = "jms.separator"; // $NON-NLS-1$
private static final String SEPARATOR_DEFAULT = ""; // $NON-NLS-1$
+ private static final String ERROR_PAUSE_BETWEEN = "jms_error_pause_between"; // $NON-NLS-1$
+ private static final String ERROR_PAUSE_BETWEEN_DEFAULT = ""; // $NON-NLS-1$
private transient boolean START_ON_SAMPLE = false;
@@ -152,6 +155,9 @@ public SampleResult sample() {
result.setSuccessful(false);
result.setResponseCode("000");
result.setResponseMessage(exceptionDuringInit.toString());
+
+ handleError(true);
+
return result;
}
if (stopBetweenSamples){ // If so, we need to start collection here
@@ -183,7 +189,10 @@ public SampleResult sample() {
extractContent(buffer, propBuffer, msg, (read == loop));
}
} catch (JMSException e) {
- log.warn("Error "+e.toString());
+ String errorCode = Optional.ofNullable(e.getErrorCode()).orElse("");
+ log.warn(String.format("Error [%s] %s", errorCode, e.toString()), e);
+
+ handleError(getIsReconnectErrorCode().test(errorCode));
}
now = System.currentTimeMillis();
}
@@ -222,6 +231,21 @@ public SampleResult sample() {
return result;
}
+ private void handleError(boolean reconnect) {
+ if (reconnect) {
+ initClient();
+ }
+
+ if (!reconnect || exceptionDuringInit != null) {
+ try {
+ Thread.sleep(getPauseBetweenErrorsAsLong());
+ } catch (InterruptedException ie) {
+ log.warn(String.format("Interrupted %s", ie.toString()), ie);
+ interrupted = true;
+ }
+ }
+ }
+
/**
* Calculate the wait time, will never be more than DEFAULT_WAIT.
*
@@ -286,6 +310,8 @@ private void extractContent(StringBuilder buffer, StringBuilder propBuffer,
*/
@Override
public void threadStarted() {
+ setIsReconnectErrorCode();
+
// Disabled thread start if listen on sample choice
if (isDestinationStatic() || START_ON_SAMPLE) {
timeout = getTimeoutAsLong();
@@ -293,6 +319,12 @@ public void threadStarted() {
exceptionDuringInit = null;
useReceive = getClientChoice().equals(JMSSubscriberGui.RECEIVE_RSC);
stopBetweenSamples = isStopBetweenSamples();
+ initClient();
+ }
+ }
+
+ private void initClient() {
+ exceptionDuringInit = null;
if (useReceive) {
try {
initReceiveClient();
@@ -316,7 +348,6 @@ public void threadStarted() {
log.error("Could not initialise client",exceptionDuringInit);
}
}
- }
public void threadStarted(boolean wts) {
if (wts) {
@@ -462,6 +493,18 @@ public void setStopBetweenSamples(boolean selected) {
setProperty(STOP_BETWEEN, selected, false);
}
+ public void setPauseBetweenErrors(String pause) {
+ setProperty(ERROR_PAUSE_BETWEEN, pause, ERROR_PAUSE_BETWEEN_DEFAULT);
+ }
+
+ public String getPauseBetweenErrors() {
+ return getPropertyAsString(ERROR_PAUSE_BETWEEN, ERROR_PAUSE_BETWEEN_DEFAULT);
+ }
+
+ public long getPauseBetweenErrorsAsLong() {
+ return getPropertyAsLong(ERROR_PAUSE_BETWEEN, DEFAULT_WAIT);
+ }
+
/**
* {@inheritDoc}
*/