> ## Documentation Index
> Fetch the complete documentation index at: https://confidence.spotify.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Java

> 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](https://github.com/spotify/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

<Tabs>
  <Tab title="Maven">
    ```xml theme={null}
    <dependency>
      <groupId>com.spotify.confidence</groupId>
      <artifactId>openfeature-provider-local</artifactId>
      <version>latest</version>
    </dependency>
    ```
  </Tab>

  <Tab title="Gradle">
    ```groovy theme={null}
    implementation 'com.spotify.confidence:openfeature-provider-local:<latest>'
    ```
  </Tab>
</Tabs>

## Quickstart

```java theme={null}
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

```java theme={null}
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:

```java theme={null}
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:

```java theme={null}
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:

```javascript theme={null}
const confidence = Confidence.create({
  clientSecret: 'not-used-but-required',
  resolveBaseUrl: 'https://your-backend.com/confidence-flags',
  applyBaseUrl: 'https://your-backend.com/confidence-flags',
});
```

<Note>
  Only `application/json` content type is supported. Requests with other content types receive a 415 response.
</Note>

## Resources

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="github" href="https://github.com/spotify/confidence-resolver/tree/main/openfeature-provider/java">
    Source code and examples
  </Card>

  <Card title="OpenFeature Java SDK" icon="flag" href="https://openfeature.dev/docs/reference/technologies/server/java">
    OpenFeature documentation
  </Card>

  <Card title="Context" icon="sliders" href="/docs/sdks/context">
    Configure evaluation context
  </Card>

  <Card title="Apply events" icon="chart-line" href="/docs/sdks/apply-event">
    Understand flag assignment tracking
  </Card>
</CardGroup>
