This is an automated email from the ASF dual-hosted git repository.
robbie pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/main by this push:
new 009687ef7c ARTEMIS-4648 support typed properties from CLI producer
009687ef7c is described below
commit 009687ef7cdad7b734a49cca546cb7fce37eb90c
Author: Justin Bertram <[email protected]>
AuthorDate: Thu Mar 7 20:47:49 2024 -0600
ARTEMIS-4648 support typed properties from CLI producer
---
artemis-cli/pom.xml | 5 +
.../artemis/cli/commands/messages/Producer.java | 13 ++
.../cli/commands/messages/ProducerThread.java | 53 +++++
.../cli/commands/messages/ProducerThreadTest.java | 234 +++++++++++++++++++++
.../apache/activemq/cli/test/CliProducerTest.java | 29 +++
5 files changed, 334 insertions(+)
diff --git a/artemis-cli/pom.xml b/artemis-cli/pom.xml
index c5d71a340f..13b182dfc4 100644
--- a/artemis-cli/pom.xml
+++ b/artemis-cli/pom.xml
@@ -154,6 +154,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
<!-- The johnzon-core and json-api contents are repackaged in -commons,
However maven can still need them during tests, which run against
diff --git
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
index 38870b09c6..32c2eb8e49 100644
---
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
+++
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/Producer.java
@@ -61,6 +61,9 @@ public class Producer extends DestAbstract {
@Option(names = "--data", description = "Messages will be read from the
specified file. Other message options will be ignored.")
String file = null;
+ @Option(names = "--properties", description = "The properties to set on the
message in JSON, e.g.:
[{\"type\":\"string\",\"key\":\"myKey1\",\"value\":\"myValue1\"},{\"type\":\"string\",\"key\":\"myKey2\",\"value\":\"myValue2\"}].
Valid types are boolean, byte, short, int, long, float, double, and string.")
+ String properties = null;
+
public boolean isNonpersistent() {
return nonpersistent;
}
@@ -88,6 +91,15 @@ public class Producer extends DestAbstract {
return this;
}
+ public String getProperties() {
+ return properties;
+ }
+
+ public Producer setProperties(String properties) {
+ this.properties = properties;
+ return this;
+ }
+
public int getTextMessageSize() {
return textMessageSize;
}
@@ -205,6 +217,7 @@ public class Producer extends DestAbstract {
.setMessageSize(messageSize)
.setTextMessageSize(textMessageSize)
.setMessage(message)
+ .setProperties(properties)
.setObjectSize(objectSize)
.setMsgTTL(msgTTL)
.setMsgGroupID(msgGroupID)
diff --git
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
index a624366fe3..ad829bfa3b 100644
---
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
+++
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThread.java
@@ -27,10 +27,14 @@ import javax.jms.TextMessage;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.StringReader;
import java.net.URL;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.json.JsonArray;
+import org.apache.activemq.artemis.json.JsonObject;
+import org.apache.activemq.artemis.utils.JsonLoader;
import org.apache.activemq.artemis.utils.ReusableLatch;
public class ProducerThread extends Thread {
@@ -54,6 +58,7 @@ public class ProducerThread extends Thread {
int transactions = 0;
final AtomicLong sentCount = new AtomicLong(0);
String message = null;
+ String properties = null;
String messageText = null;
String payloadUrl = null;
byte[] payload = null;
@@ -193,9 +198,52 @@ public class ProducerThread extends Thread {
answer.setLongProperty("count", i);
answer.setStringProperty("ThreadSent", threadName);
+
+ if (properties != null && properties.length() != 0) {
+ applyProperties(answer);
+ }
+
return answer;
}
+ protected void applyProperties(Message message) throws JMSException {
+ JsonArray propertyArray = JsonLoader.readArray(new
StringReader(properties));
+ for (int j = 0; j < propertyArray.size(); j++) {
+ JsonObject propertyEntry = propertyArray.getJsonObject(j);
+ String type = propertyEntry.getString("type");
+ String key = propertyEntry.getString("key");
+ String value = propertyEntry.getString("value");
+ switch (type.toLowerCase()) {
+ case "boolean":
+ message.setBooleanProperty(key, Boolean.parseBoolean(value));
+ break;
+ case "int":
+ message.setIntProperty(key, Integer.parseInt(value));
+ break;
+ case "long":
+ message.setLongProperty(key, Long.parseLong(value));
+ break;
+ case "byte":
+ message.setByteProperty(key, Byte.parseByte(value));
+ break;
+ case "short":
+ message.setShortProperty(key, Short.parseShort(value));
+ break;
+ case "float":
+ message.setFloatProperty(key, Float.parseFloat(value));
+ break;
+ case "double":
+ message.setDoubleProperty(key, Double.parseDouble(value));
+ break;
+ case "string":
+ message.setStringProperty(key, value);
+ break;
+ default:
+ context.err.println("Unable to set property: " + key + ". Did
not recognize type: " + type + ". Supported types are: boolean, int, long,
byte, short, float, double, string.");
+ }
+ }
+ }
+
private String readInputStream(InputStream is, int size, long
messageNumber) throws IOException {
try (InputStreamReader reader = new InputStreamReader(is)) {
char[] buffer;
@@ -333,6 +381,11 @@ public class ProducerThread extends Thread {
return this;
}
+ public ProducerThread setProperties(String properties) {
+ this.properties = properties;
+ return this;
+ }
+
public boolean isRunIndefinitely() {
return runIndefinitely;
}
diff --git
a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThreadTest.java
b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThreadTest.java
new file mode 100644
index 0000000000..1b1988f7d5
--- /dev/null
+++
b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/messages/ProducerThreadTest.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.cli.commands.messages;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
+import org.apache.activemq.artemis.utils.RandomUtil;
+import org.apache.activemq.cli.test.TestActionContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ProducerThreadTest {
+
+ ProducerThread producer;
+ Message mockMessage;
+
+ @Before
+ public void setUp() {
+ producer = new ProducerThread(null,
ActiveMQDestination.createQueue(RandomUtil.randomString()), 0, null);
+ mockMessage = Mockito.mock(Message.class);
+ }
+
+ @Test
+ public void testBooleanPropertyTrue() throws Exception {
+ doBooleanPropertyTestImpl("myTrueBoolean", true);
+ }
+
+ @Test
+ public void testBooleanPropertyFalse() throws Exception {
+ doBooleanPropertyTestImpl("myFalseBoolean", false);
+ }
+
+ private void doBooleanPropertyTestImpl(String key, boolean value) throws
JMSException {
+ producer.setProperties(createJsonProperty(boolean.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setBooleanProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testIntProperty() throws Exception {
+ String key = "myInt";
+ int value = RandomUtil.randomInt();
+
+ producer.setProperties(createJsonProperty(int.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setIntProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad int value
+ producer.setProperties(createJsonProperty(int.class.getSimpleName(),
key, "badInt"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testLongProperty() throws Exception {
+ String key = "myLong";
+ long value = RandomUtil.randomLong();
+
+ producer.setProperties(createJsonProperty(long.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setLongProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad long value
+ producer.setProperties(createJsonProperty(long.class.getSimpleName(),
key, "badLong"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testByteProperty() throws Exception {
+ String key = "myByte";
+ byte value = RandomUtil.randomByte();
+
+ producer.setProperties(createJsonProperty(byte.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setByteProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad byte value
+ producer.setProperties(createJsonProperty(byte.class.getSimpleName(),
key, "128"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testShortProperty() throws Exception {
+ String key = "myShort";
+ short value = RandomUtil.randomShort();
+
+ producer.setProperties(createJsonProperty(short.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setShortProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad short value
+ producer.setProperties(createJsonProperty(short.class.getSimpleName(),
key, "badShort"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testFloatProperty() throws Exception {
+ String key = "myFloat";
+ float value = RandomUtil.randomFloat();
+
+ producer.setProperties(createJsonProperty(float.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setFloatProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad float value
+ producer.setProperties(createJsonProperty(float.class.getSimpleName(),
key, "badFloat"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testDoubleProperty() throws Exception {
+ String key = "myDouble";
+ double value = RandomUtil.randomDouble();
+
+ producer.setProperties(createJsonProperty(double.class.getSimpleName(),
key, String.valueOf(value)));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setDoubleProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+
+ // Now with a bad double value
+ producer.setProperties(createJsonProperty(double.class.getSimpleName(),
key, "badDouble"));
+ try {
+ producer.applyProperties(mockMessage);
+ fail("should have thrown");
+ } catch (NumberFormatException e) {
+ // expected
+ }
+
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testStringProperty() throws Exception {
+ doStringPropertyTestImpl("string");
+ }
+
+ private void doStringPropertyTestImpl(String type) throws JMSException {
+ String key = "myString";
+ String value = RandomUtil.randomString();
+
+ producer.setProperties(createJsonProperty(type, key, value));
+ producer.applyProperties(mockMessage);
+
+ Mockito.verify(mockMessage).setStringProperty(key, value);
+ Mockito.verifyNoMoreInteractions(mockMessage);
+ }
+
+ @Test
+ public void testPropertyTypeIsCaseInsensitive() throws Exception {
+ doStringPropertyTestImpl("String");
+ }
+
+ @Test
+ public void testBadMessagePropertyType() throws Exception {
+ TestActionContext context = new TestActionContext();
+ producer = new ProducerThread(null,
ActiveMQDestination.createQueue(RandomUtil.randomString()), 0, context);
+
+ producer.setProperties(createJsonProperty("myType", "myKey", "myValue"));
+ producer.applyProperties(mockMessage);
+
+ assertEquals("Unable to set property: myKey. Did not recognize type:
myType. Supported types are: boolean, int, long, byte, short, float, double,
string.\n", context.getStderr());
+ }
+
+ private static String createJsonProperty(String type, String key, String
value) {
+ return ("[{'type':'" + type + "','key':'" + key + "','value':'" + value
+ "'}]").replaceAll("'", "\"");
+ }
+}
diff --git
a/artemis-cli/src/test/java/org/apache/activemq/cli/test/CliProducerTest.java
b/artemis-cli/src/test/java/org/apache/activemq/cli/test/CliProducerTest.java
index fa747131ed..a3d8429bc0 100644
---
a/artemis-cli/src/test/java/org/apache/activemq/cli/test/CliProducerTest.java
+++
b/artemis-cli/src/test/java/org/apache/activemq/cli/test/CliProducerTest.java
@@ -20,6 +20,7 @@ import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.cli.commands.messages.Producer;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.utils.CompositeAddress;
+import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -31,6 +32,7 @@ import javax.jms.TextMessage;
import java.util.List;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class CliProducerTest extends CliTestBase {
private Connection connection;
@@ -55,8 +57,13 @@ public class CliProducerTest extends CliTestBase {
}
private void produceMessages(String address, String message, int msgCount)
throws Exception {
+ produceMessages(address, message, msgCount, null);
+ }
+
+ private void produceMessages(String address, String message, int msgCount,
String properties) throws Exception {
new Producer()
.setMessage(message)
+ .setProperties(properties)
.setMessageCount(msgCount)
.setDestination(address)
.setUser("admin")
@@ -88,6 +95,28 @@ public class CliProducerTest extends CliTestBase {
checkSentMessages(session, address, null);
}
+ @Test
+ public void testSendMessageWithProperties() throws Exception {
+ String address = "test";
+ Session session = createSession(connection);
+
+ String myBooleanKey = "myBooleanKey";
+ String myStringKey = "myStringKey";
+ String myStringValue = RandomUtil.randomString();
+ String propertiesJson = ("[{'type':'boolean','key':'" + myBooleanKey +
"','value':'true'},{'type':'string','key':'" + myStringKey + "','value':'" +
myStringValue + "'}]").replaceAll("'", "\"");
+
+ produceMessages(address, null, 1, propertiesJson);
+
+ List<Message> consumedMessages = consumeMessages(session, address, 1,
false);
+ assertEquals(1, consumedMessages.size());
+
+ Message msg = consumedMessages.get(0);
+ assertTrue(msg.propertyExists(myBooleanKey));
+ assertTrue(msg.getBooleanProperty(myBooleanKey));
+ assertTrue(msg.propertyExists(myStringKey));
+ assertEquals(myStringValue, msg.getStringProperty(myStringKey));
+ }
+
@Test
public void testSendMessageFQQN() throws Exception {
String address = "test";