Shipeasy
ReferenceJava

Installation

- Java 17+ (the SDK uses java.net.http.HttpClient and modern language features). - A Shipeasy server key (SHIPEASYSERVERKEY).

Generated from the SDK's own /docs/ — also served raw at https://shipeasy-ai.github.io/sdk-java/pages/installation.md.

Requirements

  • Java 17+ (the SDK uses java.net.http.HttpClient and modern language features).
  • A Shipeasy server key (SHIPEASY_SERVER_KEY).

Coordinates

Maven

<dependency>
  <groupId>ai.shipeasy</groupId>
  <artifactId>shipeasy</artifactId>
  <version>0.10.0</version>
</dependency>

Gradle (Kotlin DSL)

implementation("ai.shipeasy:shipeasy:0.10.0")

Gradle (Groovy DSL)

implementation 'ai.shipeasy:shipeasy:0.10.0'

Optional, provided-scope dependencies

These are not pulled into your deployment unless you already supply them:

  • jakarta.servlet-api — only needed for the AnonIdFilter servlet filter that mints the shared __se_anon_id cookie. Your container already supplies it.
  • dev.openfeature:sdk — only needed for the OpenFeature provider.

Imports

import ai.shipeasy.Shipeasy;          // configure() entry point + package-level statics
import ai.shipeasy.Client;            // the cheap, user-bound handle for all reads
import ai.shipeasy.ExperimentResult;  // experiment return type
import ai.shipeasy.FlagDetail;        // value + reason

Configure once, then bind a Client per request

Configuration happens once per process. Shipeasy.configure(...) authenticates with your server key, kicks off the initial rules fetch (fire-and-forget), and registers the engine used by see(). It is first-config-wins idempotent — the first call wins; later calls are no-ops.

After it returns, construct a cheap, user-bound Client per request. Every read then takes no user argument because the user is bound at construction.

import ai.shipeasy.Shipeasy;
import ai.shipeasy.Client;
import java.util.Map;

// Once, at startup.
Shipeasy.configure(System.getenv("SHIPEASY_SERVER_KEY"));

// Per user / per request.
Client c = new Client(Map.of("user_id", "u_123", "plan", "pro"));

boolean enabled = c.getFlag("new_checkout");

The attributes transform

If your domain user object is not already a Shipeasy attribute map, register a transform once at configure() time. It runs once, in the Client constructor, mapping your object to the attribute map ({ "user_id": ..., "anonymous_id": ..., <attrs> }):

Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
    .attributes((Object u) -> {
        MyUser my = (MyUser) u;
        return Map.of("user_id", my.id(), "plan", my.plan());
    }));

boolean on = new Client(myUser).getFlag("new_checkout");

The default transform is identity — pass a Map<String, Object> to new Client(...) and it is used as the attribute map verbatim.

Identity default

The bound attribute map should carry a stable unit: user_id for logged-in users, or anonymous_id for logged-out traffic. If neither is present, the engine falls back to the request-scoped __se_anon_id cookie resolved by AnonIdFilter. An explicit user_id/anonymous_id always wins.

One-shot vs background poll

By default configure() performs a single fire-and-forget fetch. For a long-running server that should pick up rule changes without a redeploy, pass .poll(true)configure() then owns the full poll lifecycle (initial fetch + periodic refresh). You never start a poll yourself:

Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
    .poll(true));

configure() options

Build options with Shipeasy.options(apiKey) and chain the setters, then pass the result to Shipeasy.configure(...):

MethodDefaultMeaning
.baseUrl(String)https://edge.shipeasy.devOverride the edge API base URL.
.env(String)"prod"Deployment env tagged on usage telemetry and see() events.
.disableTelemetry(boolean)falseTurn off per-evaluation usage beacons.
.poll(boolean)falseStart the background poll (initial fetch + periodic refresh) instead of a one-shot fetch.
.privateAttributes(List)emptyAttribute keys usable for targeting but stripped from outbound events. See Advanced.
.stickyStore(StickyBucketStore)nonePluggable sticky-bucketing store. See Advanced.
.attributes(Function)identityMap your user object to the attribute map.
Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
    .env("staging")
    .poll(true)
    .disableTelemetry(true));

Environment variables

The SDK reads no env vars itself — you pass the key explicitly. The convention is SHIPEASY_SERVER_KEY for the server key (and a *_CLIENT_KEY public key for the browser SDK / SSR i18n tag — never the server key in the browser).


Framework wiring

Configure exactly once; the location is the only thing that differs per framework. Build a Client per request thereafter.

Spring Boot — @PostConstruct

Run configure() from a bean's @PostConstruct:

import ai.shipeasy.Shipeasy;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
class ShipeasyConfig {
    @PostConstruct
    void init() {
        // For a long-running server, start the background poll:
        Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
            .poll(true));
    }
}

Spring Boot — @PostConstruct with the attributes transform

When you map your own principal type to the attribute map, register the transform at configure() time, then build a Client(principal) per request:

import ai.shipeasy.Shipeasy;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
class ShipeasyConfig {
    @PostConstruct
    void init() {
        Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
            .poll(true)
            .attributes((Object u) -> {
                MyPrincipal p = (MyPrincipal) u;
                return Map.of("user_id", p.id(), "plan", p.plan());
            }));
    }
}
// In a controller / service — build a Client per request:
import ai.shipeasy.Client;

@GetMapping("/checkout")
String checkout(@AuthenticationPrincipal MyPrincipal principal) {
    Client c = new Client(principal);  // construct once per callsite
    return c.getFlag("new_checkout") ? "v2" : "v1";
}

For logged-out traffic, register AnonIdFilter so anonymous bucketing is shared with the browser SDK (see Servlet/Jakarta below).

Servlet / Jakarta — ServletContextListener + AnonIdFilter

Configure on context startup, and register AnonIdFilter so every request without a __se_anon_id cookie gets one minted (the shared first-party cookie every Shipeasy SDK buckets on):

import ai.shipeasy.Shipeasy;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class ShipeasyBootstrap implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
            .poll(true));
    }
}
// AnonIdFilter mints the shared __se_anon_id cookie for any request without it;
// evaluations then default to it as anonymous_id — no per-call wiring.
import ai.shipeasy.AnonIdFilter;
import jakarta.servlet.annotation.WebFilter;

@WebFilter("/*")
public class ShipeasyAnonId extends AnonIdFilter {}

In a Spring Boot servlet stack, register the same filter as a bean:

import ai.shipeasy.AnonIdFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@Bean
FilterRegistrationBean<AnonIdFilter> shipeasyAnonId() {
    var reg = new FilterRegistrationBean<>(new AnonIdFilter());
    reg.addUrlPatterns("/*");
    return reg;
}
// A logged-out request now buckets on the __se_anon_id cookie automatically:
new Client(Map.of()).getFlag("new_checkout");

jakarta.servlet-api is a provided dependency — your container already supplies it, so this adds nothing to your deployment. Non-servlet stacks (Ktor, http4k, Javalin) can use the AnonId primitives directly.

Plain main()

import ai.shipeasy.Shipeasy;
import ai.shipeasy.Client;
import java.util.Map;

public class App {
    public static void main(String[] args) {
        // Configure once; poll(true) keeps a long-running app fresh.
        Shipeasy.configure(Shipeasy.options(System.getenv("SHIPEASY_SERVER_KEY"))
            .poll(true));

        Client c = new Client(Map.of("user_id", "u_123"));
        System.out.println(c.getFlag("new_checkout"));
    }
}
Was this page helpful?
✎ Edit this page

On this page