diff --git a/CHANGES.txt b/CHANGES.txt
index e3be07e36..a99b79df4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,7 @@
+4.16.0 (May 28, 2025)
+- Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK.
+- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.
+
4.15.0 (Apr 18, 2025)
- Prevent polling threads from starting when the SDK calls destroy method.
- Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
diff --git a/client/pom.xml b/client/pom.xml
index eea9088e7..d0ae4a789 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,9 +5,9 @@
io.split.client
java-client-parent
- 4.15.0
+ 4.16.0
- 4.15.0
+ 4.16.0
java-client
jar
Java Client
@@ -168,7 +168,7 @@
org.apache.httpcomponents.client5
httpclient5
- 5.4.1
+ 5.4.4
com.google.code.gson
diff --git a/client/src/main/java/io/split/Spec.java b/client/src/main/java/io/split/Spec.java
index 9e03a59ab..b2c7de4b3 100644
--- a/client/src/main/java/io/split/Spec.java
+++ b/client/src/main/java/io/split/Spec.java
@@ -6,6 +6,7 @@ private Spec() {
// restrict instantiation
}
- public static final String SPEC_VERSION = "1.1";
+ public static final String SPEC_1_3 = "1.3";
+ public static final String SPEC_1_1 = "1.1";
}
diff --git a/client/src/main/java/io/split/client/CacheUpdaterService.java b/client/src/main/java/io/split/client/CacheUpdaterService.java
index d69c66d58..63b426634 100644
--- a/client/src/main/java/io/split/client/CacheUpdaterService.java
+++ b/client/src/main/java/io/split/client/CacheUpdaterService.java
@@ -11,7 +11,6 @@
import io.split.engine.matchers.CombiningMatcher;
import io.split.engine.matchers.strings.WhitelistMatcher;
import io.split.grammar.Treatments;
-import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
import java.util.ArrayList;
@@ -52,7 +51,7 @@ public void updateCache(Map map) {
String treatment = conditions.size() > 0 ? Treatments.CONTROL : localhostSplit.treatment;
configurations.put(localhostSplit.treatment, localhostSplit.config);
- split = new ParsedSplit(splitName, 0, false, treatment,conditions, LOCALHOST, 0, 100, 0, 0, configurations, new HashSet<>(), true);
+ split = new ParsedSplit(splitName, 0, false, treatment,conditions, LOCALHOST, 0, 100, 0, 0, configurations, new HashSet<>(), true, null);
parsedSplits.removeIf(parsedSplit -> parsedSplit.feature().equals(splitName));
parsedSplits.add(split);
}
diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
index a3e234a3e..49eb66a99 100644
--- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
@@ -2,8 +2,10 @@
import com.google.common.annotations.VisibleForTesting;
+import io.split.Spec;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.SplitHttpResponse;
+import io.split.client.dtos.SplitChangesOldPayloadDto;
import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
@@ -22,7 +24,8 @@
import java.net.URISyntaxException;
import static com.google.common.base.Preconditions.checkNotNull;
-import static io.split.Spec.SPEC_VERSION;
+import static io.split.Spec.SPEC_1_3;
+import static io.split.Spec.SPEC_1_1;
/**
* Created by adilaijaz on 5/30/15.
@@ -31,23 +34,30 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
private static final Logger _log = LoggerFactory.getLogger(HttpSplitChangeFetcher.class);
private static final String SINCE = "since";
+ private static final String RB_SINCE = "rbSince";
private static final String TILL = "till";
private static final String SETS = "sets";
private static final String SPEC = "s";
+ private String specVersion = SPEC_1_3;
+ private int PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000;
+ private Long _lastProxyCheckTimestamp = 0L;
private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
+ private final boolean _rootURIOverriden;
- public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
+ public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer,
+ boolean rootURIOverriden)
throws URISyntaxException {
- return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer);
+ return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer, rootURIOverriden);
}
- private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer, boolean rootURIOverriden) {
_client = client;
_target = uri;
checkNotNull(_target);
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
+ _rootURIOverriden = rootURIOverriden;
}
long makeRandomTill() {
@@ -56,38 +66,66 @@ long makeRandomTill() {
}
@Override
- public SplitChange fetch(long since, FetchOptions options) {
-
+ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
long start = System.currentTimeMillis();
-
try {
- URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION);
- uriBuilder.addParameter(SINCE, "" + since);
- if (!options.flagSetsFilter().isEmpty()) {
- uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
- }
- if (options.hasCustomCN()) {
- uriBuilder.addParameter(TILL, "" + options.targetCN());
+ URI uri = buildURL(options, since, sinceRBS);
+ if (specVersion.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) {
+ _log.info("Switching to new Feature flag spec ({}) and fetching.", SPEC_1_3);
+ specVersion = SPEC_1_3;
+ uri = buildURL(options, -1,-1);
}
- URI uri = uriBuilder.build();
- SplitHttpResponse response = _client.get(uri, options, null);
+ SplitHttpResponse response = _client.get(uri, options, null);
if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
if (response.statusCode() == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
_log.error("The amount of flag sets provided are big causing uri length error.");
throw new UriTooLongException(String.format("Status code: %s. Message: %s", response.statusCode(), response.statusMessage()));
}
+
+ if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && specVersion.equals(Spec.SPEC_1_3) && _rootURIOverriden) {
+ specVersion = Spec.SPEC_1_1;
+ _log.warn("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}",
+ SPEC_1_3, SPEC_1_1);
+ _lastProxyCheckTimestamp = System.currentTimeMillis();
+ return fetch(since, sinceRBS, options);
+ }
+
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
throw new IllegalStateException(
String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode())
);
}
- return Json.fromJson(response.body(), SplitChange.class);
+
+ if (specVersion.equals(Spec.SPEC_1_1)) {
+ return Json.fromJson(response.body(), SplitChangesOldPayloadDto.class).toSplitChange();
+ }
+
+ SplitChange splitChange = Json.fromJson(response.body(), SplitChange.class);
+ splitChange.clearCache = _lastProxyCheckTimestamp != 0;
+ _lastProxyCheckTimestamp = 0L;
+ return splitChange;
} catch (Exception e) {
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
} finally {
- _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
+ _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis() - start);
+ }
+ }
+
+
+ private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException {
+ URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion);
+ uriBuilder.addParameter(SINCE, "" + since);
+ if (specVersion.equals(SPEC_1_3)) {
+ uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
+ }
+ if (!options.flagSetsFilter().isEmpty()) {
+ uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
+ }
+ if (options.hasCustomCN()) {
+ uriBuilder.addParameter(TILL, "" + options.targetCN());
}
+ return uriBuilder.build();
}
@VisibleForTesting
diff --git a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java
index 6eb092464..03530d099 100644
--- a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java
@@ -1,12 +1,15 @@
package io.split.client;
+import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import io.split.client.dtos.SplitChange;
+import io.split.client.dtos.SplitChangesOldPayloadDto;
import io.split.client.utils.InputStreamProvider;
import io.split.client.utils.Json;
import io.split.client.utils.LocalhostSanitizer;
import io.split.engine.common.FetchOptions;
import io.split.engine.experiments.SplitChangeFetcher;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,47 +20,71 @@
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import static io.split.client.utils.Utils.checkExitConditions;
+
public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher {
private static final Logger _log = LoggerFactory.getLogger(JsonLocalhostSplitChangeFetcher.class);
private final InputStreamProvider _inputStreamProvider;
- private byte [] lastHash;
+ private byte [] lastHashFeatureFlags;
+ private byte [] lastHashRuleBasedSegments;
public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) {
_inputStreamProvider = inputStreamProvider;
- lastHash = new byte[0];
+ lastHashFeatureFlags = new byte[0];
+ lastHashRuleBasedSegments = new byte[0];
}
@Override
- public SplitChange fetch(long since, FetchOptions options) {
+ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try {
JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8)));
+ if (checkOldSpec(new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))))) {
+ return Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toSplitChange();
+ }
SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class);
- return processSplitChange(splitChange, since);
+ return processSplitChange(splitChange, since, sinceRBS);
} catch (Exception e) {
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
}
}
- private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException {
+ private boolean checkOldSpec(JsonReader jsonReader) {
+ return Json.fromJson(jsonReader, JsonObject.class).has("splits");
+ }
+
+ private SplitChange processSplitChange(SplitChange splitChange, long changeNumber, long changeNumberRBS) throws NoSuchAlgorithmException {
SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange);
// if the till is less than storage CN and different from the default till ignore the change
- if (splitChangeToProcess.till < changeNumber && splitChangeToProcess.till != -1) {
+ if (checkExitConditions(splitChangeToProcess.featureFlags, changeNumber) ||
+ checkExitConditions(splitChangeToProcess.ruleBasedSegments, changeNumberRBS)) {
_log.warn("The till is lower than the change number or different to -1");
return null;
}
- String splitJson = splitChange.splits.toString();
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- digest.reset();
- digest.update(splitJson.getBytes());
- // calculate the json sha
- byte [] currHash = digest.digest();
+
+ byte [] currHashFeatureFlags = getStringDigest(splitChange.featureFlags.d.toString());
+ byte [] currHashRuleBasedSegments = getStringDigest(splitChange.ruleBasedSegments.d.toString());
//if sha exist and is equal to before sha, or if till is equal to default till returns the same segmentChange with till equals to storage CN
- if (java.security.MessageDigest.isEqual(lastHash, currHash) || splitChangeToProcess.till == -1) {
- splitChangeToProcess.till = changeNumber;
+ if (Arrays.equals(lastHashFeatureFlags, currHashFeatureFlags) || splitChangeToProcess.featureFlags.t == -1) {
+ splitChangeToProcess.featureFlags.t = changeNumber;
+ }
+ if (Arrays.equals(lastHashRuleBasedSegments, currHashRuleBasedSegments) || splitChangeToProcess.ruleBasedSegments.t == -1) {
+ splitChangeToProcess.ruleBasedSegments.t = changeNumberRBS;
}
- lastHash = currHash;
- splitChangeToProcess.since = changeNumber;
+
+ lastHashFeatureFlags = currHashFeatureFlags;
+ lastHashRuleBasedSegments = currHashRuleBasedSegments;
+ splitChangeToProcess.featureFlags.s = changeNumber;
+ splitChangeToProcess.ruleBasedSegments.s = changeNumberRBS;
+
return splitChangeToProcess;
}
+
+ private byte[] getStringDigest(String json) throws NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ digest.update(json.getBytes());
+ // calculate the json sha
+ return digest.digest();
+ }
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/LegacyLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/LegacyLocalhostSplitChangeFetcher.java
index a35c92cfe..c67055ec8 100644
--- a/client/src/main/java/io/split/client/LegacyLocalhostSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/LegacyLocalhostSplitChangeFetcher.java
@@ -5,6 +5,7 @@
import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.Status;
+import io.split.client.dtos.ChangeDto;
import io.split.client.utils.LocalhostConstants;
import io.split.client.utils.LocalhostSanitizer;
import io.split.engine.common.FetchOptions;
@@ -34,11 +35,12 @@ public LegacyLocalhostSplitChangeFetcher(String directory) {
}
@Override
- public SplitChange fetch(long since, FetchOptions options) {
+ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try (BufferedReader reader = new BufferedReader(new FileReader(_splitFile))) {
SplitChange splitChange = new SplitChange();
- splitChange.splits = new ArrayList<>();
+ splitChange.featureFlags = new ChangeDto<>();
+ splitChange.featureFlags.d = new ArrayList<>();
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
String lineTrim = line.trim();
if (lineTrim.isEmpty() || lineTrim.startsWith("#")) {
@@ -51,7 +53,8 @@ public SplitChange fetch(long since, FetchOptions options) {
_log.info("Ignoring line since it does not have 2 or 3 columns: " + lineTrim);
continue;
}
- Optional splitOptional = splitChange.splits.stream().filter(split -> split.name.equals(featureTreatment[0])).findFirst();
+ Optional splitOptional = splitChange.featureFlags.d.stream().
+ filter(split -> split.name.equals(featureTreatment[0])).findFirst();
Split split = splitOptional.orElse(null);
if(split == null) {
split = new Split();
@@ -59,7 +62,7 @@ public SplitChange fetch(long since, FetchOptions options) {
split.configurations = new HashMap<>();
split.conditions = new ArrayList<>();
} else {
- splitChange.splits.remove(split);
+ splitChange.featureFlags.d.remove(split);
}
split.status = Status.ACTIVE;
split.defaultTreatment = featureTreatment[1];
@@ -67,21 +70,20 @@ public SplitChange fetch(long since, FetchOptions options) {
split.trafficAllocation = LocalhostConstants.SIZE_100;
split.trafficAllocationSeed = LocalhostConstants.SIZE_1;
- Condition condition;
- if (featureTreatment.length == 2) {
- condition = LocalhostSanitizer.createCondition(null, featureTreatment[1]);
- } else {
- condition = LocalhostSanitizer.createCondition(featureTreatment[2], featureTreatment[1]);
- }
+ Condition condition = checkCondition(featureTreatment);
if(condition.conditionType != ConditionType.ROLLOUT){
split.conditions.add(0, condition);
} else {
split.conditions.add(condition);
}
- splitChange.splits.add(split);
+ splitChange.featureFlags.d.add(split);
}
- splitChange.till = since;
- splitChange.since = since;
+ splitChange.featureFlags.t = since;
+ splitChange.featureFlags.s = since;
+ splitChange.ruleBasedSegments = new ChangeDto<>();
+ splitChange.ruleBasedSegments.s = -1;
+ splitChange.ruleBasedSegments.t = -1;
+ splitChange.ruleBasedSegments.d = new ArrayList<>();
return splitChange;
} catch (FileNotFoundException f) {
_log.warn("There was no file named " + _splitFile.getPath() + " found. " +
@@ -96,4 +98,14 @@ public SplitChange fetch(long since, FetchOptions options) {
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
}
}
+
+ private Condition checkCondition(String[] featureTreatment) {
+ Condition condition;
+ if (featureTreatment.length == 2) {
+ condition = LocalhostSanitizer.createCondition(null, featureTreatment[1]);
+ } else {
+ condition = LocalhostSanitizer.createCondition(featureTreatment[2], featureTreatment[1]);
+ }
+ return condition;
+ }
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java
index 8787c1069..fd312c3b2 100644
--- a/client/src/main/java/io/split/client/SplitClientConfig.java
+++ b/client/src/main/java/io/split/client/SplitClientConfig.java
@@ -412,6 +412,10 @@ public CustomHeaderDecorator customHeaderDecorator() {
return _customHeaderDecorator;
}
+ public boolean isSdkEndpointOverridden() {
+ return !_endpoint.equals(SDK_ENDPOINT);
+ }
+
public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; }
public static final class Builder {
diff --git a/client/src/main/java/io/split/client/SplitFactoryBuilder.java b/client/src/main/java/io/split/client/SplitFactoryBuilder.java
index 2b48fb0d3..c2271ec4f 100644
--- a/client/src/main/java/io/split/client/SplitFactoryBuilder.java
+++ b/client/src/main/java/io/split/client/SplitFactoryBuilder.java
@@ -2,7 +2,6 @@
import io.split.inputValidation.ApiKeyValidator;
import io.split.grammar.Treatments;
-import io.split.service.SplitHttpClient;
import io.split.storages.enums.StorageMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java
index 86b77b8d7..9932cbf8c 100644
--- a/client/src/main/java/io/split/client/SplitFactoryImpl.java
+++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java
@@ -54,6 +54,7 @@
import io.split.engine.experiments.SplitFetcherImp;
import io.split.engine.experiments.SplitParser;
import io.split.engine.experiments.SplitSynchronizationTask;
+import io.split.engine.experiments.RuleBasedSegmentParser;
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.engine.segments.SegmentSynchronizationTaskImp;
import io.split.integrations.IntegrationsConfig;
@@ -66,15 +67,20 @@
import io.split.storages.SplitCache;
import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
+import io.split.storages.RuleBasedSegmentCache;
+import io.split.storages.RuleBasedSegmentCacheProducer;
+import io.split.storages.RuleBasedSegmentCacheConsumer;
import io.split.storages.enums.OperationMode;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
+import io.split.storages.memory.RuleBasedSegmentCacheInMemoryImp;
import io.split.storages.pluggable.adapters.UserCustomEventAdapterProducer;
import io.split.storages.pluggable.adapters.UserCustomImpressionAdapterConsumer;
import io.split.storages.pluggable.adapters.UserCustomImpressionAdapterProducer;
import io.split.storages.pluggable.adapters.UserCustomSegmentAdapterConsumer;
import io.split.storages.pluggable.adapters.UserCustomSplitAdapterConsumer;
import io.split.storages.pluggable.adapters.UserCustomTelemetryAdapterProducer;
+import io.split.storages.pluggable.adapters.UserCustomRuleBasedSegmentAdapterConsumer;
import io.split.storages.pluggable.domain.UserStorageWrapper;
import io.split.storages.pluggable.synchronizer.TelemetryConsumerSubmitter;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
@@ -202,6 +208,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
// Cache Initialisations
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
+ RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(config.getSetsFilter());
SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter);
ImpressionsStorage impressionsStorage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
@@ -212,11 +219,13 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
splitCache, _segmentCache, telemetryStorage, _startTime);
// Segments
- _segmentSynchronizationTaskImp = buildSegments(config, segmentCache, splitCache);
+ _segmentSynchronizationTaskImp = buildSegments(config, segmentCache, splitCache, ruleBasedSegmentCache);
SplitParser splitParser = new SplitParser();
+ RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();
// SplitFetcher
- _splitFetcher = buildSplitFetcher(splitCache, splitParser, flagSetsFilter);
+ _splitFetcher = buildSplitFetcher(splitCache, splitParser, flagSetsFilter,
+ ruleBasedSegmentParser, ruleBasedSegmentCache, config.isSdkEndpointOverridden());
// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
@@ -244,7 +253,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
config.getThreadFactory());
// Evaluator
- _evaluator = new EvaluatorImp(splitCache, segmentCache);
+ _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache);
// SplitClient
_client = new SplitClientImpl(this,
@@ -269,7 +278,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
_syncManager = SyncManagerImp.build(splitTasks, _splitFetcher, splitCache, splitAPI,
segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config, splitParser,
- flagSetsFilter);
+ ruleBasedSegmentParser, flagSetsFilter, ruleBasedSegmentCache);
_syncManager.start();
// DestroyOnShutDown
@@ -333,7 +342,9 @@ protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStor
_gates = new SDKReadinessGates();
_telemetrySynchronizer = new TelemetryConsumerSubmitter(customStorageWrapper, _sdkMetadata);
- _evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer);
+ UserCustomRuleBasedSegmentAdapterConsumer userCustomRuleBasedSegmentAdapterConsumer =
+ new UserCustomRuleBasedSegmentAdapterConsumer(customStorageWrapper);
+ _evaluator = new EvaluatorImp(userCustomSplitAdapterConsumer, userCustomSegmentAdapterConsumer, userCustomRuleBasedSegmentAdapterConsumer);
_impressionsSender = PluggableImpressionSender.create(customStorageWrapper);
_uniqueKeysTracker = createUniqueKeysTracker(config);
_impressionsManager = buildImpressionsManager(config, userCustomImpressionAdapterConsumer,
@@ -392,6 +403,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(config.getSetsFilter());
SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter);
+ RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
_splitCache = splitCache;
_gates = new SDKReadinessGates();
_segmentCache = segmentCache;
@@ -409,14 +421,16 @@ protected SplitFactoryImpl(SplitClientConfig config) {
segmentCache,
_telemetryStorageProducer,
_splitCache,
- config.getThreadFactory());
+ config.getThreadFactory(),
+ ruleBasedSegmentCache);
// SplitFetcher
SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config);
SplitParser splitParser = new SplitParser();
+ RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();
_splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer,
- flagSetsFilter);
+ flagSetsFilter, ruleBasedSegmentParser, ruleBasedSegmentCache);
// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache,
@@ -428,7 +442,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
_impressionsManager, null, null, null);
// Evaluator
- _evaluator = new EvaluatorImp(splitCache, segmentCache);
+ _evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache);
EventsStorage eventsStorage = new NoopEventsStorageImp();
@@ -595,7 +609,7 @@ private static HttpClientBuilder setupProxy(HttpClientBuilder httpClientbuilder,
private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config,
SegmentCacheProducer segmentCacheProducer,
- SplitCacheConsumer splitCacheConsumer) throws URISyntaxException {
+ SplitCacheConsumer splitCacheConsumer, RuleBasedSegmentCacheConsumer ruleBasedSegmentCache) throws URISyntaxException {
SegmentChangeFetcher segmentChangeFetcher = HttpSegmentChangeFetcher.create(_splitHttpClient, _rootTarget,
_telemetryStorageProducer);
@@ -605,15 +619,17 @@ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config,
segmentCacheProducer,
_telemetryStorageProducer,
splitCacheConsumer,
- config.getThreadFactory());
+ config.getThreadFactory(),
+ ruleBasedSegmentCache);
}
private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser,
- FlagSetsFilter flagSetsFilter) throws URISyntaxException {
+ FlagSetsFilter flagSetsFilter, RuleBasedSegmentParser ruleBasedSegmentParser,
+ RuleBasedSegmentCacheProducer ruleBasedSegmentCache, boolean isRootURIOverriden) throws URISyntaxException {
SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_splitHttpClient, _rootTarget,
- _telemetryStorageProducer);
+ _telemetryStorageProducer, isRootURIOverriden);
return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer,
- flagSetsFilter);
+ flagSetsFilter, ruleBasedSegmentParser, ruleBasedSegmentCache);
}
private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config,
diff --git a/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java
index e90ca1389..b2dccfdca 100644
--- a/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/YamlLocalhostSplitChangeFetcher.java
@@ -5,6 +5,7 @@
import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.Status;
+import io.split.client.dtos.ChangeDto;
import io.split.client.utils.InputStreamProvider;
import io.split.client.utils.LocalhostConstants;
import io.split.engine.common.FetchOptions;
@@ -32,17 +33,19 @@ public YamlLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider)
}
@Override
- public SplitChange fetch(long since, FetchOptions options) {
+ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
try {
Yaml yaml = new Yaml();
List