Java SDK
Implement server-side feature flags in Java applications using the Harness FME Java SDK for secure, data-driven rollouts.
This guide provides detailed information about our Java SDK. All of our SDKs are open source. Go to our Java SDK GitHub repository to see the source code.
If you prefer to use the SDK as a standalone JAR file, it’s available for download from the Maven Central Repository. For example, the JAR for version 4.2.1 can be downloaded here. You can browse all available versions here.
Before you begin
The Java SDK supports JDK8 and later.
Initialization
To get started, set up FME in your code base using the following two steps.
1. Import the SDK into your project
Import the SDK into your project using one of the following two methods:
<dependency>
<groupId>io.split.client</groupId>
<artifactId>java-client</artifactId>
<version>4.18.3</version>
</dependency>compile 'io.split.client:java-client:4.18.3'If you cannot find the dependency, it may be due to the lag in the sync time between Sonatype and Maven central. In this case, use the following repository:
Non-shaded version (Alternative)
If you need to manage transitive dependencies manually to resolve conflicts (e.g., with OkHttp or Guava), use the non-shaded classifier:
TRANSITIVE DEPENDENCIES
When using the non-shaded version, you are responsible for providing all required libraries (like OkHttp and Gson) in your project's classpath.
2. Instantiate the SDK and create a new SDK factory client
IF UPGRADING AN EXISTING SDK - BLOCK UNTIL READY CHANGES
Starting version 3.0.1, SplitClientConfig#ready(int) is deprecated and migrated to a two part implementation:
Set the desired value in
SplitClientConfig#setBlockUntilReadyTimeout(int).Call
SplitClient#blockUntilReady()orSplitManager#blockUntilReady().
When the SDK is instantiated, it starts background tasks to update an in-memory cache with small amounts of data fetched from Harness servers. This process can take up to a few hundred milliseconds, depending on the size of data. If the SDK is asked to evaluate which treatment to show to a customer for a specific feature flag while it's in this intermediate state, it may not have the data necessary to run the evaluation. In this case, the SDK does not fail, rather, it returns the control treatment.
To make sure the SDK is properly loaded before asking it for a treatment, block until the SDK is ready. Do this by setting the desired wait using .setBlockUntilReadyTimeout() in the configuration and calling blockUntilReady() on the client. Do this all as a part of the startup sequence of your application.
We recommend instantiating the SDK factory once as a singleton and reusing it throughout your application.
Use the code snippet below with your own API key. Configure the SDK with the SDK key for the FME environment that you would like to access. In legacy Split (app.split.io) the SDK key is found on your Admin settings page, in the API keys section. Select a server-side SDK API key. See API keys to learn more.
Now you can start asking the SDK to evaluate treatments for your customers.
Use the SDK
Basic use
After you instantiate the SDK factory client, you can start using the getTreatment method of the SDK factory client to decide what version of your features your customers are served. The method requires the FEATURE_FLAG_NAME attribute that you want to ask for a treatment and a unique key attribute that corresponds to the end user that you are serving the feature to.
Then use an if-else-if block as shown below and insert the code for the different treatments that you defined in Harness FME. Remember the final else branch in your code to handle the client returning the control treatment.
Attribute syntax
To target based on custom attributes, the SDK's getTreatment method needs to be passed an attribute map at runtime.
In the example below, we are rolling out a feature flag to users. The provided attributes plan_type, registered_date, permissions, paying_customer, and deal_size are passed to the getTreatment call. These attributes are compared and evaluated against the attributes used in the Rollout plan as defined in Harness FME to decide whether to show the on or off treatment to this account.
The getTreatment method supports five types of attributes: strings, numbers, dates, booleans, and sets. The proper data type and syntax for each are:
Strings: Use type
String.Numbers: Use type
java.lang.Longorjava.lang.Integer.Dates: Express the value in
milliseconds since epoch. In Java,milliseconds since epochis of typejava.lang.Long. For example, the value for theregistered_dateattribute below isSystem.currentTimeInMillis(), which is a long.Booleans: Use type
java.lang.boolean.Sets: Use type
java.util.Collection.
Multiple evaluations at once
In some instances, you may want to evaluate treatments for multiple feature flags at once. Use the different variations of getTreatments from the SDK factory client to do this.
getTreatments: Pass a list of the feature flag names you want treatments for.getTreatmentsByFlagSet: Evaluate all flags that are part of the provided set name and are cached on the SDK instance.getTreatmentsByFlagSets: Evaluate all flags that are part of the provided set names and are cached on the SDK instance.
Get treatments with configurations
To leverage dynamic configurations with your treatments, you should use the getTreatmentWithConfig method. This method returns an object containing the treatment and associated configuration.
The config element is a stringified version of the configuration JSON defined in Harness FME. If there is no configuration defined for a treatment, the SDK returns null for the config parameter.
This method takes the exact same set of arguments as the standard getTreatment method. See below for examples on proper usage:
If you need to get multiple evaluations at once, you can also use the getTreatmentsWithConfig methods. These methods take the exact same arguments as the getTreatments methods but return a mapping of feature flag names to SplitResult instead of strings. Example usage below:
If a flag cannot be evaluated, the SDK returns the fallback treatment value (default "control" unless overridden globally or per flag). For more information, see Fallback treatments.
Append properties to impressions
Impressions are generated by the SDK each time a getTreatment method is called. These impressions are periodically sent back to Harness servers for feature monitoring and experimentation.
You can append properties to an impression by passing an object of key-value pairs to the getTreatment method. These properties are then included in the impression sent by the SDK and can provide useful context to the impression data.
Three types of properties are supported: strings, numbers, and booleans.
Shutdown
Make sure to call .destroy() before letting a process using the SDK exit as it gracefully shuts down the SDK by stopping all background threads, clearing caches, closing connections, and flushing the remaining unpublished impressions and events. The Java SDK specifically subscribes to the JVM shutdown hook (SIGTERM signal) which in normal circumstances is invoked automatically by the JVM during a shutdown process. This means that on a graceful shutdown of the server, the client will automatically call destroy() and will flush the buffers and release the resources.
In cases where you don't want our SDK to automatically destroy on shutdown, you can use the config: disableDestroyOnShutDown() (example usage in the Configuration section below) and set it to true. If you do this, the SDK ignores any signals like SIGTERM and it is your responsibility to properly call destroy at the right time. If a manual shutdown is required, you can then call:
After destroy() is called, any subsequent invocations to client.getTreatment() or manager methods result in control or empty list, respectively.
IMPORTANT!
A call to the destroy() method also destroys the factory object. When creating new client instance, first create a new factory instance.
Track
Use the track method to record any actions your customers perform. Each action is known as an event and corresponds to an event type. Calling track through one of our SDKs or via the API is the first step to and allows you to measure the impact of your feature flags on your users' actions and metrics.
Refer to the Events guide for more information about using track events in feature flags.
In the examples below you can see that the .track() method can take up to five arguments. The proper data type and syntax for each are:
key: The
keyvariable used in thegetTreatmentcall and firing this track event. The expected data type is String.TRAFFIC_TYPE: The traffic type of the key in the track call. The expected data type is String. You can only pass values that match the names of traffic types that you defined in Harness FME.
EVENT_TYPE: The event type that this event should correspond to. The expected data type is String. Full requirements on this argument are:
Contains 63 characters or fewer.
Starts with a letter or number.
Contains only letters, numbers, hyphen, underscore, or period.
This is the regular expression we use to validate the value:
[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}
VALUE: (Optional) The value used in creating the metric. This field can be sent in as null or 0 if you intend to only use the count function when creating a metric. The expected data type is Integer or Float.
PROPERTIES: (Optional) A map of key value pairs that can filter your metrics. To learn more about event property capture, refer to the Events property capture guide. FME currently supports three types of properties: strings, numbers, and booleans.
The track method returns a boolean value of true or false to indicate whether or not the SDK successfully queued the event to be sent back to Harness servers on the next event post. The SDK returns false if the current queue size is equal to the config set by eventsQueueSize or if an incorrect input to the track method is provided.
In the case that a bad input has been provided, you can read more about our SDK's expected behavior in our Events guide.
Configuration
The SDK has a number of knobs for configuring performance. Each knob is tuned to a reasonable default. However, you can override the value while instantiating the SDK. The parameters available for configuration are shown below.
Configuration
Description
Default value
featuresRefreshRate
The SDK polls Harness servers for changes to feature rollout plans. This parameter controls this polling period in seconds.
60 seconds
segmentsRefreshRate
The SDK polls Harness servers for changes to segments at this rate (in seconds).
60 seconds
impressionsRefreshRate
Controls how frequently the impressions cache expires after a write (in seconds). The treatment log captures which customer saw which treatment (on, off, etc.) and is periodically flushed back to Harness servers.
300 seconds
telemetryRefreshRate
The SDK caches diagnostic data that it periodically sends to Harness servers. This configuration controls how frequently this data is sent back to Harness servers (in seconds).
3600 seconds
eventsQueueSize
When using .track, the number of events to be kept in memory.
500
eventFlushIntervalInMillis
When using .track, how often (in milliseconds) the events queue is flushed to Harness servers.
30000 ms
connectionTimeout
HTTP client connection timeout (in ms).
15000ms
readTimeout
HTTP socket read timeout (in ms).
15000ms
setBlockUntilReadyTimeout
If specified, the client building process blocks until the SDK is ready to serve traffic or the specified time has elapsed. If the SDK is not ready within the specified time, a TimeOutException is thrown (in ms).
0ms
impressionsQueueSize
Default queue size for impressions.
30K
disableLabels
Disable labels from being sent to Harness servers. Labels may contain sensitive information.
enabled
disableIPAddress
Disable sending IP Address & hostname to the backend.
enabled
proxyHost
The location of the proxy.
localhost
proxyPort
The port of the proxy.
-1 (not set)
proxyUsername
Username to authenticate against the proxy server.
null
proxyPassword
Password to authenticate against the proxy server.
null
streamingEnabled
Boolean flag to enable the streaming service as default synchronization mechanism. In the event of an issue with streaming, the SDK falls back to the polling mechanism. If false, the SDK polls for changes as usual without attempting to use streaming.
true
impressionsMode
Defines how impressions are queued on the SDK. Supported modes are OPTIMIZED, NONE, and DEBUG.
In OPTIMIZED mode, only unique impressions are queued and posted to Harness; this is the recommended mode for experimentation use cases.
In NONE mode, no impression is tracked in Harness FME and only minimum viable data to support usage stats is, so never use this mode if you are experimenting with that instance impressions.
Use NONE when you want to optimize for feature flagging only use cases and reduce impressions network and storage load.
In DEBUG mode, all impressions are queued and sent to Harness; this is useful for validations. Use DEBUG mode when you want every impression to be logged in Harness FME when trying to debug your SDK setup. This setting does not impact the impression listener which receives all generated impressions locally.
OPTIMIZED
operationMode
Defines how the SDK synchronizes its data.
Two operation modes are currently supported:
- STANDALONE
- CONSUMER
STANDALONE
storageMode
Defines what kind of storage the SDK is going to use. With MEMORY, the SDK uses its own storage and runs as STANDALONE mode. Set REDIS mode if you want the SDK to run with this implementation as CONSUMER mode.
MEMORY
flagSetsFilter
This setting allows the SDK to only synchronize the feature flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.
null
threadFactory
Defines what kind of thread the SDK is going to use. Allows the SDK to use Virtual Threads.
null
inputStream
This setting allows the SDK supports InputStream to use localhost inside a JAR.
null
FileTypeEnum
Defines which kind of file is going to be the inputStream. Supported files are YAML and JSON for inputStream.
null
To set each of the parameters defined above, use the following syntax:
Localhost mode
For testing, a developer can put code behind feature flags on their development machine without the SDK requiring network connectivity. To achieve this, the SDK can be started in localhost mode (aka off-the-grid mode). In this mode, the SDK neither polls nor updates Harness servers. Instead, it uses an in-memory data structure to determine what treatments to show to the logged in customer for each of the features. To use the SDK in localhost mode, you must replace the API Key with "localhost" value.
With this mode, you can instantiate the SDKS using one of the following methods:
JSON: Full support, for advanced cases or replicating an environment by pulling rules from Harness FME servers (from version
4.7.0).YAML: Supports dynamic configs, individual targets and default rules (from version
3.1.0)..split: Legacy option, only treatment result.
JSON
Since version 4.7.0, our SDK supports localhost mode by using the JSON format. This version allows the user to map feature flags and segment definitions in the same format as the APIs receive the data.
This new mode needs extra configuration to be set:
Name
Description
Type
splitFile
Indicates the path of the feature flags file location to read
String
segmentDirectory
Indicates the path where all the segment files are located
String
localhostRefreshEnabled
Flag to run synchronization refresh for feature flags and segments in localhost mode.
Boolean
splitFile
The following splitFile is a JSON that represents a SplitChange:
segmentDirectory
The provided segment directory must have the json files of the corresponding segment linked to previous feature flag definitions. According to the file sample above: feature_flag_1 has segment_1 linked. That means that the segmentDirectory needs to have segment_1 definition.
YAML
Since version 3.1.0, our SDK supports a type of localhost feature flag definition file that uses the YAML format. This new format allows the user to map different keys to different treatments within a single feature flag and also add configurations to them. The format is a list of single-key maps (one per mapping feature-flag-keys-config) which is defined as follows:
In the example above, we have four entries:
The first entry defines that for feature flag
my_feature_flag, the keykeyreturns the treatmentonand theontreatment is tied to the configuration{"desc" : "this applies only to ON treatment"}.The second entry defines that the feature flag
some_other_feature_flagalways returns theofftreatment and no configuration.The third entry defines that
my_feature_flagalways returnsofffor all keys that don't match another entry (in this case, any key other thankey).The fourth entry shows how an example overrides a treatment for a set of keys.
Use the SplitConfigBuilder object to set the location of the localhost YAML file as shown in the example below:
.SPLIT file
In this mode, the SDK loads a mapping of feature flag name to treatment from a file at $HOME/.split. For a given flag, the treatment specified in the file is returned for every customer.
getTreatment calls for a feature flag and only returns the one treatment that you defined in the file. You can then change the treatment as necessary for your testing in the file. Any feature that is not provided in the features map returns the control treatment if the SDK is asked to evaluate them.
The format of this file is two columns separated by a whitespace. The left column is the feature flag name and the right column is the treatment name. The following is a sample .split file:
Input Stream
Since version 4.9.0, the SDK supports InputStream to use localhost inside a JAR. To achieve this, we added new parameters in splitFile property to set the InputStream. The first param is an InputStream of the file that we want to read. And the second is a FileTypeEnum which can be either YAML, or JSON. Here is an example code to demonstrate how to use this new feature:
State Sharing: Redis Integration
Before you get started with the cache, download the correct version of Redis to your machine. Make sure to start your Redis server. Refer to the Redis documentation for help. After that, followi the additional three steps to set up the cache with Redis.
1. Install the Redis Wrapper into your project
Import the Redis Wrapper into your project using one of the two methods below:
2. Set up the Split Synchronizer
Set up the Split Synchronizer to sync data to a Redis cache. Once you set up the synchronizer, go to the following step #3 to instantiate:
3. Instantiate the SDK factory client with Redis enabled
To run the SDK with Redis, you need to provide the Redis storage wrapper. Refer to the following to provide the wrapper:
Redis wrapper configuration
When you create a new instance for the Redis wrapper, you can provide your own configurations for some values.
Field name(s)
Description
Default value
timeout
Timeout that the connections is going to handle.
1000
host
Hostname where the Redis instance is.
localhost
port
HTTP port used in the connection.
6379
database
Numeric database to be used.
0
user
Redis cluster user. Leave empty if no User is used.
""
password
Redis cluster password. Leave empty if no password is used.
""
prefix
Best practice is to use a prefix in case the Redis instance is shared by many SDKs.
""
jedisPool
You can provide your own implementation of JedisPool.
null
maxTotal
Max number of pool connections.
8
Redis cluster support
The SDK supports Redis with Cluster. Note that a stable release of Cluster has shipped since Redis 3.0. For further information about Redis Cluster, refer to the Cluster documentation.
Use the following configuration for Redis in Cluster mode.
Variable
Type
Description
clusterNodes
Set<HostAndPort>
The list of cluster nodes.
jedis
JedisCluster
Jedis contains the list of cluster nodes.
keyHashTag
string
Custom hashtag to be used.
Manager
Use the Split Manager to get a list of features available to the SDK factory client.
To instantiate a Manager in your code base, use the same factory that you used for your client.
The Manager then has the following methods available:
The SplitView object that you see referenced above has the following structure:
Listener
FME SDKs send impression data back to Harness servers periodically and as a result of evaluating feature flags. To additionally send this information to a location of your choice, define and attach an impression listener.
The SDK sends the generated impressions to the impression listener immediately. As a result, be careful while implementing handling logic to avoid blocking the main thread. As the second parameter, specify the size of the queue acting as a buffer (see the snippet below).
If the impression listener is slow at processing the incoming data, the queue fills up and any subsequent impressions are dropped.
Logging
The Java SDK uses slf4j-api for logging. If you do not provide an implementation for slf4j, you see the following error in your logs:
You can get the SDK to log by providing a concrete implementation for SLF4J. For instance, if you are using log4j, you should import the following dependency.
If you have a log4j.properties in your classpath, the SDK log is visible. The following is an example of log4j.properties entry:
The following is an example of initializing the logger object in Java:
Thread Factory
Since version 4.10.0, the Java SDK provides support for Virtual Threads using the config threadFactory, instead of traditional threads. Below is an example of how to set it up:
Configure fallback treatments
Fallback treatments let you define a treatment value (and optional configuration) to be returned when a flag cannot be evaluated. By default, the SDK returns control, but you can override this globally or for individual flags at the SDK level.
This is useful when you want to:
Maintain a predictable user experience during outages or evaluation failures (avoid unexpected
controlin production)Protect critical user flows by returning a safe, stable treatment (for example, forcing
offduring an incident)Customize behavior per flag so each evaluation inherits appropriate safe defaults if something goes wrong
Global fallback treatment
Set a global fallback treatment when initializing the SDK factory. This value is returned whenever any flag cannot be evaluated.
Flag-level fallback treatment
You can also set a fallback treatment per flag when calling getTreatment or getTreatmentWithConfig. This flag-level fallback always takes precedence over the global fallback treatment, so if both are defined, the SDK will return the flag-level value when that flag cannot be evaluated.
For more information, see Fallback treatments.
Integrations
New Relic
The New Relic integration annotates New Relic transactions with FME feature flags information that can be used to correlate application metrics with feature flag changes. This integration is implemented as a synchronous impression listener and it can be enabled as shown below:
This integration is only enabled if the SDK detects the New Relic agent in the classpath. If the agent is not detected, the following error will be displayed in the logs (if logging is enabled):
Proxy
If your environment requires routing traffic through a proxy, you can configure the Java SDK to use one.
If you need to use a standard network proxy, set the proxyHost and proxyPort options in the SDK configuration. The SDK uses these values to perform requests through the proxy.
Connect to a Split Proxy instance
You can also connect the SDK to a Split Proxy instance instead of connecting directly to Harness FME servers. The Proxy synchronizes data and writes impressions and events back to Harness.
Use the .endpoint() property in the SplitClientConfig builder object to point the Java SDK to the Proxy endpoint, and specify the same port used in the Proxy command line. When creating the SplitFactory object, use the custom API key defined in the Proxy's client-apikeys parameter. The Proxy uses the SDK key when connecting to Harness FME servers.
To simplify maintenance and future updates, define your Proxy configuration using separate variables:
Base endpoint (for
.endpoint())Auth path (
/api/v2/auth)Telemetry path (
/api/v1)
This makes it easier to update paths or environments without changing multiple hardcoded values.
Integrate with the Harness Proxy
The Harness Proxy allows SDK traffic to securely route through a centralized, authenticated point before reaching the Harness SaaS backend. This provides full visibility and control over network traffic while keeping API keys secure and isolated.
To use the proxy, configure the SDK to point to the proxy host and port during initialization. All SDK requests are then routed through the proxy.
Configure the proxy
You can configure the Java SDK to route traffic through a forward proxy using the ProxyConfiguration class builder. This allows you to define proxy URLs, authentication credentials, and optional mTLS settings.
The following proxy configuration parameters are available:
url
Proxy server URL, provided as an instance of the java.net.URL class.
Yes
credentialsProvider
Credentials used for proxy authentication. Provide an implementation of either BearerCredentialsProvider or BasicCredentialsProvider.
Optional
mtls
Parameters for mTLS authentication, including the .p12 certificate file and password.
Optional
Harness Proxy (URL Only)
To use a proxy with no authentication, specify only the proxy URL in the configuration. Follow the pattern: http(s)://host:port.
Authentication is optional. Only one authentication method can be active at a time.
Harness Proxy with Username/Password Authentication
To authenticate using a username and password, implement the BasicCredentialsProvider interface and override the required methods.
Harness Proxy with JWT Token Authentication
To authenticate using a JWT, implement the BearerCredentialsProvider interface and override the getToken() method to return a valid JWT string.
Refresh or update the token when it expires.
Harness Proxy with mTLS Authentication
To configure mutual TLS (mTLS) authentication, use the mtls() parameter to pass the .p12 certificate file and its password.
Verify connection
After initialization, all SDK requests are routed through the configured proxy. You can verify successful routing by checking your proxy logs or a network monitor for SDK traffic.
Advanced: WebLogic container
WebLogic and the Java SDK contain a reference to Google Guava. If you are currently deploying a web application that contains our Java SDK into WebLogic, instruct the container to load Guava from the app classpath and not from the container.
If you have an existing weblogic.xml file in your deployment, add: <package-name>com.google.common.*</package-name> under the <prefer-application-packages> tag. If you do not, create the file and place it under the directory WEB-INF.
Here is a sample of a weblogic.xml file that includes the previously mentioned Guava classpath loading instruction.
Troubleshooting
Timeout Error: NoSuchMethodError: com.google.common.collect.Multisets.removeOccurrences
Using the Java SDK within certain frameworks, the SDK always times out. The logs show the following error:
This error happens because the Java SDK depends on the Google Guava library version 19.0 or higher. If your framework uses an older Guava version (< 19.0), this method will be missing, causing the error.
Upgrade the Google Guava dependency in your project to version 19.0 or above to resolve this issue.
How to change log level in the Java SDK
When integrating the Java SDK into a framework that uses Log4J, the SDK outputs many debug lines. Is it possible to change the log level?
Yes. The Java SDK respects the log4j.properties configuration file used by your Java application. To reduce logging verbosity and set the log level to ERROR, add these lines to your log4j.properties file:
This will suppress debug and info logs from the SDK, showing only error messages.
Error using JRE 6.x: "fatal alert: handshake_failure"
Using the Java SDK with JDK 1.6 (JRE 6.x), you may encounter the following SSL connection error when trying to connect to split.io:
Java 1.6 supports TLSv1 but does not support the high-strength ciphers required by split.io’s security protocol.
To fix this issue, you have two options:
Upgrade your JDK to version 1.7 or above. These versions include support for the stronger ciphers by default.
If upgrading is not an option, install the Java Cryptography Extension (JCE) provided by your JVM vendor for Java 6 to enable support for high-strength ciphers.
Exception: PKIX path building failed
When initializing the Java SDK SplitFactory object, you may see the following error:
This indicates that Java could not verify the SSL certificate from Split.io, preventing a secure connection between the SDK and Harness FME servers.
Manually install Split.io's certificates into your JVM’s trust store:
Download the certificates for both
sdk.split.ioandevents.split.io:Import the certificates into the Java
cacertskeystore (replace[JAVA_HOME]with your Java installation path):Restart your Java application.
Certificate renewals
Harness FME relies on Split.io's managed certificates for secure SDK communication. When Split.io rotates or renews its certificates, your application should continue working if:
Your JVM's default trust store already contains the required certificate authorities (most modern JDKs do).
Or, you've installed the intermediate/root certificates instead of the short-lived leaf certificates.
However, if you manually imported specific leaf certificates, you'll need to repeat the steps above when Split.io updates them. To avoid manual updates, consider updating your JDK to the latest version so its default trust store includes up-to-date CAs.
Check certificate expiry proactively
To see when Split.io's certificates expire, run:
This outputs something like the following:
If the notAfter date is approaching and you manually imported certificates, repeat the installation steps above.
Last updated
Was this helpful?