| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core.config; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; |
| |
| import org.apache.logging.log4j.core.Appender; |
| import org.apache.logging.log4j.util.PerformanceSensitive; |
| |
| /** |
| * Data structure with similar semantics to CopyOnWriteArraySet, but giving direct access to the underlying array. |
| * |
| * @since 2.6 |
| */ |
| @PerformanceSensitive |
| public class AppenderControlArraySet { |
| private static final AtomicReferenceFieldUpdater<AppenderControlArraySet, AppenderControl[]> appenderArrayUpdater = |
| AtomicReferenceFieldUpdater.newUpdater(AppenderControlArraySet.class, AppenderControl[].class, "appenderArray"); |
| private volatile AppenderControl[] appenderArray = new AppenderControl[0]; |
| |
| /** |
| * Adds an AppenderControl to this set. If this set already contains the element, the call leaves the set unchanged |
| * and returns false. |
| * |
| * @param control The AppenderControl to add. |
| * @return true if this set did not already contain the specified element |
| */ |
| public boolean add(final AppenderControl control) { |
| boolean success; |
| do { |
| final AppenderControl[] original = appenderArray; |
| for (final AppenderControl existing : original) { |
| if (existing.equals(control)) { |
| return false; // the appender is already in the list |
| } |
| } |
| final AppenderControl[] copy = Arrays.copyOf(original, original.length + 1); |
| copy[copy.length - 1] = control; |
| success = appenderArrayUpdater.compareAndSet(this, original, copy); |
| } while (!success); // could not swap: array was modified by another thread |
| return true; // successfully added |
| } |
| |
| /** |
| * Removes the AppenderControl with the specific name and returns it (or {@code null} if no such appender existed). |
| * |
| * @param name The name of the AppenderControl to remove |
| * @return the removed AppenderControl or {@code null} |
| */ |
| public AppenderControl remove(final String name) { |
| boolean success; |
| do { |
| success = true; |
| final AppenderControl[] original = appenderArray; |
| for (int i = 0; i < original.length; i++) { |
| final AppenderControl appenderControl = original[i]; |
| if (Objects.equals(name, appenderControl.getAppenderName())) { |
| final AppenderControl[] copy = removeElementAt(i, original); |
| if (appenderArrayUpdater.compareAndSet(this, original, copy)) { |
| return appenderControl; // successfully removed |
| } |
| success = false; // could not swap: array was modified by another thread |
| break; |
| } |
| } |
| } while (!success); |
| return null; // not found |
| } |
| |
| private AppenderControl[] removeElementAt(final int i, final AppenderControl[] array) { |
| final AppenderControl[] result = Arrays.copyOf(array, array.length - 1); |
| System.arraycopy(array, i + 1, result, i, result.length - i); |
| return result; |
| } |
| |
| /** |
| * Returns all Appenders as a Map. |
| * |
| * @return a Map with the Appender name as the key and the Appender as the value. |
| */ |
| public Map<String, Appender> asMap() { |
| final Map<String, Appender> result = new HashMap<>(); |
| for (final AppenderControl appenderControl : appenderArray) { |
| result.put(appenderControl.getAppenderName(), appenderControl.getAppender()); |
| } |
| return result; |
| } |
| |
| /** |
| * Atomically sets the values to an empty array and returns the old array. |
| * |
| * @return the contents before this collection was cleared. |
| */ |
| public AppenderControl[] clear() { |
| return appenderArrayUpdater.getAndSet(this, new AppenderControl[0]); |
| } |
| |
| public boolean isEmpty() { |
| return appenderArray.length == 0; |
| } |
| |
| /** |
| * Returns the underlying array. |
| * |
| * @return the array supporting this collection |
| */ |
| public AppenderControl[] get() { |
| return appenderArray; |
| } |
| |
| @Override |
| public String toString() { |
| return "AppenderControlArraySet [appenderArray=" + Arrays.toString(appenderArray) + "]"; |
| } |
| } |