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.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  
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<List<String>> stack = new ThreadLocal<List<String>>();
37  
38      private final boolean useStack;
39  
40      public DefaultThreadContextStack(final boolean useStack) {
41          this.useStack = useStack;
42      }
43  
44      @Override
45      public boolean add(final String s) {
46          if (!useStack) {
47              return false;
48          }
49          final List<String> list = stack.get();
50          final List<String> copy = list == null ? new ArrayList<String>() : new ArrayList<String>(list);
51          copy.add(s);
52          stack.set(Collections.unmodifiableList(copy));
53          return true;
54      }
55  
56      @Override
57      public boolean addAll(final Collection<? extends String> strings) {
58          if (!useStack || strings.isEmpty()) {
59              return false;
60          }
61          final List<String> list = stack.get();
62          final List<String> copy = list == null ? new ArrayList<String>() : new ArrayList<String>(list);
63          copy.addAll(strings);
64          stack.set(Collections.unmodifiableList(copy));
65          return true;
66      }
67  
68      @Override
69      public List<String> asList() {
70          final List<String> list = stack.get();
71          if (list == null) {
72              return Collections.emptyList();
73          }
74          return list;
75      }
76  
77      @Override
78      public void clear() {
79          stack.remove();
80      }
81  
82      @Override
83      public boolean contains(final Object o) {
84          final List<String> result = stack.get();
85          return result != null && result.contains(o);
86      }
87  
88      @Override
89      public boolean containsAll(final Collection<?> objects) {
90          if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
91              return true; // looks counter-intuitive, but see
92                           // j.u.AbstractCollection
93          }
94          final List<String> list = stack.get();
95          return list != null && list.containsAll(objects);
96      }
97  
98      @Override
99      public ThreadContextStack copy() {
100         List<String> result = null;
101         if (!useStack || (result = stack.get()) == null) {
102             return new MutableThreadContextStack(new ArrayList<String>());
103         }
104         return new MutableThreadContextStack(result);
105     }
106 
107     @Override
108     public boolean equals(Object obj) {
109         if (this == obj) {
110             return true;
111         }
112         if (obj == null) {
113             return false;
114         }
115         if (obj instanceof DefaultThreadContextStack) {
116             DefaultThreadContextStack other = (DefaultThreadContextStack) obj;
117             if (this.useStack != other.useStack) {
118                 return false;
119             }
120         }
121         if (!(obj instanceof ThreadContextStack)) {
122             return false;
123         }
124         ThreadContextStack other = (ThreadContextStack) obj;
125         final List<String> otherAsList = other.asList();
126         final List<String> list = stack.get();
127         if (list == null) {
128             if (otherAsList != null) {
129                 return false;
130             }
131         } else if (!list.equals(otherAsList)) {
132             return false;
133         }
134         return true;
135     }
136 
137     @Override
138     public int getDepth() {
139         final List<String> list = stack.get();
140         return list == null ? 0 : list.size();
141     }
142 
143     @Override
144     public int hashCode() {
145         final List<String> list = 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 + ((list == null) ? 0 : list.hashCode());
150         return result;
151     }
152 
153     @Override
154     public boolean isEmpty() {
155         final List<String> result = stack.get();
156         return result == null || result.isEmpty();
157     }
158 
159     @Override
160     public Iterator<String> iterator() {
161         final List<String> immutable = stack.get();
162         if (immutable == null) {
163             final List<String> empty = Collections.emptyList();
164             return empty.iterator();
165         }
166         return immutable.iterator();
167     }
168 
169     @Override
170     public String peek() {
171         final List<String> list = stack.get();
172         if (list == null || list.size() == 0) {
173             return null;
174         }
175         final int last = list.size() - 1;
176         return list.get(last);
177     }
178 
179     @Override
180     public String pop() {
181         if (!useStack) {
182             return Strings.EMPTY;
183         }
184         final List<String> list = stack.get();
185         if (list == null || list.size() == 0) {
186             throw new NoSuchElementException("The ThreadContext stack is empty");
187         }
188         final List<String> copy = new ArrayList<String>(list);
189         final int last = copy.size() - 1;
190         final String result = copy.remove(last);
191         stack.set(Collections.unmodifiableList(copy));
192         return result;
193     }
194 
195     @Override
196     public void push(final String message) {
197         if (!useStack) {
198             return;
199         }
200         add(message);
201     }
202 
203     @Override
204     public boolean remove(final Object o) {
205         if (!useStack) {
206             return false;
207         }
208         final List<String> list = stack.get();
209         if (list == null || list.size() == 0) {
210             return false;
211         }
212         final List<String> copy = new ArrayList<String>(list);
213         final boolean result = copy.remove(o);
214         stack.set(Collections.unmodifiableList(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 List<String> list = stack.get();
224         if (list == null || list.isEmpty()) {
225             return false;
226         }
227         final List<String> copy = new ArrayList<String>(list);
228         final boolean result = copy.removeAll(objects);
229         stack.set(Collections.unmodifiableList(copy));
230         return result;
231     }
232 
233     @Override
234     public boolean retainAll(final Collection<?> objects) {
235         if (!useStack || objects.isEmpty()) {
236             return false;
237         }
238         final List<String> list = stack.get();
239         if (list == null || list.isEmpty()) {
240             return false;
241         }
242         final List<String> copy = new ArrayList<String>(list);
243         final boolean result = copy.retainAll(objects);
244         stack.set(Collections.unmodifiableList(copy));
245         return result;
246     }
247 
248     @Override
249     public int size() {
250         final List<String> result = stack.get();
251         return result == null ? 0 : result.size();
252     }
253 
254     @Override
255     public Object[] toArray() {
256         final List<String> result = stack.get();
257         if (result == null) {
258             return new String[0];
259         }
260         return result.toArray(new Object[result.size()]);
261     }
262 
263     @Override
264     public <T> T[] toArray(final T[] ts) {
265         final List<String> result = stack.get();
266         if (result == null) {
267             if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
268                 ts[0] = null;
269             }
270             return ts;
271         }
272         return result.toArray(ts);
273     }
274 
275     @Override
276     public String toString() {
277         final List<String> list = stack.get();
278         return list == null ? "[]" : list.toString();
279     }
280 
281     @Override
282     public void trim(final int depth) {
283         if (depth < 0) {
284             throw new IllegalArgumentException("Maximum stack depth cannot be negative");
285         }
286         final List<String> list = stack.get();
287         if (list == null) {
288             return;
289         }
290         final List<String> copy = new ArrayList<String>();
291         final int count = Math.min(depth, list.size());
292         for (int i = 0; i < count; i++) {
293             copy.add(list.get(i));
294         }
295         stack.set(copy);
296     }
297 }