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.core.config;
18
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.concurrent.atomic.AtomicReference;
24
25 import org.apache.logging.log4j.core.Appender;
26 import org.apache.logging.log4j.util.PerformanceSensitive;
27
28 /**
29 * Data structure with similar semantics to CopyOnWriteArraySet, but giving direct access to the underlying array.
30 *
31 * @since 2.6
32 */
33 @PerformanceSensitive
34 public class AppenderControlArraySet {
35 private final AtomicReference<AppenderControl[]> appenderArray = new AtomicReference<>(new AppenderControl[0]);
36
37 /**
38 * Adds an AppenderControl to this set. If this set already contains the element, the call leaves the set unchanged
39 * and returns false.
40 *
41 * @param control The AppenderControl to add.
42 * @return true if this set did not already contain the specified element
43 */
44 public boolean add(final AppenderControl control) {
45 boolean success;
46 do {
47 final AppenderControl[] original = appenderArray.get();
48 for (final AppenderControl existing : original) {
49 if (existing.equals(control)) {
50 return false; // the appender is already in the list
51 }
52 }
53 final AppenderControl[] copy = Arrays.copyOf(original, original.length + 1);
54 copy[copy.length - 1] = control;
55 success = appenderArray.compareAndSet(original, copy);
56 } while (!success); // could not swap: array was modified by another thread
57 return true; // successfully added
58 }
59
60 /**
61 * Removes the AppenderControl with the specific name and returns it (or {@code null} if no such appender existed).
62 *
63 * @param name The name of the AppenderControl to remove
64 * @return the removed AppenderControl or {@code null}
65 */
66 public AppenderControl remove(final String name) {
67 boolean success;
68 do {
69 success = true;
70 final AppenderControl[] original = appenderArray.get();
71 for (int i = 0; i < original.length; i++) {
72 final AppenderControl appenderControl = original[i];
73 if (Objects.equals(name, appenderControl.getAppenderName())) {
74 final AppenderControl[] copy = removeElementAt(i, original);
75 if (appenderArray.compareAndSet(original, copy)) {
76 return appenderControl; // successfully removed
77 }
78 success = false; // could not swap: array was modified by another thread
79 break;
80 }
81 }
82 } while (!success);
83 return null; // not found
84 }
85
86 private AppenderControl[] removeElementAt(final int i, final AppenderControl[] array) {
87 final AppenderControl[] result = Arrays.copyOf(array, array.length - 1);
88 System.arraycopy(array, i + 1, result, i, result.length - i);
89 return result;
90 }
91
92 /**
93 * Returns all Appenders as a Map.
94 *
95 * @return a Map with the Appender name as the key and the Appender as the value.
96 */
97 public Map<String, Appender> asMap() {
98 final Map<String, Appender> result = new HashMap<>();
99 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 }