Sitelet https://github.com/canopy-labs/featureflip-java
Skip to content

Repository files navigation

Featureflip Java SDK

Java SDK for Featureflip - evaluate feature flags locally with near-zero latency.

Installation

Gradle

implementation 'io.featureflip:featureflip-java:2.10.1'

Maven

<dependency>
    <groupId>io.featureflip</groupId>
    <artifactId>featureflip-java</artifactId>
    <version>2.10.1</version>
</dependency>

Quick Start

import io.featureflip.client.FeatureflipClient;
import io.featureflip.client.EvaluationContext;

FeatureflipClient client = FeatureflipClient.get("your-sdk-key");
client.waitForInitialization();

boolean enabled = client.boolVariation("my-feature",
    EvaluationContext.builder("user-123").build(), false);

if (enabled) {
    System.out.println("Feature is enabled!");
}

client.close();

Lifetime: The client is designed to be used as a singleton. Calling FeatureflipClient.get() (or builder().build()) multiple times with the same SDK key returns handles sharing one underlying client — you cannot accidentally open duplicate streaming connections. For dependency injection, register it as a singleton bean.

Configuration

Pass a FeatureFlagConfig to get() when you need non-default options:

import io.featureflip.client.FeatureFlagConfig;

FeatureflipClient client = FeatureflipClient.get("your-sdk-key",
    FeatureFlagConfig.builder()
        .baseUrl("https://eval.featureflip.io")          // Evaluation API URL (default)
        .streaming(true)                                   // SSE for real-time updates (default)
        .pollInterval(Duration.ofSeconds(30))              // Polling interval if streaming=false
        .flushInterval(Duration.ofSeconds(30))             // Event flush interval
        .flushBatchSize(100)                               // Events per batch
        .initTimeout(Duration.ofSeconds(10))               // Max wait for initialization
        .connectTimeout(Duration.ofSeconds(5))             // HTTP connection timeout
        .readTimeout(Duration.ofSeconds(10))               // HTTP read timeout
        .build());

The SDK key can also be supplied through the FEATUREFLIP_SDK_KEY environment variable — pass null or a blank string to get() (or builder()) and it is read from there. A key passed explicitly always wins. If neither supplies one, get() throws IllegalArgumentException naming both routes.

Evaluation

EvaluationContext context = EvaluationContext.builder("user-123").build();

// Boolean flag
boolean enabled = client.boolVariation("feature-key", context, false);

// String flag
String tier = client.stringVariation("pricing-tier", context, "free");

// Integer flag
int limit = client.intVariation("rate-limit", context, 100);

// Double flag
double ratio = client.doubleVariation("rollout-ratio", context, 0.5);

// JSON flag
UiConfig config = client.jsonVariation("ui-config", context,
    new UiConfig("light"), UiConfig.class);

Detailed Evaluation

EvaluationDetail<Boolean> detail = client.boolVariationDetail(
    "feature-key", EvaluationContext.builder("123").build(), false);

System.out.println(detail.getValue());        // The evaluated value
System.out.println(detail.getReason());        // RULE_MATCH, FALLTHROUGH, FLAG_DISABLED, etc.
System.out.println(detail.getRuleId());        // Rule ID if reason is RULE_MATCH
System.out.println(detail.getErrorMessage());  // Error details if reason is ERROR

JSON flags have a detail accessor too. Read one as Object to get the served value in its plain Java shape (Map, List, String, Integer, Double, Boolean) without asserting a type up front:

EvaluationDetail<Object> detail = client.jsonVariationDetail(
    "ui-config", context, null, Object.class);

Reacting to Flag Changes

Subscribe to configuration changes to invalidate a cache, re-render, or log:

Runnable unsubscribe = client.onUpdate(flagKeys ->
    log.info("flags changed: {}", flagKeys));

// later
unsubscribe.run();

The listener receives the keys whose evaluated value may have moved, batched into one call per update. That includes flags dragged along by a change they do not themselves record: those targeting an edited segment, and those whose prerequisite moved.

The initial flag load does not fire -- a cold start is not a change. The listener runs on the SDK's streaming or polling thread, so it must not block; hand anything substantial to your own executor. Subscriptions are dropped when the client is closed, so a caller using try-with-resources need not unsubscribe.

Event Tracking

// Track custom events
client.track("checkout-completed",
    EvaluationContext.builder("123").build(),
    Map.of("total", 99.99));

// Record an identify event for analytics (does not affect flag evaluation)
client.identify(EvaluationContext.builder("123")
    .set("plan", "pro")
    .build());

// Anonymous: attributes, no identity. The event omits userId rather than
// sending an empty one. Use this instead of builder("") when there is no
// identity to carry — builder("") claims a present-but-empty identity.
client.identify(EvaluationContext.builder()
    .set("plan", "pro")
    .build());

// Force flush pending events
client.flush();

Featureflip counts each event name, per environment. The metadata you pass is transmitted with the event but is not stored, and event counts are not surfaced in the app or API.

Resource Management

The client implements AutoCloseable for try-with-resources:

try (var client = FeatureflipClient.get("your-sdk-key")) {
    client.waitForInitialization();
    boolean enabled = client.boolVariation("feature", context, false);
}
// Automatically closed and flushed

Testing

Use forTesting() to create a client with predetermined flag values -- no network calls.

FeatureflipClient client = FeatureflipClient.forTesting(Map.of(
    "my-feature", true,
    "pricing-tier", "pro"
));

client.boolVariation("my-feature", context, false);     // true
client.stringVariation("pricing-tier", context, "free"); // "pro"
client.boolVariation("unknown", context, false);         // false (default)

Features

  • Local evaluation - Near-zero latency after initialization
  • Real-time updates - SSE streaming with automatic polling fallback
  • Change notifications - onUpdate listeners for configuration changes
  • Event tracking - Automatic batching and background flushing
  • Test support - forTesting() factory for deterministic unit tests
  • AutoCloseable - Works with try-with-resources
  • Thread-safe - Safe for concurrent access from multiple threads

Requirements

  • Java 11+

License

Apache-2.0

About

Java SDK for Featureflip — feature flags for teams that ship fast (read-only mirror)

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages