View Javadoc
1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2023, 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 v1.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.joran;
16  
17  import ch.qos.logback.classic.ClassicConstants;
18  import ch.qos.logback.classic.LoggerContext;
19  import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream;
20  import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules;
21  import ch.qos.logback.classic.spi.ConfiguratorRank;
22  import ch.qos.logback.core.Context;
23  import ch.qos.logback.core.LogbackException;
24  import ch.qos.logback.core.model.Model;
25  import ch.qos.logback.core.model.ModelUtil;
26  import ch.qos.logback.core.model.processor.DefaultProcessor;
27  import ch.qos.logback.core.model.processor.ModelInterpretationContext;
28  import ch.qos.logback.classic.spi.Configurator;
29  import ch.qos.logback.core.spi.ContextAwareBase;
30  import ch.qos.logback.core.status.InfoStatus;
31  import ch.qos.logback.core.status.StatusManager;
32  import ch.qos.logback.core.util.Loader;
33  import ch.qos.logback.core.util.OptionHelper;
34  
35  import java.io.File;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.net.MalformedURLException;
39  import java.net.URL;
40  
41  import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION;
42  
43  /**
44   * @since 1.3.9/1.4.9
45   */
46  
47  // BEWARE: the fqcn is used in SerializedModelModelHandler
48  @ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL)
49  public class SerializedModelConfigurator extends ContextAwareBase implements Configurator {
50  
51      final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION;
52  
53      final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION;
54      protected ModelInterpretationContext modelInterpretationContext;
55  
56      @Override
57      public ExecutionStatus configure(LoggerContext loggerContext) {
58  
59          URL url = performMultiStepModelFileSearch(true);
60          if (url != null) {
61              configureByResource(url);
62              return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
63          } else {
64              return ExecutionStatus.INVOKE_NEXT_IF_ANY;
65          }
66      }
67  
68      private void configureByResource(URL url) {
69          final String urlString = url.toString();
70          if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) {
71              Model model = retrieveModel(url);
72              if(model == null) {
73                  addWarn("Empty model. Abandoning.");
74                  return;
75              }
76              ModelUtil.resetForReuse(model);
77              buildModelInterpretationContext(model);
78  
79              DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext);
80              ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context);
81              mc2mhl.link(defaultProcessor);
82  
83              // disallow simultaneous configurations of the same context
84              synchronized (context.getConfigurationLock()) {
85                  defaultProcessor.process(model);
86              }
87          } else {
88              throw new LogbackException(
89                      "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION);
90          }
91      }
92  
93      private void buildModelInterpretationContext(Model topModel) {
94          this.modelInterpretationContext = new ModelInterpretationContext(context, this);
95          this.modelInterpretationContext.setTopModel(topModel);
96          LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(
97                  modelInterpretationContext.getDefaultNestedComponentRegistry());
98          this.modelInterpretationContext.createAppenderBags();
99      }
100 
101     private Model retrieveModel(URL url)  {
102         long start = System.currentTimeMillis();
103         try (InputStream is = url.openStream()) {
104             HardenedModelInputStream hmis = new HardenedModelInputStream(is);
105 
106             Model model = (Model) hmis.readObject();
107             long diff = System.currentTimeMillis() - start;
108             addInfo("Model at ["+url+"] read in "+diff + " milliseconds");
109             return model;
110         } catch(IOException e) {
111             addError("Failed to open "+url, e);
112         } catch (ClassNotFoundException e) {
113             addError("Failed read model object in "+ url, e);
114         }
115         return null;
116     }
117 
118     private URL performMultiStepModelFileSearch(boolean updateState) {
119         ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
120         URL url = findModelConfigFileURLFromSystemProperties(myClassLoader);
121         if (url != null) {
122             return url;
123         }
124 
125         url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
126         if (url != null) {
127             return url;
128         }
129 
130         url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
131         return url;
132     }
133 
134     URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) {
135         String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY);
136 
137         if (logbackModelFile != null) {
138             URL result = null;
139             try {
140                 result = new URL(logbackModelFile);
141                 return result;
142             } catch (MalformedURLException e) {
143                 // so, resource is not a URL:
144                 // attempt to get the resource from the class path
145                 result = Loader.getResource(logbackModelFile, classLoader);
146                 if (result != null) {
147                     return result;
148                 }
149                 File f = new File(logbackModelFile);
150                 if (f.exists() && f.isFile()) {
151                     try {
152                         result = f.toURI().toURL();
153                         return result;
154                     } catch (MalformedURLException e1) {
155                     }
156                 }
157             } finally {
158                 statusOnResourceSearch(logbackModelFile, result);
159             }
160         }
161         return null;
162     }
163 
164 
165     private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) {
166         URL url = Loader.getResource(filename, classLoader);
167         if (updateStatus) {
168             statusOnResourceSearch(filename, url);
169         }
170         return url;
171     }
172 
173     private void statusOnResourceSearch(String resourceName, URL url) {
174         StatusManager sm = context.getStatusManager();
175         if (url == null) {
176             sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context));
177         } else {
178             sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context));
179         }
180     }
181 }