IceIce
Home
  • Getting Started

    • Quick Start
    • Core Concepts
    • Architecture
  • SDK Guide

    • Java SDK
    • Go SDK
    • Python SDK
  • Reference

    • Node Types
    • Roam API
    • Server Config
    • Client Config
Playground
FAQ
  • Changelog
  • Upgrade Guide
Sponsor
Community
GitHub
  • English
  • 简体中文
Home
  • Getting Started

    • Quick Start
    • Core Concepts
    • Architecture
  • SDK Guide

    • Java SDK
    • Go SDK
    • Python SDK
  • Reference

    • Node Types
    • Roam API
    • Server Config
    • Client Config
Playground
FAQ
  • Changelog
  • Upgrade Guide
Sponsor
Community
GitHub
  • English
  • 简体中文
  • SDK Guide

    • Java SDK
    • Go SDK
    • Python SDK

Java SDK

Ice Java SDK (ice-core) provides the rule execution engine and file client for integration into Java/Spring applications.

Dependency

<dependency>
  <groupId>com.waitmoon.ice</groupId>
  <artifactId>ice-core</artifactId>
  <version>3.0.2</version>
</dependency>

Requires Java 8+.

Initializing the Client

IceFileClient client = new IceFileClient(
    1,                    // app ID
    "./ice-data",         // shared storage path (must be the same directory as Server)
    "com.your.package"    // leaf node scan package
);
client.start();

Full parameter version:

IceFileClient client = new IceFileClient(
    1,                              // app ID
    "./ice-data",                   // shared storage path
    -1,                             // parallelism (<=0 uses default ForkJoinPool)
    Set.of("com.your.package"),     // scan package set
    5,                              // version poll interval (seconds)
    10                              // heartbeat interval (seconds)
);

With lane:

IceFileClient client = IceFileClient.newWithLane(
    1, "./ice-data", "com.your.package", "feature-xxx"
);

See Client Configuration Reference for full parameter documentation.

Spring Integration

Spring/SpringBoot projects need to bridge the Spring container to Ice so that leaf nodes can use @Resource / @Autowired to inject Beans:

@Configuration
public class IceConfig implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        AutowireCapableBeanFactory bf = ctx.getAutowireCapableBeanFactory();
        IceBeanUtils.setFactory(new IceBeanUtils.IceBeanFactory() {
            @Override
            public void autowireBean(Object bean) { bf.autowireBean(bean); }
            @Override
            public boolean containsBean(String name) { return ctx.containsBean(name); }
            @Override
            public Object getBean(String name) { return ctx.getBean(name); }
        });
    }

    @Bean(destroyMethod = "destroy")
    public IceFileClient iceFileClient() throws Exception {
        IceFileClient client = new IceFileClient(1, "./ice-data", "com.your.package");
        client.start();
        return client;
    }
}

Non-Spring projects do not need this step.

Leaf Node Development

Three Node Types

TypeBase ClassReturn ValuePurpose
FlowBaseLeafRoamFlowtrue / falseConditional checks
ResultBaseLeafRoamResulttrue / falseBusiness execution
NoneBaseLeafRoamNonenoneAuxiliary operations

Flow Node Example

@Data
@EqualsAndHashCode(callSuper = true)
public class ScoreFlow extends BaseLeafRoamFlow {

    private String key;
    private double score;

    @Override
    protected boolean doRoamFlow(IceRoam roam) {
        Double value = roam.getMulti(key);
        return value != null && value >= score;
    }
}

Result Node Example

@Data
@EqualsAndHashCode(callSuper = true)
public class AmountResult extends BaseLeafRoamResult {

    @Resource
    private SendService sendService;

    private String key;
    private double value;

    @Override
    protected boolean doRoamResult(IceRoam roam) {
        Integer uid = roam.getMulti(key);
        if (uid == null || value <= 0) {
            return false;
        }
        boolean res = sendService.sendAmount(uid, value);
        roam.put("SEND_AMOUNT", res);
        return res;
    }
}

None Node Example

@Data
@EqualsAndHashCode(callSuper = true)
public class TimeChangeNone extends BaseLeafRoamNone {

    private long changeTime;

    @Override
    protected void doRoamNone(IceRoam roam) {
        roam.put("requestTime", changeTime);
    }
}

Base Class Selection

Ice provides three levels of base classes. Choose the appropriate input granularity:

Base ClassInputUse Case
BaseLeafFlow / BaseLeafResult / BaseLeafNoneIceContextNeed full context
BaseLeafPackFlow / BaseLeafPackResult / BaseLeafPackNoneIcePackNeed requestTime, traceId
BaseLeafRoamFlow / BaseLeafRoamResult / BaseLeafRoamNoneIceRoamOnly need to read/write data (most common)

Node Annotations

@IceNode(
    name = "Amount Dispenser",
    desc = "Dispense a specified amount to the user",
    order = 10,
    alias = {"amount_result"}  // Alias for compatibility with rules configured by other language SDKs
)
public class AmountResult extends BaseLeafRoamResult {

    @IceField(name = "User Key", desc = "Key to retrieve user ID from roam")
    private String key;

    @IceField(name = "Dispense Amount", desc = "The amount to dispense")
    private double value;

    @IceIgnore  // Hidden from the configuration interface
    private String internalField;
}

Executing Rules

Assembling a Pack

IcePack pack = new IcePack();
pack.setIceId(1L);          // Trigger by iceId
// pack.setScene("recharge"); // Or trigger by scene
// pack.setConfId(123L);      // Or trigger by node ID

IceRoam roam = new IceRoam();
roam.put("uid", 12345);
roam.put("cost", 100);
pack.setRoam(roam);

Trigger priority: iceId > scene > confId.

Synchronous Execution

Ice.syncProcess(pack);
// Retrieve results from roam after execution
Object result = pack.getRoam().get("SEND_AMOUNT");

Asynchronous Execution

List<Future<IceContext>> futures = Ice.asyncProcess(pack);
for (Future<IceContext> future : futures) {
    IceContext ctx = future.get();
    // Process result
}

Convenience Methods

IceRoam roam = Ice.processSingleRoam(pack);    // Single result Roam
List<IceRoam> roams = Ice.processRoam(pack);    // Multiple result Roams
IceContext ctx = Ice.processSingleCtx(pack);     // Single result Context
List<IceContext> ctxList = Ice.processCtx(pack); // Multiple result Contexts

Error Handling

Three approaches, in descending order of priority:

1. Configure Error State

Set the node's iceErrorStateEnum in the Server configuration interface to specify the return state on error. This has the highest priority.

2. Override errorHandle in Leaf Nodes

@Override
public NodeRunStateEnum errorHandle(IceContext ctx, Throwable t) {
    log.error("Node execution failed", t);
    return NodeRunStateEnum.NONE; // Return NONE to continue execution
    // return NodeRunStateEnum.SHUT_DOWN; // Terminate the entire flow
}

3. Global Error Handler

IceErrorHandle.setHandle((node, ctx, t) -> {
    log.error("Global error handler node:{}", node.getIceNodeId(), t);
    return NodeRunStateEnum.SHUT_DOWN;
});

Integration with Expression Engines

Ice can integrate with expression engines like Aviator to simplify condition configuration:

@Data
@EqualsAndHashCode(callSuper = true)
public class AviatorFlow extends BaseLeafRoamFlow {

    private String exp;
    private Expression compiledExpression;

    @Override
    protected boolean doRoamFlow(IceRoam roam) {
        return (boolean) compiledExpression.execute(roam);
    }

    public void setExp(String exp) {
        this.exp = exp;
        this.compiledExpression = AviatorEvaluator.compile(exp, true);
    }
}

Configure exp as an Aviator expression (e.g., cost >= 100) to dynamically modify evaluation conditions in the Server interface.

Next Steps

  • Go SDK | Python SDK -- Other language SDKs
  • Node Type Reference -- All relation and leaf node types
  • Roam API -- Complete data container API
  • Core Concepts -- Understand tree-based orchestration design philosophy
Edit this page on GitHub
Next
Go SDK