1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache license, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the license for the specific language governing permissions and
15  * limitations under the license.
16  */

17 package org.apache.logging.log4j.spi;
18
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23
24 import org.apache.logging.log4j.ThreadContext.ContextStack;
25 import org.apache.logging.log4j.util.StringBuilderFormattable;
26 import org.apache.logging.log4j.util.StringBuilders;
27 import org.apache.logging.log4j.util.Strings;
28
29 /**
30  * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative
31  * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list.
32  */

33 public class DefaultThreadContextStack implements ThreadContextStack, StringBuilderFormattable {
34
35     private static final Object[] EMPTY_OBJECT_ARRAY = {};
36
37     private static final long serialVersionUID = 5050501L;
38
39     private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>();
40
41     private final boolean useStack;
42
43     public DefaultThreadContextStack(final boolean useStack) {
44         this.useStack = useStack;
45     }
46
47     private MutableThreadContextStack getNonNullStackCopy() {
48         final MutableThreadContextStack values = STACK.get();
49         return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
50     }
51
52     @Override
53     public boolean add(final String s) {
54         if (!useStack) {
55             return false;
56         }
57         final MutableThreadContextStack copy = getNonNullStackCopy();
58         copy.add(s);
59         copy.freeze();
60         STACK.set(copy);
61         return true;
62     }
63
64     @Override
65     public boolean addAll(final Collection<? extends String> strings) {
66         if (!useStack || strings.isEmpty()) {
67             return false;
68         }
69         final MutableThreadContextStack copy = getNonNullStackCopy();
70         copy.addAll(strings);
71         copy.freeze();
72         STACK.set(copy);
73         return true;
74     }
75
76     @Override
77     public List<String> asList() {
78         final MutableThreadContextStack values = STACK.get();
79         if (values == null) {
80             return Collections.emptyList();
81         }
82         return values.asList();
83     }
84
85     @Override
86     public void clear() {
87         STACK.remove();
88     }
89
90     @Override
91     public boolean contains(final Object o) {
92         final MutableThreadContextStack values = STACK.get();
93         return values != null && values.contains(o);
94     }
95
96     @Override
97     public boolean containsAll(final Collection<?> objects) {
98         if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
99             return true// looks counter-intuitive, but see
100                          // j.u.AbstractCollection
101         }
102         final MutableThreadContextStack values = STACK.get();
103         return values != null && values.containsAll(objects);
104     }
105
106     @Override
107     public ThreadContextStack copy() {
108         MutableThreadContextStack values = null;
109         if (!useStack || (values = STACK.get()) == null) {
110             return new MutableThreadContextStack();
111         }
112         return values.copy();
113     }
114
115     @Override
116     public boolean equals(final Object obj) {
117         if (this == obj) {
118             return true;
119         }
120         if (obj == null) {
121             return false;
122         }
123         if (obj instanceof DefaultThreadContextStack) {
124             final DefaultThreadContextStack other = (DefaultThreadContextStack) obj;
125             if (this.useStack != other.useStack) {
126                 return false;
127             }
128         }
129         if (!(obj instanceof ThreadContextStack)) {
130             return false;
131         }
132         final ThreadContextStack other = (ThreadContextStack) obj;
133         final MutableThreadContextStack values = STACK.get();
134         if (values == null) {
135             return false;
136         }
137         return values.equals(other);
138     }
139
140     @Override
141     public int getDepth() {
142         final MutableThreadContextStack values = STACK.get();
143         return values == null ? 0 : values.getDepth();
144     }
145
146     @Override
147     public int hashCode() {
148         final MutableThreadContextStack values = STACK.get();
149         final int prime = 31;
150         int result = 1;
151         // Factor in the stack itself to compare vs. other implementors.
152         result = prime * result + ((values == null) ? 0 : values.hashCode());
153         return result;
154     }
155
156     @Override
157     public boolean isEmpty() {
158         final MutableThreadContextStack values = STACK.get();
159         return values == null || values.isEmpty();
160     }
161
162     @Override
163     public Iterator<String> iterator() {
164         final MutableThreadContextStack values = STACK.get();
165         if (values == null) {
166             final List<String> empty = Collections.emptyList();
167             return empty.iterator();
168         }
169         return values.iterator();
170     }
171
172     @Override
173     public String peek() {
174         final MutableThreadContextStack values = STACK.get();
175         if (values == null || values.isEmpty()) {
176             return Strings.EMPTY;
177         }
178         return values.peek();
179     }
180
181     @Override
182     public String pop() {
183         if (!useStack) {
184             return Strings.EMPTY;
185         }
186         final MutableThreadContextStack values = STACK.get();
187         if (values == null || values.isEmpty()) {
188             // Like version 1.2
189             return Strings.EMPTY;
190         }
191         final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
192         final String result = copy.pop();
193         copy.freeze();
194         STACK.set(copy);
195         return result;
196     }
197
198     @Override
199     public void push(final String message) {
200         if (!useStack) {
201             return;
202         }
203         add(message);
204     }
205
206     @Override
207     public boolean remove(final Object o) {
208         if (!useStack) {
209             return false;
210         }
211         final MutableThreadContextStack values = STACK.get();
212         if (values == null || values.isEmpty()) {
213             return false;
214         }
215         final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
216         final boolean result = copy.remove(o);
217         copy.freeze();
218         STACK.set(copy);
219         return result;
220     }
221
222     @Override
223     public boolean removeAll(final Collection<?> objects) {
224         if (!useStack || objects.isEmpty()) {
225             return false;
226         }
227         final MutableThreadContextStack values = STACK.get();
228         if (values == null || values.isEmpty()) {
229             return false;
230         }
231         final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
232         final boolean result = copy.removeAll(objects);
233         copy.freeze();
234         STACK.set(copy);
235         return result;
236     }
237
238     @Override
239     public boolean retainAll(final Collection<?> objects) {
240         if (!useStack || objects.isEmpty()) {
241             return false;
242         }
243         final MutableThreadContextStack values = STACK.get();
244         if (values == null || values.isEmpty()) {
245             return false;
246         }
247         final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
248         final boolean result = copy.retainAll(objects);
249         copy.freeze();
250         STACK.set(copy);
251         return result;
252     }
253
254     @Override
255     public int size() {
256         final MutableThreadContextStack values = STACK.get();
257         return values == null ? 0 : values.size();
258     }
259
260     @Override
261     public Object[] toArray() {
262         final MutableThreadContextStack result = STACK.get();
263         if (result == null) {
264             return Strings.EMPTY_ARRAY;
265         }
266         return result.toArray(EMPTY_OBJECT_ARRAY);
267     }
268
269     @Override
270     public <T> T[] toArray(final T[] ts) {
271         final MutableThreadContextStack result = STACK.get();
272         if (result == null) {
273             if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
274                 ts[0] = null;
275             }
276             return ts;
277         }
278         return result.toArray(ts);
279     }
280
281     @Override
282     public String toString() {
283         final MutableThreadContextStack values = STACK.get();
284         return values == null ? "[]" : values.toString();
285     }
286
287     @Override
288     public void formatTo(final StringBuilder buffer) {
289         final MutableThreadContextStack values = STACK.get();
290         if (values == null) {
291             buffer.append("[]");
292         } else {
293             StringBuilders.appendValue(buffer, values);
294         }
295     }
296
297     @Override
298     public void trim(final int depth) {
299         if (depth < 0) {
300             throw new IllegalArgumentException("Maximum stack depth cannot be negative");
301         }
302         final MutableThreadContextStack values = STACK.get();
303         if (values == null) {
304             return;
305         }
306         final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
307         copy.trim(depth);
308         copy.freeze();
309         STACK.set(copy);
310     }
311
312     /*
313      * (non-Javadoc)
314      *
315      * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull()
316      */

317     @Override
318     public ContextStack getImmutableStackOrNull() {
319         return STACK.get();
320     }
321 }
322