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.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Iterator; 023import java.util.List; 024import java.util.NoSuchElementException; 025 026/** 027 * A copy-on-write thread-safe variant of 028 * {@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 030 * the underlying list. 031 */ 032public class DefaultThreadContextStack implements ThreadContextStack { 033 034 private static final long serialVersionUID = 5050501L; 035 036 private static 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 String pop() { 046 if (!useStack) { 047 return ""; 048 } 049 final List<String> list = stack.get(); 050 if (list == null || list.size() == 0) { 051 throw new NoSuchElementException("The ThreadContext stack is empty"); 052 } 053 final List<String> copy = new ArrayList<String>(list); 054 final int last = copy.size() - 1; 055 final String result = copy.remove(last); 056 stack.set(Collections.unmodifiableList(copy)); 057 return result; 058 } 059 060 @Override 061 public String peek() { 062 final List<String> list = stack.get(); 063 if (list == null || list.size() == 0) { 064 return null; 065 } 066 final int last = list.size() - 1; 067 return list.get(last); 068 } 069 070 @Override 071 public void push(final String message) { 072 if (!useStack) { 073 return; 074 } 075 add(message); 076 } 077 078 @Override 079 public int getDepth() { 080 final List<String> list = stack.get(); 081 return list == null ? 0 : list.size(); 082 } 083 084 @Override 085 public List<String> asList() { 086 final List<String> list = stack.get(); 087 if (list == null) { 088 return Collections.emptyList(); 089 } 090 return list; 091 } 092 093 @Override 094 public void trim(final int depth) { 095 if (depth < 0) { 096 throw new IllegalArgumentException( 097 "Maximum stack depth cannot be negative"); 098 } 099 final List<String> list = stack.get(); 100 if (list == null) { 101 return; 102 } 103 final List<String> copy = new ArrayList<String>(); 104 final int count = Math.min(depth, list.size()); 105 for (int i = 0; i < count; i++) { 106 copy.add(list.get(i)); 107 } 108 stack.set(copy); 109 } 110 111 @Override 112 public ThreadContextStack copy() { 113 List<String> result = null; 114 if (!useStack || (result = stack.get()) == null) { 115 return new MutableThreadContextStack(new ArrayList<String>()); 116 } 117 return new MutableThreadContextStack(result); 118 } 119 120 @Override 121 public void clear() { 122 stack.remove(); 123 } 124 125 @Override 126 public int size() { 127 final List<String> result = stack.get(); 128 return result == null ? 0 : result.size(); 129 } 130 131 @Override 132 public boolean isEmpty() { 133 final List<String> result = stack.get(); 134 return result == null || result.isEmpty(); 135 } 136 137 @Override 138 public boolean contains(final Object o) { 139 final List<String> result = stack.get(); 140 return result != null && result.contains(o); 141 } 142 143 @Override 144 public Iterator<String> iterator() { 145 final List<String> immutable = stack.get(); 146 if (immutable == null) { 147 final List<String> empty = Collections.emptyList(); 148 return empty.iterator(); 149 } 150 return immutable.iterator(); 151 } 152 153 @Override 154 public Object[] toArray() { 155 final List<String> result = stack.get(); 156 if (result == null) { 157 return new String[0]; 158 } 159 return result.toArray(new Object[result.size()]); 160 } 161 162 @Override 163 public <T> T[] toArray(final T[] ts) { 164 final List<String> result = stack.get(); 165 if (result == null) { 166 if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[]) 167 ts[0] = null; 168 } 169 return ts; 170 } 171 return result.toArray(ts); 172 } 173 174 @Override 175 public boolean add(final String s) { 176 if (!useStack) { 177 return false; 178 } 179 final List<String> list = stack.get(); 180 final List<String> copy = list == null ? new ArrayList<String>() 181 : new ArrayList<String>(list); 182 copy.add(s); 183 stack.set(Collections.unmodifiableList(copy)); 184 return true; 185 } 186 187 @Override 188 public boolean remove(final Object o) { 189 if (!useStack) { 190 return false; 191 } 192 final List<String> list = stack.get(); 193 if (list == null || list.size() == 0) { 194 return false; 195 } 196 final List<String> copy = new ArrayList<String>(list); 197 final boolean result = copy.remove(o); 198 stack.set(Collections.unmodifiableList(copy)); 199 return result; 200 } 201 202 @Override 203 public boolean containsAll(final Collection<?> objects) { 204 if (objects.isEmpty()) { // quick check before accessing the ThreadLocal 205 return true; // looks counter-intuitive, but see 206 // j.u.AbstractCollection 207 } 208 final List<String> list = stack.get(); 209 return list != null && list.containsAll(objects); 210 } 211 212 @Override 213 public boolean addAll(final Collection<? extends String> strings) { 214 if (!useStack || strings.isEmpty()) { 215 return false; 216 } 217 final List<String> list = stack.get(); 218 final List<String> copy = list == null ? new ArrayList<String>() 219 : new ArrayList<String>(list); 220 copy.addAll(strings); 221 stack.set(Collections.unmodifiableList(copy)); 222 return true; 223 } 224 225 @Override 226 public boolean removeAll(final Collection<?> objects) { 227 if (!useStack || objects.isEmpty()) { 228 return false; 229 } 230 final List<String> list = stack.get(); 231 if (list == null || list.isEmpty()) { 232 return false; 233 } 234 final List<String> copy = new ArrayList<String>(list); 235 final boolean result = copy.removeAll(objects); 236 stack.set(Collections.unmodifiableList(copy)); 237 return result; 238 } 239 240 @Override 241 public boolean retainAll(final Collection<?> objects) { 242 if (!useStack || objects.isEmpty()) { 243 return false; 244 } 245 final List<String> list = stack.get(); 246 if (list == null || list.isEmpty()) { 247 return false; 248 } 249 final List<String> copy = new ArrayList<String>(list); 250 final boolean result = copy.retainAll(objects); 251 stack.set(Collections.unmodifiableList(copy)); 252 return result; 253 } 254 255 @Override 256 public String toString() { 257 final List<String> list = stack.get(); 258 return list == null ? "[]" : list.toString(); 259 } 260}