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.core.config; 018 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.Objects; 023import java.util.concurrent.atomic.AtomicReference; 024 025import org.apache.logging.log4j.core.Appender; 026import org.apache.logging.log4j.util.PerformanceSensitive; 027 028/** 029 * Data structure with similar semantics to CopyOnWriteArraySet, but giving direct access to the underlying array. 030 * 031 * @since 2.6 032 */ 033@PerformanceSensitive 034public class AppenderControlArraySet { 035 private final AtomicReference<AppenderControl[]> appenderArray = new AtomicReference<>(new AppenderControl[0]); 036 037 /** 038 * Adds an AppenderControl to this set. If this set already contains the element, the call leaves the set unchanged 039 * and returns false. 040 * 041 * @param control The AppenderControl to add. 042 * @return true if this set did not already contain the specified element 043 */ 044 public boolean add(final AppenderControl control) { 045 boolean success; 046 do { 047 final AppenderControl[] original = appenderArray.get(); 048 for (final AppenderControl existing : original) { 049 if (existing.equals(control)) { 050 return false; // the appender is already in the list 051 } 052 } 053 final AppenderControl[] copy = Arrays.copyOf(original, original.length + 1); 054 copy[copy.length - 1] = control; 055 success = appenderArray.compareAndSet(original, copy); 056 } while (!success); // could not swap: array was modified by another thread 057 return true; // successfully added 058 } 059 060 /** 061 * Removes the AppenderControl with the specific name and returns it (or {@code null} if no such appender existed). 062 * 063 * @param name The name of the AppenderControl to remove 064 * @return the removed AppenderControl or {@code null} 065 */ 066 public AppenderControl remove(final String name) { 067 boolean success; 068 do { 069 success = true; 070 final AppenderControl[] original = appenderArray.get(); 071 for (int i = 0; i < original.length; i++) { 072 final AppenderControl appenderControl = original[i]; 073 if (Objects.equals(name, appenderControl.getAppenderName())) { 074 final AppenderControl[] copy = removeElementAt(i, original); 075 if (appenderArray.compareAndSet(original, copy)) { 076 return appenderControl; // successfully removed 077 } 078 success = false; // could not swap: array was modified by another thread 079 break; 080 } 081 } 082 } while (!success); 083 return null; // not found 084 } 085 086 private AppenderControl[] removeElementAt(final int i, final AppenderControl[] array) { 087 final AppenderControl[] result = Arrays.copyOf(array, array.length - 1); 088 System.arraycopy(array, i + 1, result, i, result.length - i); 089 return result; 090 } 091 092 /** 093 * Returns all Appenders as a Map. 094 * 095 * @return a Map with the Appender name as the key and the Appender as the value. 096 */ 097 public Map<String, Appender> asMap() { 098 final Map<String, Appender> result = new HashMap<>(); 099 for (final AppenderControl appenderControl : appenderArray.get()) { 100 result.put(appenderControl.getAppenderName(), appenderControl.getAppender()); 101 } 102 return result; 103 } 104 105 /** 106 * Atomically sets the values to an empty array and returns the old array. 107 * 108 * @return the contents before this collection was cleared. 109 */ 110 public AppenderControl[] clear() { 111 return appenderArray.getAndSet(new AppenderControl[0]); 112 } 113 114 public boolean isEmpty() { 115 return appenderArray.get().length == 0; 116 } 117 118 /** 119 * Returns the underlying array. 120 * 121 * @return the array supporting this collection 122 */ 123 public AppenderControl[] get() { 124 return appenderArray.get(); 125 } 126 127 @Override 128 public String toString() { 129 return "AppenderControlArraySet [appenderArray=" + appenderArray + "]"; 130 } 131}