    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    }