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  /**
27   * A copy-on-write thread-safe variant of
28   * {@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
30   * the underlying list.
31   */
32  public class DefaultThreadContextStack implements ThreadContextStack {
33  
34      private static final long serialVersionUID = 5050501L;
35  
36      private static 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 String pop() {
46          if (!useStack) {
47              return "";
48          }
49          final List<String> list = stack.get();
50          if (list == null || list.size() == 0) {
51              throw new NoSuchElementException("The ThreadContext stack is empty");
52          }
53          final List<String> copy = new ArrayList<String>(list);
54          final int last = copy.size() - 1;
55          final String result = copy.remove(last);
56          stack.set(Collections.unmodifiableList(copy));
57          return result;
58      }
59  
60      @Override
61      public String peek() {
62          final List<String> list = stack.get();
63          if (list == null || list.size() == 0) {
64              return null;
65          }
66          final int last = list.size() - 1;
67          return list.get(last);
68      }
69  
70      @Override
71      public void push(final String message) {
72          if (!useStack) {
73              return;
74          }
75          add(message);
76      }
77  
78      @Override
79      public int getDepth() {
80          final List<String> list = stack.get();
81          return list == null ? 0 : list.size();
82      }
83  
84      @Override
85      public List<String> asList() {
86          final List<String> list = stack.get();
87          if (list == null) {
88              return Collections.emptyList();
89          }
90          return list;
91      }
92  
93      @Override
94      public void trim(final int depth) {
95          if (depth < 0) {
96              throw new IllegalArgumentException(
97                      "Maximum stack depth cannot be negative");
98          }
99          final List<String> list = stack.get();
100         if (list == null) {
101             return;
102         }
103         final List<String> copy = new ArrayList<String>();
104         final int count = Math.min(depth, list.size());
105         for (int i = 0; i < count; i++) {
106             copy.add(list.get(i));
107         }
108         stack.set(copy);
109     }
110 
111     @Override
112     public ThreadContextStack copy() {
113         List<String> result = null;
114         if (!useStack || (result = stack.get()) == null) {
115             return new MutableThreadContextStack(new ArrayList<String>());
116         }
117         return new MutableThreadContextStack(result);
118     }
119 
120     @Override
121     public void clear() {
122         stack.remove();
123     }
124 
125     @Override
126     public int size() {
127         final List<String> result = stack.get();
128         return result == null ? 0 : result.size();
129     }
130 
131     @Override
132     public boolean isEmpty() {
133         final List<String> result = stack.get();
134         return result == null || result.isEmpty();
135     }
136 
137     @Override
138     public boolean contains(final Object o) {
139         final List<String> result = stack.get();
140         return result != null && result.contains(o);
141     }
142 
143     @Override
144     public Iterator<String> iterator() {
145         final List<String> immutable = stack.get();
146         if (immutable == null) {
147             final List<String> empty = Collections.emptyList();
148             return empty.iterator();
149         }
150         return immutable.iterator();
151     }
152 
153     @Override
154     public Object[] toArray() {
155         final List<String> result = stack.get();
156         if (result == null) {
157             return new String[0];
158         }
159         return result.toArray(new Object[result.size()]);
160     }
161 
162     @Override
163     public <T> T[] toArray(final T[] ts) {
164         final List<String> result = stack.get();
165         if (result == null) {
166             if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
167                 ts[0] = null;
168             }
169             return ts;
170         }
171         return result.toArray(ts);
172     }
173 
174     @Override
175     public boolean add(final String s) {
176         if (!useStack) {
177             return false;
178         }
179         final List<String> list = stack.get();
180         final List<String> copy = list == null ? new ArrayList<String>()
181                 : new ArrayList<String>(list);
182         copy.add(s);
183         stack.set(Collections.unmodifiableList(copy));
184         return true;
185     }
186 
187     @Override
188     public boolean remove(final Object o) {
189         if (!useStack) {
190             return false;
191         }
192         final List<String> list = stack.get();
193         if (list == null || list.size() == 0) {
194             return false;
195         }
196         final List<String> copy = new ArrayList<String>(list);
197         final boolean result = copy.remove(o);
198         stack.set(Collections.unmodifiableList(copy));
199         return result;
200     }
201 
202     @Override
203     public boolean containsAll(final Collection<?> objects) {
204         if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
205             return true; // looks counter-intuitive, but see
206                          // j.u.AbstractCollection
207         }
208         final List<String> list = stack.get();
209         return list != null && list.containsAll(objects);
210     }
211 
212     @Override
213     public boolean addAll(final Collection<? extends String> strings) {
214         if (!useStack || strings.isEmpty()) {
215             return false;
216         }
217         final List<String> list = stack.get();
218         final List<String> copy = list == null ? new ArrayList<String>()
219                 : new ArrayList<String>(list);
220         copy.addAll(strings);
221         stack.set(Collections.unmodifiableList(copy));
222         return true;
223     }
224 
225     @Override
226     public boolean removeAll(final Collection<?> objects) {
227         if (!useStack || objects.isEmpty()) {
228             return false;
229         }
230         final List<String> list = stack.get();
231         if (list == null || list.isEmpty()) {
232             return false;
233         }
234         final List<String> copy = new ArrayList<String>(list);
235         final boolean result = copy.removeAll(objects);
236         stack.set(Collections.unmodifiableList(copy));
237         return result;
238     }
239 
240     @Override
241     public boolean retainAll(final Collection<?> objects) {
242         if (!useStack || objects.isEmpty()) {
243             return false;
244         }
245         final List<String> list = stack.get();
246         if (list == null || list.isEmpty()) {
247             return false;
248         }
249         final List<String> copy = new ArrayList<String>(list);
250         final boolean result = copy.retainAll(objects);
251         stack.set(Collections.unmodifiableList(copy));
252         return result;
253     }
254 
255     @Override
256     public String toString() {
257         final List<String> list = stack.get();
258         return list == null ? "[]" : list.toString();
259     }
260 }