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

Commit 5bbaa7b

Browse files
authored
prepare 4.11.0 release (#182)
1 parent b01f593 commit 5bbaa7b

File tree

6 files changed

+93
-18
lines changed

6 files changed

+93
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to the LaunchDarkly Java SDK will be documented in this file
55

66
## [4.10.1] - 2020-01-06
77
### Fixed:
8-
- The `pom.xml` dependencies were incorrectly specifying `runtime` scope rather than `compile`, causing problems for application that did not have their own dependencies on Gson and SLF4J. ([#151](https://github.com/launchdarkly/java-client/issues/151))
8+
- The `pom.xml` dependencies were incorrectly specifying `runtime` scope rather than `compile`, causing problems for applications that did not have their own dependencies on Gson and SLF4J. ([#151](https://github.com/launchdarkly/java-client/issues/151))
99

1010
## [4.10.0] - 2019-12-13
1111
### Added:

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Date;
1616
import java.util.List;
1717
import java.util.Random;
18+
import java.util.UUID;
1819
import java.util.concurrent.ArrayBlockingQueue;
1920
import java.util.concurrent.BlockingQueue;
2021
import java.util.concurrent.Executors;
@@ -42,6 +43,7 @@ final class DefaultEventProcessor implements EventProcessor {
4243
private static final Logger logger = LoggerFactory.getLogger(DefaultEventProcessor.class);
4344
private static final String EVENT_SCHEMA_HEADER = "X-LaunchDarkly-Event-Schema";
4445
private static final String EVENT_SCHEMA_VERSION = "3";
46+
private static final String EVENT_PAYLOAD_ID_HEADER = "X-LaunchDarkly-Payload-ID";
4547

4648
private final BlockingQueue<EventProcessorMessage> inbox;
4749
private final ScheduledExecutorService scheduler;
@@ -542,7 +544,8 @@ void stop() {
542544

543545
private void postEvents(String json, int outputEventCount) {
544546
String uriStr = config.eventsURI.toString() + "/bulk";
545-
547+
String eventPayloadId = UUID.randomUUID().toString();
548+
546549
logger.debug("Posting {} event(s) to {} with payload: {}",
547550
outputEventCount, uriStr, json);
548551

@@ -558,6 +561,7 @@ private void postEvents(String json, int outputEventCount) {
558561
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json))
559562
.addHeader("Content-Type", "application/json")
560563
.addHeader(EVENT_SCHEMA_HEADER, EVENT_SCHEMA_VERSION)
564+
.addHeader(EVENT_PAYLOAD_ID_HEADER, eventPayloadId)
561565
.build();
562566

563567
long startTime = System.currentTimeMillis();

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

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,18 @@ public static enum ErrorKind {
7878
WRONG_TYPE,
7979
/**
8080
* Indicates that an unexpected exception stopped flag evaluation. An error message will always be logged
81-
* in this case.
81+
* in this case, and the exception should be available via {@link EvaluationReason.Error#getException()}.
8282
*/
8383
EXCEPTION
8484
}
8585

8686
// static instances to avoid repeatedly allocating reasons for the same errors
87-
private static final Error ERROR_CLIENT_NOT_READY = new Error(ErrorKind.CLIENT_NOT_READY);
88-
private static final Error ERROR_FLAG_NOT_FOUND = new Error(ErrorKind.FLAG_NOT_FOUND);
89-
private static final Error ERROR_MALFORMED_FLAG = new Error(ErrorKind.MALFORMED_FLAG);
90-
private static final Error ERROR_USER_NOT_SPECIFIED = new Error(ErrorKind.USER_NOT_SPECIFIED);
91-
private static final Error ERROR_WRONG_TYPE = new Error(ErrorKind.WRONG_TYPE);
92-
private static final Error ERROR_EXCEPTION = new Error(ErrorKind.EXCEPTION);
87+
private static final Error ERROR_CLIENT_NOT_READY = new Error(ErrorKind.CLIENT_NOT_READY, null);
88+
private static final Error ERROR_FLAG_NOT_FOUND = new Error(ErrorKind.FLAG_NOT_FOUND, null);
89+
private static final Error ERROR_MALFORMED_FLAG = new Error(ErrorKind.MALFORMED_FLAG, null);
90+
private static final Error ERROR_USER_NOT_SPECIFIED = new Error(ErrorKind.USER_NOT_SPECIFIED, null);
91+
private static final Error ERROR_WRONG_TYPE = new Error(ErrorKind.WRONG_TYPE, null);
92+
private static final Error ERROR_EXCEPTION = new Error(ErrorKind.EXCEPTION, null);
9393

9494
private final Kind kind;
9595

@@ -168,9 +168,19 @@ public static Error error(ErrorKind errorKind) {
168168
case MALFORMED_FLAG: return ERROR_MALFORMED_FLAG;
169169
case USER_NOT_SPECIFIED: return ERROR_USER_NOT_SPECIFIED;
170170
case WRONG_TYPE: return ERROR_WRONG_TYPE;
171-
default: return new Error(errorKind);
171+
default: return new Error(errorKind, null);
172172
}
173173
}
174+
175+
/**
176+
* Returns an instance of {@link Error} with the kind {@link ErrorKind#EXCEPTION} and an exception instance.
177+
* @param exception the exception that caused the error
178+
* @return a reason object
179+
* @since 4.11.0
180+
*/
181+
public static Error exception(Exception exception) {
182+
return new Error(ErrorKind.EXCEPTION, exception);
183+
}
174184

175185
/**
176186
* Subclass of {@link EvaluationReason} that indicates that the flag was off and therefore returned
@@ -307,11 +317,13 @@ private Fallthrough()
307317
*/
308318
public static class Error extends EvaluationReason {
309319
private final ErrorKind errorKind;
320+
private final Exception exception;
310321

311-
private Error(ErrorKind errorKind) {
322+
private Error(ErrorKind errorKind, Exception exception) {
312323
super(Kind.ERROR);
313324
checkNotNull(errorKind);
314325
this.errorKind = errorKind;
326+
this.exception = exception;
315327
}
316328

317329
/**
@@ -322,19 +334,31 @@ public ErrorKind getErrorKind() {
322334
return errorKind;
323335
}
324336

337+
/**
338+
* Returns the exception that caused the error condition, if applicable.
339+
* <p>
340+
* This is only set if {@link #getErrorKind()} is {@link ErrorKind#EXCEPTION}.
341+
*
342+
* @return the exception instance
343+
* @since 4.11.0
344+
*/
345+
public Exception getException() {
346+
return exception;
347+
}
348+
325349
@Override
326350
public boolean equals(Object other) {
327-
return other instanceof Error && errorKind == ((Error) other).errorKind;
351+
return other instanceof Error && errorKind == ((Error) other).errorKind && Objects.equals(exception, ((Error) other).exception);
328352
}
329353

330354
@Override
331355
public int hashCode() {
332-
return errorKind.hashCode();
356+
return Objects.hash(errorKind, exception);
333357
}
334358

335359
@Override
336360
public String toString() {
337-
return getKind().name() + "(" + errorKind.name() + ")";
361+
return getKind().name() + "(" + errorKind.name() + (exception == null ? "" : ("," + exception)) + ")";
338362
}
339363
}
340364
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public FeatureFlagsState allFlagsState(LDUser user, FlagsStateOption... options)
203203
} catch (Exception e) {
204204
logger.error("Exception caught for feature flag \"{}\" when evaluating all flags: {}", entry.getKey(), e.toString());
205205
logger.debug(e.toString(), e);
206-
builder.addFlag(entry.getValue(), EvaluationDetail.error(EvaluationReason.ErrorKind.EXCEPTION, LDValue.ofNull()));
206+
builder.addFlag(entry.getValue(), EvaluationDetail.fromValue(LDValue.ofNull(), null, EvaluationReason.exception(e)));
207207
}
208208
}
209209
return builder.build();
@@ -375,7 +375,7 @@ private EvaluationDetail<LDValue> evaluateInternal(String featureKey, LDUser use
375375
sendFlagRequestEvent(eventFactory.newDefaultFeatureRequestEvent(featureFlag, user, defaultValue,
376376
EvaluationReason.ErrorKind.EXCEPTION));
377377
}
378-
return EvaluationDetail.error(EvaluationReason.ErrorKind.EXCEPTION, defaultValue);
378+
return EvaluationDetail.fromValue(defaultValue, null, EvaluationReason.exception(e));
379379
}
380380
}
381381

src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.text.SimpleDateFormat;
1313
import java.util.Date;
14+
import java.util.UUID;
1415
import java.util.concurrent.TimeUnit;
1516

1617
import static com.launchdarkly.client.TestHttpUtil.httpsServerWithSelfSignedCert;
@@ -22,6 +23,7 @@
2223
import static org.hamcrest.Matchers.allOf;
2324
import static org.hamcrest.Matchers.contains;
2425
import static org.hamcrest.Matchers.equalTo;
26+
import static org.hamcrest.Matchers.not;
2527
import static org.hamcrest.Matchers.notNullValue;
2628
import static org.hamcrest.Matchers.nullValue;
2729
import static org.junit.Assert.assertEquals;
@@ -485,6 +487,50 @@ public void eventSchemaIsSent() throws Exception {
485487
}
486488
}
487489

490+
@Test
491+
public void eventPayloadIdIsSent() throws Exception {
492+
Event e = EventFactory.DEFAULT.newIdentifyEvent(user);
493+
494+
try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) {
495+
try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) {
496+
ep.sendEvent(e);
497+
}
498+
499+
RecordedRequest req = server.takeRequest();
500+
String payloadHeaderValue = req.getHeader("X-LaunchDarkly-Payload-ID");
501+
assertThat(payloadHeaderValue, notNullValue(String.class));
502+
assertThat(UUID.fromString(payloadHeaderValue), notNullValue(UUID.class));
503+
}
504+
}
505+
506+
@Test
507+
public void eventPayloadIdReusedOnRetry() throws Exception {
508+
MockResponse errorResponse = new MockResponse().setResponseCode(429);
509+
Event e = EventFactory.DEFAULT.newIdentifyEvent(user);
510+
511+
try (MockWebServer server = makeStartedServer(errorResponse, eventsSuccessResponse(), eventsSuccessResponse())) {
512+
try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) {
513+
ep.sendEvent(e);
514+
ep.flush();
515+
// Necessary to ensure the retry occurs before the second request for test assertion ordering
516+
ep.waitUntilInactive();
517+
ep.sendEvent(e);
518+
}
519+
520+
// Failed response request
521+
RecordedRequest req = server.takeRequest(0, TimeUnit.SECONDS);
522+
String payloadId = req.getHeader("X-LaunchDarkly-Payload-ID");
523+
// Retry request has same payload ID as failed request
524+
req = server.takeRequest(0, TimeUnit.SECONDS);
525+
String retryId = req.getHeader("X-LaunchDarkly-Payload-ID");
526+
assertThat(retryId, equalTo(payloadId));
527+
// Second request has different payload ID from first request
528+
req = server.takeRequest(0, TimeUnit.SECONDS);
529+
payloadId = req.getHeader("X-LaunchDarkly-Payload-ID");
530+
assertThat(retryId, not(equalTo(payloadId)));
531+
}
532+
}
533+
488534
@Test
489535
public void http400ErrorIsRecoverable() throws Exception {
490536
testRecoverableHttpError(400);

src/test/java/com/launchdarkly/client/LDClientEvaluationTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,16 @@ public void appropriateErrorIfValueWrongType() throws Exception {
261261

262262
@Test
263263
public void appropriateErrorForUnexpectedException() throws Exception {
264-
FeatureStore badFeatureStore = featureStoreThatThrowsException(new RuntimeException("sorry"));
264+
RuntimeException exception = new RuntimeException("sorry");
265+
FeatureStore badFeatureStore = featureStoreThatThrowsException(exception);
265266
LDConfig badConfig = new LDConfig.Builder()
266267
.featureStoreFactory(specificFeatureStore(badFeatureStore))
267268
.eventProcessorFactory(Components.nullEventProcessor())
268269
.updateProcessorFactory(Components.nullUpdateProcessor())
269270
.build();
270271
try (LDClientInterface badClient = new LDClient("SDK_KEY", badConfig)) {
271272
EvaluationDetail<Boolean> expectedResult = EvaluationDetail.fromValue(false, null,
272-
EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION));
273+
EvaluationReason.exception(exception));
273274
assertEquals(expectedResult, badClient.boolVariationDetail("key", user, false));
274275
}
275276
}

0 commit comments

Comments
 (0)