Skip to content

Commit aacfd86

Browse files
authored
Merge branch 'development' into feature/prerequisites
2 parents 0da2e36 + 285d29f commit aacfd86

38 files changed

+252
-188
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
4.15.0 (Apr 18, 2025)
2+
- Prevent polling threads from starting when the SDK calls destroy method.
3+
- 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.
4+
15
4.14.0 (Jan 17, 2025)
26
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs.
37
- Cleaned unused imports to fix a collision issue.

client/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.14.0</version>
8+
<version>4.15.0</version>
99
</parent>
10-
<version>4.14.0</version>
10+
<version>4.15.0</version>
1111
<artifactId>java-client</artifactId>
1212
<packaging>jar</packaging>
1313
<name>Java Client</name>

client/src/main/java/io/split/Spec.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ private Spec() {
66
// restrict instantiation
77
}
88

9-
// TODO: Change the schema to 1.3 when updating splitclient
109
public static final String SPEC_1_3 = "1.3";
1110
public static final String SPEC_1_1 = "1.1";
1211
}

client/src/main/java/io/split/client/HttpSplitChangeFetcher.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
3434
private static final Logger _log = LoggerFactory.getLogger(HttpSplitChangeFetcher.class);
3535

36-
private final Object _lock = new Object();
3736
private static final String SINCE = "since";
3837
private static final String RB_SINCE = "rbSince";
3938
private static final String TILL = "till";

client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private SplitChange processSplitChange(SplitChange splitChange, long changeNumbe
6161
_log.warn("The till is lower than the change number or different to -1");
6262
return null;
6363
}
64+
6465
byte [] currHashFeatureFlags = getStringDigest(splitChange.featureFlags.d.toString());
6566
byte [] currHashRuleBasedSegments = getStringDigest(splitChange.ruleBasedSegments.d.toString());
6667
//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
@@ -79,10 +80,10 @@ private SplitChange processSplitChange(SplitChange splitChange, long changeNumbe
7980
return splitChangeToProcess;
8081
}
8182

82-
private byte[] getStringDigest(String Json) throws NoSuchAlgorithmException {
83+
private byte[] getStringDigest(String json) throws NoSuchAlgorithmException {
8384
MessageDigest digest = MessageDigest.getInstance("SHA-1");
8485
digest.reset();
85-
digest.update(Json.getBytes());
86+
digest.update(json.getBytes());
8687
// calculate the json sha
8788
return digest.digest();
8889
}

client/src/main/java/io/split/client/LegacyLocalhostSplitChangeFetcher.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,7 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
7070
split.trafficAllocation = LocalhostConstants.SIZE_100;
7171
split.trafficAllocationSeed = LocalhostConstants.SIZE_1;
7272

73-
Condition condition;
74-
if (featureTreatment.length == 2) {
75-
condition = LocalhostSanitizer.createCondition(null, featureTreatment[1]);
76-
} else {
77-
condition = LocalhostSanitizer.createCondition(featureTreatment[2], featureTreatment[1]);
78-
}
73+
Condition condition = checkCondition(featureTreatment);
7974
if(condition.conditionType != ConditionType.ROLLOUT){
8075
split.conditions.add(0, condition);
8176
} else {
@@ -103,4 +98,14 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
10398
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
10499
}
105100
}
101+
102+
private Condition checkCondition(String[] featureTreatment) {
103+
Condition condition;
104+
if (featureTreatment.length == 2) {
105+
condition = LocalhostSanitizer.createCondition(null, featureTreatment[1]);
106+
} else {
107+
condition = LocalhostSanitizer.createCondition(featureTreatment[2], featureTreatment[1]);
108+
}
109+
return condition;
110+
}
106111
}

client/src/main/java/io/split/client/SplitClientImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ private String validateProperties(Map<String, Object> properties) {
552552

553553
ImpressionPropertiesValidator.ImpressionPropertiesValidatorResult iPValidatorResult = ImpressionPropertiesValidator.propertiesAreValid(
554554
properties);
555-
return new GsonBuilder().create().toJson(iPValidatorResult.getValue()).toString();
555+
return new GsonBuilder().create().toJson(iPValidatorResult.getValue());
556556
}
557557

558558
private Map<String, SplitResult> getTreatmentsWithConfigInternal(String matchingKey, String bucketingKey, List<String> featureFlagNames,

client/src/main/java/io/split/client/SplitFactoryImpl.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public class SplitFactoryImpl implements SplitFactory {
130130
private static final org.slf4j.Logger _log = LoggerFactory.getLogger(SplitFactoryImpl.class);
131131
private static final String LEGACY_LOG_MESSAGE = "The sdk initialize in localhost mode using Legacy file. The splitFile or "
132132
+
133-
"inputStream doesn't add it to the config.";
133+
"inputStream are not added to the config.";
134134
private final static long SSE_CONNECT_TIMEOUT = 30000;
135135
private final static long SSE_SOCKET_TIMEOUT = 70000;
136136

@@ -403,7 +403,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
403403
SegmentCache segmentCache = new SegmentCacheInMemoryImpl();
404404
FlagSetsFilter flagSetsFilter = new FlagSetsFilterImpl(config.getSetsFilter());
405405
SplitCache splitCache = new InMemoryCacheImp(flagSetsFilter);
406-
RuleBasedSegmentCache _ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
406+
RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp();
407407
_splitCache = splitCache;
408408
_gates = new SDKReadinessGates();
409409
_segmentCache = segmentCache;
@@ -422,15 +422,15 @@ protected SplitFactoryImpl(SplitClientConfig config) {
422422
_telemetryStorageProducer,
423423
_splitCache,
424424
config.getThreadFactory(),
425-
_ruleBasedSegmentCache);
425+
ruleBasedSegmentCache);
426426

427427
// SplitFetcher
428428
SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config);
429429
SplitParser splitParser = new SplitParser();
430430
RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();
431431

432432
_splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer,
433-
flagSetsFilter, ruleBasedSegmentParser, _ruleBasedSegmentCache);
433+
flagSetsFilter, ruleBasedSegmentParser, ruleBasedSegmentCache);
434434

435435
// SplitSynchronizationTask
436436
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache,
@@ -442,7 +442,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
442442
_impressionsManager, null, null, null);
443443

444444
// Evaluator
445-
_evaluator = new EvaluatorImp(splitCache, segmentCache, _ruleBasedSegmentCache);
445+
_evaluator = new EvaluatorImp(splitCache, segmentCache, ruleBasedSegmentCache);
446446

447447
EventsStorage eventsStorage = new NoopEventsStorageImp();
448448

client/src/main/java/io/split/client/dtos/ChangeDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.split.client.dtos;
22

3-
import java.util.ArrayList;
43
import java.util.List;
54

65
public class ChangeDto<T> {

client/src/main/java/io/split/client/dtos/EvaluationOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ public EvaluationOptions(Map<String, Object> properties) {
1010
}
1111
public Map<String, Object> getProperties() {
1212
return _properties;
13-
};
13+
}
1414
}

client/src/main/java/io/split/client/dtos/KeyImpression.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ public class KeyImpression {
1717
/* package private */ static final String FIELD_PREVIOUS_TIME = "pt";
1818
/* package private */ static final String FIELD_PROPERTIES = "properties";
1919

20-
public static int MAX_PROPERTIES_LENGTH_BYTES = 32 * 1024;
21-
2220
public transient String feature; // Non-serializable
2321

2422
@SerializedName(FIELD_KEY_NAME)

client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,14 @@ public ProcessImpressionOptimized(boolean listenerEnabled, ImpressionObserver im
3232
public ImpressionsResult process(List<Impression> impressions) {
3333
List<Impression> impressionsToQueue = new ArrayList<>();
3434
for(Impression impression : impressions) {
35-
if (impression.properties() != null) {
36-
impressionsToQueue.add(impression);
37-
continue;
38-
}
39-
impression = impression.withPreviousTime(_impressionObserver.testAndSet(impression));
40-
if(!Objects.isNull(impression.pt()) && impression.pt() != 0){
41-
_impressionCounter.inc(impression.split(), impression.time(), 1);
42-
}
43-
if(shouldntQueueImpression(impression)) {
44-
continue;
35+
if (impression.properties() == null) {
36+
impression = impression.withPreviousTime(_impressionObserver.testAndSet(impression));
37+
if (!Objects.isNull(impression.pt()) && impression.pt() != 0) {
38+
_impressionCounter.inc(impression.split(), impression.time(), 1);
39+
}
40+
if (shouldntQueueImpression(impression)) {
41+
continue;
42+
}
4543
}
4644
impressionsToQueue.add(impression);
4745
}

client/src/main/java/io/split/client/utils/LocalhostSanitizer.java

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,17 @@ private LocalhostSanitizer() {
3030
}
3131

3232
public static SplitChange sanitization(SplitChange splitChange) {
33-
SecureRandom random = new SecureRandom();
34-
List<Split> splitsToRemove = new ArrayList<>();
35-
List<RuleBasedSegment> ruleBasedSegmentsToRemove = new ArrayList<>();
36-
splitChange = sanitizeTillAndSince(splitChange);
33+
sanitizeTillAndSince(splitChange);
34+
splitChange.featureFlags.d = sanitizeFeatureFlags(splitChange.featureFlags.d);
35+
splitChange.ruleBasedSegments.d = sanitizeRuleBasedSegments(splitChange.ruleBasedSegments.d);
3736

38-
if (splitChange.featureFlags.d != null) {
39-
for (Split split : splitChange.featureFlags.d) {
40-
if (split.name == null) {
41-
splitsToRemove.add(split);
42-
continue;
43-
}
44-
split.trafficTypeName = sanitizeIfNullOrEmpty(split.trafficTypeName, LocalhostConstants.USER);
45-
split.status = sanitizeStatus(split.status);
46-
split.defaultTreatment = sanitizeIfNullOrEmpty(split.defaultTreatment, LocalhostConstants.CONTROL);
47-
split.changeNumber = sanitizeChangeNumber(split.changeNumber, 0);
48-
49-
if (split.trafficAllocation == null || split.trafficAllocation < 0 || split.trafficAllocation > LocalhostConstants.SIZE_100) {
50-
split.trafficAllocation = LocalhostConstants.SIZE_100;
51-
}
52-
if (split.trafficAllocationSeed == null || split.trafficAllocationSeed == 0) {
53-
split.trafficAllocationSeed = -random.nextInt(10) * LocalhostConstants.MILLI_SECONDS;
54-
}
55-
if (split.seed == 0) {
56-
split.seed = -random.nextInt(10) * LocalhostConstants.MILLI_SECONDS;
57-
}
58-
if (split.algo != LocalhostConstants.ALGO) {
59-
split.algo = LocalhostConstants.ALGO;
60-
}
61-
split.conditions = sanitizeConditions((ArrayList<Condition>) split.conditions, false, split.trafficTypeName);
62-
}
63-
splitChange.featureFlags.d.removeAll(splitsToRemove);
64-
} else {
65-
splitChange.featureFlags.d = new ArrayList<>();
66-
}
37+
return splitChange;
38+
}
6739

68-
if (splitChange.ruleBasedSegments.d != null) {
69-
for (RuleBasedSegment ruleBasedSegment : splitChange.ruleBasedSegments.d) {
40+
private static List<RuleBasedSegment> sanitizeRuleBasedSegments(List<RuleBasedSegment> ruleBasedSegments) {
41+
List<RuleBasedSegment> ruleBasedSegmentsToRemove = new ArrayList<>();
42+
if (ruleBasedSegments != null) {
43+
for (RuleBasedSegment ruleBasedSegment : ruleBasedSegments) {
7044
if (ruleBasedSegment.name == null) {
7145
ruleBasedSegmentsToRemove.add(ruleBasedSegment);
7246
continue;
@@ -79,12 +53,58 @@ public static SplitChange sanitization(SplitChange splitChange) {
7953
ruleBasedSegment.excluded.segments = sanitizeExcluded((ArrayList) ruleBasedSegment.excluded.segments);
8054
ruleBasedSegment.excluded.keys = sanitizeExcluded((ArrayList) ruleBasedSegment.excluded.keys);
8155
}
82-
splitChange.ruleBasedSegments.d.removeAll(ruleBasedSegmentsToRemove);
56+
ruleBasedSegments.removeAll(ruleBasedSegmentsToRemove);
8357
} else {
84-
splitChange.ruleBasedSegments.d = new ArrayList<>();
58+
ruleBasedSegments = new ArrayList<>();
8559
}
60+
return ruleBasedSegments;
61+
}
8662

87-
return splitChange;
63+
private static List<Split> sanitizeFeatureFlags(List<Split> featureFlags) {
64+
List<Split> splitsToRemove = new ArrayList<>();
65+
if (featureFlags != null) {
66+
for (Split split : featureFlags) {
67+
if (split.name == null) {
68+
splitsToRemove.add(split);
69+
continue;
70+
}
71+
split.trafficTypeName = sanitizeIfNullOrEmpty(split.trafficTypeName, LocalhostConstants.USER);
72+
split.status = sanitizeStatus(split.status);
73+
split.defaultTreatment = sanitizeIfNullOrEmpty(split.defaultTreatment, LocalhostConstants.CONTROL);
74+
split.changeNumber = sanitizeChangeNumber(split.changeNumber, 0);
75+
split.trafficAllocation = sanitizeTrafficAllocation(split.trafficAllocation);
76+
split.trafficAllocationSeed = sanitizeSeed(split.trafficAllocationSeed);
77+
split.seed = sanitizeSeed(split.seed);
78+
split.algo = sanitizeAlgo(split.algo);
79+
split.conditions = sanitizeConditions((ArrayList<Condition>) split.conditions, false, split.trafficTypeName);
80+
}
81+
featureFlags.removeAll(splitsToRemove);
82+
} else {
83+
featureFlags = new ArrayList<>();
84+
}
85+
return featureFlags;
86+
}
87+
88+
private static int sanitizeSeed(Integer seed) {
89+
SecureRandom random = new SecureRandom();
90+
if (seed == null || seed == 0) {
91+
seed = -random.nextInt(10) * LocalhostConstants.MILLI_SECONDS;
92+
}
93+
return seed;
94+
}
95+
96+
private static int sanitizeAlgo(int algo) {
97+
if (algo != LocalhostConstants.ALGO) {
98+
algo = LocalhostConstants.ALGO;
99+
}
100+
return algo;
101+
}
102+
103+
private static int sanitizeTrafficAllocation(Integer trafficAllocation) {
104+
if (trafficAllocation == null || trafficAllocation < 0 || trafficAllocation > LocalhostConstants.SIZE_100) {
105+
trafficAllocation = LocalhostConstants.SIZE_100;
106+
}
107+
return trafficAllocation;
88108
}
89109

90110
private static ArrayList<Condition> sanitizeConditions(ArrayList<Condition> conditions, boolean createPartition, String trafficTypeName) {
@@ -106,18 +126,18 @@ private static ArrayList<Condition> sanitizeConditions(ArrayList<Condition> cond
106126
}
107127
return conditions;
108128
}
109-
private static String sanitizeIfNullOrEmpty(String toBeSantitized, String defaultValue) {
110-
if (toBeSantitized == null || toBeSantitized.isEmpty()) {
129+
private static String sanitizeIfNullOrEmpty(String toBeSanitized, String defaultValue) {
130+
if (toBeSanitized == null || toBeSanitized.isEmpty()) {
111131
return defaultValue;
112132
}
113-
return toBeSantitized;
133+
return toBeSanitized;
114134
}
115135

116-
private static long sanitizeChangeNumber(long toBeSantitized, long defaultValue) {
117-
if (toBeSantitized < 0) {
136+
private static long sanitizeChangeNumber(long toBeSanitized, long defaultValue) {
137+
if (toBeSanitized < 0) {
118138
return defaultValue;
119139
}
120-
return toBeSantitized;
140+
return toBeSanitized;
121141
}
122142

123143
private static Status sanitizeStatus(Status toBeSanitized) {

client/src/main/java/io/split/client/utils/RuleBasedSegmentProcessor.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
public class RuleBasedSegmentProcessor {
1717
private static final Logger _log = LoggerFactory.getLogger(RuleBasedSegmentProcessor.class);
1818

19+
private RuleBasedSegmentProcessor() {
20+
throw new IllegalStateException("Utility class");
21+
}
22+
1923
public static RuleBasedSegmentsToUpdate processRuleBasedSegmentChanges(RuleBasedSegmentParser ruleBasedSegmentParser,
2024
List<RuleBasedSegment> ruleBasedSegments) {
2125
List<ParsedRuleBasedSegment> toAdd = new ArrayList<>();
@@ -31,10 +35,10 @@ public static RuleBasedSegmentsToUpdate processRuleBasedSegmentChanges(RuleBased
3135
ParsedRuleBasedSegment parsedRuleBasedSegment = ruleBasedSegmentParser.parse(ruleBasedSegment);
3236
if (parsedRuleBasedSegment == null) {
3337
_log.debug(String.format("We could not parse the rule based segment definition for: %s", ruleBasedSegment.name));
34-
continue;
38+
} else {
39+
segments.addAll(parsedRuleBasedSegment.getSegmentsNames());
40+
toAdd.add(parsedRuleBasedSegment);
3541
}
36-
segments.addAll(parsedRuleBasedSegment.getSegmentsNames());
37-
toAdd.add(parsedRuleBasedSegment);
3842
}
3943
return new RuleBasedSegmentsToUpdate(toAdd, toRemove, segments);
4044
}

client/src/main/java/io/split/client/utils/RuleBasedSegmentsToUpdate.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.split.client.utils;
22

33
import io.split.engine.experiments.ParsedRuleBasedSegment;
4-
import io.split.engine.experiments.ParsedSplit;
54

65
import java.util.List;
76
import java.util.Set;

client/src/main/java/io/split/engine/common/SyncManagerImp.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ private void startPollingMode() {
219219
case STREAMING_DOWN:
220220
_log.info("Streaming service temporarily unavailable, working in polling mode.");
221221
_pushManager.stopWorkers();
222+
// if the whole SDK is being shutdown, don't start polling,
223+
// in case the polling threads are not terminated and a graceful shutdown will fail.
224+
if(_shuttedDown.get()) {
225+
break;
226+
}
222227
_synchronizer.startPeriodicFetching();
223228
break;
224229
case STREAMING_BACKOFF:

client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ public class EvaluatorImp implements Evaluator {
2424
private static final Logger _log = LoggerFactory.getLogger(EvaluatorImp.class);
2525

2626
private final SegmentCacheConsumer _segmentCacheConsumer;
27-
private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer;
2827
private final EvaluationContext _evaluationContext;
2928
private final SplitCacheConsumer _splitCacheConsumer;
3029

3130
public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache,
3231
RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) {
3332
_splitCacheConsumer = checkNotNull(splitCacheConsumer);
3433
_segmentCacheConsumer = checkNotNull(segmentCache);
35-
_ruleBasedSegmentCacheConsumer = checkNotNull(ruleBasedSegmentCacheConsumer);
3634
_evaluationContext = new EvaluationContext(this, _segmentCacheConsumer, ruleBasedSegmentCacheConsumer);
3735
}
3836

0 commit comments

Comments
 (0)