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