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 */
017package org.apache.logging.log4j.spi;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Iterator;
022import java.util.List;
023import java.util.NoSuchElementException;
024
025import org.apache.logging.log4j.ThreadContext.ContextStack;
026import 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 */
032public 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}