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; 023import java.util.NoSuchElementException; 024 025import org.apache.logging.log4j.ThreadContext.ContextStack; 026import org.apache.logging.log4j.util.Strings; 027 028/** 029 * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative operations (add, 030 * pop, and so on) are implemented by making a fresh copy of the underlying list. 031 */ 032public class DefaultThreadContextStack implements ThreadContextStack { 033 034 private static final long serialVersionUID = 5050501L; 035 036 private static final ThreadLocal<MutableThreadContextStack> stack = new ThreadLocal<MutableThreadContextStack>(); 037 038 private final boolean useStack; 039 040 public DefaultThreadContextStack(final boolean useStack) { 041 this.useStack = useStack; 042 } 043 044 private MutableThreadContextStack getNonNullStackCopy() { 045 final MutableThreadContextStack values = stack.get(); 046 return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy()); 047 } 048 049 @Override 050 public boolean add(final String s) { 051 if (!useStack) { 052 return false; 053 } 054 final MutableThreadContextStack copy = getNonNullStackCopy(); 055 copy.add(s); 056 copy.freeze(); 057 stack.set(copy); 058 return true; 059 } 060 061 @Override 062 public boolean addAll(final Collection<? extends String> strings) { 063 if (!useStack || strings.isEmpty()) { 064 return false; 065 } 066 final MutableThreadContextStack copy = getNonNullStackCopy(); 067 copy.addAll(strings); 068 copy.freeze(); 069 stack.set(copy); 070 return true; 071 } 072 073 @Override 074 public List<String> asList() { 075 final MutableThreadContextStack values = stack.get(); 076 if (values == null) { 077 return Collections.emptyList(); 078 } 079 return values.asList(); 080 } 081 082 @Override 083 public void clear() { 084 stack.remove(); 085 } 086 087 @Override 088 public boolean contains(final Object o) { 089 final MutableThreadContextStack values = stack.get(); 090 return values != null && values.contains(o); 091 } 092 093 @Override 094 public boolean containsAll(final Collection<?> objects) { 095 if (objects.isEmpty()) { // quick check before accessing the ThreadLocal 096 return true; // looks counter-intuitive, but see 097 // j.u.AbstractCollection 098 } 099 final MutableThreadContextStack values = stack.get(); 100 return values != null && values.containsAll(objects); 101 } 102 103 @Override 104 public ThreadContextStack copy() { 105 MutableThreadContextStack values = null; 106 if (!useStack || (values = stack.get()) == null) { 107 return new MutableThreadContextStack(); 108 } 109 return values.copy(); 110 } 111 112 @Override 113 public boolean equals(final Object obj) { 114 if (this == obj) { 115 return true; 116 } 117 if (obj == null) { 118 return false; 119 } 120 if (obj instanceof DefaultThreadContextStack) { 121 final DefaultThreadContextStack other = (DefaultThreadContextStack) obj; 122 if (this.useStack != other.useStack) { 123 return false; 124 } 125 } 126 if (!(obj instanceof ThreadContextStack)) { 127 return false; 128 } 129 final ThreadContextStack other = (ThreadContextStack) obj; 130 final MutableThreadContextStack values = stack.get(); 131 if (values == null) { 132 return other == null; 133 } 134 return values.equals(other); 135 } 136 137 @Override 138 public int getDepth() { 139 final MutableThreadContextStack values = stack.get(); 140 return values == null ? 0 : values.getDepth(); 141 } 142 143 @Override 144 public int hashCode() { 145 final MutableThreadContextStack values = stack.get(); 146 final int prime = 31; 147 int result = 1; 148 // Factor in the stack itself to compare vs. other implementors. 149 result = prime * result + ((values == null) ? 0 : values.hashCode()); 150 return result; 151 } 152 153 @Override 154 public boolean isEmpty() { 155 final MutableThreadContextStack values = stack.get(); 156 return values == null || values.isEmpty(); 157 } 158 159 @Override 160 public Iterator<String> iterator() { 161 final MutableThreadContextStack values = stack.get(); 162 if (values == null) { 163 final List<String> empty = Collections.emptyList(); 164 return empty.iterator(); 165 } 166 return values.iterator(); 167 } 168 169 @Override 170 public String peek() { 171 final MutableThreadContextStack values = stack.get(); 172 if (values == null || values.size() == 0) { 173 return null; 174 } 175 return values.peek(); 176 } 177 178 @Override 179 public String pop() { 180 if (!useStack) { 181 return Strings.EMPTY; 182 } 183 final MutableThreadContextStack values = stack.get(); 184 if (values == null || values.size() == 0) { 185 throw new NoSuchElementException("The ThreadContext stack is 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 /* (non-Javadoc) 299 * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull() 300 */ 301 @Override 302 public ContextStack getImmutableStackOrNull() { 303 return stack.get(); 304 } 305}