Skip to content

Commit ca5583f

Browse files
authored
Merge pull request #429 from splitio/SDKS-7385
[SDKS-7445] Add InputStream provider for localhost mode
2 parents 9d4ad7a + 8867ece commit ca5583f

22 files changed

+395
-75
lines changed

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
4.9.0 (Sep 7, 2023)
2+
- Added InputStream config for localhost mode providing a solution when the file is inside a jar.
3+
- Fixed track impressions to send all impressions to the listener.
4+
- Fixed SyncManager shutdown to stop SSE only when is streaming mode on.
5+
16
4.8.1 (Aug 1, 2023)
27
- Applied linting rules to the code.
38
- Fixed an issue when the prefix is empty for Redis settings.

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.8.1</version>
8+
<version>4.9.0</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>

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

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,44 @@
22

33
import com.google.gson.stream.JsonReader;
44
import io.split.client.dtos.SplitChange;
5+
import io.split.client.utils.InputStreamProvider;
56
import io.split.client.utils.Json;
67
import io.split.client.utils.LocalhostSanitizer;
78
import io.split.engine.common.FetchOptions;
89
import io.split.engine.experiments.SplitChangeFetcher;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
1112

12-
import java.io.File;
13-
import java.io.FileNotFoundException;
14-
import java.io.FileReader;
15-
import java.io.UnsupportedEncodingException;
13+
import java.io.BufferedReader;
14+
import java.io.InputStreamReader;
15+
import java.nio.charset.StandardCharsets;
1616
import java.security.MessageDigest;
1717
import java.security.NoSuchAlgorithmException;
1818
import java.util.Arrays;
1919

2020
public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher {
2121

2222
private static final Logger _log = LoggerFactory.getLogger(JsonLocalhostSplitChangeFetcher.class);
23-
private final File _file;
23+
private final InputStreamProvider _inputStreamProvider;
2424
private byte [] lastHash;
2525

26-
public JsonLocalhostSplitChangeFetcher(String filePath) {
27-
_file = new File(filePath);
26+
public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) {
27+
_inputStreamProvider = inputStreamProvider;
2828
lastHash = new byte[0];
2929
}
3030

3131
@Override
3232
public SplitChange fetch(long since, FetchOptions options) {
33-
3433
try {
35-
JsonReader jsonReader = new JsonReader(new FileReader(_file));
34+
JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8)));
3635
SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class);
3736
return processSplitChange(splitChange, since);
38-
} catch (FileNotFoundException f){
39-
_log.warn(String.format("There was no file named %s found. " +
40-
"We created a split client that returns default treatments for all feature flags for all of your users. " +
41-
"If you wish to return a specific treatment for a feature flag, enter the name of that feature flag name and " +
42-
"treatment name separated by whitespace in %s; one pair per line. Empty lines or lines starting with '#' are " +
43-
"considered comments",
44-
_file.getPath(), _file.getPath()), f);
45-
throw new IllegalStateException("Problem fetching splitChanges: " + f.getMessage(), f);
4637
} catch (Exception e) {
47-
_log.warn(String.format("Problem to fetch split change using the file %s",
48-
_file.getPath()), e);
4938
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
5039
}
5140
}
5241

53-
private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException, UnsupportedEncodingException {
42+
private SplitChange processSplitChange(SplitChange splitChange, long changeNumber) throws NoSuchAlgorithmException {
5443
SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange);
5544
// if the till is less than storage CN and different from the default till ignore the change
5645
if (splitChangeToProcess.till < changeNumber && splitChangeToProcess.till != -1) {

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import io.split.client.impressions.ImpressionListener;
44
import io.split.client.impressions.ImpressionsManager;
5+
import io.split.client.utils.FileTypeEnum;
56
import io.split.integrations.IntegrationsConfig;
67
import io.split.storages.enums.OperationMode;
78
import io.split.storages.enums.StorageMode;
89
import org.apache.hc.core5.http.HttpHost;
910
import pluggable.CustomStorageWrapper;
1011

1112
import java.io.IOException;
13+
import java.io.InputStream;
1214
import java.util.Properties;
1315
import java.util.concurrent.ThreadFactory;
1416

@@ -49,6 +51,8 @@ public class SplitClientConfig {
4951
private final int _maxStringLength;
5052
private final boolean _destroyOnShutDown;
5153
private final String _splitFile;
54+
private final FileTypeEnum _fileType;
55+
private final InputStream _inputStream;
5256
private final String _segmentDirectory;
5357
private final IntegrationsConfig _integrationsConfig;
5458
private final boolean _streamingEnabled;
@@ -81,7 +85,6 @@ public class SplitClientConfig {
8185
public static String splitSdkVersion;
8286
private final long _lastSeenCacheSize;
8387

84-
8588
public static Builder builder() {
8689
return new Builder();
8790
}
@@ -111,6 +114,8 @@ private SplitClientConfig(String endpoint,
111114
int maxStringLength,
112115
boolean destroyOnShutDown,
113116
String splitFile,
117+
FileTypeEnum fileType,
118+
InputStream inputStream,
114119
String segmentDirectory,
115120
IntegrationsConfig integrationsConfig,
116121
boolean streamingEnabled,
@@ -159,6 +164,8 @@ private SplitClientConfig(String endpoint,
159164
_maxStringLength = maxStringLength;
160165
_destroyOnShutDown = destroyOnShutDown;
161166
_splitFile = splitFile;
167+
_fileType = fileType;
168+
_inputStream = inputStream;
162169
_segmentDirectory = segmentDirectory;
163170
_integrationsConfig = integrationsConfig;
164171
_streamingEnabled = streamingEnabled;
@@ -301,6 +308,14 @@ public String splitFile() {
301308
return _splitFile;
302309
}
303310

311+
public FileTypeEnum fileType() {
312+
return _fileType;
313+
}
314+
315+
public InputStream inputStream(){
316+
return _inputStream;
317+
}
318+
304319
public String segmentDirectory() {
305320
return _segmentDirectory;
306321
}
@@ -394,6 +409,8 @@ public static final class Builder {
394409
private int _maxStringLength = 250;
395410
private boolean _destroyOnShutDown = true;
396411
private String _splitFile = null;
412+
private FileTypeEnum _fileType = null;
413+
private InputStream _inputStream = null;
397414
private String _segmentDirectory = null;
398415
private IntegrationsConfig _integrationsConfig = null;
399416
private boolean _streamingEnabled = true;
@@ -748,6 +765,12 @@ public Builder splitFile(String splitFile) {
748765
return this;
749766
}
750767

768+
public Builder splitFile(InputStream inputStream, FileTypeEnum fileType) {
769+
_fileType = fileType;
770+
_inputStream = inputStream;
771+
return this;
772+
}
773+
751774
/**
752775
* Set the location of the directory where are the segment json files for localhost mode.
753776
* This setting is optional.
@@ -1005,6 +1028,8 @@ public SplitClientConfig build() {
10051028
_maxStringLength,
10061029
_destroyOnShutDown,
10071030
_splitFile,
1031+
_fileType,
1032+
_inputStream,
10081033
_segmentDirectory,
10091034
_integrationsConfig,
10101035
_streamingEnabled,

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

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

3+
import com.google.common.io.Files;
34
import io.split.client.dtos.Metadata;
45
import io.split.client.events.EventsSender;
56
import io.split.client.events.EventsStorage;
@@ -30,7 +31,11 @@
3031
import io.split.client.interceptors.GzipDecoderResponseInterceptor;
3132
import io.split.client.interceptors.GzipEncoderRequestInterceptor;
3233
import io.split.client.interceptors.SdkMetadataInterceptorFilter;
34+
import io.split.client.utils.FileInputStreamProvider;
35+
import io.split.client.utils.FileTypeEnum;
36+
import io.split.client.utils.InputStreamProvider;
3337
import io.split.client.utils.SDKMetadata;
38+
import io.split.client.utils.StaticContentInputStreamProvider;
3439
import io.split.engine.SDKReadinessGates;
3540
import io.split.engine.common.ConsumerSyncManager;
3641
import io.split.engine.common.ConsumerSynchronizer;
@@ -99,6 +104,7 @@
99104
import pluggable.CustomStorageWrapper;
100105

101106
import java.io.IOException;
107+
import java.io.InputStream;
102108
import java.net.InetAddress;
103109
import java.net.URI;
104110
import java.net.URISyntaxException;
@@ -111,6 +117,8 @@
111117

112118
public class SplitFactoryImpl implements SplitFactory {
113119
private static final Logger _log = LoggerFactory.getLogger(SplitFactory.class);
120+
private static final String LEGACY_LOG_MESSAGE = "The sdk initialize in localhost mode using Legacy file. The splitFile or " +
121+
"inputStream doesn't add it to the config.";
114122
private final static long SSE_CONNECT_TIMEOUT = 30000;
115123
private final static long SSE_SOCKET_TIMEOUT = 70000;
116124

@@ -366,16 +374,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
366374
config.getThreadFactory());
367375

368376
// SplitFetcher
369-
SplitChangeFetcher splitChangeFetcher;
370-
String splitFile = config.splitFile();
371-
if (splitFile != null && splitFile.toLowerCase().endsWith(".json")){
372-
splitChangeFetcher = new JsonLocalhostSplitChangeFetcher(config.splitFile());
373-
} else if (splitFile != null && !splitFile.isEmpty() && (splitFile.endsWith(".yaml") || splitFile.endsWith(".yml"))) {
374-
splitChangeFetcher = new YamlLocalhostSplitChangeFetcher(splitFile);
375-
} else {
376-
splitChangeFetcher = new LegacyLocalhostSplitChangeFetcher(config.splitFile());
377-
}
378-
377+
SplitChangeFetcher splitChangeFetcher = createSplitChangeFetcher(config);
379378
SplitParser splitParser = new SplitParser();
380379

381380
_splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer);
@@ -643,4 +642,53 @@ private UniqueKeysTracker createUniqueKeysTracker(SplitClientConfig config){
643642
}
644643
return null;
645644
}
645+
646+
private SplitChangeFetcher createSplitChangeFetcher(SplitClientConfig splitClientConfig) {
647+
String splitFile = splitClientConfig.splitFile();
648+
InputStream inputStream = splitClientConfig.inputStream();
649+
FileTypeEnum fileType = splitClientConfig.fileType();
650+
InputStreamProvider inputStreamProvider;
651+
if (splitFile != null || !isInputStreamConfigValid(inputStream, fileType)) {
652+
if (splitFile == null) {
653+
_log.warn("The InputStream config is invalid");
654+
}
655+
fileType = getFileTypeFromFileName(splitFile);
656+
inputStreamProvider = new FileInputStreamProvider(splitFile);
657+
} else {
658+
inputStreamProvider = new StaticContentInputStreamProvider(inputStream);
659+
}
660+
try {
661+
switch (fileType){
662+
case JSON:
663+
return new JsonLocalhostSplitChangeFetcher(inputStreamProvider);
664+
case YAML:
665+
return new YamlLocalhostSplitChangeFetcher(inputStreamProvider);
666+
default:
667+
_log.warn(LEGACY_LOG_MESSAGE);
668+
return new LegacyLocalhostSplitChangeFetcher(splitFile);
669+
}
670+
} catch (Exception e) {
671+
_log.warn(String.format("There was no file named %s found. " +
672+
"We created a split client that returns default treatments for all feature flags for all of your users. " +
673+
"If you wish to return a specific treatment for a feature flag, enter the name of that feature flag name and " +
674+
"treatment name separated by whitespace in %s; one pair per line. Empty lines or lines starting with '#' are " +
675+
"considered comments",
676+
splitFile, splitFile), e);
677+
}
678+
_log.warn(LEGACY_LOG_MESSAGE);
679+
return new LegacyLocalhostSplitChangeFetcher(splitFile);
680+
}
681+
682+
private Boolean isInputStreamConfigValid(InputStream inputStream, FileTypeEnum fileType) {
683+
return inputStream != null && fileType != null;
684+
}
685+
686+
private FileTypeEnum getFileTypeFromFileName(String fileName) {
687+
try {
688+
return FileTypeEnum.valueOf(Files.getFileExtension(fileName).toUpperCase());
689+
} catch (Exception e) {
690+
return FileTypeEnum.LEGACY;
691+
}
692+
693+
}
646694
}

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

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
import io.split.client.dtos.Split;
66
import io.split.client.dtos.SplitChange;
77
import io.split.client.dtos.Status;
8+
import io.split.client.utils.InputStreamProvider;
89
import io.split.client.utils.LocalhostConstants;
910
import io.split.engine.common.FetchOptions;
1011
import io.split.engine.experiments.SplitChangeFetcher;
12+
1113
import org.slf4j.Logger;
1214
import org.slf4j.LoggerFactory;
1315
import org.yaml.snakeyaml.Yaml;
1416

15-
import java.io.File;
16-
import java.io.FileNotFoundException;
17-
import java.io.FileReader;
1817
import java.util.ArrayList;
1918
import java.util.HashMap;
2019
import java.util.List;
@@ -23,21 +22,20 @@
2322

2423
import static io.split.client.utils.LocalhostSanitizer.createCondition;
2524

26-
2725
public class YamlLocalhostSplitChangeFetcher implements SplitChangeFetcher {
2826

2927
private static final Logger _log = LoggerFactory.getLogger(YamlLocalhostSplitChangeFetcher.class);
30-
private final File _splitFile;
28+
private final InputStreamProvider _inputStreamProvider;
3129

32-
public YamlLocalhostSplitChangeFetcher(String filePath) {
33-
_splitFile = new File(filePath);
30+
public YamlLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) {
31+
_inputStreamProvider = inputStreamProvider;
3432
}
3533

3634
@Override
3735
public SplitChange fetch(long since, FetchOptions options) {
3836
try {
3937
Yaml yaml = new Yaml();
40-
List<Map<String, Map<String, Object>>> yamlSplits = yaml.load(new FileReader(_splitFile));
38+
List<Map<String, Map<String, Object>>> yamlSplits = yaml.load(_inputStreamProvider.get());
4139
SplitChange splitChange = new SplitChange();
4240
splitChange.splits = new ArrayList<>();
4341
for(Map<String, Map<String, Object>> aSplit : yamlSplits) {
@@ -76,17 +74,8 @@ public SplitChange fetch(long since, FetchOptions options) {
7674
splitChange.till = since;
7775
splitChange.since = since;
7876
return splitChange;
79-
} catch (FileNotFoundException f) {
80-
_log.warn(String.format("There was no file named %s found. We created a split client that returns default treatments " +
81-
"for all feature flags for all of your users. If you wish to return a specific treatment for a feature flag, " +
82-
"enter the name of that feature flag name and treatment name separated by whitespace in %s; one pair per line. " +
83-
"Empty lines or lines starting with '#' are considered comments",
84-
_splitFile.getPath(), _splitFile.getPath()), f);
85-
throw new IllegalStateException("Problem fetching splitChanges: " + f.getMessage(), f);
8677
} catch (Exception e) {
87-
_log.warn(String.format("Problem to fetch split change using the file %s",
88-
_splitFile.getPath()), e);
89-
throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e);
78+
throw new IllegalStateException("Problem fetching splitChanges using a yaml file: " + e.getMessage(), e);
9079
}
9180
}
9281
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.split.client.exceptions;
2+
3+
public class InputStreamProviderException extends Exception {
4+
5+
public InputStreamProviderException(String message) {
6+
super(message);
7+
}
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.split.client.utils;
2+
3+
import io.split.client.exceptions.InputStreamProviderException;
4+
5+
import java.io.FileInputStream;
6+
import java.io.FileNotFoundException;
7+
import java.io.InputStream;
8+
9+
public class FileInputStreamProvider implements InputStreamProvider {
10+
11+
private final String _fileName;
12+
13+
public FileInputStreamProvider(String fileName) {
14+
_fileName = fileName;
15+
}
16+
17+
@Override
18+
public InputStream get() throws InputStreamProviderException {
19+
try {
20+
return new FileInputStream(_fileName);
21+
} catch (FileNotFoundException f) {
22+
throw new InputStreamProviderException(String.format("Problem fetching splitChanges using file named %s: %s",
23+
_fileName, f.getMessage()));
24+
}
25+
}
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.split.client.utils;
2+
3+
public enum FileTypeEnum {
4+
LEGACY,
5+
YAML,
6+
JSON
7+
}

0 commit comments

Comments
 (0)