    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     */
    017    package org.apache.logging.slf4j;
    018
    019    import java.util.ArrayList;
    020    import java.util.Collection;
    021    import java.util.Iterator;
    022    import java.util.concurrent.ConcurrentHashMap;
    023    import java.util.concurrent.ConcurrentMap;
    024
    025    import org.apache.logging.log4j.Logger;
    026    import org.apache.logging.log4j.MarkerManager;
    027    import org.apache.logging.log4j.status.StatusLogger;
    028    import org.slf4j.IMarkerFactory;
    029    import org.slf4j.Marker;
    030
    031    /**
    032     * Log4j/SLF4J bridge to create SLF4J Markers based on name or based on existing SLF4J Markers.
    033     */
    034    public class Log4jMarkerFactory implements IMarkerFactory {
    035
    036        private static final Logger LOGGER = StatusLogger.getLogger();
    037
    038        private final ConcurrentMap<String, Marker> markerMap = new ConcurrentHashMap<String, Marker>();
    039
    040        /**
    041         * Returns a Log4j Marker that is compatible with SLF4J.
    042         * @param name The name of the Marker.
    043         * @return A Marker.
    044         */
    045        @Override
    046        public Marker getMarker(final String name) {
    047            if (name == null) {
    048                throw new IllegalArgumentException("Marker name must not be null");
    049            }
    050            final Marker marker = markerMap.get(name);
    051            if (marker != null) {
    052                return marker;
    053            }
    054            final org.apache.logging.log4j.Marker log4jMarker = MarkerManager.getMarker(name);
    055            return addMarkerIfAbsent(name, log4jMarker);
    056        }
    057
    058        private Marker addMarkerIfAbsent(final String name, final org.apache.logging.log4j.Marker log4jMarker) {
    059            final Marker marker = new Log4jMarker(log4jMarker);
    060            final Marker existing = markerMap.putIfAbsent(name, marker);
    061            return existing == null ? marker : existing;
    062        }
    063
    064        /**
    065         * Returns a Log4j Marker converted from an existing custom SLF4J Marker.
    066         * @param marker The SLF4J Marker to convert.
    067         * @return A converted Log4j/SLF4J Marker.
    068         * @since 2.1
    069         */
    070        public Marker getMarker(final Marker marker) {
    071            if (marker == null) {
    072                throw new IllegalArgumentException("Marker must not be null");
    073            }
    074            final Marker m = markerMap.get(marker.getName());
    075            if (m != null) {
    076                return m;
    077            }
    078            return addMarkerIfAbsent(marker.getName(), convertMarker(marker));
    079        }
    080
    081        private static org.apache.logging.log4j.Marker convertMarker(final Marker original) {
    082            if (original == null) {
    083                throw new IllegalArgumentException("Marker must not be null");
    084            }
    085            return convertMarker(original, new ArrayList<Marker>());
    086        }
    087
    088        private static org.apache.logging.log4j.Marker convertMarker(final Marker original,
    089                                                                     final Collection<Marker> visited) {
    090            final org.apache.logging.log4j.Marker marker = MarkerManager.getMarker(original.getName());
    091            if (original.hasReferences()) {
    092                final Iterator it = original.iterator();
    093                while (it.hasNext()) {
    094                    final Marker next = (Marker) it.next();
    095                    if (visited.contains(next)) {
    096                        LOGGER.warn("Found a cycle in Marker [{}]. Cycle will be broken.", next.getName());
    097                    } else {
    098                        visited.add(next);
    099                        marker.addParents(convertMarker(next, visited));
    100                    }
    101                }
    102            }
    103            return marker;
    104        }
    105
    106        /**
    107         * Returns true if the Marker exists.
    108         * @param name The Marker name.
    109         * @return {@code true} if the Marker exists, {@code false} otherwise.
    110         */
    111        @Override
    112        public boolean exists(final String name) {
    113            return markerMap.containsKey(name);
    114        }
    115
    116        /**
    117         * Log4j does not support detached Markers. This method always returns false.
    118         * @param name The Marker name.
    119         * @return {@code false}
    120         */
    121        @Override
    122        public boolean detachMarker(final String name) {
    123            return false;
    124        }
    125
    126        /**
    127         * Log4j does not support detached Markers for performance reasons. The returned Marker is attached.
    128         * @param name The Marker name.
    129         * @return The named Marker (unmodified).
    130         */
    131        @Override
    132        public Marker getDetachedMarker(final String name) {
    133            LOGGER.warn("Log4j does not support detached Markers. Returned Marker [{}] will be unchanged.", name);
    134            return getMarker(name);
    135        }
    136
    137
    138    }