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.Strings;
026
027/**
028 * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative
029 * operations (add, pop, and so on) are implemented by making a fresh copy of the underlying list.
030 */
031public class DefaultThreadContextStack implements ThreadContextStack {
032
033    private static final long serialVersionUID = 5050501L;
034
035    private static final ThreadLocal<MutableThreadContextStack> STACK = new ThreadLocal<>();
036
037    private final boolean useStack;
038
039    public DefaultThreadContextStack(final boolean useStack) {
040        this.useStack = useStack;
041    }
042
043    private MutableThreadContextStack getNonNullStackCopy() {
044        final MutableThreadContextStack values = STACK.get();
045        return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
046    }
047
048    @Override
049    public boolean add(final String s) {
050        if (!useStack) {
051            return false;
052        }
053        final MutableThreadContextStack copy = getNonNullStackCopy();
054        copy.add(s);
055        copy.freeze();
056        STACK.set(copy);
057        return true;
058    }
059
060    @Override
061    public boolean addAll(final Collection<? extends String> strings) {
062        if (!useStack || strings.isEmpty()) {
063            return false;
064        }
065        final MutableThreadContextStack copy = getNonNullStackCopy();
066        copy.addAll(strings);
067        copy.freeze();
068        STACK.set(copy);
069        return true;
070    }
071
072    @Override
073    public List<String> asList() {
074        final MutableThreadContextStack values = STACK.get();
075        if (values == null) {
076            return Collections.emptyList();
077        }
078        return values.asList();
079    }
080
081    @Override
082    public void clear() {
083        STACK.remove();
084    }
085
086    @Override
087    public boolean contains(final Object o) {
088        final MutableThreadContextStack values = STACK.get();
089        return values != null && values.contains(o);
090    }
091
092    @Override
093    public boolean containsAll(final Collection<?> objects) {
094        if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
095            return true; // looks counter-intuitive, but see
096                         // j.u.AbstractCollection
097        }
098        final MutableThreadContextStack values = STACK.get();
099        return values != null && values.containsAll(objects);
100    }
101
102    @Override
103    public ThreadContextStack copy() {
104        MutableThreadContextStack values = null;
105        if (!useStack || (values = STACK.get()) == null) {
106            return new MutableThreadContextStack();
107        }
108        return values.copy();
109    }
110
111    @Override
112    public boolean equals(final Object obj) {
113        if (this == obj) {
114            return true;
115        }
116        if (obj == null) {
117            return false;
118        }
119        if (obj instanceof DefaultThreadContextStack) {
120            final DefaultThreadContextStack other = (DefaultThreadContextStack) obj;
121            if (this.useStack != other.useStack) {
122                return false;
123            }
124        }
125        if (!(obj instanceof ThreadContextStack)) {
126            return false;
127        }
128        final ThreadContextStack other = (ThreadContextStack) obj;
129        final MutableThreadContextStack values = STACK.get();
130        if (values == null) {
131            return false;
132        }
133        return values.equals(other);
134    }
135
136    @Override
137    public int getDepth() {
138        final MutableThreadContextStack values = STACK.get();
139        return values == null ? 0 : values.getDepth();
140    }
141
142    @Override
143    public int hashCode() {
144        final MutableThreadContextStack values = STACK.get();
145        final int prime = 31;
146        int result = 1;
147        // Factor in the stack itself to compare vs. other implementors.
148        result = prime * result + ((values == null) ? 0 : values.hashCode());
149        return result;
150    }
151
152    @Override
153    public boolean isEmpty() {
154        final MutableThreadContextStack values = STACK.get();
155        return values == null || values.isEmpty();
156    }
157
158    @Override
159    public Iterator<String> iterator() {
160        final MutableThreadContextStack values = STACK.get();
161        if (values == null) {
162            final List<String> empty = Collections.emptyList();
163            return empty.iterator();
164        }
165        return values.iterator();
166    }
167
168    @Override
169    public String peek() {
170        final MutableThreadContextStack values = STACK.get();
171        if (values == null || values.size() == 0) {
172            return Strings.EMPTY;
173        }
174        return values.peek();
175    }
176
177    @Override
178    public String pop() {
179        if (!useStack) {
180            return Strings.EMPTY;
181        }
182        final MutableThreadContextStack values = STACK.get();
183        if (values == null || values.size() == 0) {
184            // Like version 1.2
185            return Strings.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    /*
299     * (non-Javadoc)
300     * 
301     * @see org.apache.logging.log4j.ThreadContext.ContextStack#getImmutableStackOrNull()
302     */
303    @Override
304    public ContextStack getImmutableStackOrNull() {
305        return STACK.get();
306    }
307}