Skip to main content
The Confidence Java SDK provides ultra-low latency feature flag evaluation for Java applications using the Confidence Resolver—a Rust-based resolver that runs natively.

Features

  • Local evaluation: Flag rules evaluate on your infrastructure in microseconds
  • OpenFeature compatible: Standard feature flag API through OpenFeature provider
  • Background sync: Flag rules and logging sync with Confidence in the background
  • High reliability: No network dependency at evaluation time

Installation

<dependency>
  <groupId>com.spotify.confidence</groupId>
  <artifactId>openfeature-provider-local</artifactId>
  <version>0.11.2</version>
</dependency>

Quickstart

import com.spotify.confidence.OpenFeatureLocalResolveProvider;
import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.MutableContext;

public class App {
    public static void main(String[] args) {
        // Initialize the Confidence provider
        OpenFeatureLocalResolveProvider provider =
            new OpenFeatureLocalResolveProvider("your-client-secret");

        // Register with OpenFeature
        OpenFeatureAPI.getInstance().setProviderAndWait(provider);
        Client client = OpenFeatureAPI.getInstance().getClient();

        // Evaluate a flag
        MutableContext ctx = new MutableContext("user-123");
        ctx.add("country", "US");

        Boolean value = client.getBooleanValue("my-feature-flag.enabled", false, ctx);
        System.out.println("Flag value: " + value);
    }
}

HTTP Proxy Service

The FlagResolverService lets you proxy flag resolution requests from client SDKs through your Java backend. Instead of client SDKs connecting directly to Confidence servers, they connect to your service, which resolves flags locally. The proxy service enables:
  • Backend-controlled credentials: Client SDKs don’t need their own client secrets
  • Context enrichment: Add server-side context (user ID from auth, request metadata) before resolution
  • Low-latency resolution: Client SDKs make requests to your backend instead of Confidence servers, which can sometimes improve latency

Setup

import com.spotify.confidence.OpenFeatureLocalResolveProvider;
import com.spotify.confidence.FlagResolverService;

// Create and initialize the provider
OpenFeatureLocalResolveProvider provider =
    new OpenFeatureLocalResolveProvider("your-client-secret");
OpenFeatureAPI.getInstance().setProviderAndWait(provider);

// Create the HTTP service
FlagResolverService flagResolver = new FlagResolverService(provider);

Context Decoration

Add server-side context to requests before resolution:
FlagResolverService flagResolver = new FlagResolverService(provider,
    ContextDecorator.sync((ctx, req) -> {
        // Replace with your own header key
        List<String> userIds = req.headers().get("X-User-Id");
        if (userIds != null && !userIds.isEmpty()) {
            return ctx.merge(new ImmutableContext(userIds.get(0)));
        }
        return ctx;
    }));

Framework Integration

The service exposes handleResolve and handleApply methods that accept a ConfidenceHttpRequest and return a ConfidenceHttpResponse. Adapt these to any HTTP framework:
public class FlagServlet extends HttpServlet {
    private final FlagResolverService flagResolverService;

    public FlagServlet(OpenFeatureLocalResolveProvider provider) {
        this.flagResolverService = new FlagResolverService(provider,
            ContextDecorator.sync((context, request) -> {
                List<String> userIds = request.headers().get("X-User-Id");
                if (userIds != null && !userIds.isEmpty()) {
                    return context.merge(new ImmutableContext(userIds.get(0)));
                }
                return context;
            }));
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        ConfidenceHttpResponse response;

        if (req.getPathInfo().endsWith("v1/flags:resolve")) {
            response = flagResolverService.handleResolve(toConfidenceRequest(req))
                    .toCompletableFuture().join();
        } else if (req.getPathInfo().endsWith("v1/flags:apply")) {
            response = flagResolverService.handleApply(toConfidenceRequest(req))
                    .toCompletableFuture().join();
        } else {
            resp.setStatus(404);
            return;
        }

        resp.setStatus(response.statusCode());
        response.headers().forEach(resp::setHeader);
        resp.getOutputStream().write(response.body());
    }

    private ConfidenceHttpRequest toConfidenceRequest(HttpServletRequest req) {
        final byte[] bodyBytes;
        try {
            bodyBytes = req.getInputStream().readAllBytes();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }

        return new ConfidenceHttpRequest() {
            @Override
            public String method() { return req.getMethod(); }

            @Override
            public byte[] body() { return bodyBytes; }

            @Override
            public Map<String, List<String>> headers() {
                Map<String, List<String>> headers = new HashMap<>();
                Collections.list(req.getHeaderNames()).forEach(name ->
                    headers.put(name, Collections.list(req.getHeaders(name))));
                return headers;
            }
        };
    }
}

Client SDK configuration

Configure client SDKs to resolve flags through your backend:
const confidence = Confidence.create({
  clientSecret: 'not-used-but-required',
  resolveBaseUrl: 'https://your-backend.com/confidence-flags',
  applyBaseUrl: 'https://your-backend.com/confidence-flags',
});
Only application/json content type is supported. Requests with other content types receive a 415 response.

Resources