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>
implementation 'com.spotify.confidence:openfeature-provider-local:0.11.2'
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