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.core.model.processor;
16  
17  import ch.qos.logback.core.Context;
18  import ch.qos.logback.core.joran.GenericXMLConfigurator;
19  import ch.qos.logback.core.joran.event.SaxEvent;
20  import ch.qos.logback.core.joran.event.SaxEventRecorder;
21  import ch.qos.logback.core.joran.spi.JoranException;
22  import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
23  import ch.qos.logback.core.model.IncludeModel;
24  import ch.qos.logback.core.model.Model;
25  import ch.qos.logback.core.spi.ContextAwarePropertyContainer;
26  import ch.qos.logback.core.spi.ErrorCodes;
27  import ch.qos.logback.core.util.Loader;
28  import ch.qos.logback.core.util.OptionHelper;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.net.MalformedURLException;
34  import java.net.URI;
35  import java.net.URL;
36  import java.util.List;
37  import java.util.function.Supplier;
38  
39  import static ch.qos.logback.core.joran.JoranConstants.CONFIGURATION_TAG;
40  import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG;
41  
42  /**
43   * @since 1.5.5
44   */
45  public class IncludeModelHandler extends ResourceHandlerBase {
46      boolean inError = false;
47  
48      public IncludeModelHandler(Context context) {
49          super(context);
50      }
51  
52      static public IncludeModelHandler makeInstance(Context context, ModelInterpretationContext mic) {
53          return new IncludeModelHandler(context);
54      }
55  
56      @Override
57      protected Class<IncludeModel> getSupportedModelClass() {
58          return IncludeModel.class;
59      }
60  
61      @Override
62      public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
63          IncludeModel includeModel = (IncludeModel) model;
64  
65          URL topURL = mic.getTopURL();
66          Boolean topScan = mic.getTopScanBoolean();
67          Model modelFromIncludedFile = buildModelFromIncludedFile(mic, topURL, topScan, includeModel);
68          if (modelFromIncludedFile == null) {
69              warnIfRequired("Failed to build include model from included file");
70              return;
71          }
72          processModelFromIncludedFile(includeModel, modelFromIncludedFile);
73      }
74  
75      /**
76       * This method is called by logback-tyler at TylerConfigurator run-time.
77       *
78       * @param capc
79       * @param includeModel
80       * @throws ModelHandlerException
81       * @since 1.5.11
82       */
83      public Model buildModelFromIncludedFile(ContextAwarePropertyContainer capc, URL topURL, Boolean topScan, IncludeModel includeModel) throws ModelHandlerException {
84  
85          this.optional = OptionHelper.toBoolean(includeModel.getOptional(), false);
86  
87          if (!checkAttributes(includeModel)) {
88              inError = true;
89              return null;
90          }
91  
92  
93          URL inputURL = getInputURL(capc, includeModel);
94          if (inputURL == null) {
95              inError = true;
96              return null;
97          }
98  
99          InputStream in = openURL(inputURL);
100         if (in == null) {
101             inError = true;
102             return null;
103         }
104 
105         updateConfigurationWatchList(inputURL, topURL, topScan);
106 
107 
108         SaxEventRecorder recorder = null;
109 
110         try {
111             recorder = populateSaxEventRecorder(in);
112 
113             List<SaxEvent> saxEvents = recorder.getSaxEventList();
114             if (saxEvents.isEmpty()) {
115                 addWarn("Empty sax event list");
116                 return null;
117             }
118 
119             Supplier<? extends GenericXMLConfigurator> jcSupplier = capc.getConfiguratorSupplier();
120             if (jcSupplier == null) {
121                 addError("null configurator supplier. Abandoning inclusion of [" + attributeInUse + "]");
122                 inError = true;
123                 return null;
124             }
125 
126             GenericXMLConfigurator genericXMLConfigurator = jcSupplier.get();
127             genericXMLConfigurator.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG);
128 
129             Model modelFromIncludedFile = genericXMLConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList());
130             return modelFromIncludedFile;
131         } catch (JoranException e) {
132             inError = true;
133             addError("Error processing XML data in [" + attributeInUse + "]", e);
134             return null;
135         }
136     }
137 
138     private void processModelFromIncludedFile(IncludeModel includeModel, Model modelFromIncludedFile) {
139         includeModel.getSubModels().addAll(modelFromIncludedFile.getSubModels());
140     }
141 
142     public SaxEventRecorder populateSaxEventRecorder(final InputStream inputStream) throws JoranException {
143         SaxEventRecorder recorder = new SaxEventRecorder(context);
144         recorder.recordEvents(inputStream);
145         return recorder;
146     }
147 
148     private void updateConfigurationWatchList(URL inputURL, URL topURL, Boolean topScan) throws ModelHandlerException {
149 
150         if(topScan == Boolean.TRUE) {
151             if(topURL != null) {
152                 ConfigurationWatchListUtil.addToWatchList(context, inputURL);
153             } else {
154                 addWarn("No top URL for XML configuration file. Will not add [" + inputURL + "] to watch list.");
155             }
156         }
157 
158     }
159 
160 }