Getting Started
What's New in 2.0.0
Released July 2026
RuleFlow — Pipeline Orchestration
The headline feature of 2.0.0: a fluent, pipeline-style API for composing Rules, RuleSets, and other RuleFlows into a single executable flow — with typed input parameters, conditionals, loops, scopes, exception handling, and async execution built in. RuleFlow is now the recommended default way to run rules and rulesets, replacing hand-rolled orchestration around direct run() calls.
RuleFlow<Boolean> flow = RuleFlow.builder()
.name("myFlow")
.bind(x -> 42)
.run(rule, spec -> spec.as("result")
.onException(UnrulyException.class, b -> b.bind(ok -> false)))
.when(cond, b -> b.run(rule2))
.onException(Exception.class, b -> b.bind(handled -> true))
.<Boolean>returning(function((Boolean handled) -> handled))
.build();
Async Pipeline & AsyncRunnable
RuleFlows launch steps asynchronously with asyncRun(...) and coordinate them with await / awaitAll / awaitAny, non-blocking thenRun continuations, and async exception handlers. A new AsyncRunnable interface adds runAsync(RuleContext) to RuleSet and RuleFlow.
Registry & Tracing Integration
RuleRegistrygainsgetRuleFlow(name)andgetRuleFlows()— flows register like Rules and RuleSets- New
RuleFlowListenerfiresTracerevents for flow and command start, completion, and errors - New
BindingDeclaration.of(name, value)factory for runtime-known binding names RuleExecutionStatusgainsERROR(andisError())
Correctness & Hardening Pass
A systematic review across every package; the test suite grew from 1039 to 1274 tests. Highlights:
- Immutable bindings now actually enforce immutability; scope-shadowing fixes across the
bindpackage - Numeric validation rules coerce strings via
BigDecimal— decimal strings like"10.5"no longer falsely fail - Composite conditions short-circuit; text converters no longer misparse leading-zero strings as octal
- Bounded internal executor with a JVM shutdown hook; thread-safety fixes in registry, script, trace, and util packages
- RuleSet
isAnyPass()/isAnySkip()/isAnyFail()inversion fixed;validating()andfinalizer()now compose instead of overwriting each other
XML RuleFlows
The full RuleFlow command grammar is available as a Spring XML namespace — bind, run/async-run, when, for-each, scope, await, on-exception, and more — with script expressions substituting the builder's lambdas.
<r:ruleflow name="CheckoutFlow" returning="#ctx.status">
<r:run name="ValidateOrderRule"/>
<r:when condition="#ctx.valid == true">
<r:then><r:execute>#ctx.status = 'priced'</r:execute></r:then>
<r:otherwise><r:exit/></r:otherwise>
</r:when>
</r:ruleflow>
Environment Placeholders & Terse Attributes
Script expressions may contain ${property:default} placeholders, resolved once at startup with @Value semantics. And simple rules collapse to one-liners with the new expression attributes:
<r:rule name="ApproveRule" given="#ctx.total >= ${order.minTotal:100}"
then="#ctx.approved = true" otherwise="#ctx.approved = false"/>
Executor Service & Listener Auto-Configuration
- New
rulii.executorServicebean — a bounded thread pool used for async rule/flow execution; override it by defining a bean with that exact name Tracerauto-configuration registers anyRuliiListener,RuleListener,RuleSetListener, andRuleFlowListenerbeans found in the context- Converter precedence fixed: custom rulii converters → Spring
ConversionService→ rulii built-ins - Registry lookups inside flows resolve against the Spring registry at execution time