Confidence Java SDK for server-side feature flag evaluation.
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.
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
import com.spotify.confidence.OpenFeatureLocalResolveProvider;import com.spotify.confidence.FlagResolverService;// Create and initialize the providerOpenFeatureLocalResolveProvider provider = new OpenFeatureLocalResolveProvider("your-client-secret");OpenFeatureAPI.getInstance().setProviderAndWait(provider);// Create the HTTP serviceFlagResolverService flagResolver = new FlagResolverService(provider);
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; } }; }}