001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.spi;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.Collections;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.NoSuchElementException;
025    
026    import org.apache.logging.log4j.util.Strings;
027    
028    /**
029     * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative operations (add,
030     * pop, and so on) are implemented by making a fresh copy of the underlying list.
031     */
032    public class DefaultThreadContextStack implements ThreadContextStack {
033    
034        private static final long serialVersionUID = 5050501L;
035    
036        private static final ThreadLocal<List<String>> stack = new ThreadLocal<List<String>>();
037    
038        private final boolean useStack;
039    
040        public DefaultThreadContextStack(final boolean useStack) {
041            this.useStack = useStack;
042        }
043    
044        @Override
045        public boolean add(final String s) {
046            if (!useStack) {
047                return false;
048            }
049            final List<String> list = stack.get();
050            final List<String> copy = list == null ? new ArrayList<String>() : new ArrayList<String>(list);
051            copy.add(s);
052            stack.set(Collections.unmodifiableList(copy));
053            return true;
054        }
055    
056        @Override
057        public boolean addAll(final Collection<? extends String> strings) {
058            if (!useStack || strings.isEmpty()) {
059                return false;
060            }
061            final List<String> list = stack.get();
062            final List<String> copy = list == null ? new ArrayList<String>() : new ArrayList<String>(list);
063            copy.addAll(strings);
064            stack.set(Collections.unmodifiableList(copy));
065            return true;
066        }
067    
068        @Override
069        public List<String> asList() {
070            final List<String> list = stack.get();
071            if (list == null) {
072                return Collections.emptyList();
073            }
074            return list;
075        }
076    
077        @Override
078        public void clear() {
079            stack.remove();
080        }
081    
082        @Override
083        public boolean contains(final Object o) {
084            final List<String> result = stack.get();
085            return result != null && result.contains(o);
086        }
087    
088        @Override
089        public boolean containsAll(final Collection<?> objects) {
090            if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
091                return true; // looks counter-intuitive, but see
092                             // j.u.AbstractCollection
093            }
094            final List<String> list = stack.get();
095            return list != null && list.containsAll(objects);
096        }
097    
098        @Override
099        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    }