View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.spi;
    18
    19  import java.util.Collection;
    20  import java.util.Collections;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.NoSuchElementException;
    24
    25  import org.apache.logging.log4j.ThreadContext.ContextStack;
    26  import org.apache.logging.log4j.util.Strings;
    27
    28  /**
    29   * A copy-on-write thread-safe variant of {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative operations (add,
    30   * pop, and so on) are implemented by making a fresh copy of the underlying list.
    31   */
    32  public class DefaultThreadContextStack implements ThreadContextStack {
    33
    34      private static final long serialVersionUID = 5050501L;
    35
    36      private static final ThreadLocal<MutableThreadContextStack> stack = new ThreadLocal<MutableThreadContextStack>();
    37
    38      private final boolean useStack;
    39
    40      public DefaultThreadContextStack(final boolean useStack) {
    41          this.useStack = useStack;
    42      }
    43
    44      private MutableThreadContextStack getNonNullStackCopy() {
    45          final MutableThreadContextStack values = stack.get();
    46          return (MutableThreadContextStack) (values == null ? new MutableThreadContextStack() : values.copy());
    47      }
    48
    49      @Override
    50      public boolean add(final String s) {
    51          if (!useStack) {
    52              return false;
    53          }
    54          final MutableThreadContextStack copy = getNonNullStackCopy();
    55          copy.add(s);
    56          copy.freeze();
    57          stack.set(copy);
    58          return true;
    59      }
    60
    61      @Override
    62      public boolean addAll(final Collection<? extends String> strings) {
    63          if (!useStack || strings.isEmpty()) {
    64              return false;
    65          }
    66          final MutableThreadContextStack copy = getNonNullStackCopy();
    67          copy.addAll(strings);
    68          copy.freeze();
    69          stack.set(copy);
    70          return true;
    71      }
    72
    73      @Override
    74      public List<String> asList() {
    75          final MutableThreadContextStack values = stack.get();
    76          if (values == null) {
    77              return Collections.emptyList();
    78          }
    79          return values.asList();
    80      }
    81
    82      @Override
    83      public void clear() {
    84          stack.remove();
    85      }
    86
    87      @Override
    88      public boolean contains(final Object o) {
    89          final MutableThreadContextStack values = stack.get();
    90          return values != null && values.contains(o);
    91      }
    92
    93      @Override
    94      public boolean containsAll(final Collection<?> objects) {
    95          if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
    96              return true; // looks counter-intuitive, but see
    97                           // j.u.AbstractCollection
    98          }
    99          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 }