Closed salvisbh closed 5 years ago
@salvisbh we are looking into this and will get back to you shortly.
In the mean time, I just wanted to make sure you are using the new format:
- split_1:
keys: user_b
treatment: 'off'
config: '{ "size" : 20 }'
- split_1:
keys: [user_a]
treatment: 'on'
- split_2:
keys: user_b
treatment: 'off'
config: '{ "size" : 44 }'
- splitWithKeys:
keys: [user_1, user_2, user_3]
treatment: 'v1'
config: '{ "size" : 44 }'
- splitWithNoKeys:
treatment: 'v2'
config: '{ "size" : 999 }'
We are in the process of updating the documentation to reflect this new format. Legacy format should still work though.
Alright, I can confirm the code works just fine for us (the second test you provided is correct:
The reason for the failure is that you are providing a location where the yaml file does not exist. The reason being is that you are referencing a file (\C:\ieu\Projects\GITHUB\splitio\java-client\client\target\test-classes\split.yaml
) that is part of Split source code but unless you are within the Open Source SplitClient project, the bundle of java SplitClient does not get shipped with the test code. It's only part of the source code.
Download this sample file: https://github.com/splitio/java-client/blob/master/client/src/test/resources/split.yaml , place it somewhere where you can reference it's location, and replace it here:
@Test
public void localhostModeWithAbsolutePathYaml() throws Exception {
//-- arrange
String file = getClass().getClassLoader().getResource(<ABSOLUTE_LOCATION_OF_THE_FILE>).getFile();
//-- act
SplitClientConfig clientConfig = SplitClientConfig.builder()
.splitFile(file)
.build();
SplitFactory splitFactory = SplitFactoryBuilder.local(clientConfig);
SplitClient client = splitFactory.client();
//-- assert
assertThat(client.getTreatment("user_a", "split_1"), is(equalTo("on")));
}
Let me know how if that works for you.
Hello Split-Friends Unfortunately, it seems that the library behaves incorrectly in the Windows environment, as shown by the test cases. I didn't create the test case in my module, but directly in the Split-java-client cloned from Github, because there is no explicit test case for the SplitFactoryBuilder, unlike most other classes. Accordingly the file split.yaml is available locally and in the right structure. To check if split.yaml really exists, I extended the test case. ` @Test public void localhostModeWithAbsolutePathYaml() throws Exception {
//-- arrange
URL testTogglesUrl = getClass().getClassLoader().getResource(SplitClientConfig.LOCALHOST_DEFAULT_FILE);
String absolutePath = Paths.get(testTogglesUrl.toURI()).toFile().getAbsolutePath();
File yaml = new File(absolutePath);
boolean yamlExists = yaml.exists();
//-- act
SplitClientConfig clientConfig = SplitClientConfig.builder()
.splitFile(absolutePath)
.build();
SplitFactory splitFactory = SplitFactoryBuilder.local(clientConfig);
SplitClient client = splitFactory.client();
//-- assert
Assert.assertTrue(yamlExists);
assertThat(client.getTreatment("user_a", "split_1"), is(equalTo("on")));
}
` The following screenshot shows that the file exists.
I did the tests with both JDK 1.8 and OpenJDK. On the Windows platform, the error always occurs. During debugging we discovered that the constructor of java.io.File is not called correctly in class io.split.client.AbstractLocalhostSplitFile. The empty directory (parent) causes a separator to precede the FQN of split.yaml via fs.getDefaultParent() (File, line 321).
With the following adjustment in AbstractLocalhostSplitFile, at least the test case for the absolute path works correctly on all environments. ` public AbstractLocalhostSplitFile(LocalhostSplitFactory splitFactory, String directory, String fileName) throws IOException { Preconditions.checkNotNull(directory); Preconditions.checkNotNull(fileName);
_splitFactory = Preconditions.checkNotNull(splitFactory);
// If no directory is set, instantiate the file without parent, otherwise the path separator is inserted
// before the filename in the java.io.File.File(java.lang.String, java.lang.String) class (see line 319).
_file = (directory.length() > 0) ?
new File(directory, fileName) :
new File(fileName);
// Original-Code
// _file = new File(directory, fileName);
_watcher = FileSystems.getDefault().newWatchService();
_stop = new AtomicBoolean(false);
}
` In summary, what is the real problem?
Version 3.0.x of the java-client had the following method signature for local tests in SplitFactoryBuilder
public static SplitFactory local(String home) throws IOException { return new LocalhostSplitFactory(home); }
Version 3.1.x breaks the signature without deprecation and now requires a SplitClientConfig instance instead of a directory.
`
/**
@throws IOException if there were problems reading the override file from disk.
*/
public static SplitFactory local(SplitClientConfig config) throws IOException {
return LocalhostSplitFactory.createLocalhostSplitFactory(config);
}
The idea behind replacing the string with the SplitClientConfig is cool, but its implementation is problematic. The builder expects the location of the new yaml file.
/**
@return this builder
*/
public Builder splitFile(String splitFile) {
_splitFile = splitFile;
return this;
}
The following test case sets the absolute location WITHOUT file names.
@Test
public void localhostModeWithAbsoluteLocation() throws Exception {
//-- arrange URL testTogglesUrl = getClass().getClassLoader().getResource(SplitClientConfig.LOCALHOST_DEFAULT_FILE); String absolutePath = Paths.get(testTogglesUrl.toURI()).toFile().getAbsolutePath();
File yaml = new File(absolutePath); String location = yaml.getParent();
//-- act SplitClientConfig clientConfig = SplitClientConfig.builder() .splitFile(location) .build();
//correct assigned in Debugger SplitFactory splitFactory = SplitFactoryBuilder.local(clientConfig); SplitClient client = splitFactory.client();
//leads to C:\Users\Uxxxx (directory) and .split (filename) in splitFactory //-- assert assertThat(client.getTreatment("user_a", "split_1"), is(equalTo("on")));
} ` Since io.split.client.LocalhostSplitFactory distinguishes between legacy and yaml based on the file extension, the library in the example creates a LegacyLocalhostSplitFile with the location required by the API, which is of course wrong.
The easiest way to solve the problem would probably be to use Builder.splitFile(File splitFile) as parameter and expect a file instead of a string. Then the library would have the ability to determine the type (path, file) and would be more fault tolerant.
In summary, it must be possible to set a path or file name (absolute or relative) for yaml or .split as in version 3.0.x, otherwise a build fails in the continuous delivery pipeline. There is no userhome with source code in the CD pipeline.
Incorrectly closed by manipulation. Please excuse my lack of experience with Github.
Thanks again for the detailed research. We'll look into this again and get back to you.
@salvisbh we tested your patch under the branch localhost-windows
using windows platform.
It passed the build and also our manual test for both default behavior using legacy .split file as well as a custom location using SplitClientConfig.builder().splitFile(...)
.
I'll be publishing a minor release within 24 hours.
Please take a look and let us know if it fixes the issues you ran into.
Let me know if this latest release address the issue.
Hello Patricio, your new version 3.1.1 runs stable in our Continous Delivery Pipeline. Many thanks for the fix.
I see that this error is fixed but I actually have a similar issue while trying to use split.io in localhost mode. When I try to load the file based on the solution provided above I keep getting the error below.
2019-09-06 09:08:04 INFO [main] LocalhostSplitFactory:39 Starting Split in localhost mode with file at /Users/madhurmehta/Desktop/repo/deejay/deejay-ecommerce-library/out/test/resources/config/split_io_localhost.yaml
java.lang.UnsupportedOperationException: Modifier not supported
at sun.nio.fs.PollingWatchService.register(PollingWatchService.java:112)
at sun.nio.fs.UnixPath.register(UnixPath.java:897)
at io.split.client.AbstractLocalhostSplitFile.registerWatcher(AbstractLocalhostSplitFile.java:54)
at io.split.client.LocalhostSplitFactory.<init>(LocalhostSplitFactory.java:50)
at io.split.client.LocalhostSplitFactory.createLocalhostSplitFactory(LocalhostSplitFactory.java:32)
at io.split.client.SplitFactoryBuilder.local(SplitFactoryBuilder.java:70)
at com.deliverr.deejay.ecommerce.commands.ProcessOrderCreateCommandTest.setUp(ProcessOrderCreateCommandTest.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:133)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)`
Below is the code that I added to get SplitClient working
URL splitFileURL = getClass().getClassLoader().getResource("./config/split_io_localhost.yaml");
assert splitFileURL != null;
String splitFilePath = Paths.get(splitFileURL.toURI()).toFile().getAbsolutePath();
SplitClientConfig splitClientConfig = SplitClientConfig.builder().splitFile(splitFilePath).build();
SplitFactory splitFactory = SplitFactoryBuilder.local(splitClientConfig);
this.splitClient = splitFactory.client();```
Any help at this point will be really appreciated.
https://github.com/splitio/java-client/issues/101
Hi @maddy2308
Could you give us more details about the environment you are running from? Is it Mac, Linux or windows?
With version 3.1.0 of the java-client it is no longer possible to use
SplitFactoryBuilder.local(myTestFilePath)
Instead, a new SplitClientConfig instance must be passed to the local - static method. In our CD environment we have to be able to set the path for the local test file, because we cannot store a configuration file in the userhome during the test. No client can be created with configured split.yaml, regardless of whether the path is configured absolute or relative withSplitClientConfig.builder() .splitFile("Testsplits.yaml") .build();
The following error is written to the log:
java.io.FileNotFoundException: \C:\ieu\Projects\GITHUB\splitio\java-client\client\target\test-classes\split.yaml (The filename, directory name, or volume label syntax is incorrect) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileReader.<init>(FileReader.java:72) at io.split.client.YamlLocalhostSplitFile.readOnSplits(YamlLocalhostSplitFile.java:27) at io.split.client.LocalhostSplitFactory.<init>(LocalhostSplitFactory.java:46) at io.split.client.LocalhostSplitFactory.createLocalhostSplitFactory(LocalhostSplitFactory.java:32) at io.split.client.SplitFactoryBuilder.local(SplitFactoryBuilder.java:70) at io.split.client.SplitFactoryBuilderTest.localhostModeWithAbsolutePathYaml(SplitFactoryBuilderTest.java:44)
Test for reproducing the Exception: `public class SplitFactoryBuilderTest {
}`