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.Strings;
26  
27  /**
28   * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative
29   * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list.
30   */
31  public class DefaultThreadContextStack implements ThreadContextStack {
32  
33      private static final long serialVersionUID = 5050501L;
34  
35      private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>();
36  
37      private final boolean useStack;
38  
39      public DefaultThreadContextStack(final boolean useStack) {
40          this.useStack = useStack;
41      }
42  
43      private MutableThreadContextStack getNonNullStackCopy() {
44          final MutableThreadContextStack values = STACK.get();
45          return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
46      }
47  
48      @Override
49      public boolean add(final String s) {
50          if (!useStack) {
51              return false;
52          }
53          final MutableThreadContextStack copy = getNonNullStackCopy();
54          copy.add(s);
55          copy.freeze();
56          STACK.set(copy);
57          return true;
58      }
59  
60      @Override
61      public boolean addAll(final Collection<? extends String> strings) {
62          if (!useStack || strings.isEmpty()) {
63              return false;
64          }
65          final MutableThreadContextStack copy = getNonNullStackCopy();
66          copy.addAll(strings);
67          copy.freeze();
68          STACK.set(copy);
69          return true;
70      }
71  
72      @Override
73      public List<String> asList() {
74          final MutableThreadContextStack values = STACK.get();
75          if (values == null) {
76              return Collections.emptyList();
77          }
78          return values.asList();
79      }
80  
81      @Override
82      public void clear() {
83          STACK.remove();
84      }
85  
86      @Override
87      public boolean contains(final Object o) {
88          final MutableThreadContextStack values = STACK.get();
89          return values != null && values.contains(o);
90      }
91  
92      @Override
93      public boolean containsAll(final Collection<?> objects) {
94          if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
95              return true; // looks counter-intuitive, but see
96                           // j.u.AbstractCollection
97          }
98          final MutableThreadContextStack values = STACK.get();
99          return values != null && values.containsAll(objects);
100     }
101 
102     @Override
103     public ThreadContextStack copy() {
104         MutableThreadContextStack values = null;
105         if (!useStack || (values = STACK.get()) == null) {
106             return new MutableThreadContextStack();
107         }
108         return values.copy();
109     }
110 
111     @Override
112     public boolean equals(final Object obj) {
113         if (this == obj) {
114             return true;
115         }
116         if (obj == null) {
117             return false;
118         }
119         if (obj instanceof DefaultThreadContextStack) {
120             final DefaultThreadContextStack other = (DefaultThreadContextStack) obj;
121             if (this.useStack != other.useStack) {
122                 return false;
123             }
124         }
125         if (!(obj instanceof ThreadContextStack)) {
126             return false;
127         }
128         final ThreadContextStack other = (ThreadContextStack) obj;
129         final MutableThreadContextStack values = STACK.get();
130         if (values == null) {
131             return false;
132         }
133         return values.equals(other);
134     }
135 
136     @Override
137     public int getDepth() {
138         final MutableThreadContextStack values = STACK.get();
139         return values == null ? 0 : values.getDepth();
140     }
141 
142     @Override
143     public int hashCode() {
144         final MutableThreadContextStack values = STACK.get();
145         final int prime = 31;
146         int result = 1;
147         // Factor in the stack itself to compare vs. other implementors.
148         result = prime * result + ((values == null) ? 0 : values.hashCode());
149         return result;
150     }
151 
152     @Override
153     public boolean isEmpty() {
154         final MutableThreadContextStack values = STACK.get();
155         return values == null || values.isEmpty();
156     }
157 
158     @Override
159     public Iterator<String> iterator() {
160         final MutableThreadContextStack values = STACK.get();
161         if (values == null) {
162             final List<String> empty = Collections.emptyList();
163             return empty.iterator();
164         }
165         return values.iterator();
166     }
167 
168     @Override
169     public String peek() {
170         final MutableThreadContextStack values = STACK.get();
171         if (values == null || values.size() == 0) {
172             return Strings.EMPTY;
173         }
174         return values.peek();
175     }
176 
177     @Override
178     public String pop() {
179         if (!useStack) {
180             return Strings.EMPTY;
181         }
182         final MutableThreadContextStack values = STACK.get();
183         if (values == null || values.size() == 0) {
184             // Like version 1.2
185             return Strings.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     /*
299      * (non-Javadoc)
300      * 
301      * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull()
302      */
303     @Override
304     public ContextStack getImmutableStackOrNull() {
305         return STACK.get();
306     }
307 }