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