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} */