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;
    18
    19  import java.util.Arrays;
    20  import java.util.concurrent.ConcurrentHashMap;
    21  import java.util.concurrent.ConcurrentMap;
    22
    23
    24  /**
    25   * Applications create Markers by using the Marker Manager. All Markers created by this Manager are
    26   * immutable.
    27   */
    28  public final class MarkerManager {
    29
    30      private static final ConcurrentMap<String, Marker> MARKERS = new ConcurrentHashMap<String, Marker>();
    31
    32      private MarkerManager() {
    33          // do nothing
    34      }
    35
    36      /**
    37       * Clears all markers.
    38       */
    39      public static void clear() {
    40          MARKERS.clear();
    41      }
    42
    43      /**
    44       * Retrieve a Marker or create a Marker that has no parent.
    45       * @param name The name of the Marker.
    46       * @return The Marker with the specified name.
    47       * @throws IllegalArgumentException if the argument is {@code null}
    48       */
    49      public static Marker getMarker(final String name) {
    50          MARKERS.putIfAbsent(name, new Log4jMarker(name));
    51          return MARKERS.get(name);
    52      }
    53
    54      /**
    55       * Retrieves or creates a Marker with the specified parent. The parent must have been previously created.
    56       * @param name The name of the Marker.
    57       * @param parent The name of the parent Marker.
    58       * @return The Marker with the specified name.
    59       * @throws IllegalArgumentException if the parent Marker does not exist.
    60       * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
    61       */
    62      @Deprecated
    63      public static Marker getMarker(final String name, final String parent) {
    64          final Marker parentMarker = MARKERS.get(parent);
    65          if (parentMarker == null) {
    66              throw new IllegalArgumentException("Parent Marker " + parent + " has not been defined");
    67          }
    68          @SuppressWarnings("deprecation")
    69          final Marker marker = getMarker(name, parentMarker);
    70          return marker;
    71      }
    72
    73      /**
    74       * Retrieves or creates a Marker with the specified parent.
    75       * @param name The name of the Marker.
    76       * @param parent The parent Marker.
    77       * @return The Marker with the specified name.
    78       * @throws IllegalArgumentException if any argument is {@code null}
    79       * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
    80       */
    81      @Deprecated
    82      public static Marker getMarker(final String name, final Marker parent) {
    83          MARKERS.putIfAbsent(name, new Log4jMarker(name));
    84          return MARKERS.get(name).addParents(parent);
    85      }
    86
    87      /**
    88       * <em>Consider this class private, it is only public to satisfy Jackson for XML and JSON IO.</em>
    89       * <p>
    90       * The actual Marker implementation.
    91       * </p>
    92       * <p>
    93       * <em>Internal note: We could make this class package private instead of public if the class
    94       * {@code org.apache.logging.log4j.core.jackson.MarkerMixIn}
    95       * is moved to this package and would of course stay in its current module.</em>
    96       * </p>
    97       */
    98      public static class Log4jMarker implements Marker {
    99
    100         private static final long serialVersionUID = 100L;
    101
    102         private final String name;
    103
    104         private volatile Marker[] parents;
    105
    106         /**
    107          * Required by JAXB and Jackson for XML and JSON IO.
    108          */
    109         @SuppressWarnings("unused")
    110         private Log4jMarker() {
    111             this.name = null;
    112             this.parents = null;
    113         }
    114
    115         /**
    116          * Constructs a new Marker.
    117          * @param name the name of the Marker.
    118          * @throws IllegalArgumentException if the argument is {@code null}
    119          */
    120         public Log4jMarker(final String name) {
    121             if (name == null) {
    122                 // we can't store null references in a ConcurrentHashMap as it is, not to mention that a null Marker
    123                 // name seems rather pointless. To get an "anonymous" Marker, just use an empty string.
    124                 throw new IllegalArgumentException("Marker name cannot be null.");
    125             }
    126             this.name = name;
    127             this.parents = null;
    128         }
    129
    130         // TODO: use java.util.concurrent
    131
    132         @Override
    133         public synchronized Marker addParents(final Marker... parents) {
    134             if (parents == null) {
    135                 throw new IllegalArgumentException("A parent marker must be specified");
    136             }
    137             // It is not strictly necessary to copy the variable here but it should perform better than
    138             // Accessing a volatile variable multiple times.
    139             final Marker[] localParents = this.parents;
    140             // Don't add a parent that is already in the hierarchy.
    141             int count = 0;
    142             int size = parents.length;
    143             if (localParents != null) {
    144                 for (final Marker parent : parents) {
    145                     if (!(contains(parent, localParents) || parent.isInstanceOf(this))) {
    146                         ++count;
    147                     }
    148                 }
    149                 if (count == 0) {
    150                     return this;
    151                 }
    152                 size = localParents.length + count;
    153             }
    154             final Marker[] markers = new Marker[size];
    155             if (localParents != null) {
    156                 // It's perfectly OK to call arraycopy in a synchronized context; it's still faster
    157                 //noinspection CallToNativeMethodWhileLocked
    158                 System.arraycopy(localParents, 0, markers, 0, localParents.length);
    159             }
    160             int index = localParents == null ? 0 : localParents.length;
    161             for (final Marker parent : parents) {
    162                 if (localParents == null || !(contains(parent, localParents) || parent.isInstanceOf(this))) {
    163                     markers[index++] = parent;
    164                 }
    165             }
    166             this.parents = markers;
    167             return this;
    168         }
    169
    170         @Override
    171         public synchronized boolean remove(final Marker parent) {
    172             if (parent == null) {
    173                 throw new IllegalArgumentException("A parent marker must be specified");
    174             }
    175             final Marker[] localParents = this.parents;
    176             if (localParents == null) {
    177                 return false;
    178             }
    179             final int localParentsLength = localParents.length;
    180             if (localParentsLength == 1) {
    181                 if (localParents[0].equals(parent)) {
    182                     parents = null;
    183                     return true;
    184                 }
    185                 return false;
    186             }
    187             int index = 0;
    188             final Marker[] markers = new Marker[localParentsLength - 1];
    189             //noinspection ForLoopReplaceableByForEach
    190             for (int i = 0; i < localParentsLength; i++) {
    191                 final Marker marker = localParents[i];
    192                 if (!marker.equals(parent)) {
    193                     if (index == localParentsLength - 1) {
    194                         // no need to swap array
    195                         return false;
    196                     }
    197                     markers[index++] = marker;
    198                 }
    199             }
    200             parents = markers;
    201             return true;
    202         }
    203
    204         @Override
    205         public Marker setParents(final Marker... markers) {
    206             if (markers == null || markers.length == 0) {
    207                 this.parents = null;
    208             } else {
    209                 final Marker[] array = new Marker[markers.length];
    210                 System.arraycopy(markers, 0, array, 0, markers.length);
    211                 this.parents = array;
    212             }
    213             return this;
    214         }
    215
    216         @Override
    217         public String getName() {
    218             return this.name;
    219         }
    220
    221         @Override
    222         public Marker[] getParents() {
    223             if (this.parents == null) {
    224                 return null;
    225             }
    226             return Arrays.copyOf(this.parents, this.parents.length);
    227         }
    228
    229         @Override
    230         public boolean hasParents() {
    231             return this.parents != null;
    232         }
    233
    234         @Override
    235         public boolean isInstanceOf(final Marker marker) {
    236             if (marker == null) {
    237                 throw new IllegalArgumentException("A marker parameter is required");
    238             }
    239             if (this == marker) {
    240                 return true;
    241             }
    242             final Marker[] localParents = parents;
    243             if (localParents != null) {
    244                 // With only one or two parents the for loop is slower.
    245                 final int localParentsLength = localParents.length;
    246                 if (localParentsLength == 1) {
    247                     return checkParent(localParents[0], marker);
    248                 }
    249                 if (localParentsLength == 2) {
    250                     return checkParent(localParents[0], marker) || checkParent(localParents[1], marker);
    251                 }
    252                 //noinspection ForLoopReplaceableByForEach
    253                 for (int i = 0; i < localParentsLength; i++) {
    254                     final Marker localParent = localParents[i];
    255                     if (checkParent(localParent, marker)) {
    256                         return true;
    257                     }
    258                 }
    259             }
    260             return false;
    261         }
    262
    263         @Override
    264         public boolean isInstanceOf(final String markerName) {
    265             if (markerName == null) {
    266                 throw new IllegalArgumentException("A marker name is required");
    267             }
    268             if (markerName.equals(this.getName())) {
    269                 return true;
    270             }
    271             // Use a real marker for child comparisons. It is faster than comparing the names.
    272             final Marker marker = MARKERS.get(markerName);
    273             if (marker == null) {
    274                 return false;
    275             }
    276             final Marker[] localParents = parents;
    277             if (localParents != null) {
    278                 final int localParentsLength = localParents.length;
    279                 if (localParentsLength == 1) {
    280                     return checkParent(localParents[0], marker);
    281                 }
    282                 if (localParentsLength == 2) {
    283                     return checkParent(localParents[0], marker) || checkParent(localParents[1], marker);
    284                 }
    285                 //noinspection ForLoopReplaceableByForEach
    286                 for (int i = 0; i < localParentsLength; i++) {
    287                     final Marker localParent = localParents[i];
    288                     if (checkParent(localParent, marker)) {
    289                         return true;
    290                     }
    291                 }
    292             }
    293
    294             return false;
    295         }
    296
    297         private static boolean checkParent(final Marker parent, final Marker marker) {
    298             if (parent == marker) {
    299                 return true;
    300             }
    301             final Marker[] localParents = parent instanceof Log4jMarker ? ((Log4jMarker)parent).parents : parent.getParents();
    302             if (localParents != null) {
    303                 final int localParentsLength = localParents.length;
    304                 if (localParentsLength == 1) {
    305                     return checkParent(localParents[0], marker);
    306                 }
    307                 if (localParentsLength == 2) {
    308                     return checkParent(localParents[0], marker) || checkParent(localParents[1], marker);
    309                 }
    310                 //noinspection ForLoopReplaceableByForEach
    311                 for (int i = 0; i < localParentsLength; i++) {
    312                     final Marker localParent = localParents[i];
    313                     if (checkParent(localParent, marker)) {
    314                         return true;
    315                     }
    316                 }
    317             }
    318             return false;
    319         }
    320
    321         /*
    322          * Called from add while synchronized.
    323          */
    324         private static boolean contains(final Marker parent, final Marker... localParents) {
    325             //noinspection ForLoopReplaceableByForEach
    326             // performance tests showed a normal for loop is slightly faster than a for-each loop on some platforms
    327             for (int i = 0, localParentsLength = localParents.length; i < localParentsLength; i++) {
    328                 final Marker marker = localParents[i];
    329                 if (marker == parent) {
    330                     return true;
    331                 }
    332             }
    333             return false;
    334         }
    335
    336         @Override
    337         public boolean equals(final Object o) {
    338             if (this == o) {
    339                 return true;
    340             }
    341             if (o == null || !(o instanceof Marker)) {
    342                 return false;
    343             }
    344             final Marker marker = (Marker) o;
    345             return name.equals(marker.getName());
    346         }
    347
    348         @Override
    349         public int hashCode() {
    350             return name.hashCode();
    351         }
    352
    353         @Override
    354         public String toString() {
    355             // FIXME: might want to use an initial capacity; the default is 16 (or str.length() + 16)
    356             final StringBuilder sb = new StringBuilder(name);
    357             final Marker[] localParents = parents;
    358             if (localParents != null) {
    359                 addParentInfo(sb, localParents);
    360             }
    361             return sb.toString();
    362         }
    363
    364         private static void addParentInfo(final StringBuilder sb, final Marker... parents) {
    365             sb.append("[ ");
    366             boolean first = true;
    367             //noinspection ForLoopReplaceableByForEach
    368             for (int i = 0, parentsLength = parents.length; i < parentsLength; i++) {
    369                 final Marker marker = parents[i];
    370                 if (!first) {
    371                     sb.append(", ");
    372                 }
    373                 first = false;
    374                 sb.append(marker.getName());
    375                 final Marker[] p = marker instanceof Log4jMarker ? ((Log4jMarker) marker).parents : marker.getParents();
    376                 if (p != null) {
    377                     addParentInfo(sb, p);
    378                 }
    379             }
    380             sb.append(" ]");
    381         }
    382     }
    383 }