LOG4J2-1349 made ThreadContextMap classes abstract
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/efb22938 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/efb22938 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/efb22938 Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext Commit: efb2293850576385684b6ace9e9f3dbb3e3107a2 Parents: 1118b27 Author: rpopma <[email protected]> Authored: Fri Aug 19 23:14:00 2016 +0900 Committer: rpopma <[email protected]> Committed: Tue Aug 23 00:31:04 2016 +0900 ---------------------------------------------------------------------- ...AbstractCopyOnWriteMutableThreadContext.java | 173 +++++++++++++++++++ ...AbstractGarbageFreeMutableThreadContext.java | 171 ++++++++++++++++++ .../CopyOnWriteSortedArrayThreadContext.java | 156 ----------------- .../log4j/spi/SortedArrayThreadContext.java | 156 ----------------- 4 files changed, 344 insertions(+), 312 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java new file mode 100644 index 0000000..fe6c367 --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spi; + +import java.util.Collections; +import java.util.Map; + +import org.apache.logging.log4j.util.PropertiesUtil; + +/** + * ThreadContextMap implementation backed by {@code MutableContextData}. + * A new ThreadContext Map is created each time it is updated and the Map stored is always + * immutable. This means the Map can be passed to other threads without concern that it will be updated. Since it is + * expected that the Map will be passed to many more log events than the number of keys it contains the performance + * should be much better than if the Map was copied for each event. + */ +public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadContextMap { + /** + * 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 final ThreadLocal<MutableContextData> localMap; + + public AbstractCopyOnWriteMutableThreadContext() { + this.localMap = createThreadLocalMap(); + } + + // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured. + // (This method is package protected for JUnit tests.) + private ThreadLocal<MutableContextData> createThreadLocalMap() { + final PropertiesUtil managerProps = PropertiesUtil.getProperties(); + final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP); + if (inheritable) { + return new InheritableThreadLocal<MutableContextData>() { + @Override + protected MutableContextData childValue(final MutableContextData parentValue) { + return parentValue != null ? createMutableContextData(parentValue) : null; + } + }; + } + // if not inheritable, return plain ThreadLocal with null as initial value + return new ThreadLocal<>(); + } + + protected abstract MutableContextData createMutableContextData(); + + protected abstract MutableContextData createMutableContextData(final MutableContextData original); + + @Override + public void put(final String key, final String value) { + MutableContextData map = localMap.get(); + map = map == null ? createMutableContextData() : createMutableContextData(map); + map.putValue(key, value); + localMap.set(map); + } + + @Override + public void putAll(final Map<String, String> values) { + if (values == null || values.isEmpty()) { + return; + } + MutableContextData map = localMap.get(); + map = map == null ? createMutableContextData() : createMutableContextData(map); + for (final Map.Entry<String, String> entry : values.entrySet()) { + map.putValue(entry.getKey(), entry.getValue()); + } + localMap.set(map); + } + + @Override + public String get(final String key) { + final MutableContextData map = localMap.get(); + return map == null ? null : (String) map.getValue(key); + } + + @Override + public void remove(final String key) { + final MutableContextData map = localMap.get(); + if (map != null) { + final MutableContextData copy = createMutableContextData(map); + copy.remove(key); + localMap.set(copy); + } + } + + @Override + public void clear() { + localMap.remove(); + } + + @Override + public boolean containsKey(final String key) { + final MutableContextData map = localMap.get(); + return map != null && map.containsKey(key); + } + + @Override + public Map<String, String> getCopy() { + final MutableContextData map = localMap.get(); + return map == null ? Collections.<String, String>emptyMap() : map.asMap(); + } + + public ContextData getContextData() { + return localMap.get(); + } + + @Override + public Map<String, String> getImmutableMapOrNull() { + final MutableContextData map = localMap.get(); + return map == null ? null : Collections.unmodifiableMap(map.asMap()); + } + + @Override + public boolean isEmpty() { + final MutableContextData map = localMap.get(); + return map == null || map.size() == 0; + } + + @Override + public String toString() { + final MutableContextData map = localMap.get(); + return map == null ? "{}" : map.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + final MutableContextData map = this.localMap.get(); + result = prime * result + ((map == null) ? 0 : map.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThreadContextMap)) { + return false; + } + final ThreadContextMap other = (ThreadContextMap) obj; + final Map<String, String> map = this.getImmutableMapOrNull(); + final Map<String, String> otherMap = other.getImmutableMapOrNull(); + if (map == null) { + if (otherMap != null) { + return false; + } + } else if (!map.equals(otherMap)) { + return false; + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java new file mode 100644 index 0000000..45010a0 --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spi; + +import java.util.Collections; +import java.util.Map; + +import org.apache.logging.log4j.util.PropertiesUtil; + +/** + * Garbage-free ThreadContextMap implementation backed by {@code MutableContextData}. + */ +public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadContextMap { + /** + * 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 final ThreadLocal<MutableContextData> localMap; + + public AbstractGarbageFreeMutableThreadContext() { + this.localMap = createThreadLocalMap(); + } + + // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured. + // (This method is package protected for JUnit tests.) + private ThreadLocal<MutableContextData> createThreadLocalMap() { + final PropertiesUtil managerProps = PropertiesUtil.getProperties(); + final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP); + if (inheritable) { + return new InheritableThreadLocal<MutableContextData>() { + @Override + protected MutableContextData childValue(final MutableContextData parentValue) { + return parentValue != null ? createMutableContextData(parentValue) : null; + } + }; + } + // if not inheritable, return plain ThreadLocal with null as initial value + return new ThreadLocal<>(); + } + + protected abstract MutableContextData createMutableContextData(); + + protected abstract MutableContextData createMutableContextData(final MutableContextData original); + + private MutableContextData getThreadLocalMap() { + MutableContextData map = localMap.get(); + if (map == null) { + map = createMutableContextData(); + localMap.set(map); + } + return map; + } + + @Override + public void put(final String key, final String value) { + getThreadLocalMap().putValue(key, value); + } + + @Override + public void putAll(final Map<String, String> values) { + if (values == null || values.isEmpty()) { + return; + } + final MutableContextData map = getThreadLocalMap(); + for (final Map.Entry<String, String> entry : values.entrySet()) { + map.putValue(entry.getKey(), entry.getValue()); + } + } + + @Override + public String get(final String key) { + final MutableContextData map = localMap.get(); + return map == null ? null : (String) map.getValue(key); + } + + @Override + public void remove(final String key) { + final MutableContextData map = localMap.get(); + if (map != null) { + map.remove(key); + } + } + + @Override + public void clear() { + localMap.remove(); + } + + @Override + public boolean containsKey(final String key) { + final MutableContextData map = localMap.get(); + return map != null && map.containsKey(key); + } + + @Override + public Map<String, String> getCopy() { + final MutableContextData map = localMap.get(); + return map == null ? Collections.<String, String>emptyMap() : map.asMap(); + } + + public ContextData getContextData() { + return localMap.get(); + } + + @Override + public Map<String, String> getImmutableMapOrNull() { + final MutableContextData map = localMap.get(); + return map == null ? null : Collections.unmodifiableMap(map.asMap()); + } + + @Override + public boolean isEmpty() { + final MutableContextData map = localMap.get(); + return map == null || map.size() == 0; + } + + @Override + public String toString() { + final MutableContextData map = localMap.get(); + return map == null ? "{}" : map.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + final MutableContextData map = this.localMap.get(); + result = prime * result + ((map == null) ? 0 : map.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThreadContextMap)) { + return false; + } + final ThreadContextMap other = (ThreadContextMap) obj; + final Map<String, String> map = this.getImmutableMapOrNull(); + final Map<String, String> otherMap = other.getImmutableMapOrNull(); + if (map == null) { + if (otherMap != null) { + return false; + } + } else if (!map.equals(otherMap)) { + return false; + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java deleted file mode 100644 index be172b4..0000000 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spi; - -import java.util.Collections; -import java.util.Map; - -import org.apache.logging.log4j.util.PropertiesUtil; - -/** - * ThreadContextMap implementation backed by {@code SortedArrayContextData}. - * A new ThreadContext Map is created each time it is updated and the Map stored is always - * immutable. This means the Map can be passed to other threads without concern that it will be updated. Since it is - * expected that the Map will be passed to many more log events than the number of keys it contains the performance - * should be much better than if the Map was copied for each event. - */ -public class CopyOnWriteSortedArrayThreadContext implements ThreadContextMap { - /** - * 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 final ThreadLocal<ArrayContextData> localMap; - - public CopyOnWriteSortedArrayThreadContext() { - this.localMap = createThreadLocalMap(); - } - - // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured. - // (This method is package protected for JUnit tests.) - static ThreadLocal<ArrayContextData> createThreadLocalMap() { - final PropertiesUtil managerProps = PropertiesUtil.getProperties(); - final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP); - if (inheritable) { - return new InheritableThreadLocal<ArrayContextData>() { - @Override - protected ArrayContextData childValue(final ArrayContextData parentValue) { - return parentValue != null ? new ArrayContextData(parentValue) : null; - } - }; - } - // if not inheritable, return plain ThreadLocal with null as initial value - return new ThreadLocal<>(); - } - - @Override - public void put(final String key, final String value) { - ArrayContextData map = localMap.get(); - map = map == null ? new ArrayContextData() : new ArrayContextData(map); - map.put(key, value); - localMap.set(map); - } - - @Override - public String get(final String key) { - final ArrayContextData map = localMap.get(); - return map == null ? null : map.get(key); - } - - @Override - public void remove(final String key) { - final ArrayContextData map = localMap.get(); - if (map != null) { - final ArrayContextData copy = new ArrayContextData(map); - copy.remove(key); - localMap.set(copy); - } - } - - @Override - public void clear() { - localMap.remove(); - } - - @Override - public boolean containsKey(final String key) { - final ArrayContextData map = localMap.get(); - return map != null && map.containsKey(key); - } - - @Override - public Map<String, String> getCopy() { - final ArrayContextData map = localMap.get(); - return map == null ? Collections.<String, String>emptyMap() : map.asMap(); - } - - public ContextData getContextData() { - return localMap.get(); - } - - @Override - public Map<String, String> getImmutableMapOrNull() { - final ArrayContextData map = localMap.get(); - return map == null ? null : Collections.unmodifiableMap(map.asMap()); - } - - @Override - public boolean isEmpty() { - final ArrayContextData map = localMap.get(); - return map == null || map.size() == 0; - } - - @Override - public String toString() { - final ArrayContextData map = localMap.get(); - return map == null ? "{}" : map.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - final ArrayContextData map = this.localMap.get(); - result = prime * result + ((map == null) ? 0 : map.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof ThreadContextMap)) { - return false; - } - final ThreadContextMap other = (ThreadContextMap) obj; - final Map<String, String> map = this.getImmutableMapOrNull(); - final Map<String, String> otherMap = other.getImmutableMapOrNull(); - if (map == null) { - if (otherMap != null) { - return false; - } - } else if (!map.equals(otherMap)) { - return false; - } - return true; - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java deleted file mode 100644 index f3438c5..0000000 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spi; - -import java.util.Collections; -import java.util.Map; - -import org.apache.logging.log4j.util.PropertiesUtil; - -/** - * Garbage-free ThreadContextMap implementation backed by {@code SortedArrayContextData}. - */ -public class SortedArrayThreadContext implements ThreadContextMap { - /** - * 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 final ThreadLocal<ArrayContextData> localMap; - - public SortedArrayThreadContext() { - this.localMap = createThreadLocalMap(); - } - - // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured. - // (This method is package protected for JUnit tests.) - static ThreadLocal<ArrayContextData> createThreadLocalMap() { - final PropertiesUtil managerProps = PropertiesUtil.getProperties(); - final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP); - if (inheritable) { - return new InheritableThreadLocal<ArrayContextData>() { - @Override - protected ArrayContextData childValue(final ArrayContextData parentValue) { - return parentValue != null ? new ArrayContextData(parentValue) : null; - } - }; - } - // if not inheritable, return plain ThreadLocal with null as initial value - return new ThreadLocal<>(); - } - - private ArrayContextData getThreadLocalMap() { - ArrayContextData map = localMap.get(); - if (map == null) { - map = new ArrayContextData(); - localMap.set(map); - } - return map; - } - - @Override - public void put(final String key, final String value) { - getThreadLocalMap().put(key, value); - } - - @Override - public String get(final String key) { - final ArrayContextData map = localMap.get(); - return map == null ? null : map.get(key); - } - - @Override - public void remove(final String key) { - final ArrayContextData map = localMap.get(); - if (map != null) { - map.remove(key); - } - } - - @Override - public void clear() { - localMap.remove(); - } - - @Override - public boolean containsKey(final String key) { - final ArrayContextData map = localMap.get(); - return map != null && map.containsKey(key); - } - - @Override - public Map<String, String> getCopy() { - final ArrayContextData map = localMap.get(); - return map == null ? Collections.<String, String>emptyMap() : map.asMap(); - } - - public ContextData getContextData() { - return localMap.get(); - } - - @Override - public Map<String, String> getImmutableMapOrNull() { - final ArrayContextData map = localMap.get(); - return map == null ? null : Collections.unmodifiableMap(map.asMap()); - } - - @Override - public boolean isEmpty() { - final ArrayContextData map = localMap.get(); - return map == null || map.size() == 0; - } - - @Override - public String toString() { - final ArrayContextData map = localMap.get(); - return map == null ? "{}" : map.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - final ArrayContextData map = this.localMap.get(); - result = prime * result + ((map == null) ? 0 : map.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof ThreadContextMap)) { - return false; - } - final ThreadContextMap other = (ThreadContextMap) obj; - final Map<String, String> map = this.getImmutableMapOrNull(); - final Map<String, String> otherMap = other.getImmutableMapOrNull(); - if (map == null) { - if (otherMap != null) { - return false; - } - } else if (!map.equals(otherMap)) { - return false; - } - return true; - } -}
