From 8a284c940bbf779e6e3e50d092fe857c9da51995 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Mon, 29 Jun 2026 18:47:54 +0200 Subject: [PATCH] UNOMI-875: Cherry-pick final unomi-3-dev hygiene into master Port KafkaInjector executor shutdown on deactivate and null-safe property condition evaluator unit tests; production behavior already on master. --- .../PropertyConditionEvaluatorTest.java | 149 ++++++++++++++++++ .../org/apache/unomi/kafka/KafkaInjector.java | 24 ++- 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/plugins/baseplugin/src/test/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluatorTest.java b/plugins/baseplugin/src/test/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluatorTest.java index 07bc4d2e8..dd29d597f 100644 --- a/plugins/baseplugin/src/test/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluatorTest.java +++ b/plugins/baseplugin/src/test/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluatorTest.java @@ -17,6 +17,8 @@ package org.apache.unomi.plugins.baseplugin.conditions; import org.apache.unomi.api.*; +import org.apache.unomi.api.conditions.Condition; +import org.apache.unomi.api.conditions.ConditionType; import org.apache.unomi.api.rules.Rule; import org.apache.unomi.plugins.baseplugin.conditions.accessors.HardcodedPropertyAccessor; import org.apache.unomi.scripting.ExpressionFilter; @@ -32,6 +34,7 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNull; +import static org.junit.Assert.assertFalse; public class PropertyConditionEvaluatorTest { @@ -190,6 +193,152 @@ public static Session generateMockSession(Profile mockProfile) { return mockSession; } + @Test + public void testNullSafeIntegerComparison_NonNumericActualValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("nonNumericProperty", "notANumber"); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.nonNumericProperty"); + condition.setParameter("comparisonOperator", "greaterThan"); + condition.setParameter("propertyValueInteger", 10); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-numeric value should not match greaterThan comparison with integer", result); + } + + @Test + public void testNullSafeIntegerComparison_NonNumericExpectedValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("numericProperty", 42); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.numericProperty"); + condition.setParameter("comparisonOperator", "greaterThan"); + condition.setParameter("propertyValueInteger", "notANumber"); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-numeric expected value should not match greaterThan comparison", result); + } + + @Test + public void testNullSafeDoubleComparison_NonNumericActualValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("nonNumericProperty", "notANumber"); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.nonNumericProperty"); + condition.setParameter("comparisonOperator", "lessThan"); + condition.setParameter("propertyValueDouble", 10.5); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-numeric value should not match lessThan comparison with double", result); + } + + @Test + public void testNullSafeDoubleComparison_NonNumericExpectedValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("numericProperty", 42.5); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.numericProperty"); + condition.setParameter("comparisonOperator", "lessThan"); + condition.setParameter("propertyValueDouble", "notANumber"); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-numeric expected value should not match lessThan comparison", result); + } + + @Test + public void testNullSafeDateComparison_NonDateActualValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("nonDateProperty", "notADate"); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.nonDateProperty"); + condition.setParameter("comparisonOperator", "greaterThan"); + condition.setParameter("propertyValueDate", new Date()); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-date value should not match greaterThan comparison with date", result); + } + + @Test + public void testNullSafeDateComparison_NonDateExpectedValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + Date testDate = new Date(); + testProfile.getProperties().put("dateProperty", testDate); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.dateProperty"); + condition.setParameter("comparisonOperator", "lessThan"); + condition.setParameter("propertyValueDate", "notADate"); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-date expected value should not match lessThan comparison", result); + } + + @Test + public void testNullSafeDateExprComparison_NonDateActualValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + testProfile.getProperties().put("nonDateProperty", "notADate"); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.nonDateProperty"); + condition.setParameter("comparisonOperator", "greaterThanOrEqualTo"); + condition.setParameter("propertyValueDateExpr", new Date()); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-date value should not match greaterThanOrEqualTo comparison with dateExpr", result); + } + + @Test + public void testNullSafeDateExprComparison_NonDateExpectedValue() throws Exception { + Profile testProfile = new Profile(); + testProfile.setItemId("testProfile"); + Date testDate = new Date(); + testProfile.getProperties().put("dateProperty", testDate); + + Condition condition = new Condition(); + ConditionType conditionType = new ConditionType(); + conditionType.setItemId("propertyCondition"); + condition.setConditionType(conditionType); + condition.setParameter("propertyName", "properties.dateProperty"); + condition.setParameter("comparisonOperator", "lessThanOrEqualTo"); + condition.setParameter("propertyValueDateExpr", "notADate"); + + boolean result = propertyConditionEvaluator.eval(condition, testProfile, new HashMap<>(), null); + assertFalse("Non-date expected value should not match lessThanOrEqualTo comparison", result); + } + class HardcodedWorker implements Callable { @Override diff --git a/plugins/kafka-injector/src/main/java/org/apache/unomi/kafka/KafkaInjector.java b/plugins/kafka-injector/src/main/java/org/apache/unomi/kafka/KafkaInjector.java index 4f8154b1b..70ec898ff 100644 --- a/plugins/kafka-injector/src/main/java/org/apache/unomi/kafka/KafkaInjector.java +++ b/plugins/kafka-injector/src/main/java/org/apache/unomi/kafka/KafkaInjector.java @@ -26,6 +26,7 @@ import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +36,9 @@ import java.util.Arrays; import java.util.Dictionary; import java.util.Properties; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @Component( name = "org.apache.unomi.kafka", @@ -51,6 +54,7 @@ public class KafkaInjector implements Runnable { private String messageType; private boolean consuming = false; private ObjectMapper objectMapper; + private ExecutorService executorService; @Reference private EventService eventService; @@ -141,7 +145,25 @@ public void activate(ComponentContext componentContext) { Thread.currentThread().setContextClassLoader(originClassLoader); } consuming = true; - Executors.newSingleThreadExecutor().execute(this); + executorService = Executors.newSingleThreadExecutor(); + executorService.execute(this); + } + + @Deactivate + public void deactivate() { + consuming = false; + if (executorService != null) { + executorService.shutdown(); + try { + if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException e) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + executorService = null; + } } @Override