ARTEMIS-573 clarify filter documentation
Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/8a66d5f4 Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/8a66d5f4 Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/8a66d5f4 Branch: refs/heads/master Commit: 8a66d5f4857a3168b416d67d0bce49208694437d Parents: d7edf06 Author: jbertram <[email protected]> Authored: Wed Jul 13 15:47:24 2016 -0500 Committer: Andy Taylor <[email protected]> Committed: Thu Jul 14 11:29:05 2016 +0100 ---------------------------------------------------------------------- docs/user-manual/en/filter-expressions.md | 22 ++++++ .../en/protocols-interoperability.md | 7 ++ .../tests/integration/stomp/StompTest.java | 78 ++++++++++++++++++++ 3 files changed, 107 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/8a66d5f4/docs/user-manual/en/filter-expressions.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/filter-expressions.md b/docs/user-manual/en/filter-expressions.md index ea7224e..88b8b1b 100644 --- a/docs/user-manual/en/filter-expressions.md +++ b/docs/user-manual/en/filter-expressions.md @@ -49,3 +49,25 @@ refer to attributes of the core message in an expression: Any other identifiers used in core filter expressions will be assumed to be properties of the message. + +The JMS spec states that a String property should not get converted to a +numeric when used in a selector. So for example, if a message has the `age` +property set to String `21` then the following selector should not match +it: `age > 18`. Since Apache ActiveMQ Artemis supports STOMP clients which +can only send messages with string properties, that restriction is a bit +limiting. Therefore, if you want your filter expressions to auto-convert String +properties the the appropriate number type, just prefix it with +`convert_string_expressions:`. If you changed the filter expression in the +previous example to be `convert_string_expressions:age > 18`, then it would +match the aforementioned message. + +The JMS spec also states that property identifiers (and therefore the +identifiers which are valid for use in a filter expression) are an, +"unlimited-length sequence of letters and digits, the first of which must be +a letter. A letter is any character for which the method +`Character.isJavaLetter` returns `true`. This includes `_` and `$`. A letter +or digit is any character for which the method `Character.isJavaLetterOrDigit` +returns `true`." This constraint means that hyphens (i.e. `-`) cannot be used. +However, this constraint can be overcome by using the `hyphenated_props:` +prefix. For example, if a message had the `foo-bar` property set to `0` then +the filter expression `hyphenated_props:foo-bar = 0` would match it. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/8a66d5f4/docs/user-manual/en/protocols-interoperability.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/protocols-interoperability.md b/docs/user-manual/en/protocols-interoperability.md index abfdcf8..5c19311 100644 --- a/docs/user-manual/en/protocols-interoperability.md +++ b/docs/user-manual/en/protocols-interoperability.md @@ -311,6 +311,13 @@ seconds. > users can use heart-beats to maintain the life cycle of stomp > connections. +### Selector/Filter expressions + +Stomp subscribers can specify an expression used to select or filter +what the subscriber receives using the `selector` header. The filter +expression syntax follows the *core filter syntax* described in the +[Filter Expressions](filter-expressions.md) documentation. + ### Stomp and JMS interoperability #### Using JMS destinations http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/8a66d5f4/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java ---------------------------------------------------------------------- diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java index 41b5c35..8155898 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTest.java @@ -31,6 +31,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.api.core.client.ActiveMQClient; +import org.apache.activemq.artemis.api.core.client.ClientMessage; +import org.apache.activemq.artemis.api.core.client.ClientProducer; +import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; +import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.api.core.management.ResourceNames; import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; @@ -436,6 +442,35 @@ public class StompTest extends StompTestBase { } @Test + public void testSendMessageWithCustomHeadersAndHyphenatedSelector() throws Exception { + + MessageConsumer consumer = session.createConsumer(queue, "hyphenated_props:b-ar = '123'"); + + String frame = "CONNECT\n" + "login: brianm\n" + "passcode: wombats\n\n" + Stomp.NULL; + sendFrame(frame); + + frame = receiveFrame(10000); + Assert.assertTrue(frame.startsWith("CONNECTED")); + + frame = "SEND\n" + "foo:abc\n" + + "b-ar:123\n" + + "destination:" + + getQueuePrefix() + + getQueueName() + + "\n\n" + + "Hello World" + + Stomp.NULL; + + sendFrame(frame); + + TextMessage message = (TextMessage) consumer.receive(1000); + Assert.assertNotNull(message); + Assert.assertEquals("Hello World", message.getText()); + Assert.assertEquals("foo", "abc", message.getStringProperty("foo")); + Assert.assertEquals("b-ar", "123", message.getStringProperty("b-ar")); + } + + @Test public void testSendMessageWithStandardHeaders() throws Exception { MessageConsumer consumer = session.createConsumer(queue); @@ -755,6 +790,49 @@ public class StompTest extends StompTestBase { sendFrame(frame); } + + @Test + public void testSubscribeWithAutoAckAndHyphenatedSelector() throws Exception { + + String frame = "CONNECT\n" + "login: brianm\n" + "passcode: wombats\n\n" + Stomp.NULL; + sendFrame(frame); + + frame = receiveFrame(100000); + Assert.assertTrue(frame.startsWith("CONNECTED")); + + frame = "SUBSCRIBE\n" + "destination:" + + getQueuePrefix() + + getQueueName() + + "\n" + + "selector: hyphenated_props:foo-bar = 'zzz'\n" + + "ack:auto\n\n" + + Stomp.NULL; + sendFrame(frame); + + ServerLocator serverLocator = addServerLocator(ActiveMQClient.createServerLocator("vm://0")); + ClientSessionFactory clientSessionFactory = serverLocator.createSessionFactory(); + ClientSession clientSession = clientSessionFactory.createSession(true, true); + ClientProducer producer = clientSession.createProducer(getQueuePrefix() + getQueueName()); + + ClientMessage ignoredMessage = clientSession.createMessage(false); + ignoredMessage.putStringProperty("foo-bar", "1234"); + ignoredMessage.getBodyBuffer().writeNullableSimpleString(SimpleString.toSimpleString("Ignored message")); + + ClientMessage realMessage = clientSession.createMessage(false); + realMessage.putStringProperty("foo-bar", "zzz"); + realMessage.getBodyBuffer().writeNullableSimpleString(SimpleString.toSimpleString("Real message")); + + producer.send(ignoredMessage); + producer.send(realMessage); + + frame = receiveFrame(10000); + Assert.assertTrue(frame.startsWith("MESSAGE")); + Assert.assertTrue("Should have received the real message but got: " + frame, frame.indexOf("Real message") > 0); + + frame = "DISCONNECT\n" + "\n\n" + Stomp.NULL; + sendFrame(frame); + } + @Test public void testSubscribeWithClientAck() throws Exception {
