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