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