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

Commit 5bb05d4

Browse files
authored
prepare 4.2.0 release (#129)
1 parent 3360dfb commit 5bb05d4

15 files changed

+375
-117
lines changed

src/main/java/com/launchdarkly/client/Components.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, Fea
113113
FeatureRequestor requestor = new FeatureRequestor(sdkKey, config);
114114
if (config.stream) {
115115
logger.info("Enabling streaming API");
116-
return new StreamProcessor(sdkKey, config, requestor, featureStore);
116+
return new StreamProcessor(sdkKey, config, requestor, featureStore, null);
117117
} else {
118118
logger.info("Disabling streaming API");
119119
logger.warn("You should only disable the streaming API if instructed to do so by LaunchDarkly support");

src/main/java/com/launchdarkly/client/DefaultEventProcessor.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.concurrent.atomic.AtomicLong;
2626

2727
import static com.launchdarkly.client.Util.getRequestBuilder;
28+
import static com.launchdarkly.client.Util.httpErrorMessage;
29+
import static com.launchdarkly.client.Util.isHttpErrorRecoverable;
2830

2931
import okhttp3.MediaType;
3032
import okhttp3.Request;
@@ -386,9 +388,12 @@ private void handleResponse(Response response) {
386388
} catch (ParseException e) {
387389
}
388390
}
389-
if (response.code() == 401) {
391+
if (!isHttpErrorRecoverable(response.code())) {
390392
disabled.set(true);
391-
logger.error("Received 401 error, no further events will be posted since SDK key is invalid");
393+
logger.error(httpErrorMessage(response.code(), "posting events", "some events were dropped"));
394+
// It's "some events were dropped" because we're not going to retry *this* request any more times -
395+
// we only get to this point if we have used up our retry attempts. So the last batch of events was
396+
// lost, even though we will still try to post *other* events in the future.
392397
}
393398
}
394399
}
@@ -530,7 +535,7 @@ private void postEvents(List<EventOutput> eventsOut) {
530535
logger.debug("Event delivery took {} ms, response status {}", endTime - startTime, response.code());
531536
if (!response.isSuccessful()) {
532537
logger.warn("Unexpected response status when posting events: {}", response.code());
533-
if (response.code() >= 500) {
538+
if (isHttpErrorRecoverable(response.code())) {
534539
continue;
535540
}
536541
}

src/main/java/com/launchdarkly/client/FeatureRequestor.java

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,27 @@ static class AllData {
3737
this.config = config;
3838
}
3939

40-
Map<String, FeatureFlag> getAllFlags() throws IOException, InvalidSDKKeyException {
40+
Map<String, FeatureFlag> getAllFlags() throws IOException, HttpErrorException {
4141
String body = get(GET_LATEST_FLAGS_PATH);
4242
return FeatureFlag.fromJsonMap(config, body);
4343
}
4444

45-
FeatureFlag getFlag(String featureKey) throws IOException, InvalidSDKKeyException {
45+
FeatureFlag getFlag(String featureKey) throws IOException, HttpErrorException {
4646
String body = get(GET_LATEST_FLAGS_PATH + "/" + featureKey);
4747
return FeatureFlag.fromJson(config, body);
4848
}
4949

50-
Map<String, Segment> getAllSegments() throws IOException, InvalidSDKKeyException {
50+
Map<String, Segment> getAllSegments() throws IOException, HttpErrorException {
5151
String body = get(GET_LATEST_SEGMENTS_PATH);
5252
return Segment.fromJsonMap(config, body);
5353
}
5454

55-
Segment getSegment(String segmentKey) throws IOException, InvalidSDKKeyException {
55+
Segment getSegment(String segmentKey) throws IOException, HttpErrorException {
5656
String body = get(GET_LATEST_SEGMENTS_PATH + "/" + segmentKey);
5757
return Segment.fromJson(config, body);
5858
}
5959

60-
AllData getAllData() throws IOException, InvalidSDKKeyException {
60+
AllData getAllData() throws IOException, HttpErrorException {
6161
String body = get(GET_LATEST_ALL_PATH);
6262
return config.gson.fromJson(body, AllData.class);
6363
}
@@ -69,7 +69,7 @@ AllData getAllData() throws IOException, InvalidSDKKeyException {
6969
return ret;
7070
}
7171

72-
private String get(String path) throws IOException, InvalidSDKKeyException {
72+
private String get(String path) throws IOException, HttpErrorException {
7373
Request request = getRequestBuilder(sdkKey)
7474
.url(config.baseURI.toString() + path)
7575
.get()
@@ -81,12 +81,7 @@ private String get(String path) throws IOException, InvalidSDKKeyException {
8181
String body = response.body().string();
8282

8383
if (!response.isSuccessful()) {
84-
if (response.code() == 401) {
85-
logger.error("[401] Invalid SDK key when accessing URI: " + request.url());
86-
throw new InvalidSDKKeyException();
87-
}
88-
throw new IOException("Unexpected response when retrieving Feature Flag(s): " + response + " using url: "
89-
+ request.url() + " with body: " + body);
84+
throw new HttpErrorException(response.code());
9085
}
9186
logger.debug("Get flag(s) response: " + response.toString() + " with body: " + body);
9287
logger.debug("Network response: " + response.networkResponse());
@@ -98,10 +93,4 @@ private String get(String path) throws IOException, InvalidSDKKeyException {
9893
return body;
9994
}
10095
}
101-
102-
@SuppressWarnings("serial")
103-
public static class InvalidSDKKeyException extends Exception {
104-
public InvalidSDKKeyException() {
105-
}
106-
}
10796
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.launchdarkly.client;
2+
3+
@SuppressWarnings("serial")
4+
class HttpErrorException extends Exception {
5+
private final int status;
6+
7+
public HttpErrorException(int status) {
8+
super("HTTP error " + status);
9+
this.status = status;
10+
}
11+
12+
public int getStatus() {
13+
return status;
14+
}
15+
}

src/main/java/com/launchdarkly/client/LDClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ public LDClient(String sdkKey, LDConfig config) {
9797
} catch (Exception e) {
9898
logger.error("Exception encountered waiting for LaunchDarkly client initialization", e);
9999
}
100+
if (!updateProcessor.initialized()) {
101+
logger.warn("LaunchDarkly client was not successfully initialized");
102+
}
100103
}
101104
}
102105

src/main/java/com/launchdarkly/client/LDConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ protected LDConfig(Builder builder) {
113113
.connectTimeout(connectTimeoutMillis, TimeUnit.MILLISECONDS)
114114
.readTimeout(socketTimeoutMillis, TimeUnit.MILLISECONDS)
115115
.writeTimeout(socketTimeoutMillis, TimeUnit.MILLISECONDS)
116-
.retryOnConnectionFailure(true);
116+
.retryOnConnectionFailure(false); // we will implement our own retry logic
117117

118118
// When streaming is enabled, http GETs made by FeatureRequester will
119119
// always guarantee a new flag state. So, disable http response caching

src/main/java/com/launchdarkly/client/LDUser.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -608,11 +608,7 @@ public Builder privateEmail(String email) {
608608
* @return the builder
609609
*/
610610
public Builder custom(String k, String v) {
611-
checkCustomAttribute(k);
612-
if (k != null && v != null) {
613-
custom.put(k, new JsonPrimitive(v));
614-
}
615-
return this;
611+
return custom(k, v == null ? null : new JsonPrimitive(v));
616612
}
617613

618614
/**
@@ -625,11 +621,7 @@ public Builder custom(String k, String v) {
625621
* @return the builder
626622
*/
627623
public Builder custom(String k, Number n) {
628-
checkCustomAttribute(k);
629-
if (k != null && n != null) {
630-
custom.put(k, new JsonPrimitive(n));
631-
}
632-
return this;
624+
return custom(k, n == null ? null : new JsonPrimitive(n));
633625
}
634626

635627
/**
@@ -642,9 +634,22 @@ public Builder custom(String k, Number n) {
642634
* @return the builder
643635
*/
644636
public Builder custom(String k, Boolean b) {
637+
return custom(k, b == null ? null : new JsonPrimitive(b));
638+
}
639+
640+
/**
641+
* Add a custom attribute whose value can be any JSON type. When set to one of the
642+
* <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">built-in
643+
* user attribute keys</a>, this custom attribute will be ignored.
644+
*
645+
* @param k the key for the custom attribute
646+
* @param v the value for the custom attribute
647+
* @return the builder
648+
*/
649+
public Builder custom(String k, JsonElement v) {
645650
checkCustomAttribute(k);
646-
if (k != null && b != null) {
647-
custom.put(k, new JsonPrimitive(b));
651+
if (k != null && v != null) {
652+
custom.put(k, v);
648653
}
649654
return this;
650655
}
@@ -757,6 +762,21 @@ public Builder privateCustom(String k, Boolean b) {
757762
return custom(k, b);
758763
}
759764

765+
/**
766+
* Add a custom attribute of any JSON type, that will not be sent back to LaunchDarkly.
767+
* When set to one of the
768+
* <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">built-in
769+
* user attribute keys</a>, this custom attribute will be ignored.
770+
*
771+
* @param k the key for the custom attribute
772+
* @param v the value for the custom attribute
773+
* @return the builder
774+
*/
775+
public Builder privateCustom(String k, JsonElement v) {
776+
privateAttrNames.add(k);
777+
return custom(k, v);
778+
}
779+
760780
/**
761781
* Add a list of {@link java.lang.String}-valued custom attributes. When set to one of the
762782
* <a href="http://docs.launchdarkly.com/docs/targeting-users#targeting-based-on-user-attributes">

src/main/java/com/launchdarkly/client/PollingProcessor.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22

33
import com.google.common.util.concurrent.SettableFuture;
44
import com.google.common.util.concurrent.ThreadFactoryBuilder;
5+
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
78

89
import java.io.IOException;
9-
import java.util.concurrent.*;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.Future;
12+
import java.util.concurrent.ScheduledExecutorService;
13+
import java.util.concurrent.ThreadFactory;
14+
import java.util.concurrent.TimeUnit;
1015
import java.util.concurrent.atomic.AtomicBoolean;
1116

17+
import static com.launchdarkly.client.Util.httpErrorMessage;
18+
import static com.launchdarkly.client.Util.isHttpErrorRecoverable;
19+
1220
class PollingProcessor implements UpdateProcessor {
1321
private static final Logger logger = LoggerFactory.getLogger(PollingProcessor.class);
1422

@@ -55,9 +63,12 @@ public void run() {
5563
logger.info("Initialized LaunchDarkly client.");
5664
initFuture.set(null);
5765
}
58-
} catch (FeatureRequestor.InvalidSDKKeyException e) {
59-
logger.error("Received 401 error, no further polling requests will be made since SDK key is invalid");
60-
scheduler.shutdown();
66+
} catch (HttpErrorException e) {
67+
logger.error(httpErrorMessage(e.getStatus(), "polling request", "will retry"));
68+
if (!isHttpErrorRecoverable(e.getStatus())) {
69+
scheduler.shutdown();
70+
initFuture.set(null); // if client is initializing, make it stop waiting; has no effect if already inited
71+
}
6172
} catch (IOException e) {
6273
logger.error("Encountered exception in LaunchDarkly client when retrieving update", e);
6374
}

0 commit comments

Comments
 (0)