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;
023
024import org.apache.logging.log4j.ThreadContext.ContextStack;
025import org.apache.logging.log4j.util.StringBuilderFormattable;
026import org.apache.logging.log4j.util.StringBuilders;
027import org.apache.logging.log4j.util.Strings;
028
029/**
030 * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative
031 * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list.
032 */
033public class DefaultThreadContextStack implements ThreadContextStack, StringBuilderFormattable {
034
035    private static final Object[] EMPTY_OBJECT_ARRAY = {};
036
037    private static final long serialVersionUID = 5050501L;
038
039    private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>();
040
041    private final boolean useStack;
042
043    public DefaultThreadContextStack(final boolean useStack) {
044        this.useStack = useStack;
045    }
046
047    private MutableThreadContextStack getNonNullStackCopy() {
048        final MutableThreadContextStack values = STACK.get();
049        return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
050    }
051
052    @Override
053    public boolean add(final String s) {
054        if (!useStack) {
055            return false;
056        }
057        final MutableThreadContextStack copy = getNonNullStackCopy();
058        copy.add(s);
059        copy.freeze();
060        STACK.set(copy);
061        return true;
062    }
063
064    @Override
065    public boolean addAll(final Collection<? extends String> strings) {
066        if (!useStack || strings.isEmpty()) {
067            return false;
068        }
069        final MutableThreadContextStack copy = getNonNullStackCopy();
070        copy.addAll(strings);
071        copy.freeze();
072        STACK.set(copy);
073        return true;
074    }
075
076    @Override
077    public List<String> asList() {
078        final MutableThreadContextStack values = STACK.get();
079        if (values == null) {
080            return Collections.emptyList();
081        }
082        return values.asList();
083    }
084
085    @Override
086    public void clear() {
087        STACK.remove();
088    }
089
090    @Override
091    public boolean contains(final Object o) {
092        final MutableThreadContextStack values = STACK.get();
093        return values != null && values.contains(o);
094    }
095
096    @Override
097    public boolean containsAll(final Collection<?> objects) {
098        if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
099            return true; // looks counter-intuitive, but see
100                         // j.u.AbstractCollection
101        }
102        final MutableThreadContextStack values = STACK.get();
103        return values != null && values.containsAll(objects);
104    }
105
106    @Override
107    public ThreadContextStack copy() {
108        MutableThreadContextStack values = null;
109        if (!useStack || (values = STACK.get()) == null) {
110            return new MutableThreadContextStack();
111        }
112        return values.copy();
113    }
114
115    @Override
116    public boolean equals(final Object obj) {
117        if (this == obj) {
118            return true;
119        }
120        if (obj == null) {
121            return false;
122        }
123        if (obj instanceof DefaultThreadContextStack) {
124            final DefaultThreadContextStack other = (DefaultThreadContextStack) obj;
125            if (this.useStack != other.useStack) {
126                return false;
127            }
128        }
129        if (!(obj instanceof ThreadContextStack)) {
130            return false;
131        }
132        final ThreadContextStack other = (ThreadContextStack) obj;
133        final MutableThreadContextStack values = STACK.get();
134        if (values == null) {
135            return false;
136        }
137        return values.equals(other);
138    }
139
140    @Override
141    public int getDepth() {
142        final MutableThreadContextStack values = STACK.get();
143        return values == null ? 0 : values.getDepth();
144    }
145
146    @Override
147    public int hashCode() {
148        final MutableThreadContextStack values = STACK.get();
149        final int prime = 31;
150        int result = 1;
151        // Factor in the stack itself to compare vs. other implementors.
152        result = prime * result + ((values == null) ? 0 : values.hashCode());
153        return result;
154    }
155
156    @Override
157    public boolean isEmpty() {
158        final MutableThreadContextStack values = STACK.get();
159        return values == null || values.isEmpty();
160    }
161
162    @Override
163    public Iterator<String> iterator() {
164        final MutableThreadContextStack values = STACK.get();
165        if (values == null) {
166            final List<String> empty = Collections.emptyList();
167            return empty.iterator();
168        }
169        return values.iterator();
170    }
171
172    @Override
173    public String peek() {
174        final MutableThreadContextStack values = STACK.get();
175        if (values == null || values.isEmpty()) {
176            return Strings.EMPTY;
177        }
178        return values.peek();
179    }
180
181    @Override
182    public String pop() {
183        if (!useStack) {
184            return Strings.EMPTY;
185        }
186        final MutableThreadContextStack values = STACK.get();
187        if (values == null || values.isEmpty()) {
188            // Like version 1.2
189            return Strings.EMPTY;
190        }
191        final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
192        final String result = copy.pop();
193        copy.freeze();
194        STACK.set(copy);
195        return result;
196    }
197
198    @Override
199    public void push(final String message) {
200        if (!useStack) {
201            return;
202        }
203        add(message);
204    }
205
206    @Override
207    public boolean remove(final Object o) {
208        if (!useStack) {
209            return false;
210        }
211        final MutableThreadContextStack values = STACK.get();
212        if (values == null || values.isEmpty()) {
213            return false;
214        }
215        final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
216        final boolean result = copy.remove(o);
217        copy.freeze();
218        STACK.set(copy);
219        return result;
220    }
221
222    @Override
223    public boolean removeAll(final Collection<?> objects) {
224        if (!useStack || objects.isEmpty()) {
225            return false;
226        }
227        final MutableThreadContextStack values = STACK.get();
228        if (values == null || values.isEmpty()) {
229            return false;
230        }
231        final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
232        final boolean result = copy.removeAll(objects);
233        copy.freeze();
234        STACK.set(copy);
235        return result;
236    }
237
238    @Override
239    public boolean retainAll(final Collection<?> objects) {
240        if (!useStack || objects.isEmpty()) {
241            return false;
242        }
243        final MutableThreadContextStack values = STACK.get();
244        if (values == null || values.isEmpty()) {
245            return false;
246        }
247        final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
248        final boolean result = copy.retainAll(objects);
249        copy.freeze();
250        STACK.set(copy);
251        return result;
252    }
253
254    @Override
255    public int size() {
256        final MutableThreadContextStack values = STACK.get();
257        return values == null ? 0 : values.size();
258    }
259
260    @Override
261    public Object[] toArray() {
262        final MutableThreadContextStack result = STACK.get();
263        if (result == null) {
264            return Strings.EMPTY_ARRAY;
265        }
266        return result.toArray(EMPTY_OBJECT_ARRAY);
267    }
268
269    @Override
270    public <T> T[] toArray(final T[] ts) {
271        final MutableThreadContextStack result = STACK.get();
272        if (result == null) {
273            if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
274                ts[0] = null;
275            }
276            return ts;
277        }
278        return result.toArray(ts);
279    }
280
281    @Override
282    public String toString() {
283        final MutableThreadContextStack values = STACK.get();
284        return values == null ? "[]" : values.toString();
285    }
286
287    @Override
288    public void formatTo(final StringBuilder buffer) {
289        final MutableThreadContextStack values = STACK.get();
290        if (values == null) {
291            buffer.append("[]");
292        } else {
293            StringBuilders.appendValue(buffer, values);
294        }
295    }
296
297    @Override
298    public void trim(final int depth) {
299        if (depth < 0) {
300            throw new IllegalArgumentException("Maximum stack depth cannot be negative");
301        }
302        final MutableThreadContextStack values = STACK.get();
303        if (values == null) {
304            return;
305        }
306        final MutableThreadContextStack copy = (MutableThreadContextStack) values.copy();
307        copy.trim(depth);
308        copy.freeze();
309        STACK.set(copy);
310    }
311
312    /*
313     * (non-Javadoc)
314     *
315     * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull()
316     */
317    @Override
318    public ContextStack getImmutableStackOrNull() {
319        return STACK.get();
320    }
321}