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.Collection;
020    import java.util.Collections;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.NoSuchElementException;
024    
025    import org.apache.logging.log4j.ThreadContext.ContextStack;
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<MutableThreadContextStack> stack = new ThreadLocal<MutableThreadContextStack>();
037    
038        private final boolean useStack;
039    
040        public DefaultThreadContextStack(final boolean useStack) {
041            this.useStack = useStack;
042        }
043        
044        private MutableThreadContextStack getNonNullStackCopy() {
045            final MutableThreadContextStack values = stack.get();
046            return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
047        }
048    
049        @Override
050        public boolean add(final String s) {
051            if (!useStack) {
052                return false;
053            }
054            final MutableThreadContextStack copy = getNonNullStackCopy();
055            copy.add(s);
056            copy.freeze();
057            stack.set(copy);
058            return true;
059        }
060    
061        @Override
062        public boolean addAll(final Collection<? extends String> strings) {
063            if (!useStack || strings.isEmpty()) {
064                return false;
065            }
066            final MutableThreadContextStack copy = getNonNullStackCopy();
067            copy.addAll(strings);
068            copy.freeze();
069            stack.set(copy);
070            return true;
071        }
072    
073        @Override
074        public List<String> asList() {
075            final MutableThreadContextStack values = stack.get();
076            if (values == null) {
077                return Collections.emptyList();
078            }
079            return values.asList();
080        }
081    
082        @Override
083        public void clear() {
084            stack.remove();
085        }
086    
087        @Override
088        public boolean contains(final Object o) {
089            final MutableThreadContextStack values = stack.get();
090            return values != null && values.contains(o);
091        }
092    
093        @Override
094        public boolean containsAll(final Collection<?> objects) {
095            if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
096                return true; // looks counter-intuitive, but see
097                             // j.u.AbstractCollection
098            }
099            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    }