Skip to main content

Generated Java protobuf and gRPC client for Flaggr

Java SDK

Generated Java protobuf types and gRPC service stubs for Flaggr. Use standard gRPC-Java to connect.

Installation

Maven

<dependencies>
  <dependency>
    <groupId>dev.flaggr</groupId>
    <artifactId>flaggr-proto</artifactId>
    <version>0.1.0</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.62.2</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.62.2</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.62.2</version>
  </dependency>
</dependencies>

Gradle

dependencies {
    implementation 'dev.flaggr:flaggr-proto:0.1.0'
    implementation 'io.grpc:grpc-netty-shaded:1.62.2'
    implementation 'io.grpc:grpc-protobuf:1.62.2'
    implementation 'io.grpc:grpc-stub:1.62.2'
}

gRPC Client Setup

import dev.flaggr.proto.v1.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
 
public class FlaggrClient {
    private final EvaluationServiceGrpc.EvaluationServiceBlockingStub evaluationStub;
    private final FlagStreamServiceGrpc.FlagStreamServiceStub streamStub;
    private final ManagedChannel channel;
 
    public FlaggrClient(String host, int port) {
        this.channel = ManagedChannelBuilder.forAddress(host, port)
            .useTransportSecurity()
            .build();
        this.evaluationStub = EvaluationServiceGrpc.newBlockingStub(channel);
        this.streamStub = FlagStreamServiceGrpc.newStub(channel);
    }
 
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS);
    }
}

Flag Evaluation

Boolean

EvaluationContext context = EvaluationContext.newBuilder()
    .setTargetingKey("user-456")
    .putStringAttributes("plan", "enterprise")
    .putStringAttributes("country", "AU")
    .build();
 
ResolveBooleanResponse resp = evaluationStub.resolveBoolean(
    ResolveBooleanRequest.newBuilder()
        .setFlagKey("checkout-v2")
        .setDefaultValue(false)
        .setContext(context)
        .setServiceId("web-app")
        .build()
);
 
System.out.printf("checkout-v2 = %s (reason: %s)%n", resp.getValue(), resp.getReason());

String

ResolveStringResponse resp = evaluationStub.resolveString(
    ResolveStringRequest.newBuilder()
        .setFlagKey("button-color")
        .setDefaultValue("blue")
        .setContext(context)
        .setServiceId("web-app")
        .build()
);

Number

ResolveNumberResponse resp = evaluationStub.resolveNumber(
    ResolveNumberRequest.newBuilder()
        .setFlagKey("rate-limit")
        .setDefaultValue(100.0)
        .setContext(context)
        .setServiceId("web-app")
        .build()
);

Object (JSON)

ResolveObjectResponse resp = evaluationStub.resolveObject(
    ResolveObjectRequest.newBuilder()
        .setFlagKey("banner-config")
        .setDefaultValue("{\"text\":\"\",\"color\":\"blue\",\"dismissible\":true}")
        .setContext(context)
        .setServiceId("web-app")
        .build()
);

Bulk Evaluation

BulkEvaluateFlagsResponse resp = evaluationStub.bulkEvaluateFlags(
    BulkEvaluateFlagsRequest.newBuilder()
        .addFlagKeys("checkout-v2")
        .addFlagKeys("dark-mode")
        .addFlagKeys("rate-limit")
        .setContext(context)
        .setServiceId("web-app")
        .build()
);
 
resp.getFlagsMap().forEach((key, result) -> {
    System.out.printf("%s = %s (reason: %s)%n", key, result.getValue(), result.getReason());
});

Streaming

Subscribe to real-time flag updates with server-side streaming:

import io.grpc.stub.StreamObserver;
 
streamStub.streamFlags(
    StreamFlagsRequest.newBuilder()
        .setServiceId("web-app")
        .setEnvironment(Environment.ENVIRONMENT_PRODUCTION)
        .setClientId("java-service-01")
        .setApiToken("flg_your_token")
        .build(),
    new StreamObserver<FlagUpdate>() {
        @Override
        public void onNext(FlagUpdate update) {
            switch (update.getEventType()) {
                case FLAG_EVENT_TYPE_UPDATED:
                    System.out.printf("flag %s updated%n", update.getFlagKey());
                    break;
                case FLAG_EVENT_TYPE_TOGGLED:
                    System.out.printf("flag %s toggled%n", update.getFlagKey());
                    break;
                case FLAG_EVENT_TYPE_HEARTBEAT:
                    // connection alive
                    break;
                default:
                    break;
            }
        }
 
        @Override
        public void onError(Throwable t) {
            System.err.println("Stream error: " + t.getMessage());
        }
 
        @Override
        public void onCompleted() {
            System.out.println("Stream completed");
        }
    }
);

OpenFeature Integration

Use with the OpenFeature Java SDK:

import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.MutableContext;
 
public class Main {
    public static void main(String[] args) {
        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
 
        // Register the Flaggr provider
        api.setProvider(new FlaggrProvider("flaggr.dev", 443, "web-app", "flg_your_token"));
 
        Client client = api.getClient();
 
        MutableContext ctx = new MutableContext("user-456");
        ctx.add("plan", "enterprise");
        ctx.add("country", "AU");
 
        boolean value = client.getBooleanValue("checkout-v2", false, ctx);
        System.out.printf("checkout-v2 = %s%n", value);
    }
}

The FlaggrProvider wraps the gRPC client and implements the FeatureProvider interface. See the OpenFeature Java SDK docs for more on evaluation context, hooks, and event handling.

Async Client

For non-blocking evaluation, use the async stub:

EvaluationServiceGrpc.EvaluationServiceFutureStub asyncStub =
    EvaluationServiceGrpc.newFutureStub(channel);
 
ListenableFuture<ResolveBooleanResponse> future = asyncStub.resolveBoolean(
    ResolveBooleanRequest.newBuilder()
        .setFlagKey("checkout-v2")
        .setDefaultValue(false)
        .setContext(context)
        .setServiceId("web-app")
        .build()
);
 
// Add callback or block on result
Futures.addCallback(future, new FutureCallback<ResolveBooleanResponse>() {
    @Override
    public void onSuccess(ResolveBooleanResponse result) {
        System.out.printf("checkout-v2 = %s%n", result.getValue());
    }
 
    @Override
    public void onFailure(Throwable t) {
        System.err.println("Evaluation failed: " + t.getMessage());
    }
}, MoreExecutors.directExecutor());