diff --git a/CHANGELOG.md b/CHANGELOG.md index 582db1c..b76898a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ on [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [3.6.0] - 2020-03-12 + +### Added +- Command _`sendmessage`_ that sends the contents of a text file as a text message to the DLQ ## [3.5.0] - 2019-06-20 diff --git a/README.md b/README.md index 9237e97..21ecc05 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,27 @@ Only messages that have the same JMSMessageId and different Consumer (_AMQ_ORIG_ `java -jar artemis-manager.jar browse @artemis.config -report created-at-name-total-report` +## Send a Text Message to the DLQ + +* Sends a text message to the DLQ. The message must be contained in a text file, the path of which must be included in the command + +**Note: SendMessage uses JMS to connect to the Artemis broker.** + +`java -jar artemis-manager.jar sendmessage -messageFile path/to/some/text/file/containing/the/message.txt @artemis.config` + +**Warning. In order to have the correct permissions to run this command, you will need to update your +broker.xml file in your Artemis configuration. This will disable your security** + +Add these 2 lines to broker.xml directly under the + + +tag + + false + true + +For an example, look at src/test/resources/artemis/broker.xml + ## Chaining Commands * Chaining commands diff --git a/pom.xml b/pom.xml index 9de91dc..1a514b6 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,11 @@ utilities-core ${utilities.version} + + commons-io + commons-io + + @@ -125,11 +130,6 @@ json-path-assert test - - commons-io - commons-io - test - org.mockito mockito-core @@ -229,6 +229,7 @@ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=false + ${basedir}/src/test/resources/artemis diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/ArtemisConnector.java b/src/main/java/uk/gov/justice/artemis/manager/connector/ArtemisConnector.java index e0aed8f..2daa084 100644 --- a/src/main/java/uk/gov/justice/artemis/manager/connector/ArtemisConnector.java +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/ArtemisConnector.java @@ -35,4 +35,6 @@ void setParameters(final List jmxUrls, final String jmsUrl, final String jmsUsername, final String jmsPassword) throws MalformedURLException; -} \ No newline at end of file + + String sendTextMessage(final String destinationName, final String message); +} diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/CombinedJmsAndJmxArtemisConnector.java b/src/main/java/uk/gov/justice/artemis/manager/connector/CombinedJmsAndJmxArtemisConnector.java index 7973f33..59a98e6 100644 --- a/src/main/java/uk/gov/justice/artemis/manager/connector/CombinedJmsAndJmxArtemisConnector.java +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/CombinedJmsAndJmxArtemisConnector.java @@ -206,4 +206,17 @@ public Map topicMessageCount(final Collection topicNames) .flatMap(m -> m.entrySet().stream()) .collect(groupingBy(Entry::getKey, summingLong(Entry::getValue))); } -} \ No newline at end of file + + @Override + public String sendTextMessage(final String destinationName, final String message) { + + return jmxProcessor + .processQueueControl( + jmxServiceUrls, + jmxEnvironment, + objectNameBuilder, + destinationName, + jmxManagement.sendTextMessage(message)) + .collect(toList()).toString(); + } +} diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/JmxArtemisConnector.java b/src/main/java/uk/gov/justice/artemis/manager/connector/JmxArtemisConnector.java index 8607092..6a308b3 100644 --- a/src/main/java/uk/gov/justice/artemis/manager/connector/JmxArtemisConnector.java +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/JmxArtemisConnector.java @@ -147,4 +147,17 @@ public Map topicMessageCount(final Collection topicNames) groupingBy(Entry::getKey, summingLong(Entry::getValue))); } -} \ No newline at end of file + + @Override + public String sendTextMessage(final String destinationName, final String message) { + + return jmxProcessor + .processQueueControl( + jmxServiceUrls, + jmxEnvironment, + objectNameBuilder, + destinationName, + jmxManagement.sendTextMessage(message)) + .collect(toList()).toString(); + } +} diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/MessageFileException.java b/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/MessageFileException.java new file mode 100644 index 0000000..47d61eb --- /dev/null +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/MessageFileException.java @@ -0,0 +1,12 @@ +package uk.gov.justice.artemis.manager.connector.filehandling; + +public class MessageFileException extends RuntimeException { + + public MessageFileException(final String message) { + super(message); + } + + public MessageFileException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/TextMessageFileContentsReader.java b/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/TextMessageFileContentsReader.java new file mode 100644 index 0000000..2c79cc9 --- /dev/null +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/filehandling/TextMessageFileContentsReader.java @@ -0,0 +1,26 @@ +package uk.gov.justice.artemis.manager.connector.filehandling; + +import static java.lang.String.format; +import static java.nio.charset.Charset.defaultCharset; +import static org.apache.commons.io.FileUtils.getFile; +import static org.apache.commons.io.FileUtils.readFileToString; + +import java.io.File; + +public class TextMessageFileContentsReader { + + public String readContentsOf(final String pathToFile) { + + try { + final File file = getFile(pathToFile); + + if(! file.exists()) { + throw new MessageFileException(format("Failed to find file '%s'", file.getAbsolutePath())); + } + + return readFileToString(file, defaultCharset()); + } catch (final Exception e) { + throw new MessageFileException(format("Failed to read file '%s'", pathToFile), e); + } + } +} diff --git a/src/main/java/uk/gov/justice/artemis/manager/connector/jmx/JmxManagement.java b/src/main/java/uk/gov/justice/artemis/manager/connector/jmx/JmxManagement.java index 616d534..4592553 100644 --- a/src/main/java/uk/gov/justice/artemis/manager/connector/jmx/JmxManagement.java +++ b/src/main/java/uk/gov/justice/artemis/manager/connector/jmx/JmxManagement.java @@ -98,4 +98,15 @@ public JmxManagementFunction reprocessAllMessages() { return reprocessedMessageCount; }; } + + public JmxManagementFunction sendTextMessage(final String message) { + return queueControl -> { + try { + return queueControl.sendTextMessage(message); + } catch (final Exception exception) { + outputPrinter.writeException(exception); + return format("%s: %s", exception.getClass().getSimpleName(), exception.getMessage()); + } + }; + } } diff --git a/src/main/java/uk/gov/justice/framework/tools/command/AbstractArtemisCommand.java b/src/main/java/uk/gov/justice/framework/tools/command/AbstractArtemisCommand.java index fdbbf58..1ef7422 100644 --- a/src/main/java/uk/gov/justice/framework/tools/command/AbstractArtemisCommand.java +++ b/src/main/java/uk/gov/justice/framework/tools/command/AbstractArtemisCommand.java @@ -16,7 +16,7 @@ abstract class AbstractArtemisCommand { public static final String DEFAULT_BROKER_NAME = "default"; public static final String DEFAULT_JMS_URL = "tcp://localhost:61616?clientID=artemis-manager"; - final OutputPrinter outputPrinter = new ConsolePrinter(); + OutputPrinter outputPrinter = new ConsolePrinter(); ArtemisConnector artemisConnector = new CombinedJmsAndJmxArtemisConnector(); @@ -41,6 +41,9 @@ abstract class AbstractArtemisCommand { @Parameter(names = "-jmsPassword", description = "JMS Password (optional)") String jmsPassword; + @Parameter(names = "-messageFile", description = "The text message file that should be sent when calling sendmessage. If not calling sendmessage this parameter is not required") + String textMessageFile; + @Parameter(names = "-help", help = true) private boolean help; @@ -53,4 +56,4 @@ public void setup() throws MalformedURLException { this.jmsUsername, this.jmsPassword); } -} \ No newline at end of file +} diff --git a/src/main/java/uk/gov/justice/framework/tools/command/SendMessage.java b/src/main/java/uk/gov/justice/framework/tools/command/SendMessage.java new file mode 100644 index 0000000..64ab8d0 --- /dev/null +++ b/src/main/java/uk/gov/justice/framework/tools/command/SendMessage.java @@ -0,0 +1,39 @@ +package uk.gov.justice.framework.tools.command; + + +import uk.gov.justice.artemis.manager.connector.filehandling.MessageFileException; +import uk.gov.justice.artemis.manager.connector.filehandling.TextMessageFileContentsReader; +import uk.gov.justice.framework.tools.common.command.ShellCommand; + +public class SendMessage extends AbstractArtemisCommand implements ShellCommand { + + private final TextMessageFileContentsReader textMessageFileContentsReader; + + public SendMessage() { + this(new TextMessageFileContentsReader()); + } + + public SendMessage(final TextMessageFileContentsReader textMessageFileContentsReader) { + this.textMessageFileContentsReader = textMessageFileContentsReader; + } + + @Override + public void run(final String[] args) { + + try { + super.setup(); + + if(textMessageFile == null || textMessageFile.isEmpty()) { + throw new MessageFileException("Cannot read message file. No file name set. Please use -messageFile parameter when calling this command"); + } + + final String message = textMessageFileContentsReader.readContentsOf(textMessageFile); + + final String results = artemisConnector.sendTextMessage("DLQ", message); + outputPrinter.write(results); + + } catch (final Exception exception) { + outputPrinter.writeStackTrace(exception); + } + } +} diff --git a/src/main/java/uk/gov/justice/framework/tools/common/command/Bootstrap.java b/src/main/java/uk/gov/justice/framework/tools/common/command/Bootstrap.java index 50bde87..6c033f0 100644 --- a/src/main/java/uk/gov/justice/framework/tools/common/command/Bootstrap.java +++ b/src/main/java/uk/gov/justice/framework/tools/common/command/Bootstrap.java @@ -7,11 +7,10 @@ import java.util.Optional; import java.util.Set; +import com.beust.jcommander.JCommander; import org.reflections.Reflections; import org.slf4j.Logger; -import com.beust.jcommander.JCommander; - public class Bootstrap { private static final Logger LOGGER = getLogger(Bootstrap.class); @@ -60,4 +59,4 @@ private void createInstanceAndAddToJCommander(final Class exceptionCaptor; + + @Test + public void shouldSendReadContentsOfSuppliedFileAndSendAsTextMessage() throws Exception { + + final String fileName = "some/messageFile.txt"; + final String message = "a message"; + final String results = "this message succeeded"; + + sendMessage.textMessageFile = fileName; + + when(textMessageFileContentsReader.readContentsOf(fileName)).thenReturn(message); + when(artemisConnector.sendTextMessage("DLQ", message)).thenReturn(results); + + sendMessage.run(new String[0]); + + verify(outputPrinter).write(results); + } + + @Test + public void shouldFailIfSuppliedFileNameIsNull() throws Exception { + + sendMessage.textMessageFile = null; + + sendMessage.run(new String[0]); + + verify(outputPrinter).writeStackTrace(exceptionCaptor.capture()); + + final Exception exception = exceptionCaptor.getValue(); + + assertThat(exception, is(instanceOf(MessageFileException.class))); + assertThat(exception.getMessage(), is("Cannot read message file. No file name set. Please use -messageFile parameter when calling this command")); + } + + @Test + public void shouldFailIfSuppliedFileNameIsEmpty() throws Exception { + + sendMessage.textMessageFile = ""; + + sendMessage.run(new String[0]); + + verify(outputPrinter).writeStackTrace(exceptionCaptor.capture()); + + final Exception exception = exceptionCaptor.getValue(); + + assertThat(exception, is(instanceOf(MessageFileException.class))); + assertThat(exception.getMessage(), is("Cannot read message file. No file name set. Please use -messageFile parameter when calling this command")); + } + + @Test + public void shouldFailIfWriteExceptionIfSendingMessageFails() throws Exception { + + final NullPointerException nullPointerException = new NullPointerException("Ooops"); + + final String fileName = "some/messageFile.txt"; + final String message = "a message"; + + sendMessage.textMessageFile = fileName; + + when(textMessageFileContentsReader.readContentsOf(fileName)).thenReturn(message); + when(artemisConnector.sendTextMessage("DLQ", message)).thenThrow(nullPointerException); + + sendMessage.run(new String[0]); + + verify(outputPrinter).writeStackTrace(exceptionCaptor.capture()); + + final Exception exception = exceptionCaptor.getValue(); + + assertThat(exception, is(nullPointerException)); + } +} diff --git a/src/test/resources/artemis/broker.xml b/src/test/resources/artemis/broker.xml new file mode 100644 index 0000000..8829de7 --- /dev/null +++ b/src/test/resources/artemis/broker.xml @@ -0,0 +1,105 @@ + + + + + + + + + 0.0.0.0 + true + + NIO + ./data/paging + ./data/bindings + ./data/journal + ./data/large-messages + true + 2 + -1 + + + + + + + + + + + + 5000 + + 90 + + 104857600 + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE + + tcp://0.0.0.0:5672?protocols=AMQP + + tcp://0.0.0.0:61613?protocols=STOMP + + tcp://0.0.0.0:5445?protocols=HORNETQ,STOMP + + tcp://0.0.0.0:1883?protocols=MQTT + + + + + + + + + + + + + + + false + true + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + 0 + + -1 + 10 + PAGE + + + + diff --git a/src/test/resources/messages/messageForSendingToArtemis.txt b/src/test/resources/messages/messageForSendingToArtemis.txt new file mode 100644 index 0000000..b7fe9da --- /dev/null +++ b/src/test/resources/messages/messageForSendingToArtemis.txt @@ -0,0 +1,3 @@ +{ + "message": "All your base are belong to us" +}