From 520262bcd46fbf5d9d0a4c75bb3258028e993575 Mon Sep 17 00:00:00 2001 From: Amar Wadhwani Date: Sun, 27 Apr 2025 20:43:43 +0200 Subject: [PATCH 1/3] BAEL-9210: Stream Gatherers --- core-java-modules/core-java-streams-7/pom.xml | 4 +- .../streams/gatherer/NumericSumGatherer.java | 39 ++++++++++ .../gatherer/SentenceSplitterGatherer.java | 36 +++++++++ .../gatherer/SlidingWindowGatherer.java | 40 ++++++++++ .../streams/gatherer/GathererUnitTest.java | 62 +++++++++++++++ .../gatherer/NumericSumGathererUnitTest.java | 18 +++++ .../SentenceSplitterGathererUnitTest.java | 19 +++++ .../SlidingWindowGathererUnitTest.java | 21 +++++ core-java-modules/pom.xml | 2 +- pom.xml | 77 +++++++++++++++++++ 10 files changed, 315 insertions(+), 3 deletions(-) create mode 100644 core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/NumericSumGatherer.java create mode 100644 core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SentenceSplitterGatherer.java create mode 100644 core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SlidingWindowGatherer.java create mode 100644 core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/GathererUnitTest.java create mode 100644 core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/NumericSumGathererUnitTest.java create mode 100644 core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SentenceSplitterGathererUnitTest.java create mode 100644 core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SlidingWindowGathererUnitTest.java diff --git a/core-java-modules/core-java-streams-7/pom.xml b/core-java-modules/core-java-streams-7/pom.xml index 892ad66f94f4..39cb2e46aa0b 100644 --- a/core-java-modules/core-java-streams-7/pom.xml +++ b/core-java-modules/core-java-streams-7/pom.xml @@ -35,8 +35,8 @@ - 12 - 12 + 24 + 24 0.10.2 3.23.1 3.12.0 diff --git a/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/NumericSumGatherer.java b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/NumericSumGatherer.java new file mode 100644 index 000000000000..62ac79880f94 --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/NumericSumGatherer.java @@ -0,0 +1,39 @@ +package com.baeldung.streams.gatherer; + +import java.util.ArrayList; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Gatherer; + +public class NumericSumGatherer implements Gatherer, Integer> { + + @Override + public Supplier> initializer() { + return ArrayList::new; + } + + @Override + public Integrator, Integer, Integer> integrator() { + return new Integrator<>() { + @Override + public boolean integrate(ArrayList state, Integer element, Downstream downstream) { + if (state.isEmpty()) { + state.add(element); + } else { + state.addFirst(state.getFirst() + element); + } + return true; + } + }; + } + + @Override + public BiConsumer, Downstream> finisher() { + return (state, downstream) -> { + if (!downstream.isRejecting() && !state.isEmpty()) { + downstream.push(state.getFirst()); + state.clear(); + } + }; + } +} diff --git a/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SentenceSplitterGatherer.java b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SentenceSplitterGatherer.java new file mode 100644 index 000000000000..af3b2f7acd74 --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SentenceSplitterGatherer.java @@ -0,0 +1,36 @@ +package com.baeldung.streams.gatherer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.BinaryOperator; +import java.util.function.Supplier; +import java.util.stream.Gatherer; + +public class SentenceSplitterGatherer implements Gatherer,String> { + + @Override + public Supplier> initializer() { + return ArrayList::new; + } + + @Override + public BinaryOperator> combiner() { + return (left, right) -> { + left.addAll(right); + return left; + }; + } + + @Override + public Integrator, String, String> integrator() { + return (state, element, downstream) -> { + var words = element.split("\\s+"); + for (var word : words) { + state.add(word); + downstream.push(word); + } + return true; + }; + } +} diff --git a/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SlidingWindowGatherer.java b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SlidingWindowGatherer.java new file mode 100644 index 000000000000..4b85ab58663b --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/main/java/com/baeldung/streams/gatherer/SlidingWindowGatherer.java @@ -0,0 +1,40 @@ +package com.baeldung.streams.gatherer; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Gatherer; + +public class SlidingWindowGatherer implements Gatherer, List> { + + @Override + public Supplier> initializer() { + return ArrayList::new; + } + + @Override + public Integrator, Integer, List> integrator() { + return new Integrator<>() { + @Override + public boolean integrate(ArrayList state, Integer element, Downstream> downstream) { + state.add(element); + if (state.size() == 3) { + downstream.push(new ArrayList<>(state)); + state.removeFirst(); + } + return true; + } + }; + } + + @Override + public BiConsumer, Downstream>> finisher() { + return (state, downstream) -> { + if (state.size()==3) { + downstream.push(new ArrayList<>(state)); + } + }; + + } +} diff --git a/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/GathererUnitTest.java b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/GathererUnitTest.java new file mode 100644 index 000000000000..5373745dea96 --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/GathererUnitTest.java @@ -0,0 +1,62 @@ +package com.baeldung.streams.gatherer; + +import java.util.List; +import java.util.stream.Gatherer; +import java.util.stream.Gatherers; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class GathererUnitTest { + + @Test + void givenNumbers_whenFolded_thenSumIsEmitted() { + Stream numbers = Stream.of(1, 2, 3, 4, 5); + Stream folded = numbers.gather(Gatherers.fold(() -> 0, Integer::sum)); + List resultList = folded.toList(); + Assertions.assertEquals(1, resultList.size()); + Assertions.assertEquals(Integer.valueOf(15), resultList.getFirst()); + } + + @Test + void givenWords_whenMappedConcurrently_thenUppercasedWordsAreEmitted() { + Stream words = Stream.of("a", "b", "c", "d"); + List resultList = words.gather(Gatherers.mapConcurrent(2, String::toUpperCase)) + .toList(); + Assertions.assertEquals(4, resultList.size()); + Assertions.assertEquals(List.of("A", "B", "C", "D"), resultList); + } + + @Test + void givenNumbers_whenScanned_thenRunningTotalsAreEmitted() { + Stream numbers = Stream.of(1, 2, 3, 4); + List resultList = numbers.gather(Gatherers.scan(() -> 0, Integer::sum)) + .toList(); + Assertions.assertEquals(4, resultList.size()); + Assertions.assertEquals(List.of(1, 3, 6, 10), resultList); + } + + @Test + void givenNumbers_whenWindowedSliding_thenOverlappingWindowsAreEmitted() { + List> expectedOutput = List.of(List.of(1, 2, 3), List.of(2, 3, 4), List.of(3, 4, 5)); + Stream numbers = Stream.of(1, 2, 3, 4, 5); + List> resultList = numbers.gather(Gatherers.windowSliding(3)) + .toList(); + Assertions.assertEquals(3, resultList.size()); + Assertions.assertEquals(expectedOutput, resultList); + } + + @Test + void givenStrings_whenUsingCustomGatherer_thenLengthsAreCalculated() { + List expectedOutput = List.of(5, 6, 3); + Stream inputStrings = Stream.of("apple", "banana", "cat"); + List resultList = inputStrings.gather(Gatherer.of((state, element, downstream) -> { + downstream.push(element.length()); + return true; + })) + .toList(); + Assertions.assertEquals(3, resultList.size()); + Assertions.assertEquals(expectedOutput, resultList); + } +} diff --git a/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/NumericSumGathererUnitTest.java b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/NumericSumGathererUnitTest.java new file mode 100644 index 000000000000..df16bf8f05f5 --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/NumericSumGathererUnitTest.java @@ -0,0 +1,18 @@ +package com.baeldung.streams.gatherer; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class NumericSumGathererUnitTest { + + @Test + void givenNumbers_whenUsingCustomManyToOneGatherer_thenSumIsCalculated() { + Stream inputValues = Stream.of(1, 2, 3, 4, 5, 6); + List result = inputValues.gather(new NumericSumGatherer()) + .toList(); + Assertions.assertEquals(Integer.valueOf(21), result.getFirst()); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SentenceSplitterGathererUnitTest.java b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SentenceSplitterGathererUnitTest.java new file mode 100644 index 000000000000..8a1b9eb4bf7e --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SentenceSplitterGathererUnitTest.java @@ -0,0 +1,19 @@ +package com.baeldung.streams.gatherer; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SentenceSplitterGathererUnitTest { + + @Test + void givenSentences_whenUsingCustomOneToManyGatherer_thenWordsAreExtracted() { + List expectedOutput = List.of("hello", "world", "java", "streams"); + Stream sentences = Stream.of("hello world", "java streams"); + List words = sentences.gather(new SentenceSplitterGatherer()) + .toList(); + Assertions.assertEquals(expectedOutput, words); + } +} diff --git a/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SlidingWindowGathererUnitTest.java b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SlidingWindowGathererUnitTest.java new file mode 100644 index 000000000000..99e6f02018e0 --- /dev/null +++ b/core-java-modules/core-java-streams-7/src/test/java/com/baeldung/streams/gatherer/SlidingWindowGathererUnitTest.java @@ -0,0 +1,21 @@ +package com.baeldung.streams.gatherer; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SlidingWindowGathererUnitTest { + + @Test + void givenNumbers_whenWindowedSliding_thenOverlappingWindowsAreEmitted() { + List> expectedOutput = List.of(List.of(1, 2, 3), List.of(2, 3, 4), List.of(3, 4, 5)); + Stream numbers = Stream.of(1, 2, 3, 4, 5); + List> resultList = numbers.gather(new SlidingWindowGatherer()) + .toList(); + Assertions.assertEquals(3, resultList.size()); + Assertions.assertEquals(expectedOutput, resultList); + } + +} \ No newline at end of file diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index 93c923a683eb..66fd2c39031e 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -61,7 +61,7 @@ core-java-streams-4 core-java-streams-5 core-java-streams-6 - core-java-streams-7 + core-java-streams-collect core-java-streams-maps core-java-string-operations-3 diff --git a/pom.xml b/pom.xml index 73621491ce6d..780b2396ed31 100644 --- a/pom.xml +++ b/pom.xml @@ -921,6 +921,47 @@ + + default-jdk24 + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + + SpringContextTest + **/*UnitTest + + + **/*IntegrationTest.java + **/*IntTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + **/JdbcTest.java + **/*LiveTest.java + + + + + + + + core-java-modules/core-java-streams-7 + + + + UTF-8 + 24 + 24 + 24 + 3.26.0 + + + + integration-jdk17 @@ -1299,6 +1340,42 @@ + + integration-jdk24 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*ManualTest.java + **/*LiveTest.java + + + **/*IntegrationTest.java + **/*IntTest.java + + + + + + + + core-java-modules/core-java-streams-7 + + + + UTF-8 + 24 + 24 + 24 + 3.26.0 + + + + live-all From fc9bf77eec8b86e9747d40576a0987d9da66c646 Mon Sep 17 00:00:00 2001 From: Amar Wadhwani Date: Sat, 9 Aug 2025 13:46:01 +0530 Subject: [PATCH 2/3] BAEL-9162: Simple Rule Engine --- rule-engines-modules/pom.xml | 1 + .../simple-rule-engine/pom.xml | 43 +++++++++++++++++++ ...irstOrderHighValueSpecialDiscountRule.java | 17 ++++++++ .../java/com/baeldung/ruleengine/IRule.java | 8 ++++ .../ruleengine/LoyaltyDiscountRule.java | 16 +++++++ .../com/baeldung/ruleengine/RuleEngine.java | 21 +++++++++ .../com/baeldung/ruleengine/SpelRule.java | 31 +++++++++++++ .../baeldung/ruleengine/model/Customer.java | 26 +++++++++++ .../com/baeldung/ruleengine/model/Order.java | 25 +++++++++++ .../src/main/resources/logback.xml | 13 ++++++ .../ruleengine/RuleEngineUnitTest.java | 40 +++++++++++++++++ .../baeldung/ruleengine/SpelRuleUnitTest.java | 34 +++++++++++++++ .../src/test/resources/logback.xml | 13 ++++++ 13 files changed, 288 insertions(+) create mode 100644 rule-engines-modules/simple-rule-engine/pom.xml create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java create mode 100644 rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml create mode 100644 rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java create mode 100644 rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java create mode 100644 rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml diff --git a/rule-engines-modules/pom.xml b/rule-engines-modules/pom.xml index dcd97c5f275a..bc163f8034a6 100644 --- a/rule-engines-modules/pom.xml +++ b/rule-engines-modules/pom.xml @@ -18,6 +18,7 @@ evrete openl-tablets rulebook + simple-rule-engine diff --git a/rule-engines-modules/simple-rule-engine/pom.xml b/rule-engines-modules/simple-rule-engine/pom.xml new file mode 100644 index 000000000000..0ff8b0427e66 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + com.baeldung.simpleruleengine + simple-rule-engine + 1.0 + simple-rule-engine + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + + + + com.baeldung + rule-engines-modules + 1.0.0-SNAPSHOT + + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.springframework + spring-expression + 7.0.0-M7 + + + + \ No newline at end of file diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java new file mode 100644 index 000000000000..514cc74ad4cd --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java @@ -0,0 +1,17 @@ +package com.baeldung.ruleengine; + +import com.baeldung.ruleengine.model.Order; + +public class FirstOrderHighValueSpecialDiscountRule implements IRule { + + @Override + public boolean evaluate(Order order) { + return order.getCustomer() + .isFirstOrder() && order.getAmount() > 500; + } + + @Override + public String description() { + return "First Order Special Discount Rule: First Time customer with high value order"; + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java new file mode 100644 index 000000000000..1f79f49fdd4b --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java @@ -0,0 +1,8 @@ +package com.baeldung.ruleengine; + +import com.baeldung.ruleengine.model.Order; + +public interface IRule { + boolean evaluate(Order order); + String description(); +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java new file mode 100644 index 000000000000..86bd39fab28a --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java @@ -0,0 +1,16 @@ +package com.baeldung.ruleengine; + +import com.baeldung.ruleengine.model.Order; + +public class LoyaltyDiscountRule implements IRule{ + + @Override + public boolean evaluate(Order order) { + return order.getCustomer().getLoyaltyPoints() > 500; + } + + @Override + public String description() { + return "Loyalty Discount Rule: Customer has more than 500 points"; + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java new file mode 100644 index 000000000000..cc199cfa85da --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java @@ -0,0 +1,21 @@ +package com.baeldung.ruleengine; + +import java.util.List; +import java.util.stream.Collectors; + +import com.baeldung.ruleengine.model.Order; + +public class RuleEngine { + private final List rules; + + public RuleEngine(List rules) { + this.rules = rules; + } + + public List evaluate(Order order) { + return rules.stream() + .filter(rule -> rule.evaluate(order)) + .map(IRule::description) + .collect(Collectors.toList()); + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java new file mode 100644 index 000000000000..9a386bf9f2f4 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java @@ -0,0 +1,31 @@ +package com.baeldung.ruleengine; + +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import com.baeldung.ruleengine.model.Order; + + +public class SpelRule { + + private final String expression; + private final String description; + + public SpelRule(String expression, String description) { + this.expression = expression; + this.description = description; + } + + public boolean evaluate(Order order) { + ExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(order); + context.setVariable("order", order); + return parser.parseExpression(expression) + .getValue(context, Boolean.class); + } + + public String getDescription() { + return description; + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java new file mode 100644 index 000000000000..8264a6afaf4e --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java @@ -0,0 +1,26 @@ +package com.baeldung.ruleengine.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldDefaults; + +@Setter +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class Customer { + + String name; + int loyaltyPoints; + boolean firstOrder; + + public Customer() { + } + + public Customer(String name, int loyaltyPoints, boolean firstOrder) { + this.name = name; + this.loyaltyPoints = loyaltyPoints; + this.firstOrder = firstOrder; + } + +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java new file mode 100644 index 000000000000..4f9d0c7979f7 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java @@ -0,0 +1,25 @@ +package com.baeldung.ruleengine.model; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.FieldDefaults; + +@Getter +@Setter +public class Order { + + private Double amount; + private Customer customer; + + public Order() { + } + + public Order(Double amount, Customer customer) { + this.amount = amount; + this.customer = customer; + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml b/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml new file mode 100644 index 000000000000..7d900d8ea884 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java new file mode 100644 index 000000000000..c6f792e01776 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.ruleengine; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import com.baeldung.ruleengine.model.Customer; +import com.baeldung.ruleengine.model.Order; + +public class RuleEngineUnitTest { + + @Test + void whenTwoRulesTriggered_thenBothDescriptionsReturned() { + Customer customer = new Customer("Max", 550, true); + Order order = new Order(600.0, customer); + + RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule())); + + List results = engine.evaluate(order); + + assertEquals(2, results.size()); + assertTrue(results.contains("Loyalty Discount Rule: Customer has more than 500 points")); + assertTrue(results.contains("First Order Special Discount Rule: First Time customer with high value order")); + } + + @Test + void whenNoRulesTriggered_thenEmptyListReturned() { + Customer customer = new Customer("Max", 50, false); + Order order = new Order(200.0, customer); + + RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule())); + + List results = engine.evaluate(order); + + assertTrue(results.isEmpty()); + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java new file mode 100644 index 000000000000..3d4a2c131027 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java @@ -0,0 +1,34 @@ +package com.baeldung.ruleengine; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.baeldung.ruleengine.model.Customer; +import com.baeldung.ruleengine.model.Order; + +public class SpelRuleUnitTest { + + @Test + void whenLoyalCustomer_thenEligibleForDiscount() { + Customer customer = new Customer("Bob", 730, false); + Order order = new Order(200.0, customer); + + SpelRule rule = new SpelRule( + "#order.customer.loyaltyPoints > 500", + "Loyalty discount rule" + ); + assertTrue(rule.evaluate(order)); + } + + @Test + void whenFirstOrderHighAmount_thenEligibleForSpecialDiscount() { + Customer customer = new Customer("Bob", 0, true); + Order order = new Order(800.0, customer); + + SpelRule approvalRule = new SpelRule( + "#order.customer.firstOrder and #order.amount > 500", + "First-time customer with high order gets special discount" + ); + assertTrue(approvalRule.evaluate(order)); + } +} diff --git a/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml b/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml new file mode 100644 index 000000000000..7d900d8ea884 --- /dev/null +++ b/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file From 07b4b731a06dc1342b91e7cf0e6777877fb8f051 Mon Sep 17 00:00:00 2001 From: Amar Wadhwani Date: Fri, 22 Aug 2025 22:50:17 +0530 Subject: [PATCH 3/3] BAEL-9162: Simple Rule Engine --- .../simple-rule-engine/pom.xml | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/rule-engines-modules/simple-rule-engine/pom.xml b/rule-engines-modules/simple-rule-engine/pom.xml index 0ff8b0427e66..4911464dfe7a 100644 --- a/rule-engines-modules/simple-rule-engine/pom.xml +++ b/rule-engines-modules/simple-rule-engine/pom.xml @@ -7,18 +7,6 @@ simple-rule-engine 1.0 simple-rule-engine - - - - org.apache.maven.plugins - maven-compiler-plugin - - 9 - 9 - - - - com.baeldung @@ -36,8 +24,12 @@ org.springframework spring-expression - 7.0.0-M7 + ${spring-expression.version} + + 7.0.0-M7 + + \ No newline at end of file