Skip to content

Commit 001b500

Browse files
committed
feat(context): Throw defensive errors on null keys and carriers
1 parent 25fc0a4 commit 001b500

File tree

7 files changed

+36
-35
lines changed

7 files changed

+36
-35
lines changed

components/context/src/main/java/datadog/context/Context.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ static Context detachFrom(Object carrier) {
121121
* @return a new context with the implicitly keyed value set.
122122
* @see #with(ContextKey, Object)
123123
*/
124-
default Context with(ImplicitContextKeyed value) {
124+
default Context with(@Nullable ImplicitContextKeyed value) {
125125
if (value == null) {
126-
return null;
126+
return root();
127127
}
128128
return value.storeInto(this);
129129
}

components/context/src/main/java/datadog/context/EmptyContext.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.context;
22

3+
import static java.util.Objects.requireNonNull;
4+
35
import javax.annotation.Nullable;
46
import javax.annotation.ParametersAreNonnullByDefault;
57

@@ -16,7 +18,8 @@ public <T> T get(ContextKey<T> key) {
1618

1719
@Override
1820
public <T> Context with(ContextKey<T> key, @Nullable T value) {
19-
if (key == null || value == null) {
21+
requireNonNull(key, "Context kex can't be null");
22+
if (value == null) {
2023
return this;
2124
}
2225
return new SingletonContext(key.index, value);

components/context/src/main/java/datadog/context/IndexedContext.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static java.lang.Math.max;
44
import static java.util.Arrays.copyOfRange;
5+
import static java.util.Objects.requireNonNull;
56

67
import java.util.Arrays;
78
import javax.annotation.Nullable;
@@ -20,18 +21,14 @@ final class IndexedContext implements Context {
2021
@Nullable
2122
@SuppressWarnings("unchecked")
2223
public <T> T get(ContextKey<T> key) {
23-
if (key == null) {
24-
return null;
25-
}
24+
requireNonNull(key, "Context kex can't be null");
2625
int index = key.index;
2726
return index < store.length ? (T) store[index] : null;
2827
}
2928

3029
@Override
3130
public <T> Context with(ContextKey<T> key, @Nullable T value) {
32-
if (key == null) {
33-
return this;
34-
}
31+
requireNonNull(key, "Context kex can't be null");
3532
int index = key.index;
3633
Object[] newStore = copyOfRange(store, 0, max(store.length, index + 1));
3734
newStore[index] = value;

components/context/src/main/java/datadog/context/SingletonContext.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.context;
22

33
import static java.lang.Math.max;
4+
import static java.util.Objects.requireNonNull;
45

56
import java.util.Objects;
67
import javax.annotation.Nullable;
@@ -21,14 +22,13 @@ final class SingletonContext implements Context {
2122
@Nullable
2223
@SuppressWarnings("unchecked")
2324
public <V> V get(ContextKey<V> key) {
24-
return key != null && index == key.index ? (V) this.value : null;
25+
requireNonNull(key, "Context kex can't be null");
26+
return this.index == key.index ? (V) this.value : null;
2527
}
2628

2729
@Override
2830
public <V> Context with(ContextKey<V> secondKey, @Nullable V secondValue) {
29-
if (secondKey == null) {
30-
return this;
31-
}
31+
requireNonNull(secondKey, "Context kex can't be null");
3232
int secondIndex = secondKey.index;
3333
if (this.index == secondIndex) {
3434
return secondValue == null
@@ -47,14 +47,14 @@ public boolean equals(Object o) {
4747
if (this == o) return true;
4848
if (o == null || getClass() != o.getClass()) return false;
4949
SingletonContext that = (SingletonContext) o;
50-
return index == that.index && Objects.equals(value, that.value);
50+
return this.index == that.index && Objects.equals(this.value, that.value);
5151
}
5252

5353
@Override
5454
public int hashCode() {
5555
int result = 31;
56-
result = 31 * result + index;
57-
result = 31 * result + Objects.hashCode(value);
56+
result = 31 * result + this.index;
57+
result = 31 * result + Objects.hashCode(this.value);
5858
return result;
5959
}
6060
}

components/context/src/main/java/datadog/context/WeakMapContextBinder.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,35 @@
11
package datadog.context;
22

33
import static datadog.context.Context.root;
4+
import static java.util.Collections.synchronizedMap;
5+
import static java.util.Objects.requireNonNull;
46

5-
import java.util.Collections;
67
import java.util.Map;
78
import java.util.WeakHashMap;
89
import javax.annotation.ParametersAreNonnullByDefault;
910

1011
/** {@link ContextBinder} that uses a global weak map of carriers to contexts. */
1112
@ParametersAreNonnullByDefault
1213
final class WeakMapContextBinder implements ContextBinder {
13-
private static final Map<Object, Context> TRACKED =
14-
Collections.synchronizedMap(new WeakHashMap<>());
14+
private static final Map<Object, Context> TRACKED = synchronizedMap(new WeakHashMap<>());
1515

1616
@Override
1717
public Context from(Object carrier) {
18-
if (carrier == null) {
19-
return root();
20-
}
18+
requireNonNull(carrier, "Context carrier can't be null");
2119
Context bound = TRACKED.get(carrier);
2220
return null != bound ? bound : root();
2321
}
2422

2523
@Override
2624
public void attachTo(Object carrier, Context context) {
27-
if (carrier == null || context == null) {
28-
return;
29-
}
25+
requireNonNull(carrier, "Context carrier can't be null");
26+
requireNonNull(context, "Context can't be null. Use detachFrom() instead.");
3027
TRACKED.put(carrier, context);
3128
}
3229

3330
@Override
3431
public Context detachFrom(Object carrier) {
35-
if (carrier == null) {
36-
return root();
37-
}
32+
requireNonNull(carrier, "Context kex can't be null");
3833
Context previous = TRACKED.remove(carrier);
3934
return null != previous ? previous : root();
4035
}

components/context/src/test/java/datadog/context/ContextBinderTest.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import static datadog.context.Context.from;
66
import static datadog.context.Context.root;
77
import static datadog.context.ContextTest.STRING_KEY;
8-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
98
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
1010

1111
import org.junit.jupiter.api.BeforeEach;
1212
import org.junit.jupiter.api.Test;
@@ -35,10 +35,12 @@ void testAttachAndDetach() {
3535

3636
@Test
3737
void testNullCarrier() {
38-
assertDoesNotThrow(
38+
assertThrows(
39+
NullPointerException.class,
3940
() -> assertEquals(root(), from(null), "Binder expected to return non-null context"),
4041
"Null carrier expected to hold root context");
41-
assertDoesNotThrow(
42+
assertThrows(
43+
NullPointerException.class,
4244
() -> assertEquals(root(), detachFrom(null), "Binder expected to return non-null context"),
4345
"Null carrier expected to hold root context");
4446
}
@@ -47,7 +49,9 @@ void testNullCarrier() {
4749
void testNullContext() {
4850
ContextBinder binder = ContextProviders.binder();
4951
Object carrier = new Object();
50-
assertDoesNotThrow(
51-
() -> binder.attachTo(carrier, null), "Attaching null context not expected to throw");
52+
assertThrows(
53+
NullPointerException.class,
54+
() -> binder.attachTo(carrier, null),
55+
"Attaching null context not expected to throw");
5256
}
5357
}

components/context/src/test/java/datadog/context/ContextTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.junit.jupiter.api.Assertions.assertFalse;
77
import static org.junit.jupiter.api.Assertions.assertNotEquals;
88
import static org.junit.jupiter.api.Assertions.assertNull;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
910
import static org.junit.jupiter.api.Assertions.assertTrue;
1011

1112
import java.util.stream.Stream;
@@ -53,7 +54,8 @@ void testWith(Context context) {
5354
Context context3 = context2.with(STRING_KEY, null);
5455
assertNull(context3.get(STRING_KEY));
5556
// Test null key handling
56-
assertDoesNotThrow(() -> context.with(null, "test"), "Null key should not throw exception");
57+
assertThrows(
58+
NullPointerException.class, () -> context.with(null, "test"), "Context forbids null keys");
5759
// Test null value handling
5860
assertDoesNotThrow(
5961
() -> context.with(BOOLEAN_KEY, null), "Null value should not throw exception");
@@ -68,7 +70,7 @@ void testGet(Context original) {
6870
String value = "value";
6971
Context context = original.with(STRING_KEY, value);
7072
// Test null key handling
71-
assertDoesNotThrow(() -> context.get(null), "Context expected to not throw exception");
73+
assertThrows(NullPointerException.class, () -> context.get(null), "Context forbids null keys");
7274
// Test unset key
7375
assertNull(context.get(BOOLEAN_KEY), "Missing key expected to return null");
7476
// Test set key

0 commit comments

Comments
 (0)