1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.classic.util;
16  
17  import ch.qos.logback.classic.ClassicConstants;
18  import ch.qos.logback.classic.LoggerContext;
19  import ch.qos.logback.classic.joran.JoranConfigurator;
20  import ch.qos.logback.classic.spi.ConfiguratorRank;
21  import ch.qos.logback.core.Context;
22  import ch.qos.logback.core.LogbackException;
23  import ch.qos.logback.core.joran.spi.JoranException;
24  import ch.qos.logback.classic.spi.Configurator;
25  import ch.qos.logback.core.spi.ContextAwareBase;
26  import ch.qos.logback.core.status.InfoStatus;
27  import ch.qos.logback.core.status.StatusManager;
28  import ch.qos.logback.core.util.Loader;
29  import ch.qos.logback.core.util.OptionHelper;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.net.MalformedURLException;
34  import java.net.URL;
35  import java.util.Set;
36  
37  /**
38   * @since 1.3.0-beta1
39   */
40  // Note that DefaultJoranConfigurator is invoked via reflection
41  @ConfiguratorRank(value = ConfiguratorRank.NOMINAL)
42  public class DefaultJoranConfigurator extends ContextAwareBase implements Configurator {
43  
44      @Override
45      public ExecutionStatus configure(LoggerContext context) {
46          URL url = performMultiStepConfigurationFileSearch(true);
47          if (url != null) {
48              try {
49                  configureByResource(url);
50              } catch (JoranException e) {
51                  e.printStackTrace();
52              }
53              // You tried and that counts Mary.
54              return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
55          } else {
56              return ExecutionStatus.INVOKE_NEXT_IF_ANY;
57          }
58      }
59  
60      private URL performMultiStepConfigurationFileSearch(boolean updateStatus) {
61          ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
62          URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
63          if (url != null) {
64              return url;
65          }
66  
67          url = getResource(ClassicConstants.TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
68          if (url != null) {
69              return url;
70          }
71  
72          return getResource(ClassicConstants.AUTOCONFIG_FILE, myClassLoader, updateStatus);
73      }
74      public void configureByResource(URL url) throws JoranException {
75          if (url == null) {
76              throw new IllegalArgumentException("URL argument cannot be null");
77          }
78          final String urlString = url.toString();
79          if (urlString.endsWith("xml")) {
80              JoranConfigurator configurator = new JoranConfigurator();
81              configurator.setContext(context);
82              configurator.doConfigure(url);
83          } else {
84              throw new LogbackException(
85                      "Unexpected filename extension of file [" + url.toString() + "]. Should be .xml");
86          }
87      }
88  
89  
90      /**
91       * Perform multi-search for configuration file
92       * @param updateStatus
93       * @return
94       *
95       * @deprecated  with no replacement
96       */
97      public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
98          return performMultiStepConfigurationFileSearch(updateStatus);
99      }
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 }