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  import java.util.NoSuchElementException;
24  
25  import org.apache.logging.log4j.ThreadContext.ContextStack;
26  import org.apache.logging.log4j.util.Strings;
27  
28  /**
29   * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative operations (add,
30   * pop, and so on) are implemented by making a fresh copy of the underlying list.
31   */
32  public class DefaultThreadContextStack implements ThreadContextStack {
33  
34      private static final long serialVersionUID = 5050501L;
35  
36      private static final ThreadLocal<MutableThreadContextStack> stack = new ThreadLocal<MutableThreadContextStack>();
37  
38      private final boolean useStack;
39  
40      public DefaultThreadContextStack(final boolean useStack) {
41          this.useStack = useStack;
42      }
43      
44      private MutableThreadContextStack getNonNullStackCopy() {
45          final MutableThreadContextStack values = stack.get();
46          return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
47      }
48  
49      @Override
50      public boolean add(final String s) {
51          if (!useStack) {
52              return false;
53          }
54          final MutableThreadContextStack copy = getNonNullStackCopy();
55          copy.add(s);
56          copy.freeze();
57          stack.set(copy);
58          return true;
59      }
60  
61      @Override
62      public boolean addAll(final Collection<? extends String> strings) {
63          if (!useStack || strings.isEmpty()) {
64              return false;
65          }
66          final MutableThreadContextStack copy = getNonNullStackCopy();
67          copy.addAll(strings);
68          copy.freeze();
69          stack.set(copy);
70          return true;
71      }
72  
73      @Override
74      public List<String> asList() {
75          final MutableThreadContextStack values = stack.get();
76          if (values == null) {
77              return Collections.emptyList();
78          }
79          return values.asList();
80      }
81  
82      @Override
83      public void clear() {
84          stack.remove();
85      }
86  
87      @Override
88      public boolean contains(final Object o) {
89          final MutableThreadContextStack values = stack.get();
90          return values != null && values.contains(o);
91      }
92  
93      @Override
94      public boolean containsAll(final Collection<?> objects) {
95          if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
96              return true; // looks counter-intuitive, but see
97                           // j.u.AbstractCollection
98          }
99          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 }