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.Strings; 026 027/** 028 * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative 029 * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list. 030 */ 031public class DefaultThreadContextStack implements ThreadContextStack { 032 033 private static final long serialVersionUID = 5050501L; 034 035 private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>(); 036 037 private final boolean useStack; 038 039 public DefaultThreadContextStack(final boolean useStack) { 040 this.useStack = useStack; 041 } 042 043 private MutableThreadContextStack getNonNullStackCopy() { 044 final MutableThreadContextStack values = STACK.get(); 045 return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy()); 046 } 047 048 @Override 049 public boolean add(final String s) { 050 if (!useStack) { 051 return false; 052 } 053 final MutableThreadContextStack copy = getNonNullStackCopy(); 054 copy.add(s); 055 copy.freeze(); 056 STACK.set(copy); 057 return true; 058 } 059 060 @Override 061 public boolean addAll(final Collection<? extends String> strings) { 062 if (!useStack || strings.isEmpty()) { 063 return false; 064 } 065 final MutableThreadContextStack copy = getNonNullStackCopy(); 066 copy.addAll(strings); 067 copy.freeze(); 068 STACK.set(copy); 069 return true; 070 } 071 072 @Override 073 public List<String> asList() { 074 final MutableThreadContextStack values = STACK.get(); 075 if (values == null) { 076 return Collections.emptyList(); 077 } 078 return values.asList(); 079 } 080 081 @Override 082 public void clear() { 083 STACK.remove(); 084 } 085 086 @Override 087 public boolean contains(final Object o) { 088 final MutableThreadContextStack values = STACK.get(); 089 return values != null && values.contains(o); 090 } 091 092 @Override 093 public boolean containsAll(final Collection<?> objects) { 094 if (objects.isEmpty()) { // quick check before accessing the ThreadLocal 095 return true; // looks counter-intuitive, but see 096 // j.u.AbstractCollection 097 } 098 final MutableThreadContextStack values = STACK.get(); 099 return values != null && values.containsAll(objects); 100 } 101 102 @Override 103 public ThreadContextStack copy() { 104 MutableThreadContextStack values = null; 105 if (!useStack || (values = STACK.get()) == null) { 106 return new MutableThreadContextStack(); 107 } 108 return values.copy(); 109 } 110 111 @Override 112 public boolean equals(final Object obj) { 113 if (this == obj) { 114 return true; 115 } 116 if (obj == null) { 117 return false; 118 } 119 if (obj instanceof DefaultThreadContextStack) { 120 final DefaultThreadContextStack other = (DefaultThreadContextStack) obj; 121 if (this.useStack != other.useStack) { 122 return false; 123 } 124 } 125 if (!(obj instanceof ThreadContextStack)) { 126 return false; 127 } 128 final ThreadContextStack other = (ThreadContextStack) obj; 129 final MutableThreadContextStack values = STACK.get(); 130 if (values == null) { 131 return false; 132 } 133 return values.equals(other); 134 } 135 136 @Override 137 public int getDepth() { 138 final MutableThreadContextStack values = STACK.get(); 139 return values == null ? 0 : values.getDepth(); 140 } 141 142 @Override 143 public int hashCode() { 144 final MutableThreadContextStack values = STACK.get(); 145 final int prime = 31; 146 int result = 1; 147 // Factor in the stack itself to compare vs. other implementors. 148 result = prime * result + ((values == null) ? 0 : values.hashCode()); 149 return result; 150 } 151 152 @Override 153 public boolean isEmpty() { 154 final MutableThreadContextStack values = STACK.get(); 155 return values == null || values.isEmpty(); 156 } 157 158 @Override 159 public Iterator<String> iterator() { 160 final MutableThreadContextStack values = STACK.get(); 161 if (values == null) { 162 final List<String> empty = Collections.emptyList(); 163 return empty.iterator(); 164 } 165 return values.iterator(); 166 } 167 168 @Override 169 public String peek() { 170 final MutableThreadContextStack values = STACK.get(); 171 if (values == null || values.size() == 0) { 172 return Strings.EMPTY; 173 } 174 return values.peek(); 175 } 176 177 @Override 178 public String pop() { 179 if (!useStack) { 180 return Strings.EMPTY; 181 } 182 final MutableThreadContextStack values = STACK.get(); 183 if (values == null || values.size() == 0) { 184 // Like version 1.2 185 return Strings.EMPTY; 186 } 187 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 188 final String result = copy.pop(); 189 copy.freeze(); 190 STACK.set(copy); 191 return result; 192 } 193 194 @Override 195 public void push(final String message) { 196 if (!useStack) { 197 return; 198 } 199 add(message); 200 } 201 202 @Override 203 public boolean remove(final Object o) { 204 if (!useStack) { 205 return false; 206 } 207 final MutableThreadContextStack values = STACK.get(); 208 if (values == null || values.size() == 0) { 209 return false; 210 } 211 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 212 final boolean result = copy.remove(o); 213 copy.freeze(); 214 STACK.set(copy); 215 return result; 216 } 217 218 @Override 219 public boolean removeAll(final Collection<?> objects) { 220 if (!useStack || objects.isEmpty()) { 221 return false; 222 } 223 final MutableThreadContextStack values = STACK.get(); 224 if (values == null || values.isEmpty()) { 225 return false; 226 } 227 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 228 final boolean result = copy.removeAll(objects); 229 copy.freeze(); 230 STACK.set(copy); 231 return result; 232 } 233 234 @Override 235 public boolean retainAll(final Collection<?> objects) { 236 if (!useStack || objects.isEmpty()) { 237 return false; 238 } 239 final MutableThreadContextStack values = STACK.get(); 240 if (values == null || values.isEmpty()) { 241 return false; 242 } 243 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 244 final boolean result = copy.retainAll(objects); 245 copy.freeze(); 246 STACK.set(copy); 247 return result; 248 } 249 250 @Override 251 public int size() { 252 final MutableThreadContextStack values = STACK.get(); 253 return values == null ? 0 : values.size(); 254 } 255 256 @Override 257 public Object[] toArray() { 258 final MutableThreadContextStack result = STACK.get(); 259 if (result == null) { 260 return new String[0]; 261 } 262 return result.toArray(new Object[result.size()]); 263 } 264 265 @Override 266 public <T> T[] toArray(final T[] ts) { 267 final MutableThreadContextStack result = STACK.get(); 268 if (result == null) { 269 if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[]) 270 ts[0] = null; 271 } 272 return ts; 273 } 274 return result.toArray(ts); 275 } 276 277 @Override 278 public String toString() { 279 final MutableThreadContextStack values = STACK.get(); 280 return values == null ? "[]" : values.toString(); 281 } 282 283 @Override 284 public void trim(final int depth) { 285 if (depth < 0) { 286 throw new IllegalArgumentException("Maximum stack depth cannot be negative"); 287 } 288 final MutableThreadContextStack values = STACK.get(); 289 if (values == null) { 290 return; 291 } 292 final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy(); 293 copy.trim(depth); 294 copy.freeze(); 295 STACK.set(copy); 296 } 297 298 /* 299 * (non-Javadoc) 300 * 301 * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull() 302 */ 303 @Override 304 public ContextStack getImmutableStackOrNull() { 305 return STACK.get(); 306 } 307}