Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 62ae654

Browse files
prepare 5.1.1 release (#215)
1 parent c4ae35d commit 62ae654

21 files changed

+260
-57
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ This is a major rewrite that introduces a cleaner API design, adds new features,
8787
- The Redis integration is no longer built into the main SDK library. See: https://github.com/launchdarkly/java-server-sdk-redis
8888
- The deprecated New Relic integration has been removed.
8989

90+
## [4.14.4] - 2020-09-28
91+
### Fixed:
92+
- Restored compatibility with Java 7. A transitive dependency that required Java 8 had accidentally been included, and the CI build did not detect this because the tests were being run in Java 8 even though the compiler target was 7. CI builds now verify that the SDK really can run in Java 7. This fix is only for 4.x; the 5.x SDK still does not support Java 7.
93+
- Bumped OkHttp version to 3.12.12 to avoid a crash on Java 8u252.
94+
- Removed an obsolete comment that said the `trackMetric` method was not yet supported by the LaunchDarkly service; it is.
95+
9096
## [4.14.3] - 2020-09-03
9197
### Fixed:
9298
- Bump SnakeYAML from 1.19 to 1.26 to address CVE-2017-18640. The SDK only parses YAML if the application has configured the SDK with a flag data file, so it's unlikely this CVE would affect SDK usage as it would require configuration and access to a local file.

build.gradle

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ ext.versions = [
7171
"commonsCodec": "1.10",
7272
"gson": "2.7",
7373
"guava": "28.2-jre",
74-
"jackson": "2.10.0",
74+
"jackson": "2.11.2",
7575
"launchdarklyJavaSdkCommon": "1.0.0",
7676
"okhttp": "4.8.1", // specify this for the SDK build instead of relying on the transitive dependency from okhttp-eventsource
7777
"okhttpEventsource": "2.3.1",
@@ -96,7 +96,9 @@ libraries.internal = [
9696
// Add dependencies to "libraries.external" that are exposed in our public API, or that have
9797
// global state that must be shared between the SDK and the caller.
9898
libraries.external = [
99-
"org.slf4j:slf4j-api:${versions.slf4j}"
99+
"org.slf4j:slf4j-api:${versions.slf4j}",
100+
"com.fasterxml.jackson.core:jackson-core:${versions.jackson}",
101+
"com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
100102
]
101103

102104
// Add dependencies to "libraries.test" that are used only in unit tests.
@@ -169,6 +171,7 @@ shadowJar {
169171

170172
dependencies {
171173
exclude(dependency('org.slf4j:.*:.*'))
174+
exclude(dependency('com.fasterxml.jackson.core:.*:.*'))
172175
}
173176

174177
// Kotlin metadata for shaded classes should not be included - it confuses IDEs
@@ -198,12 +201,16 @@ task shadowJarAll(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJ
198201
description = "Builds a Shaded fat jar including SLF4J"
199202
from(project.convention.getPlugin(JavaPluginConvention).sourceSets.main.output)
200203
configurations = [project.configurations.runtimeClasspath]
201-
exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA')
202204

205+
exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA')
203206
exclude '**/*.kotlin_metadata'
204207
exclude '**/*.kotlin_module'
205208
exclude '**/*.kotlin_builtins'
206209

210+
dependencies {
211+
exclude(dependency('com.fasterxml.jackson.core:.*:.*'))
212+
}
213+
207214
// doFirst causes the following steps to be run during Gradle's execution phase rather than the
208215
// configuration phase; this is necessary because they access the build products
209216
doFirst {
@@ -384,7 +391,9 @@ def addOsgiManifest(jarTask, List<Configuration> importConfigs, List<Configurati
384391
}
385392

386393
def bundleImport(packageName, importVersion, versionLimit) {
387-
packageName + ";version=\"[" + importVersion + "," + versionLimit + ")\""
394+
def optional = packageName.startsWith("com.fasterxml.jackson")
395+
packageName + ";version=\"[" + importVersion + "," + versionLimit + ")\"" +
396+
(optional ? ";resolution:=optional" : "")
388397
}
389398

390399
def bundleExport(packageName, exportVersion) {
@@ -613,5 +622,11 @@ gitPublish {
613622
contents {
614623
from javadoc
615624
}
625+
preserve {
626+
// There's a dummy .circleci/config.yml file on the gh-pages branch so CircleCI won't
627+
// complain when it sees a commit there. The git-publish plugin would delete that file if
628+
// we didn't protect it here.
629+
include '.circleci/config.yml'
630+
}
616631
commitMessage = 'publishing javadocs'
617632
}

packaging-test/Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@ export TEMP_BUNDLE_DIR=$(FELIX_DIR)/app-bundles
3636
# for OSGi, which is currently true; if that weren't true, we would have to do something different
3737
# to put them on the system classpath in the OSGi test.
3838
RUN_JARS_test-all-jar=$(TEST_APP_JAR) $(SDK_ALL_JAR) \
39-
$(shell ls $(TEMP_DIR)/dependencies-external/gson*.jar 2>/dev/null)
39+
$(shell ls $(TEMP_DIR)/dependencies-external/gson*.jar 2>/dev/null) \
40+
$(shell ls $(TEMP_DIR)/dependencies-external/jackson*.jar 2>/dev/null)
4041
RUN_JARS_test-default-jar=$(TEST_APP_JAR) $(SDK_DEFAULT_JAR) \
4142
$(shell ls $(TEMP_DIR)/dependencies-external/*.jar 2>/dev/null)
4243
RUN_JARS_test-thin-jar=$(TEST_APP_JAR) $(SDK_THIN_JAR) \
4344
$(shell ls $(TEMP_DIR)/dependencies-internal/*.jar 2>/dev/null) \
4445
$(shell ls $(TEMP_DIR)/dependencies-external/*.jar 2>/dev/null)
4546

4647
classes_prepare=echo " checking $(1)..." && jar tf $(1) | grep '\.class$$' >$(TEMP_OUTPUT)
47-
classes_should_contain=echo " should contain $(2)" && grep "^$(1)/[^/]*$$" $(TEMP_OUTPUT) >/dev/null
48-
classes_should_not_contain=echo " should not contain $(2)" && ! grep "^$(1)/[^/]*$$" $(TEMP_OUTPUT)
48+
classes_should_contain=echo " should contain $(2)" && grep "^$(1)/.*\.class$$" $(TEMP_OUTPUT) >/dev/null
49+
classes_should_not_contain=echo " should not contain $(2)" && ! grep "^$(1)/.*\.class$$" $(TEMP_OUTPUT)
4950

5051
verify_sdk_classes= \
5152
$(call classes_should_contain,com/launchdarkly/sdk,com.launchdarkly.sdk) && \
@@ -79,6 +80,8 @@ test-all-jar-classes: $(SDK_ALL_JAR) $(TEMP_DIR)
7980
@$(call classes_should_not_contain,com/launchdarkly/shaded/com/launchdarkly/sdk,shaded SDK classes)
8081
@$(call classes_should_contain,com/launchdarkly/shaded/com/google/gson,shaded Gson)
8182
@$(call classes_should_not_contain,com/google/gson,unshaded Gson)
83+
@$(call classes_should_not_contain,com/fasterxml/jackson,unshaded Jackson)
84+
@$(call classes_should_not_contain,com/launchdarkly/shaded/com/fasterxml/jackson,shaded Jackson)
8285
@$(call classes_should_not_contain,com/launchdarkly/shaded/org/slf4j,shaded SLF4j)
8386

8487
test-default-jar-classes: $(SDK_DEFAULT_JAR) $(TEMP_DIR)
@@ -89,6 +92,8 @@ test-default-jar-classes: $(SDK_DEFAULT_JAR) $(TEMP_DIR)
8992
@$(call classes_should_contain,com/launchdarkly/shaded/com/google/gson,shaded Gson)
9093
@$(call classes_should_not_contain,com/launchdarkly/shaded/org/slf4j,shaded SLF4j)
9194
@$(call classes_should_not_contain,com/google/gson,unshaded Gson)
95+
@$(call classes_should_not_contain,com/fasterxml/jackson,unshaded Jackson)
96+
@$(call classes_should_not_contain,com/launchdarkly/shaded/com/fasterxml/jackson,shaded Jackson)
9297
@$(call classes_should_not_contain,org/slf4j,unshaded SLF4j)
9398

9499
test-thin-jar-classes: $(SDK_THIN_JAR) $(TEMP_DIR)
@@ -121,6 +126,7 @@ $(TEMP_DIR)/dependencies-external: $(TEMP_DIR)/dependencies-all
121126
[ -d $@ ] || mkdir -p $@
122127
cp $(TEMP_DIR)/dependencies-all/slf4j*.jar $@
123128
cp $(TEMP_DIR)/dependencies-all/gson*.jar $@
129+
cp $(TEMP_DIR)/dependencies-all/jackson*.jar $@
124130

125131
$(TEMP_DIR)/dependencies-internal: $(TEMP_DIR)/dependencies-all
126132
[ -d $@ ] || mkdir -p $@

packaging-test/run-non-osgi-test.sh

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/bin/bash
22

3+
set -e
4+
35
function run_test() {
46
rm -f ${TEMP_OUTPUT}
57
touch ${TEMP_OUTPUT}
@@ -8,29 +10,41 @@ function run_test() {
810
grep "TestApp: PASS" ${TEMP_OUTPUT} >/dev/null
911
}
1012

11-
echo ""
12-
echo " non-OSGi runtime test - with Gson"
13-
run_test $@
14-
grep "LDGson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDGson tests but did not" && exit 1)
15-
1613
# It does not make sense to test the "thin" jar without Gson. The SDK uses Gson internally
1714
# and can't work without it; in the default jar and the "all" jar, it has its own embedded
1815
# copy of Gson, but the "thin" jar does not include any third-party dependencies so you must
1916
# provide all of them including Gson.
20-
thin_sdk_regex=".*launchdarkly-java-server-sdk-[^ ]*-thin\\.jar"
17+
echo ""
2118
if [[ "$@" =~ $thin_sdk_regex ]]; then
22-
exit 0
19+
echo " non-OSGi runtime test - without Jackson"
20+
filtered_deps=""
21+
json_jar_regex=".*jackson.*"
22+
for dep in $@; do
23+
if [[ ! "$dep" =~ $json_jar_regex ]]; then
24+
filtered_deps="$filtered_deps $dep"
25+
fi
26+
done
27+
run_test $filtered_deps
28+
grep "skipping LDJackson tests" ${TEMP_OUTPUT} >/dev/null || \
29+
(echo "FAIL: should have skipped LDJackson tests but did not; test setup was incorrect" && exit 1)
30+
else
31+
echo " non-OSGi runtime test - without Gson or Jackson"
32+
filtered_deps=""
33+
json_jar_regex=".*gson.*|.*jackson.*"
34+
for dep in $@; do
35+
if [[ ! "$dep" =~ $json_jar_regex ]]; then
36+
filtered_deps="$filtered_deps $dep"
37+
fi
38+
done
39+
run_test $filtered_deps
40+
grep "skipping LDGson tests" ${TEMP_OUTPUT} >/dev/null || \
41+
(echo "FAIL: should have skipped LDGson tests but did not; test setup was incorrect" && exit 1)
42+
grep "skipping LDJackson tests" ${TEMP_OUTPUT} >/dev/null || \
43+
(echo "FAIL: should have skipped LDJackson tests but did not; test setup was incorrect" && exit 1)
2344
fi
2445

2546
echo ""
26-
echo " non-OSGi runtime test - without Gson"
27-
deps_except_json=""
28-
json_jar_regex=".*gson.*"
29-
for dep in $@; do
30-
if [[ ! "$dep" =~ $json_jar_regex ]]; then
31-
deps_except_json="$deps_except_json $dep"
32-
fi
33-
done
34-
run_test $deps_except_json
35-
grep "skipping LDGson tests" ${TEMP_OUTPUT} >/dev/null || \
36-
(echo "FAIL: should have skipped LDGson tests but did not; test setup was incorrect" && exit 1)
47+
echo " non-OSGi runtime test - with Gson and Jackson"
48+
run_test $@
49+
grep "LDGson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDGson tests but did not" && exit 1)
50+
grep "LDJackson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDJackson tests but did not" && exit 1)

packaging-test/run-osgi-test.sh

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,57 @@
11
#!/bin/bash
22

3+
set -e
4+
5+
# This script uses Felix to run the test application as an OSGi bundle, with or without
6+
# additional bundles to support the optional Gson and Jackson integrations. We are
7+
# verifying that the SDK itself works correctly as an OSGi bundle, and also that its
8+
# imports of other bundles work correctly.
9+
#
10+
# This test is being run in CI using the lowest compatible JDK version. It may not work
11+
# in higher JDK versions due to incompatibilities with the version of Felix we are using.
12+
13+
JAR_DEPS="$@"
14+
315
# We can't test the "thin" jar in OSGi, because some of our third-party dependencies
416
# aren't available as OSGi bundles. That isn't a plausible use case anyway.
517
thin_sdk_regex=".*launchdarkly-java-server-sdk-[^ ]*-thin\\.jar"
6-
if [[ "$@" =~ $thin_sdk_regex ]]; then
18+
if [[ "${JAR_DEPS}" =~ $thin_sdk_regex ]]; then
719
exit 0
820
fi
921

1022
rm -rf ${TEMP_BUNDLE_DIR}
1123
mkdir -p ${TEMP_BUNDLE_DIR}
1224

25+
function copy_deps() {
26+
if [ -n "${JAR_DEPS}" ]; then
27+
cp ${JAR_DEPS} ${TEMP_BUNDLE_DIR}
28+
fi
29+
cp ${FELIX_BASE_BUNDLE_DIR}/* ${TEMP_BUNDLE_DIR}
30+
}
31+
1332
function run_test() {
1433
rm -rf ${FELIX_DIR}/felix-cache
1534
rm -f ${TEMP_OUTPUT}
1635
touch ${TEMP_OUTPUT}
17-
cd ${FELIX_DIR} && java -jar ${FELIX_JAR} -b ${TEMP_BUNDLE_DIR} | tee ${TEMP_OUTPUT}
36+
cd ${FELIX_DIR}
37+
java -jar ${FELIX_JAR} -b ${TEMP_BUNDLE_DIR} | tee ${TEMP_OUTPUT}
1838
grep "TestApp: PASS" ${TEMP_OUTPUT} >/dev/null
1939
}
2040

2141
echo ""
22-
echo " OSGi runtime test - with Gson"
23-
cp $@ ${FELIX_BASE_BUNDLE_DIR}/* ${TEMP_BUNDLE_DIR}
42+
echo " OSGi runtime test - without Gson or Jackson"
43+
copy_deps
44+
rm ${TEMP_BUNDLE_DIR}/*gson*.jar ${TEMP_BUNDLE_DIR}/*jackson*.jar
45+
ls ${TEMP_BUNDLE_DIR}
2446
run_test
25-
grep "LDGson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDGson tests but did not" && exit 1)
47+
grep "skipping LDGson tests" ${TEMP_OUTPUT} >/dev/null || \
48+
(echo "FAIL: should have skipped LDGson tests but did not; test setup was incorrect" && exit 1)
49+
grep "skipping LDJackson tests" ${TEMP_OUTPUT} >/dev/null || \
50+
(echo "FAIL: should have skipped LDJackson tests but did not; test setup was incorrect" && exit 1)
2651

2752
echo ""
28-
echo " OSGi runtime test - without Gson"
29-
rm ${TEMP_BUNDLE_DIR}/*gson*.jar
53+
echo " OSGi runtime test - with Gson and Jackson"
54+
copy_deps
3055
run_test
31-
grep "skipping LDGson tests" ${TEMP_OUTPUT} >/dev/null || \
32-
(echo "FAIL: should have skipped LDGson tests but did not; test setup was incorrect" && exit 1)
56+
grep "LDGson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDGson tests but did not" && exit 1)
57+
grep "LDJackson tests OK" ${TEMP_OUTPUT} >/dev/null || (echo "FAIL: should have run LDJackson tests but did not" && exit 1)

packaging-test/test-app/build.gradle

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,21 @@ allprojects {
2121
group = "com.launchdarkly"
2222
version = "1.0.0"
2323
archivesBaseName = 'test-app-bundle'
24-
sourceCompatibility = 1.7
25-
targetCompatibility = 1.7
24+
sourceCompatibility = 1.8
25+
targetCompatibility = 1.8
2626
}
2727

28+
ext.versions = [
29+
"gson": "2.7",
30+
"jackson": "2.10.0"
31+
]
32+
2833
dependencies {
2934
// Note, the SDK build must have already been run before this, since we're using its product as a dependency
3035
implementation fileTree(dir: "../../build/libs", include: "launchdarkly-java-server-sdk-*-thin.jar")
31-
implementation "com.google.code.gson:gson:2.7"
36+
implementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
37+
implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
38+
implementation "com.google.code.gson:gson:${versions.gson}"
3239
implementation "org.slf4j:slf4j-api:1.7.22"
3340
implementation "org.osgi:osgi_R4_core:1.0"
3441
osgiRuntime "org.slf4j:slf4j-simple:1.7.22"
@@ -47,7 +54,8 @@ jar {
4754
'Import-Package': 'com.launchdarkly.sdk,com.launchdarkly.sdk.json' +
4855
',com.launchdarkly.sdk.server,org.slf4j' +
4956
',org.osgi.framework' +
50-
',com.google.gson;resolution:=optional'
57+
',com.google.gson;resolution:=optional' +
58+
',com.fasterxml.jackson.*;resolution:=optional'
5159
)
5260
}
5361

packaging-test/test-app/src/main/java/testapp/TestApp.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ public static void main(String[] args) throws Exception {
4848
addError("unexpected error in LDGson tests", e);
4949
}
5050

51+
try {
52+
Class.forName("testapp.TestAppJacksonTests"); // see TestAppJacksonTests for why we're loading it in this way
53+
} catch (NoClassDefFoundError e) {
54+
log("skipping LDJackson tests because Jackson is not in the classpath");
55+
} catch (RuntimeException e) {
56+
addError("unexpected error in LDJackson tests", e);
57+
}
58+
5159
if (errors.isEmpty()) {
5260
log("PASS");
5361
} else {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package testapp;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.launchdarkly.sdk.*;
5+
import com.launchdarkly.sdk.json.*;
6+
7+
// This code is in its own class that is loaded dynamically because some of our test scenarios
8+
// involve running TestApp without having Jackson in the classpath, to make sure the SDK does not
9+
// *require* the presence of an external Jackson even though it can interoperate with one.
10+
11+
public class TestAppJacksonTests {
12+
// Use static block so simply loading this class causes the tests to execute
13+
static {
14+
// First try referencing Jackson, so we fail right away if it's not on the classpath
15+
Class<?> c = ObjectMapper.class;
16+
try {
17+
runJacksonTests();
18+
} catch (Exception e) {
19+
// If we've even gotten to this static block, then Jackson itself *is* on the application's
20+
// classpath, so this must be some other kind of classloading error that we do want to
21+
// report. For instance, a NoClassDefFound error for Jackson at this point, if we're in
22+
// OSGi, would mean that the SDK bundle is unable to see the external Jackson classes.
23+
TestApp.addError("unexpected error in LDJackson tests", e);
24+
}
25+
}
26+
27+
public static void runJacksonTests() throws Exception {
28+
ObjectMapper jacksonMapper = new ObjectMapper();
29+
jacksonMapper.registerModule(LDJackson.module());
30+
31+
boolean ok = true;
32+
for (JsonSerializationTestData.TestItem item: JsonSerializationTestData.TEST_ITEMS) {
33+
String actualJson = jacksonMapper.writeValueAsString(item.objectToSerialize);
34+
if (!JsonSerializationTestData.assertJsonEquals(item.expectedJson, actualJson, item.objectToSerialize)) {
35+
ok = false;
36+
}
37+
}
38+
39+
if (ok) {
40+
TestApp.log("LDJackson tests OK");
41+
}
42+
}
43+
}

src/main/java/com/launchdarkly/sdk/server/DefaultEventSender.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.UUID;
1717

1818
import static com.launchdarkly.sdk.server.Util.checkIfErrorIsRecoverableAndLog;
19+
import static com.launchdarkly.sdk.server.Util.concatenateUriPath;
1920
import static com.launchdarkly.sdk.server.Util.configureHttpClientBuilder;
2021
import static com.launchdarkly.sdk.server.Util.describeDuration;
2122
import static com.launchdarkly.sdk.server.Util.getHeadersBuilderFor;
@@ -91,7 +92,7 @@ public Result sendEventData(EventDataKind kind, String data, int eventCount, URI
9192
throw new IllegalArgumentException("kind"); // COVERAGE: unreachable code, those are the only enum values
9293
}
9394

94-
URI uri = eventsBaseUri.resolve(eventsBaseUri.getPath().endsWith("/") ? path : ("/" + path));
95+
URI uri = concatenateUriPath(eventsBaseUri, path);
9596
Headers headers = headersBuilder.build();
9697
RequestBody body = RequestBody.create(data, JSON_CONTENT_TYPE);
9798
boolean mustShutDown = false;

0 commit comments

Comments
 (0)