1
2
3
4
5
6
7
8
9
10
11
12
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.ErrorCodes;
26 import ch.qos.logback.core.util.Loader;
27 import ch.qos.logback.core.util.OptionHelper;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.net.MalformedURLException;
33 import java.net.URI;
34 import java.net.URL;
35 import java.util.List;
36 import java.util.function.Supplier;
37
38 import static ch.qos.logback.core.joran.JoranConstants.CONFIGURATION_TAG;
39 import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG;
40
41
42
43
44 public class IncludeModelHandler extends ModelHandlerBase {
45 boolean inError = false;
46 private String attributeInUse;
47 private boolean optional;
48
49 public IncludeModelHandler(Context context) {
50 super(context);
51 }
52
53 static public IncludeModelHandler makeInstance(Context context, ModelInterpretationContext mic) {
54 return new IncludeModelHandler(context);
55 }
56
57 @Override
58 protected Class<IncludeModel> getSupportedModelClass() {
59 return IncludeModel.class;
60 }
61
62 @Override
63 public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
64 IncludeModel includeModel = (IncludeModel) model;
65
66 this.optional = OptionHelper.toBoolean(includeModel.getOptional(), false);
67
68 if (!checkAttributes(includeModel)) {
69 inError = true;
70 return;
71 }
72
73 InputStream in = getInputStream(mic, includeModel);
74 if(in == null) {
75 inError = true;
76 return;
77 }
78
79 SaxEventRecorder recorder = null;
80
81 try {
82 recorder = populateSaxEventRecorder(in);
83
84 List<SaxEvent> saxEvents = recorder.getSaxEventList();
85 if (saxEvents.isEmpty()) {
86 addWarn("Empty sax event list");
87 return;
88 }
89
90
91
92 Supplier<? extends GenericXMLConfigurator> jcSupplier = mic.getConfiguratorSupplier();
93 if (jcSupplier == null) {
94 addError("null configurator supplier. Abandoning inclusion of [" + attributeInUse + "]");
95 inError = true;
96 return;
97 }
98
99 GenericXMLConfigurator genericXMLConfigurator = jcSupplier.get();
100 genericXMLConfigurator.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG);
101
102 Model modelFromIncludedFile = genericXMLConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList());
103 if (modelFromIncludedFile == null) {
104 addError(ErrorCodes.EMPTY_MODEL_STACK);
105 return;
106 }
107
108 includeModel.getSubModels().addAll(modelFromIncludedFile.getSubModels());
109
110 } catch (JoranException e) {
111 inError = true;
112 addError("Error processing XML data in [" + attributeInUse + "]", e);
113 }
114 }
115
116 public SaxEventRecorder populateSaxEventRecorder(final InputStream inputStream) throws JoranException {
117 SaxEventRecorder recorder = new SaxEventRecorder(context);
118 recorder.recordEvents(inputStream);
119 return recorder;
120 }
121
122 private void trimHeadAndTail( List<SaxEvent> saxEventList) {
123
124
125
126
127
128 if (saxEventList.size() == 0) {
129 return;
130 }
131
132 SaxEvent first = saxEventList.get(0);
133 if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) {
134 saxEventList.remove(0);
135 }
136
137 SaxEvent last = saxEventList.get(saxEventList.size() - 1);
138 if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) {
139 saxEventList.remove(saxEventList.size() - 1);
140 }
141 }
142
143 InputStream getInputStream(ModelInterpretationContext mic, IncludeModel includeModel) {
144 URL inputURL = getInputURL(mic, includeModel);
145 if (inputURL == null)
146 return null;
147
148 ConfigurationWatchListUtil.addToWatchList(context, inputURL);
149 return openURL(inputURL);
150 }
151
152 InputStream openURL(URL url) {
153 try {
154 return url.openStream();
155 } catch (IOException e) {
156 optionalWarning("Failed to open [" + url.toString() + "]");
157 return null;
158 }
159 }
160
161 private boolean checkAttributes(IncludeModel includeModel) {
162 String fileAttribute = includeModel.getFile();
163 String urlAttribute = includeModel.getUrl();
164 String resourceAttribute = includeModel.getResource();
165
166 int count = 0;
167
168 if (!OptionHelper.isNullOrEmptyOrAllSpaces(fileAttribute)) {
169 count++;
170 }
171 if (!OptionHelper.isNullOrEmptyOrAllSpaces(urlAttribute)) {
172 count++;
173 }
174 if (!OptionHelper.isNullOrEmptyOrAllSpaces(resourceAttribute)) {
175 count++;
176 }
177
178 if (count == 0) {
179 addError("One of \"path\", \"resource\" or \"url\" attributes must be set.");
180 return false;
181 } else if (count > 1) {
182 addError("Only one of \"file\", \"url\" or \"resource\" attributes should be set.");
183 return false;
184 } else if (count == 1) {
185 return true;
186 }
187 throw new IllegalStateException("Count value [" + count + "] is not expected");
188 }
189
190 URL getInputURL(ModelInterpretationContext mic, IncludeModel includeModel) {
191 String fileAttribute = includeModel.getFile();
192 String urlAttribute = includeModel.getUrl();
193 String resourceAttribute = includeModel.getResource();
194
195 if (!OptionHelper.isNullOrEmptyOrAllSpaces(fileAttribute)) {
196 this.attributeInUse = mic.subst(fileAttribute);
197 return filePathAsURL(attributeInUse);
198 }
199
200 if (!OptionHelper.isNullOrEmptyOrAllSpaces(urlAttribute)) {
201 this.attributeInUse = mic.subst(urlAttribute);
202 return attributeToURL(attributeInUse);
203 }
204
205 if (!OptionHelper.isNullOrEmptyOrAllSpaces(resourceAttribute)) {
206 this.attributeInUse = mic.subst(resourceAttribute);
207 return resourceAsURL(attributeInUse);
208 }
209
210 throw new IllegalStateException("A URL stream should have been returned at this stage");
211
212 }
213
214 URL filePathAsURL(String path) {
215 URI uri = new File(path).toURI();
216 try {
217 return uri.toURL();
218 } catch (MalformedURLException e) {
219
220 e.printStackTrace();
221 return null;
222 }
223 }
224
225 URL attributeToURL(String urlAttribute) {
226 try {
227 return new URL(urlAttribute);
228 } catch (MalformedURLException mue) {
229 String errMsg = "URL [" + urlAttribute + "] is not well formed.";
230 addError(errMsg, mue);
231 return null;
232 }
233 }
234
235 URL resourceAsURL(String resourceAttribute) {
236 URL url = Loader.getResourceBySelfClassLoader(resourceAttribute);
237 if (url == null) {
238 optionalWarning("Could not find resource corresponding to [" + resourceAttribute + "]");
239 return null;
240 } else
241 return url;
242 }
243
244 private void optionalWarning(String msg) {
245 if (!optional) {
246 addWarn(msg);
247 }
248 }
249 }