Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
# featurevisor-example-java

A simple Java application that uses Featurevisor Java SDK.
A small Java application showing how to use the [Featurevisor Java SDK](https://github.com/featurevisor/featurevisor-java).

Learn more about Featurevisor: https://featurevisor.com
Learn more about Featurevisor [here](https://featurevisor.com).

## Installation
It fetches a production datafile, creates a Featurevisor instance with customer context, then evaluates a flag, a variation, feature variables, and global variables.

Clone the repository, and install the dependencies:
## GitHub Packages access

## Setup `settings.xml`

Create a new file at `~/.m2/settings.xml` with the following content:
The SDK is published through GitHub Packages. Create `~/.m2/settings.xml` with credentials that have the `read:packages` scope:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
<servers>
<server>
<id>github</id>
<username>YOUR_GITHUB_USERNAME</username>
<password>YOUR_GITHUB_TOKEN</password>
</server>
</servers>

</settings>
```

You can generate a new GitHub token here with `read:packages` scope: https://github.com/settings/tokens
Keep this file outside the repository.

This file is meant to not be committed to the repository, as `.gitignore` is configured to ignore it.
## Run the example

## Install dependencies

```
$ mvn install
```sh
mvn clean compile
mvn exec:java -Dexec.mainClass="com.example.App"
```

## Compile
The application evaluates `commerce_platform`, `checkout_experience`, `max_items`, `payment_methods`, `serviceEndpoints`, and `supportContact` from this datafile:

```
$ mvn clean compile
```text
https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json
```

## Usage
Expected output:

```
$ mvn exec:java -Dexec.mainClass="com.example.App"
```text
Commerce platform enabled: true
Checkout variation: express
Maximum checkout items: 25
Payment methods: [card, wallet]
Service endpoint: https://api.eu.example.com (timeout: 1200 ms, retries: 4)
Support contact: [email protected]
```

Change the context in `App.java` to see how Featurevisor selects different rules, variations, and global variable overrides.

## Featurevisor project

Uses this Featurevisor project to fetch the configuration from: https://github.com/featurevisor/featurevisor-example-cloudflare
The datafile is generated by the [Featurevisor Cloudflare example](https://github.com/featurevisor/featurevisor-example-cloudflare).

Learn more in the [Featurevisor Java SDK documentation](https://featurevisor.com/docs/sdks/java/).
11 changes: 3 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Expand All @@ -31,12 +32,7 @@
<dependency>
<groupId>com.featurevisor</groupId>
<artifactId>featurevisor-java</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
<version>4.0.0</version>
</dependency>
</dependencies>

Expand All @@ -47,8 +43,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
<release>11</release>
</configuration>
</plugin>
<plugin>
Expand Down
88 changes: 65 additions & 23 deletions src/main/java/com/example/App.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,86 @@
package com.example;

import com.featurevisor.sdk.Featurevisor;
import com.featurevisor.sdk.FeaturevisorLogLevel;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.Map;

public class App {
public static void main(String[] args) {
private static final String DATAFILE_URL =
"https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json";

public static void main(String[] args) throws Exception {
String datafile = fetchDatafile();
Map<String, Object> context = Map.of(
"userId", "customer-123",
"country", "nl",
"locale", "nl-NL",
"accountPlan", "pro"
);

Featurevisor f = Featurevisor.createFeaturevisor(
new Featurevisor.FeaturevisorOptions()
.datafileString(datafile)
.context(context)
.logLevel(FeaturevisorLogLevel.ERROR)
);

try {
// Fetch JSON content from the Featurevisor datafile URL
String jsonContent = fetchJsonFromUrl("https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-tag-all.json");
System.out.println("Fetched JSON content");

// Create Featurevisor SDK instance with the datafile content
Featurevisor instance = Featurevisor.createFeaturevisor(
new Featurevisor.FeaturevisorOptions().datafileString(jsonContent));

// evaluate feature
boolean isFeatureEnabled = instance.isEnabled("my_feature");
System.out.println("Feature is enabled: " + isFeatureEnabled);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
boolean commerceEnabled = f.isEnabled("commerce_platform");
String checkoutVariation = f.getVariation("checkout_experience");
Integer maxItems = f.getVariableInteger("checkout_experience", "max_items");
List<String> paymentMethods = f.getVariableArray(
"checkout_experience",
"payment_methods"
);
Map<String, Object> endpoints = f.getVariableObject("serviceEndpoints");
String supportContact = f.getVariableString("supportContact");

System.out.println("Commerce platform enabled: " + commerceEnabled);
System.out.println("Checkout variation: " + valueOrUnavailable(checkoutVariation));
System.out.println("Maximum checkout items: " + valueOrUnavailable(maxItems));
System.out.println("Payment methods: " + paymentMethods);
System.out.printf(
"Service endpoint: %s (timeout: %s ms, retries: %s)%n",
endpoints.get("baseUrl"),
endpoints.get("timeoutMs"),
endpoints.get("retries")
);
System.out.println("Support contact: " + valueOrUnavailable(supportContact));
} finally {
f.close();
}
}

private static String fetchJsonFromUrl(String url) throws Exception {
HttpClient client = HttpClient.newHttpClient();
private static String fetchDatafile() throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.uri(URI.create(DATAFILE_URL))
.timeout(Duration.ofSeconds(10))
.header("Accept", "application/json")
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() != 200) {
throw new RuntimeException("Failed to fetch JSON: HTTP " + response.statusCode());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new IllegalStateException(
"Datafile request failed with HTTP " + response.statusCode()
);
}

return response.body();
}

private static Object valueOrUnavailable(Object value) {
return value == null ? "unavailable" : value;
}
}