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.Collection;
020 import java.util.Collections;
021 import java.util.Iterator;
022 import java.util.List;
023 import java.util.NoSuchElementException;
024
025 import org.apache.logging.log4j.ThreadContext.ContextStack;
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<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 }