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.ArrayList;
020    import java.util.Collection;
021    import java.util.Collections;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.NoSuchElementException;
025    
026    /**
027     * A copy-on-write thread-safe variant of
028     * {@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
030     * the underlying list.
031     */
032    public class DefaultThreadContextStack implements ThreadContextStack {
033    
034        private static final long serialVersionUID = 5050501L;
035    
036        private final boolean useStack;
037        private static ThreadLocal<List<String>> stack = new ThreadLocal<List<String>>();
038    
039        public DefaultThreadContextStack(boolean useStack) {
040            this.useStack = useStack;
041        }
042    
043        public String pop() {
044            if (!useStack) {
045                return "";
046            }
047            final List<String> list = stack.get();
048            if (list == null || list.size() == 0) {
049                throw new NoSuchElementException("The ThreadContext stack is empty");
050            }
051            final List<String> copy = new ArrayList<String>(list);
052            final int last = copy.size() - 1;
053            final String result = copy.remove(last);
054            stack.set(Collections.unmodifiableList(copy));
055            return result;
056        }
057    
058        public String peek() {
059            final List<String> list = stack.get();
060            if (list == null || list.size() == 0) {
061                return null;
062            }
063            final int last = list.size() - 1;
064            return list.get(last);
065        }
066    
067        public void push(final String message) {
068            if (!useStack) {
069                return;
070            }
071            add(message);
072        }
073    
074        public int getDepth() {
075            final List<String> list = stack.get();
076            return list == null ? 0 : list.size();
077        }
078    
079        public List<String> asList() {
080            final List<String> list = stack.get();
081            if (list == null) {
082                return Collections.emptyList();
083            }
084            return list;
085        }
086    
087        public void trim(final int depth) {
088            if (depth < 0) {
089                throw new IllegalArgumentException(
090                        "Maximum stack depth cannot be negative");
091            }
092            final List<String> list = stack.get();
093            if (list == null) {
094                return;
095            }
096            final List<String> copy = new ArrayList<String>();
097            int count = Math.min(depth, list.size());
098            for(int i = 0; i < count; i++) {
099                copy.add(list.get(i));
100            }
101            stack.set(copy);
102        }
103    
104        public ThreadContextStack copy() {
105            List<String> result = null;
106            if (!useStack || (result = stack.get()) == null) {
107                return new MutableThreadContextStack(new ArrayList<String>());
108            }
109            return new MutableThreadContextStack(result);
110        }
111    
112        public void clear() {
113            stack.remove();
114        }
115    
116        public int size() {
117            final List<String> result = stack.get();
118            return result == null ? 0 : result.size();
119        }
120    
121        public boolean isEmpty() {
122            final List<String> result = stack.get();
123            return result == null || result.isEmpty();
124        }
125    
126        public boolean contains(Object o) {
127            final List<String> result = stack.get();
128            return result != null && result.contains(o);
129        }
130    
131        public Iterator<String> iterator() {
132            final List<String> immutable = stack.get();
133            if (immutable == null) {
134                final List<String> empty = Collections.emptyList();
135                return empty.iterator();
136            }
137            return immutable.iterator();
138        }
139    
140        public Object[] toArray() {
141            final List<String> result = stack.get();
142            if (result == null) {
143                return new String[0];
144            }
145            return result.toArray(new Object[result.size()]);
146        }
147    
148        public <T> T[] toArray(T[] ts) {
149            List<String> result = stack.get();
150            if (result == null) {
151                if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
152                    ts[0] = null;
153                }
154                return ts;
155            }
156            return result.toArray(ts);
157        }
158    
159        public boolean add(String s) {
160            if (!useStack) {
161                return false;
162            }
163            final List<String> list = stack.get();
164            final List<String> copy = list == null ? new ArrayList<String>()
165                    : new ArrayList<String>(list);
166            copy.add(s);
167            stack.set(Collections.unmodifiableList(copy));
168            return true;
169        }
170    
171        public boolean remove(Object o) {
172            if (!useStack) {
173                return false;
174            }
175            final List<String> list = stack.get();
176            if (list == null || list.size() == 0) {
177                return false;
178            }
179            final List<String> copy = new ArrayList<String>(list);
180            final boolean result = copy.remove(o);
181            stack.set(Collections.unmodifiableList(copy));
182            return result;
183        }
184    
185        public boolean containsAll(Collection<?> objects) {
186            if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
187                return true; // looks counter-intuitive, but see
188                             // j.u.AbstractCollection
189            }
190            final List<String> list = stack.get();
191            return list != null && list.containsAll(objects);
192        }
193    
194        public boolean addAll(Collection<? extends String> strings) {
195            if (!useStack || strings.isEmpty()) {
196                return false;
197            }
198            final List<String> list = stack.get();
199            final List<String> copy = list == null ? new ArrayList<String>()
200                    : new ArrayList<String>(list);
201            copy.addAll(strings);
202            stack.set(Collections.unmodifiableList(copy));
203            return true;
204        }
205    
206        public boolean removeAll(Collection<?> objects) {
207            if (!useStack || objects.isEmpty()) {
208                return false;
209            }
210            final List<String> list = stack.get();
211            if (list == null || list.isEmpty()) {
212                return false;
213            }
214            final List<String> copy = new ArrayList<String>(list);
215            boolean result = copy.removeAll(objects);
216            stack.set(Collections.unmodifiableList(copy));
217            return result;
218        }
219    
220        public boolean retainAll(Collection<?> objects) {
221            if (!useStack || objects.isEmpty()) {
222                return false;
223            }
224            final List<String> list = stack.get();
225            if (list == null || list.isEmpty()) {
226                return false;
227            }
228            final List<String> copy = new ArrayList<String>(list);
229            final boolean result = copy.retainAll(objects);
230            stack.set(Collections.unmodifiableList(copy));
231            return result;
232        }
233    }