Skip to content

Commit

Permalink
Add inheritability to StringArrayThreadContextMap
Browse files Browse the repository at this point in the history
  • Loading branch information
ppkarwasz committed May 20, 2024
1 parent f43b42d commit 9c512f3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ static Stream<ThreadContextMap> inheritableMaps() {
props.setProperty("log4j2.isThreadContextMapInheritable", "true");
final PropertiesUtil util = new PropertiesUtil(props);
return Stream.of(
new CopyOnWriteSortedArrayThreadContextMap(util), new GarbageFreeSortedArrayThreadContextMap(util));
new StringArrayThreadContextMap(util),
new CopyOnWriteSortedArrayThreadContextMap(util),
new GarbageFreeSortedArrayThreadContextMap(util));
}

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static Collection<String[]> threadContextMapClassNames() {
return asList(new String[][] {
{"org.apache.logging.log4j.core.context.CopyOnWriteSortedArrayThreadContextMap"},
{"org.apache.logging.log4j.core.context.GarbageFreeSortedArrayThreadContextMap"},
// {"org.apache.logging.log4j.core.context.StringArrayThreadContextMap"}
{"org.apache.logging.log4j.core.context.StringArrayThreadContextMap"}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@

/**
* Commons base class for {@link StringMap}-based implementations of {@code ThreadContextMap}.
* @since 2.24.0
*/
public abstract class AbstractSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap {

/**
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
* {@code ThreadLocal} (value is not "true") in the implementation.
*/
public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
private static final String INHERITABLE_MAP = "isThreadContextMapInheritable";

/**
* The default initial capacity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
*/
package org.apache.logging.log4j.core.context;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.logging.log4j.core.context.internal.UnmodifiableArrayBackedMap;
import org.apache.logging.log4j.spi.ThreadContextMap;
import org.apache.logging.log4j.util.BiConsumer;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.TriConsumer;
import org.jspecify.annotations.NullMarked;
Expand All @@ -41,31 +43,48 @@
public class StringArrayThreadContextMap implements ThreadContextMap, ReadOnlyStringMap {
private static final long serialVersionUID = -2635197170958057849L;

private ThreadLocal<Object @Nullable []> threadLocalMapState;
/**
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
* {@code ThreadLocal} (value is not "true") in the implementation.
*/
private static final String INHERITABLE_MAP = "isThreadContextMapInheritable";

private ThreadLocal<Object @Nullable []> localMap;

public StringArrayThreadContextMap() {
threadLocalMapState = new ThreadLocal<>();
localMap = new ThreadLocal<>();
}

StringArrayThreadContextMap(final PropertiesUtil properties) {
localMap = properties.getBooleanProperty(INHERITABLE_MAP)
? new InheritableThreadLocal<Object @Nullable []>() {
@Override
protected Object @Nullable [] childValue(final Object @Nullable [] parentValue) {
return parentValue != null ? Arrays.copyOf(parentValue, parentValue.length) : null;
}
}
: new ThreadLocal<>();
}

@Override
public void put(final String key, final String value) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
final UnmodifiableArrayBackedMap modifiedMap =
UnmodifiableArrayBackedMap.getInstance(state).copyAndPut(key, value);
threadLocalMapState.set(modifiedMap.getBackingArray());
localMap.set(modifiedMap.getBackingArray());
}

@Override
public void putAll(final Map<String, String> m) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
final UnmodifiableArrayBackedMap modifiedMap =
UnmodifiableArrayBackedMap.getInstance(state).copyAndPutAll(m);
threadLocalMapState.set(modifiedMap.getBackingArray());
localMap.set(modifiedMap.getBackingArray());
}

@Override
public @Nullable String get(final String key) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
if (state == null) {
return null;
}
Expand All @@ -74,27 +93,27 @@ public void putAll(final Map<String, String> m) {

@Override
public void remove(final String key) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
if (state != null) {
final UnmodifiableArrayBackedMap modifiedMap =
UnmodifiableArrayBackedMap.getInstance(state).copyAndRemove(key);
threadLocalMapState.set(modifiedMap.getBackingArray());
localMap.set(modifiedMap.getBackingArray());
}
}

@Override
public void removeAll(final Iterable<String> keys) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
if (state != null) {
final UnmodifiableArrayBackedMap modifiedMap =
UnmodifiableArrayBackedMap.getInstance(state).copyAndRemoveAll(keys);
threadLocalMapState.set(modifiedMap.getBackingArray());
localMap.set(modifiedMap.getBackingArray());
}
}

@Override
public void clear() {
threadLocalMapState.remove();
localMap.remove();
}

@Override
Expand All @@ -104,13 +123,13 @@ public Map<String, String> toMap() {

@Override
public boolean containsKey(final String key) {
final Object @Nullable [] state = threadLocalMapState.get();
final Object @Nullable [] state = localMap.get();
return (state != null && (UnmodifiableArrayBackedMap.getInstance(state)).containsKey(key));
}

@Override
public <V> void forEach(final BiConsumer<String, ? super V> action) {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
if (state == null) {
return;
}
Expand All @@ -120,7 +139,7 @@ public <V> void forEach(final BiConsumer<String, ? super V> action) {

@Override
public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
final Object[] localState = threadLocalMapState.get();
final Object[] localState = localMap.get();
if (localState == null) {
return;
}
Expand All @@ -136,7 +155,7 @@ public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final

@Override
public Map<String, String> getCopy() {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
if (state == null) {
return new HashMap<>(0);
}
Expand All @@ -145,7 +164,7 @@ public Map<String, String> getCopy() {

@Override
public @Nullable Map<String, String> getImmutableMapOrNull() {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
return (state == null ? null : UnmodifiableArrayBackedMap.getInstance(state));
}

Expand All @@ -156,13 +175,13 @@ public boolean isEmpty() {

@Override
public int size() {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
return UnmodifiableArrayBackedMap.getInstance(state).size();
}

@Override
public String toString() {
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
return state == null
? "{}"
: UnmodifiableArrayBackedMap.getInstance(state).toString();
Expand All @@ -172,7 +191,7 @@ public String toString() {
public int hashCode() {
final int prime = 31;
int result = 1;
final Object[] state = threadLocalMapState.get();
final Object[] state = localMap.get();
result = prime * result
+ ((state == null)
? 0
Expand All @@ -192,7 +211,7 @@ public boolean equals(final Object obj) {
return false;
}
final ThreadContextMap other = (ThreadContextMap) obj;
final Map<String, String> map = UnmodifiableArrayBackedMap.getInstance(this.threadLocalMapState.get());
final Map<String, String> map = UnmodifiableArrayBackedMap.getInstance(this.localMap.get());
final Map<String, String> otherMap = other.getImmutableMapOrNull();
return Objects.equals(map, otherMap);
}
Expand Down

0 comments on commit 9c512f3

Please sign in to comment.