001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v2.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014
015package ch.qos.logback.classic.util;
016
017import ch.qos.logback.classic.ClassicConstants;
018import ch.qos.logback.classic.LoggerContext;
019import ch.qos.logback.classic.joran.JoranConfigurator;
020import ch.qos.logback.classic.spi.ConfiguratorRank;
021import ch.qos.logback.core.Context;
022import ch.qos.logback.core.LogbackException;
023import ch.qos.logback.core.joran.spi.JoranException;
024import ch.qos.logback.classic.spi.Configurator;
025import ch.qos.logback.core.spi.ContextAwareBase;
026import ch.qos.logback.core.status.InfoStatus;
027import ch.qos.logback.core.status.StatusManager;
028import ch.qos.logback.core.util.Loader;
029import ch.qos.logback.core.util.OptionHelper;
030
031import java.io.File;
032import java.io.IOException;
033import java.net.MalformedURLException;
034import java.net.URL;
035import java.util.Set;
036
037/**
038 * @since 1.3.0-beta1
039 */
040// Note that DefaultJoranConfigurator is invoked via reflection
041@ConfiguratorRank(value = ConfiguratorRank.NOMINAL)
042public class DefaultJoranConfigurator extends ContextAwareBase implements Configurator {
043
044    @Override
045    public ExecutionStatus configure(LoggerContext context) {
046        URL url = performMultiStepConfigurationFileSearch(true);
047        if (url != null) {
048            try {
049                configureByResource(url);
050            } catch (JoranException e) {
051                e.printStackTrace();
052            }
053            // You tried and that counts Mary.
054            return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
055        } else {
056            return ExecutionStatus.INVOKE_NEXT_IF_ANY;
057        }
058    }
059
060    private URL performMultiStepConfigurationFileSearch(boolean updateStatus) {
061        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
062        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
063        if (url != null) {
064            return url;
065        }
066
067        url = getResource(ClassicConstants.TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
068        if (url != null) {
069            return url;
070        }
071
072        return getResource(ClassicConstants.AUTOCONFIG_FILE, myClassLoader, updateStatus);
073    }
074    public void configureByResource(URL url) throws JoranException {
075        if (url == null) {
076            throw new IllegalArgumentException("URL argument cannot be null");
077        }
078        final String urlString = url.toString();
079        if (urlString.endsWith("xml")) {
080            JoranConfigurator configurator = new JoranConfigurator();
081            configurator.setContext(context);
082            configurator.doConfigure(url);
083        } else {
084            throw new LogbackException(
085                    "Unexpected filename extension of file [" + url.toString() + "]. Should be .xml");
086        }
087    }
088
089
090    /**
091     * Perform multi-search for configuration file
092     * @param updateStatus
093     * @return
094     *
095     * @deprecated  with no replacement
096     */
097    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
098        return performMultiStepConfigurationFileSearch(updateStatus);
099    }
100
101    private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boolean updateStatus) {
102        String logbackConfigFile = OptionHelper.getSystemProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
103        if (logbackConfigFile != null) {
104            URL result = null;
105            try {
106                result = new URL(logbackConfigFile);
107                return result;
108            } catch (MalformedURLException e) {
109                // so, resource is not a URL:
110                // attempt to get the resource from the class path
111                result = Loader.getResource(logbackConfigFile, classLoader);
112                if (result != null) {
113                    return result;
114                }
115                // if the above fails, try to find as a file
116                File f = new File(logbackConfigFile);
117                if (f.exists() && f.isFile()) {
118                    try {
119                        result = f.toURI().toURL();
120                        return result;
121                    } catch (MalformedURLException e1) {
122                    }
123                }
124            } finally {
125                if (updateStatus) {
126                    statusOnResourceSearch(logbackConfigFile, classLoader, result);
127                }
128            }
129        }
130        return null;
131    }
132
133    private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
134        URL url = Loader.getResource(filename, myClassLoader);
135        if (updateStatus) {
136            statusOnResourceSearch(filename, myClassLoader, url);
137        }
138        return url;
139    }
140
141    private void statusOnResourceSearch(String resourceName, ClassLoader classLoader, URL url) {
142        StatusManager sm = context.getStatusManager();
143        if (url == null) {
144            sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context));
145        } else {
146            sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context));
147            multiplicityWarning(resourceName, classLoader);
148        }
149    }
150
151    private void multiplicityWarning(String resourceName, ClassLoader classLoader) {
152        Set<URL> urlSet = null;
153        try {
154            urlSet = Loader.getResources(resourceName, classLoader);
155        } catch (IOException e) {
156            addError("Failed to get url list for resource [" + resourceName + "]", e);
157        }
158        if (urlSet != null && urlSet.size() > 1) {
159            addWarn("Resource [" + resourceName + "] occurs multiple times on the classpath.");
160            for (URL url : urlSet) {
161                addWarn("Resource [" + resourceName + "] occurs at [" + url.toString() + "]");
162            }
163        }
164    }
165}