TinyRuleKit

Setup

This document explains how to add TinyRuleKit to an application project and compile a first engine from JSON.

The minimal setup is: define enum collections for rule tokens, expose a RuleContext, add static condition and action factories, then build and fire the engine.

Maven

XML
<dependency>
    <groupId>org.tinyrulekit</groupId>
    <artifactId>tinyrulekit</artifactId>
    <version>1.0.0</version>
</dependency>

Manual Classpath

Applications that load JARs manually can download the current release from TinyRuleKit v1.0.0. They need TinyRuleKit and its static-rule runtime dependency:

TEXT
tinyrulekit-1.0.0.jar
gson-2.10.1.jar

Add mvel2-2.5.2.Final.jar only when using the default MVEL expression engine. Static rules and custom expression engines do not need MVEL.

See Packaging for module-path and distribution details.

Add Domain Token Enums

TinyRuleKit does not require consumers to switch on raw strings. Define enum collections for the trigger, condition, and action names your JSON files are allowed to use.

JAVA
enum GameTrigger implements RuleToken {
    PLAYER_JOINED
}

enum GameCondition implements RuleToken {
    LEVEL_AT_LEAST
}

enum GameAction implements RuleToken {
    GIVE_COINS
}

These enums are your public rule vocabulary:

  • GameTrigger: events the engine can fire.
  • GameCondition: condition types that can be compiled.
  • GameAction: action types that can be compiled.

JSON uses the enum names, while Java keeps type-safe control over what those names mean.

Add A Context

JAVA
record GameContext(Player player, Wallet wallet) implements RuleContext {
    @Override
    public RuleVariables ruleVariables() {
        return RuleVariables.create()
                .put("player", player)
                .put("wallet", wallet);
    }
}

Add Static Resolver Factories

Resolvers are usually small static factory methods. They receive the JSON type and params, validate them during engine construction, and return a compiled Java function.

The condition factory returns CompiledCondition<GameContext>:

JAVA
final class GameRules {
    static CompiledCondition<GameContext> condition(String type, RuleParams params) {
        return switch (RuleToken.enumValue(GameCondition.class, type)) {
            case LEVEL_AT_LEAST -> {
                int level = params.getInt("level");
                yield (context, variables) -> context.player().level() >= level;
            }
        };
    }
}

The action factory returns CompiledAction<GameContext>:

JAVA
final class GameRules {
    static CompiledAction<GameContext> action(String type, RuleParams params) {
        return switch (RuleToken.enumValue(GameAction.class, type)) {
            case GIVE_COINS -> {
                int amount = params.getInt("amount", 1);
                yield (context, variables) -> context.wallet().addCoins(amount);
            }
        };
    }
}

Both factories should parse parameters once, during compilation. Runtime rule execution should only use already-validated values.

Build And Fire

JAVA
RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .buildFromJson(rulesJson);

engine.fire(GameTrigger.PLAYER_JOINED, context);

Choose A Document Format

The default build method detects the top-level JSON shape automatically:

JAVA
RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .buildFromJson(rulesJson);

When you already know the document shape, pass it explicitly so TinyRuleKit can skip generic detection and use a more direct loading path:

  • RuleDocumentFormat.SIMPLE: the document is only a raw JSON array of rules.
  • RuleDocumentFormat.SPECIAL: the document is an object with optional requires metadata and a rules array.

The automatic mode is not a format you pass. It is simply what happens when no format is provided. Explicit formats are most useful for larger files, reload paths, or applications that own a fixed rule-file schema.

JAVA
RuleEngine<GameContext> small = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .buildFromJson(rulesJson, RuleDocumentFormat.SIMPLE);

RuleEngine<GameContext> large = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .buildFromJson(rulesPath, RuleDocumentFormat.SPECIAL);