From 6e9ecdfe21b8bb894ae9a4473a76eb3548e84b87 Mon Sep 17 00:00:00 2001 From: Timothy Bish Date: Mon, 18 Aug 2014 13:23:56 -0400 Subject: [PATCH] https://issues.apache.org/jira/browse/AMQ-5281 Apply patch correctly honor JMS selector behavior for unknown values. --- .../activemq/filter/ComparisonExpression.java | 3 + .../activemq/filter/LogicExpression.java | 25 ++- .../selector/UnknownHandlingSelectorTest.java | 182 ++++++++++++++++++ 3 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 activemq-unit-tests/src/test/java/org/apache/activemq/selector/UnknownHandlingSelectorTest.java diff --git a/activemq-client/src/main/java/org/apache/activemq/filter/ComparisonExpression.java b/activemq-client/src/main/java/org/apache/activemq/filter/ComparisonExpression.java index 0f77399c8a..62d53d1d5e 100755 --- a/activemq-client/src/main/java/org/apache/activemq/filter/ComparisonExpression.java +++ b/activemq-client/src/main/java/org/apache/activemq/filter/ComparisonExpression.java @@ -212,6 +212,9 @@ public abstract class ComparisonExpression extends BinaryExpression implements B // If one of the values is null if (lv == null ^ rv == null) { + if (lv == null) { + return null; + } return Boolean.FALSE; } if (lv == rv || lv.equals(rv)) { diff --git a/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java b/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java index bddd3d7862..fdc022baef 100755 --- a/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java +++ b/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java @@ -39,13 +39,17 @@ public abstract class LogicExpression extends BinaryExpression implements Boolea public Object evaluate(MessageEvaluationContext message) throws JMSException { Boolean lv = (Boolean)left.evaluate(message); - // Can we do an OR shortcut?? if (lv != null && lv.booleanValue()) { return Boolean.TRUE; } - Boolean rv = (Boolean)right.evaluate(message); - return rv == null ? null : rv; + if (rv != null && rv.booleanValue()) { + return Boolean.TRUE; + } + if (lv == null || rv == null) { + return null; + } + return Boolean.FALSE; } public String getExpressionSymbol() { @@ -61,16 +65,17 @@ public abstract class LogicExpression extends BinaryExpression implements Boolea Boolean lv = (Boolean)left.evaluate(message); - // Can we do an AND shortcut?? - if (lv == null) { - return null; - } - if (!lv.booleanValue()) { + if (lv != null && !lv.booleanValue()) { return Boolean.FALSE; } - Boolean rv = (Boolean)right.evaluate(message); - return rv == null ? null : rv; + if (rv != null && !rv.booleanValue()) { + return Boolean.FALSE; + } + if (lv == null || rv == null) { + return null; + } + return Boolean.TRUE; } public String getExpressionSymbol() { diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/selector/UnknownHandlingSelectorTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/selector/UnknownHandlingSelectorTest.java new file mode 100644 index 0000000000..5c9a8eecf8 --- /dev/null +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/selector/UnknownHandlingSelectorTest.java @@ -0,0 +1,182 @@ +/** + * 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.selector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.jms.JMSException; +import javax.jms.Message; + +import org.apache.activemq.command.ActiveMQMessage; +import org.apache.activemq.command.ActiveMQTopic; +import org.apache.activemq.filter.BooleanExpression; +import org.apache.activemq.filter.MessageEvaluationContext; +import org.junit.Before; +import org.junit.Test; + +public class UnknownHandlingSelectorTest { + + private Message message; + + @Before + public void setUp() throws Exception { + message = new ActiveMQMessage(); + message.setJMSDestination(new ActiveMQTopic("FOO.BAR")); + message.setJMSType("selector-test"); + message.setJMSMessageID("connection:1:1:1:1"); + message.setBooleanProperty("trueProp", true); + message.setBooleanProperty("falseProp", false); + message.setObjectProperty("nullProp", null); + } + + /** + * | NOT + * +------+------ + * | T | F + * | F | T + * | U | U + * +------+------- + */ + @Test + public void notEvaluation() throws Exception { + assertSelector("not(trueProp)", false); + assertSelector("not(falseProp)", true); + assertSelector("not(unknownProp)", false); + } + + /** + * | AND | T | F | U + * +------+-------+-------+------- + * | T | T | F | U + * | F | F | F | F + * | U | U | F | U + * +------+-------+-------+------- + */ + @Test + public void andEvaluation() throws Exception { + assertSelectorEvaluatesToTrue("trueProp AND trueProp"); + assertSelectorEvaluatesToFalse("trueProp AND falseProp"); + assertSelectorEvaluatesToFalse("falseProp AND trueProp"); + assertSelectorEvaluatesToFalse("falseProp AND falseProp"); + assertSelectorEvaluatesToFalse("falseProp AND unknownProp"); + assertSelectorEvaluatesToFalse("unknownProp AND falseProp"); + assertSelectorEvaluatesToUnknown("trueProp AND unknownProp"); + assertSelectorEvaluatesToUnknown("unknownProp AND trueProp"); + assertSelectorEvaluatesToUnknown("unknownProp AND unknownProp"); + } + + /** + * | OR | T | F | U + * +------+-------+-------+-------- + * | T | T | T | T + * | F | T | F | U + * | U | T | U | U + * +------+-------+-------+------- + */ + @Test + public void orEvaluation() throws Exception { + assertSelectorEvaluatesToTrue("trueProp OR trueProp"); + assertSelectorEvaluatesToTrue("trueProp OR falseProp"); + assertSelectorEvaluatesToTrue("falseProp OR trueProp"); + assertSelectorEvaluatesToTrue("trueProp OR unknownProp"); + assertSelectorEvaluatesToTrue("unknownProp OR trueProp"); + assertSelectorEvaluatesToFalse("falseProp OR falseProp"); + assertSelectorEvaluatesToUnknown("falseProp OR unknownProp"); + assertSelectorEvaluatesToUnknown("unknownProp OR falseProp"); + assertSelectorEvaluatesToUnknown("unknownProp OR unknownProp"); + } + + @Test + public void comparisonWithUnknownShouldEvaluateToUnknown() throws Exception { + assertSelectorEvaluatesToUnknown("unknownProp = 0"); + assertSelectorEvaluatesToUnknown("unknownProp > 0"); + assertSelectorEvaluatesToUnknown("unknownProp >= 0"); + assertSelectorEvaluatesToUnknown("unknownProp < 0"); + assertSelectorEvaluatesToUnknown("unknownProp <= 0"); + assertSelectorEvaluatesToUnknown("unknownProp <> 0"); + assertSelectorEvaluatesToUnknown("unknownProp LIKE 'zero'"); + assertSelectorEvaluatesToUnknown("unknownProp NOT LIKE 'zero'"); + assertSelectorEvaluatesToUnknown("unknownProp IN ('zero')"); + assertSelectorEvaluatesToUnknown("unknownProp NOT IN ('zero')"); + assertSelectorEvaluatesToUnknown("unknownProp BETWEEN 1 AND 2"); + assertSelectorEvaluatesToUnknown("unknownProp NOT BETWEEN 1 AND 2"); + } + + @Test + public void comparisonWithNullPropShouldEvaluateToUnknown() throws Exception { + assertSelectorEvaluatesToUnknown("nullProp = 0"); + assertSelectorEvaluatesToUnknown("nullProp > 0"); + assertSelectorEvaluatesToUnknown("nullProp >= 0"); + assertSelectorEvaluatesToUnknown("nullProp < 0"); + assertSelectorEvaluatesToUnknown("nullProp <= 0"); + assertSelectorEvaluatesToUnknown("nullProp <> 0"); + assertSelectorEvaluatesToUnknown("nullProp LIKE 'zero'"); + assertSelectorEvaluatesToUnknown("nullProp NOT LIKE 'zero'"); + assertSelectorEvaluatesToUnknown("nullProp IN ('zero')"); + assertSelectorEvaluatesToUnknown("nullProp NOT IN ('zero')"); + assertSelectorEvaluatesToUnknown("nullProp BETWEEN 1 AND 2"); + assertSelectorEvaluatesToUnknown("nullProp NOT BETWEEN 1 AND 2"); + } + + @Test + public void isNullIsNotNull() throws Exception { + assertSelectorEvaluatesToTrue("unknownProp IS NULL"); + assertSelectorEvaluatesToTrue("nullProp IS NULL"); + assertSelectorEvaluatesToFalse("trueProp IS NULL"); + assertSelectorEvaluatesToFalse("unknownProp IS NOT NULL"); + assertSelectorEvaluatesToFalse("nullProp IS NOT NULL"); + assertSelectorEvaluatesToTrue("trueProp IS NOT NULL"); + } + + @Test + public void arithmeticWithNull() throws Exception { + assertSelectorEvaluatesToUnknown("-unknownProp = 0"); + assertSelectorEvaluatesToUnknown("+unknownProp = 0"); + assertSelectorEvaluatesToUnknown("unknownProp * 2 = 0"); + assertSelectorEvaluatesToUnknown("unknownProp / 2 = 0"); + assertSelectorEvaluatesToUnknown("unknownProp + 2 = 0"); + assertSelectorEvaluatesToUnknown("unknownProp - 2 = 0"); + } + + protected void assertSelectorEvaluatesToUnknown(String selector) throws JMSException { + assertSelector(selector, false); + assertSelector(not(selector), false); + } + protected void assertSelectorEvaluatesToTrue(String selector) throws JMSException { + assertSelector(selector, true); + assertSelector(not(selector), false); + } + + protected void assertSelectorEvaluatesToFalse(String selector) throws JMSException { + assertSelector(selector, false); + assertSelector(not(selector), true); + } + + protected void assertSelector(String text, boolean matches) throws JMSException { + BooleanExpression selector = SelectorParser.parse(text); + assertTrue("Created a valid selector", selector != null); + MessageEvaluationContext context = new MessageEvaluationContext(); + context.setMessageReference((org.apache.activemq.command.Message)message); + boolean value = selector.matches(context); + assertEquals("Selector for: " + text, matches, value); + } + + private static String not(String selector) { + return "not(" + selector + ")"; + } +} \ No newline at end of file