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    }