diff --git a/.circleci/config.yml b/.circleci/config.yml index 9d09ffa0d..7e6e84da4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,6 +7,8 @@ workflows: test: jobs: - build-linux + - test-linux-jdk7: + name: Java 7 - Linux - OpenJDK - test-linux: name: Java 8 - Linux - OpenJDK docker-image: circleci/openjdk:8 @@ -76,6 +78,30 @@ jobs: - store_artifacts: path: ~/junit + test-linux-jdk7: + # This build uses LaunchDarkly's ld-jdk7-jdk8 image which has both OpenJDK 7 and + # OpenJDK 8 installed, with 8 being the default that is used to run Gradle. + # See: https://github.com/launchdarkly/sdks-ci-docker + docker: + - image: ldcircleci/ld-jdk7-jdk8 + - image: redis + steps: + - checkout + - run: cp gradle.properties.example gradle.properties + - run: + name: Run tests + command: ./gradlew -i -Dorg.gradle.project.overrideJavaHome=$JDK7_HOME test sourcesJar javadocJar + - run: + name: Save test results + command: | + mkdir -p ~/junit/ + find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; + when: always + - store_test_results: + path: ~/junit + - store_artifacts: + path: ~/junit + build-test-windows: executor: name: win/vs2019 diff --git a/build.gradle b/build.gradle index 5df30ac67..02ba7e1c6 100644 --- a/build.gradle +++ b/build.gradle @@ -58,8 +58,8 @@ ext.versions = [ "gson": "2.7", "guava": "19.0", "jodaTime": "2.9.3", - "okhttp": "3.14.9", // specify this for the SDK build instead of relying on the transitive dependency from okhttp-eventsource - "okhttpEventsource": "1.11.0", + "okhttp": "3.12.2", // specify this for the SDK build instead of relying on the transitive dependency from okhttp-eventsource + "okhttpEventsource": "1.11.2", "slf4j": "1.7.21", "snakeyaml": "1.26", "jedis": "2.9.0" @@ -445,3 +445,34 @@ gitPublish { } commitMessage = 'publishing javadocs' } + +// Overriding JAVA_HOME/executable paths allows us to build/test under Java 7 even though +// Gradle itself has to run in Java 8+. + +tasks.withType(AbstractCompile) { + options.with { + if (overrideJavaHome != "") { + System.out.println("Building with JAVA_HOME=" + overrideJavaHome) + fork = true + forkOptions.javaHome = file(overrideJavaHome) + } + } +} + +tasks.withType(Javadoc) { + if (overrideJavaHome != "") { + executable = new File(overrideJavaHome, "bin/javadoc") + } +} + +tasks.withType(Test) { + if (overrideJavaHome != "") { + executable = new File(overrideJavaHome, "bin/java") + } +} + +tasks.withType(JavaExec) { + if (overrideJavaHome != "") { + executable = new File(overrideJavaHome, "bin/java") + } +} diff --git a/gradle.properties b/gradle.properties index 870f8a050..9a505c15b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,8 @@ version=4.14.3 ossrhUsername= ossrhPassword= +# See build.gradle +overrideJavaHome= + # See https://github.com/gradle/gradle/issues/11308 regarding the following property systemProp.org.gradle.internal.publish.checksums.insecure=true diff --git a/gradle.properties.example b/gradle.properties.example index 058697d17..053018541 100644 --- a/gradle.properties.example +++ b/gradle.properties.example @@ -6,3 +6,6 @@ signing.password = SIGNING_PASSWORD signing.secretKeyRingFile = SECRET_RING_FILE ossrhUsername = launchdarkly ossrhPassword = OSSHR_PASSWORD + +# See build.gradle +overrideJavaHome= diff --git a/src/main/java/com/launchdarkly/client/LDClientInterface.java b/src/main/java/com/launchdarkly/client/LDClientInterface.java index 80db0168b..775f25934 100644 --- a/src/main/java/com/launchdarkly/client/LDClientInterface.java +++ b/src/main/java/com/launchdarkly/client/LDClientInterface.java @@ -50,11 +50,6 @@ public interface LDClientInterface extends Closeable { /** * Tracks that a user performed an event, and provides an additional numeric value for custom metrics. - *

- * As of this version’s release date, the LaunchDarkly service does not support the {@code metricValue} - * parameter. As a result, calling this overload of {@code track} will not yet produce any different - * behavior from calling {@link #track(String, LDUser, JsonElement)} without a {@code metricValue}. - * Refer to the SDK reference guide for the latest status. * * @param eventName the name of the event * @param user the user that performed the event @@ -70,11 +65,6 @@ public interface LDClientInterface extends Closeable { /** * Tracks that a user performed an event, and provides an additional numeric value for custom metrics. - *

- * As of this version’s release date, the LaunchDarkly service does not support the {@code metricValue} - * parameter. As a result, calling this overload of {@code track} will not yet produce any different - * behavior from calling {@link #trackData(String, LDUser, LDValue)} without a {@code metricValue}. - * Refer to the SDK reference guide for the latest status. * * @param eventName the name of the event * @param user the user that performed the event diff --git a/src/main/java/com/launchdarkly/client/value/LDValue.java b/src/main/java/com/launchdarkly/client/value/LDValue.java index 996e7b41d..20cd622e8 100644 --- a/src/main/java/com/launchdarkly/client/value/LDValue.java +++ b/src/main/java/com/launchdarkly/client/value/LDValue.java @@ -3,6 +3,7 @@ import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.collect.Ordering; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.annotations.JsonAdapter; @@ -468,7 +469,9 @@ public int hashCode() { return ah; case OBJECT: int oh = 0; - for (String name: keys()) { + // We sort the keys here to guarantee ordering equivalence with LDValueJsonElement + // wrapping JsonObjects. + for (String name: Ordering.natural().immutableSortedCopy(keys())) { oh = (oh * 31 + name.hashCode()) * 31 + get(name).hashCode(); } return oh; diff --git a/src/test/java/com/launchdarkly/client/LDUserTest.java b/src/test/java/com/launchdarkly/client/LDUserTest.java index 49216608b..d8d771ae4 100644 --- a/src/test/java/com/launchdarkly/client/LDUserTest.java +++ b/src/test/java/com/launchdarkly/client/LDUserTest.java @@ -7,6 +7,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import com.launchdarkly.client.value.LDValue; @@ -29,6 +30,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; @SuppressWarnings("javadoc") public class LDUserTest { @@ -344,8 +346,14 @@ private Map getUserPropertiesJsonMap() { @Test public void defaultJsonEncodingHasPrivateAttributeNames() { LDUser user = new LDUser.Builder("userkey").privateName("x").privateEmail("y").build(); - String expected = "{\"key\":\"userkey\",\"name\":\"x\",\"email\":\"y\",\"privateAttributeNames\":[\"name\",\"email\"]}"; - assertEquals(defaultGson.fromJson(expected, JsonElement.class), defaultGson.toJsonTree(user)); + JsonObject serialized = defaultGson.toJsonTree(user).getAsJsonObject(); + assertEquals(serialized.get("key").getAsJsonPrimitive().getAsString(), "userkey"); + assertEquals(serialized.get("name").getAsJsonPrimitive().getAsString(), "x"); + assertEquals(serialized.get("email").getAsJsonPrimitive().getAsString(), "y"); + JsonArray privateAttrs = serialized.get("privateAttributeNames").getAsJsonArray(); + assertEquals(privateAttrs.size(), 2); + assertTrue(privateAttrs.contains(new JsonPrimitive("name"))); + assertTrue(privateAttrs.contains(new JsonPrimitive("email"))); } @Test diff --git a/src/test/java/com/launchdarkly/client/TestHttpUtil.java b/src/test/java/com/launchdarkly/client/TestHttpUtil.java index 79fd8f30a..b6a850557 100644 --- a/src/test/java/com/launchdarkly/client/TestHttpUtil.java +++ b/src/test/java/com/launchdarkly/client/TestHttpUtil.java @@ -17,7 +17,6 @@ import okhttp3.mockwebserver.MockWebServer; import okhttp3.tls.HandshakeCertificates; import okhttp3.tls.HeldCertificate; -import okhttp3.tls.internal.TlsUtil; class TestHttpUtil { static MockWebServer makeStartedServer(MockResponse... responses) throws IOException { @@ -72,9 +71,14 @@ public ServerWithCert() throws IOException, GeneralSecurityException { .certificateAuthority(1) .commonName(hostname) .addSubjectAlternativeName(hostname) + .rsa2048() .build(); - HandshakeCertificates hc = TlsUtil.localhost(); + HandshakeCertificates hc = new HandshakeCertificates.Builder() + .addPlatformTrustedCertificates() + .heldCertificate(cert) + .addTrustedCertificate(cert.certificate()) + .build(); socketFactory = hc.sslSocketFactory(); trustManager = hc.trustManager(); diff --git a/src/test/java/com/launchdarkly/client/value/LDValueTest.java b/src/test/java/com/launchdarkly/client/value/LDValueTest.java index e4397a676..87b0d70f1 100644 --- a/src/test/java/com/launchdarkly/client/value/LDValueTest.java +++ b/src/test/java/com/launchdarkly/client/value/LDValueTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static org.junit.Assert.assertEquals; @@ -277,7 +278,7 @@ public void objectKeysCanBeEnumerated() { for (String key: LDValue.buildObject().put("1", LDValue.of("x")).put("2", LDValue.of("y")).build().keys()) { keys.add(key); } - keys.sort(null); + Collections.sort(keys); assertEquals(ImmutableList.of("1", "2"), keys); } @@ -287,7 +288,7 @@ public void objectValuesCanBeEnumerated() { for (LDValue value: LDValue.buildObject().put("1", LDValue.of("x")).put("2", LDValue.of("y")).build().values()) { values.add(value.stringValue()); } - values.sort(null); + Collections.sort(values); assertEquals(ImmutableList.of("x", "y"), values); }