1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2024, 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.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          Model modelFromIncludedFile = buildModelFromIncludedFile(mic, includeModel);
65          if (modelFromIncludedFile == null) {
66              warnIfRequired("Failed to build include model from included file");
67              return;
68          }
69          processModelFromIncludedFile(includeModel, modelFromIncludedFile);
70      }
71  
72      /**
73       * This method is called by logback-tyler at TylerConfigurator run-time.
74       *
75       * @param capc
76       * @param includeModel
77       * @throws ModelHandlerException
78       * @since 1.5.11
79       */
80      public Model buildModelFromIncludedFile(ContextAwarePropertyContainer capc, IncludeModel includeModel) throws ModelHandlerException {
81  
82          this.optional = OptionHelper.toBoolean(includeModel.getOptional(), false);
83  
84          if (!checkAttributes(includeModel)) {
85              inError = true;
86              return null;
87          }
88  
89          InputStream in = getInputStream(capc, includeModel);
90          if (in == null) {
91              inError = true;
92              return null;
93          }
94  
95          SaxEventRecorder recorder = null;
96  
97          try {
98              recorder = populateSaxEventRecorder(in);
99  
100             List<SaxEvent> saxEvents = recorder.getSaxEventList();
101             if (saxEvents.isEmpty()) {
102                 addWarn("Empty sax event list");
103                 return null;
104             }
105 
106             Supplier<? extends GenericXMLConfigurator> jcSupplier = capc.getConfiguratorSupplier();
107             if (jcSupplier == null) {
108                 addError("null configurator supplier. Abandoning inclusion of [" + attributeInUse + "]");
109                 inError = true;
110                 return null;
111             }
112 
113             GenericXMLConfigurator genericXMLConfigurator = jcSupplier.get();
114             genericXMLConfigurator.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG);
115 
116             Model modelFromIncludedFile = genericXMLConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList());
117             return modelFromIncludedFile;
118         } catch (JoranException e) {
119             inError = true;
120             addError("Error processing XML data in [" + attributeInUse + "]", e);
121             return null;
122         }
123     }
124 
125     private void processModelFromIncludedFile(IncludeModel includeModel, Model modelFromIncludedFile) {
126         includeModel.getSubModels().addAll(modelFromIncludedFile.getSubModels());
127     }
128 
129     public SaxEventRecorder populateSaxEventRecorder(final InputStream inputStream) throws JoranException {
130         SaxEventRecorder recorder = new SaxEventRecorder(context);
131         recorder.recordEvents(inputStream);
132         return recorder;
133     }
134 
135     private InputStream getInputStream(ContextAwarePropertyContainer capc, IncludeModel includeModel) {
136         URL inputURL = getInputURL(capc, includeModel);
137         if (inputURL == null)
138             return null;
139         ConfigurationWatchListUtil.addToWatchList(context, inputURL);
140         return openURL(inputURL);
141     }
142 
143 }