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.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 }