Java SDK

Flipswitch SDK for Java with real-time SSE support

The Flipswitch Java SDK provides an OpenFeature-compatible provider with automatic cache invalidation via Server-Sent Events (SSE).

GitHub: github.com/flipswitch-io/java-sdk

Requirements

  • Java 17+
  • OpenFeature SDK 1.x

Installation

<dependency>
    <groupId>io.flipswitch</groupId>
    <artifactId>flipswitch-sdk</artifactId>
</dependency>

Check Maven Central for the latest version.

implementation 'io.flipswitch:flipswitch-sdk'

Check Maven Central for the latest version.

Quick Start

import io.flipswitch.FlipswitchProvider;
import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.MutableContext;

// API key is required, all other options have sensible defaults
FlipswitchProvider provider = FlipswitchProvider.builder("YOUR_API_KEY").build();

// Register with OpenFeature
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
api.setProviderAndWait(provider);

// Get a client
Client client = api.getClient();

// Evaluate flags
boolean darkMode = client.getBooleanValue("dark-mode", false);
String welcome = client.getStringValue("welcome-message", "Hello!");
int maxItems = client.getIntegerValue("max-items-per-page", 10);

Configuration Options

FlipswitchProvider provider = FlipswitchProvider.builder("YOUR_API_KEY")
    .baseUrl("https://api.flipswitch.io")      // Optional: defaults to production url
    .enableRealtime(true)                      // Optional: defaults to true
    .enablePollingFallback(true)               // Optional: fall back to polling when SSE fails
    .pollingIntervalMs(30000)                  // Optional: polling interval in milliseconds
    .maxSseRetries(5)                          // Optional: max SSE retries before fallback
    .httpClient(customOkHttpClient)            // Optional: custom OkHttpClient
    .build();
OptionTypeDefaultDescription
apiKeyStringrequiredEnvironment API key from dashboard
baseUrlStringhttps://api.flipswitch.ioYour Flipswitch server URL
enableRealtimebooleantrueEnable SSE for real-time flag updates
httpClientOkHttpClientdefaultCustom HTTP client
enablePollingFallbackbooleantrueFall back to polling when SSE fails
pollingIntervalMslong30000Polling interval in milliseconds
maxSseRetriesint5Max SSE retries before polling fallback

Evaluation Context

Pass user attributes for targeting:

MutableContext context = new MutableContext("user-123");
context.add("email", "user@example.com");
context.add("plan", "premium");
context.add("country", "SE");

boolean showFeature = client.getBooleanValue("new-feature", false, context);

Real-Time Updates

When enableRealtime(true) is set (default), the SDK maintains an SSE connection to receive instant flag changes:

  1. The SSE client receives a flag-updated or config-updated event
  2. The local cache is immediately invalidated
  3. Next flag evaluation fetches the fresh value

Event Listeners

provider.addFlagChangeListener(event -> {
    if (event.flagKey() != null) {
        System.out.println("Flag changed: " + event.flagKey());
    } else {
        System.out.println("All flags invalidated");
    }
    System.out.println("Timestamp: " + event.getTimestampAsInstant());
});

Connection Status

// Check current SSE status
SseClient.ConnectionStatus status = provider.getSseStatus();
// CONNECTING, CONNECTED, DISCONNECTED, ERROR

// Force reconnect
provider.reconnectSse();

Polling Fallback

When SSE connection fails repeatedly, the SDK falls back to polling:

FlipswitchProvider provider = FlipswitchProvider.builder("YOUR_API_KEY")
    .enablePollingFallback(true)  // default: true
    .pollingIntervalMs(30000)     // Poll every 30 seconds
    .maxSseRetries(5)             // Fall back after 5 failed SSE attempts
    .build();

// Check if polling is active
if (provider.isPollingActive()) {
    System.out.println("Polling fallback is active");
}

Detailed Evaluation

Get full evaluation details including variant and reason:

FlagEvaluationDetails<Boolean> details = client.getBooleanDetails("feature-flag", false, context);

System.out.println("Value: " + details.getValue());
System.out.println("Variant: " + details.getVariant());
System.out.println("Reason: " + details.getReason());

Object Flags

For complex flag values (JSON objects):

Value config = client.getObjectValue("feature-config", new Value(), context);

// Access structure
if (config.isStructure()) {
    Structure s = config.asStructure();
    String theme = s.getValue("theme").asString();
    int timeout = s.getValue("timeout").asInteger();
}

Bulk Flag Evaluation

Evaluate all flags at once:

// Evaluate all flags
List<FlagEvaluation> flags = provider.evaluateAllFlags(context);
for (FlagEvaluation flag : flags) {
    System.out.println(flag.getKey() + " (" + flag.getValueType() + "): " + flag.getValueAsString());
}

// Evaluate a single flag with full details
FlagEvaluation flag = provider.evaluateFlag("dark-mode", context);
if (flag != null) {
    System.out.println("Value: " + flag.getValue());
    System.out.println("Reason: " + flag.getReason());
    System.out.println("Variant: " + flag.getVariant());
}

Spring Boot Integration

@Configuration
public class FeatureFlagConfig {

    @Bean
    public FlipswitchProvider flipswitchProvider(
            @Value("${flipswitch.api-key}") String apiKey) {
        return FlipswitchProvider.builder(apiKey).build();
    }

    @Bean
    public Client openFeatureClient(FlipswitchProvider provider) throws Exception {
        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
        api.setProviderAndWait(provider);
        return api.getClient();
    }
}

Then inject and use in your services:

@Service
public class MyService {
    private final Client featureClient;

    public MyService(Client featureClient) {
        this.featureClient = featureClient;
    }

    public void doSomething(User user) {
        MutableContext context = new MutableContext(user.getId());
        context.add("email", user.getEmail());

        if (featureClient.getBooleanValue("new-feature", false, context)) {
            // New feature logic
        }
    }
}

application.yml:

flipswitch:
  api-key: ${FLIPSWITCH_API_KEY}

Reconnection Strategy

The SSE client automatically reconnects with exponential backoff:

  • Initial delay: 1 second
  • Maximum delay: 30 seconds
  • Backoff multiplier: 2x

When reconnected, the provider state changes from STALE back to READY.

Shutdown

Always shutdown the provider when done:

provider.shutdown();
// or
OpenFeatureAPI.getInstance().shutdown();

In Spring Boot, the provider will be automatically shut down when the application context closes.

Troubleshooting

SSE connection keeps disconnecting

Check if your proxy or load balancer supports long-lived connections. Configure timeout settings accordingly.

Flags not updating in real-time

Verify that enableRealtime(true) is set and check the SSE status:

System.out.println(provider.getSseStatus());

Memory leaks

Ensure you call shutdown() when the provider is no longer needed, especially in testing scenarios.

On this page