001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.spi; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.List; 023 024import org.apache.logging.log4j.ThreadContext.ContextStack; 025import org.apache.logging.log4j.util.StringBuilderFormattable; 026import org.apache.logging.log4j.util.StringBuilders; 027import org.apache.logging.log4j.util.Strings; 028 029/** 030 * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative 031 * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list. 032 */ 033public class DefaultThreadContextStack implements ThreadContextStack, StringBuilderFormattable { 034 035 private static final long serialVersionUID = 5050501L; 036 037 private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>(); 038 039 private final boolean useStack; 040 041 public DefaultThreadContextStack(final boolean useStack) { 042 this.useStack = useStack; 043 } 044 045 private MutableThreadContextStack getNonNullStackCopy() { 046 final MutableThreadContextStack values = STACK.get(); 047 return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy()); 048 } 049 050 @Override 051 public boolean add(final String s) { 052 if (!useStack) { 053 return false; 054 } 055 final MutableThreadContextStack copy = getNonNullStackCopy(); 056 copy.add(s); 057 copy.freeze(); 058 STACK.set(copy); 059 return true; 060 } 061 062 @Override 063 public boolean addAll(final Collection<? extends String> strings) { 064 if (!useStack || strings.isEmpty()) { 065 return false; 066 } 067 final MutableThreadContextStack copy = getNonNullStackCopy(); 068 copy.addAll(strings); 069 copy.freeze(); 070 STACK.set(copy); 071 return true; 072 } 073 074 @Override 075 public List<String> asList() { 076 final MutableThreadContextStack values = STACK.get(); 077 if (values == null) { 078 return Collections.emptyList(); 079 } 080 return values.asList(); 081 } 082 083 @Override 084 public void clear() { 085 STACK.remove(); 086 } 087 088 @Override 089 public boolean contains(final Object o) { 090 final MutableThreadContextStack values = STACK.get(); 091 return values != null && values.contains(o); 092 } 093 094 @Override 095 public boolean containsAll(final Collection<?> objects) { 096 if (objects.isEmpty()) { // quick check before accessing the ThreadLocal 097 return true; // looks counter-intuitive, but see 098 // j.u.AbstractCollection 099 } 100 final MutableThreadContextStack values = STACK.get(); 101 return values != null && values.containsAll(objects); 102 } 103 104 @Override 105 public ThreadContextStack copy() { 106 MutableThreadContextStack values = null; 107 if (!useStack || (values = STACK.get()) == null) { 108 return new MutableThreadContextStack(); 109 } 110 return values.copy(); 111 } 112 113 @Override 114 public boolean equals(final Object obj) { 115 if (this == obj) { 116 return true; 117 } 118 if (obj == null) { 119 return false; 120 } 121 if (obj instanceof DefaultThreadContextStack) { 122 final DefaultThreadContextStack other = (DefaultThreadContextStack) obj; 123 if (this.useStack != other.useStack) { 124 return false; 125 } 126 } 127 if (!(obj instanceof ThreadContextStack)) { 128 return false; 129 } 130 final ThreadContextStack other = (ThreadContextStack) obj; 131 final MutableThreadContextStack values = STACK.get(); 132 if (values == null) { 133 return false; 134 } 135 return values.equals(other); 136 } 137 138 @Override 139 public int getDepth() { 140 final MutableThreadContextStack values = STACK.get(); 141 return values == null ? 0 : values.getDepth(); 142 } 143 144 @Override 145 public int hashCode() { 146 final MutableThreadContextStack values = STACK.get(); 147 final int prime = 31; 148 int result = 1; 149 // Factor in the stack itself to compare vs. other implementors. 150 result = prime * result + ((values == null) ? 0 : values.hashCode()); 151 return result; 152 } 153 154 @Override 155 public boolean isEmpty() { 156 final MutableThreadContextStack values = STACK.get(); 157 return values == null || values.isEmpty(); 158 } 159 160 @Override 161 public Iterator<String> iterator() { 162 final MutableThreadContextStack values = STACK.get(); 163 if (values == null) { 164 final List<String> empty = Collections.emptyList(); 165 return empty.iterator(); 166 } 167 return values.iterator(); 168 } 169 170 @Override 171 public String peek() { 172 final MutableThreadContextStack values = STACK.get(); 173 if (values == null || values.isEmpty()) { 174 return Strings.EMPTY; 175 } 176 return values.peek(); 177 } 178 179 @Override 180 public String pop() { 181 if (!useStack) { 182 return Strings.EMPTY; 183 } 184 final MutableThreadContextStack values = STACK.get(); 185 if (values == null || values.isEmpty()) { 186 // Like version 1.2 187 return Strings.EMPTY; 188 } 189 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 190 final String result = copy.pop(); 191 copy.freeze(); 192 STACK.set(copy); 193 return result; 194 } 195 196 @Override 197 public void push(final String message) { 198 if (!useStack) { 199 return; 200 } 201 add(message); 202 } 203 204 @Override 205 public boolean remove(final Object o) { 206 if (!useStack) { 207 return false; 208 } 209 final MutableThreadContextStack values = STACK.get(); 210 if (values == null || values.isEmpty()) { 211 return false; 212 } 213 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 214 final boolean result = copy.remove(o); 215 copy.freeze(); 216 STACK.set(copy); 217 return result; 218 } 219 220 @Override 221 public boolean removeAll(final Collection<?> objects) { 222 if (!useStack || objects.isEmpty()) { 223 return false; 224 } 225 final MutableThreadContextStack values = STACK.get(); 226 if (values == null || values.isEmpty()) { 227 return false; 228 } 229 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 230 final boolean result = copy.removeAll(objects); 231 copy.freeze(); 232 STACK.set(copy); 233 return result; 234 } 235 236 @Override 237 public boolean retainAll(final Collection<?> objects) { 238 if (!useStack || objects.isEmpty()) { 239 return false; 240 } 241 final MutableThreadContextStack values = STACK.get(); 242 if (values == null || values.isEmpty()) { 243 return false; 244 } 245 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 246 final boolean result = copy.retainAll(objects); 247 copy.freeze(); 248 STACK.set(copy); 249 return result; 250 } 251 252 @Override 253 public int size() { 254 final MutableThreadContextStack values = STACK.get(); 255 return values == null ? 0 : values.size(); 256 } 257 258 @Override 259 public Object[] toArray() { 260 final MutableThreadContextStack result = STACK.get(); 261 if (result == null) { 262 return Strings.EMPTY_ARRAY; 263 } 264 return result.toArray(new Object[result.size()]); 265 } 266 267 @Override 268 public <T> T[] toArray(final T[] ts) { 269 final MutableThreadContextStack result = STACK.get(); 270 if (result == null) { 271 if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[]) 272 ts[0] = null; 273 } 274 return ts; 275 } 276 return result.toArray(ts); 277 } 278 279 @Override 280 public String toString() { 281 final MutableThreadContextStack values = STACK.get(); 282 return values == null ? "[]" : values.toString(); 283 } 284 285 @Override 286 public void formatTo(final StringBuilder buffer) { 287 final MutableThreadContextStack values = STACK.get(); 288 if (values == null) { 289 buffer.append("[]"); 290 } else { 291 StringBuilders.appendValue(buffer, values); 292 } 293 } 294 295 @Override 296 public void trim(final int depth) { 297 if (depth < 0) { 298 throw new IllegalArgumentException("Maximum stack depth cannot be negative"); 299 } 300 final MutableThreadContextStack values = STACK.get(); 301 if (values == null) { 302 return; 303 } 304 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 305 copy.trim(depth); 306 copy.freeze(); 307 STACK.set(copy); 308 } 309 310 /* 311 * (non-Javadoc) 312 * 313 * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull() 314 */ 315 @Override 316 public ContextStack getImmutableStackOrNull() { 317 return STACK.get(); 318 } 319}