Java library for flagd-evaluator with bundled WASM runtime.
This library provides a standalone Java artifact that bundles the flagd-evaluator WASM module and Chicory runtime, making it easy to evaluate feature flags in Java applications without manual WASM management.
- ✅ OpenFeature SDK Integration - Built on official OpenFeature SDK types
- ✅ Type-safe API - Generic evaluation methods with compile-time type checking
- ✅ Bundled WASM module - No need to manually copy WASM files
- ✅ Thread-safe - Safe for concurrent use
- ✅ JIT compiled - Uses Chicory's JIT compiler for performance
- ✅ Full feature support - All flagd evaluation features including targeting rules
- ✅ Performance benchmarks - JMH benchmarks for tracking performance over time
Add the dependency to your pom.xml:
<dependency>
<groupId>dev.openfeature</groupId>
<artifactId>flagd-evaluator-java</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>This library includes:
- OpenFeature SDK (1.19.2) - Provides core types and context management
- Chicory WASM Runtime (1.6.1) - Pure Java WebAssembly runtime with JIT compilation
- Jackson (2.18.2) - JSON serialization with custom OpenFeature serializers
- flagd-evaluator WASM module - Bundled in the JAR
- JMH Benchmarks (1.37) - Performance benchmarking suite (test scope)
import dev.openfeature.flagd.evaluator.FlagEvaluator;
import dev.openfeature.flagd.evaluator.EvaluationResult;
import dev.openfeature.flagd.evaluator.UpdateStateResult;
// Create evaluator
FlagEvaluator evaluator = new FlagEvaluator();
// Load flag configuration
String config = """
{
"flags": {
"my-flag": {
"state": "ENABLED",
"defaultVariant": "on",
"variants": {
"on": true,
"off": false
}
}
}
}
""";
UpdateStateResult updateResult = evaluator.updateState(config);
System.out.println("Flags changed: " + updateResult.getChangedFlags());
// Evaluate boolean flag
EvaluationResult<Boolean> result = evaluator.evaluateFlag(Boolean.class, "my-flag", "{}");
System.out.println("Value: " + result.getValue());
System.out.println("Variant: " + result.getVariant());
System.out.println("Reason: " + result.getReason());The library supports type-safe flag evaluation for all OpenFeature types:
import dev.openfeature.flagd.evaluator.EvaluationResult;
// Boolean flags
EvaluationResult<Boolean> boolResult = evaluator.evaluateFlag(Boolean.class, "feature-enabled", "{}");
boolean isEnabled = boolResult.getValue();
// String flags
EvaluationResult<String> stringResult = evaluator.evaluateFlag(String.class, "color-scheme", "{}");
String color = stringResult.getValue();
// Integer flags
EvaluationResult<Integer> intResult = evaluator.evaluateFlag(Integer.class, "max-items", "{}");
int maxItems = intResult.getValue();
// Double flags
EvaluationResult<Double> doubleResult = evaluator.evaluateFlag(Double.class, "threshold", "{}");
double threshold = doubleResult.getValue();import java.util.Map;
import dev.openfeature.flagd.evaluator.EvaluationResult;
import com.fasterxml.jackson.databind.ObjectMapper;
Map<String, Object> context = Map.of(
"targetingKey", "user-123",
"email", "user@example.com",
"age", 25
);
String contextJson = new ObjectMapper().writeValueAsString(context);
EvaluationResult<Boolean> result = evaluator.evaluateFlag(Boolean.class, "premium-feature", contextJson);import com.fasterxml.jackson.databind.ObjectMapper;
String config = """
{
"flags": {
"premium-feature": {
"state": "ENABLED",
"defaultVariant": "standard",
"variants": {
"standard": false,
"premium": true
},
"targeting": {
"if": [
{
"==": [
{ "var": "email" },
"premium@example.com"
]
},
"premium",
null
]
}
}
}
}
""";
evaluator.updateState(config);
ObjectMapper mapper = new ObjectMapper();
// Premium user
Map<String, Object> premiumContext = Map.of("email", "premium@example.com");
String premiumContextJson = mapper.writeValueAsString(premiumContext);
EvaluationResult<Boolean> result = evaluator.evaluateFlag(Boolean.class, "premium-feature", premiumContextJson);
// result.getValue() == true
// Regular user
Map<String, Object> regularContext = Map.of("email", "regular@example.com");
String regularContextJson = mapper.writeValueAsString(regularContext);
result = evaluator.evaluateFlag(Boolean.class, "premium-feature", regularContextJson);
// result.getValue() == false// Strict mode (default) - rejects invalid configurations
FlagEvaluator strictEvaluator = new FlagEvaluator();
// Permissive mode - accepts invalid configurations with warnings
FlagEvaluator permissiveEvaluator = new FlagEvaluator(
FlagEvaluator.ValidationMode.PERMISSIVE
);Main class for flag evaluation.
FlagEvaluator()- Creates evaluator with strict validationFlagEvaluator(ValidationMode mode)- Creates evaluator with specified validation mode
UpdateStateResult updateState(String jsonConfig)- Updates flag configuration<T> EvaluationResult<T> evaluateFlag(Class<T> type, String flagKey, String contextJson)- Type-safe flag evaluation with JSON context<T> EvaluationResult<T> evaluateFlag(Class<T> type, String flagKey, Map<String, Object> context)- Type-safe flag evaluation with Map context
Supported Types:
Boolean.class- For boolean flagsString.class- For string flagsInteger.class- For integer flagsDouble.class- For double/number flagsValue.class- For structured/object flags
Generic result class containing the outcome of flag evaluation.
T getValue()- The resolved value (type-safe based on generic parameter)String getVariant()- The selected variant nameString getReason()- Resolution reason (STATIC, TARGETING_MATCH, DEFAULT, DISABLED, ERROR, FLAG_NOT_FOUND)boolean isError()- Whether the evaluation encountered an errorString getErrorCode()- Error code if evaluation failedString getErrorMessage()- Error message if evaluation failedImmutableMetadata getMetadata()- Flag metadata
Contains the result of updating flag state.
boolean isSuccess()- Whether the update succeededString getError()- Error message if update failedList<String> getChangedFlags()- List of changed flag keys
- JDK 11+
- Rust toolchain with
wasm32-unknown-unknowntarget
Note: Maven is not required - the project includes the Maven wrapper (mvnw).
# Build the WASM module and Java library
cd java
./mvnw clean installThe build process:
- Compiles the Rust WASM module (from parent directory)
- Copies the WASM file to Java resources
- Compiles Java code
- Runs tests
- Generates Javadoc
- Packages JAR with bundled WASM
This library bundles:
- WASM Module: The flagd-evaluator compiled to WebAssembly
- Chicory Runtime: Pure Java WASM runtime with JIT compilation
- OpenFeature SDK: Official OpenFeature SDK for type-safe flag evaluation
- Host Functions: 9 required host functions for WASM interop
- Jackson Serialization: Custom serializers for OpenFeature types
- Java API: Type-safe wrapper around WASM exports
At runtime:
- WASM module is loaded from classpath during class initialization
- Chicory JIT compiles the WASM to optimized bytecode
- Custom Jackson serializers handle OpenFeature SDK types (
ImmutableMetadata,LayeredEvaluationContext) - Each
FlagEvaluatorinstance creates its own WASM instance - Type-safe evaluation returns
EvaluationResult<T>with compile-time type checking - Evaluations are synchronized for thread safety
- Startup: WASM module compiled once during class loading (~100ms)
- Evaluation: ~13,000 ops/s with realistic layered contexts (JIT compiled)
- Memory: ~3MB for WASM module + Chicory runtime
- Object Churn: ~12.8 KB allocated per layered evaluation, ~6.9 KB per simple evaluation
The library includes JMH (Java Microbenchmark Harness) benchmarks for performance tracking:
# Run JMH benchmarks
./mvnw exec:java@run-jmh-benchmarkBenchmark Results (example from development machine):
Benchmark Mode Cnt Score Error Units
FlagEvaluatorJmhBenchmark.evaluateWithLayeredContext thrpt 5 13035.383 ± 4173.375 ops/s
FlagEvaluatorJmhBenchmark.evaluateWithSimpleContext thrpt 5 14748.099 ± 2689.011 ops/s
FlagEvaluatorJmhBenchmark.serializeLayeredContext thrpt 5 222863.374 ± 151002.720 ops/s
Object Churn (GC allocation rates):
Benchmark Alloc Rate Alloc/Op
evaluateWithLayeredContext 159.6 MB/sec 12.8 KB/op
evaluateWithSimpleContext 97.8 MB/sec 6.9 KB/op
Benchmark Scenarios:
- evaluateWithLayeredContext: Full flag evaluation with 4-layer context (API, Transaction, Client, Invocation) and 100+ entries per layer
- evaluateWithSimpleContext: Baseline evaluation with minimal context
- serializeLayeredContext: JSON serialization overhead measurement
To run with GC profiling:
./mvnw exec:java -Dexec.classpathScope=test -Dexec.mainClass=org.openjdk.jmh.Main \
-Dexec.args="FlagEvaluatorJmhBenchmark -prof gc -f 0"The JUnit-based benchmark test suite is also available:
# Run performance benchmark tests
./mvnw test -Dtest=FlagEvaluatorBenchmarkTestFlagEvaluator is thread-safe and can be shared across threads. All evaluation and state update operations are synchronized.
- AOT Compilation: When Chicory supports AOT, compile WASM → Java at build time for better performance
- Async API: Non-blocking evaluation methods
- Streaming Updates: Support for flag configuration streams
- flagd-evaluator - Rust-based WASM evaluator
- OpenFeature Java SDK - Official OpenFeature SDK for Java
- Chicory - Pure Java WASM runtime
- OpenFeature - Vendor-agnostic feature flagging
Apache License 2.0