1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.joran.action;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.MalformedURLException;
20 import java.net.URI;
21 import java.net.URL;
22 import java.util.List;
23
24 import org.xml.sax.Attributes;
25
26 import ch.qos.logback.core.joran.event.SaxEvent;
27 import ch.qos.logback.core.joran.event.SaxEventRecorder;
28 import ch.qos.logback.core.joran.spi.ActionException;
29 import ch.qos.logback.core.joran.spi.JoranException;
30 import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
31 import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
32 import ch.qos.logback.core.model.IncludeModel;
33 import ch.qos.logback.core.model.Model;
34 import ch.qos.logback.core.util.Loader;
35 import ch.qos.logback.core.util.OptionHelper;
36
37 import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG;
38
39
40
41
42
43
44 public class IncludeAction extends Action {
45
46 private static final String FILE_ATTR = "file";
47 private static final String URL_ATTR = "url";
48 private static final String RESOURCE_ATTR = "resource";
49 private static final String OPTIONAL_ATTR = "optional";
50
51 private String attributeInUse;
52 private boolean optional;
53
54 Model parentModel;
55 IncludeModel includeModel;
56 boolean inError = false;
57
58 @Override
59 public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) throws ActionException {
60
61 parentModel = null;
62 includeModel = null;
63
64 SaxEventRecorder recorder = new SaxEventRecorder(context);
65
66 String optionalStr = attributes.getValue(OPTIONAL_ATTR);
67
68 createModelForAlternateUse(ec, name, attributes, optionalStr);
69
70
71 this.attributeInUse = null;
72 this.optional = OptionHelper.toBoolean(optionalStr, false);
73
74 if (!checkAttributes(attributes)) {
75 inError = true;
76 return;
77 }
78
79 InputStream in = getInputStream(ec, attributes);
80
81 try {
82 if (in != null) {
83 parseAndRecord(in, recorder);
84
85 trimHeadAndTail(recorder);
86
87
88
89 ec.getSaxEventInterpreter().getEventPlayer().addEventsDynamically(recorder.getSaxEventList(), 2);
90 }
91 } catch (JoranException e) {
92 addError("Error while parsing " + attributeInUse, e);
93 } finally {
94 close(in);
95 }
96
97 }
98
99
100 private void createModelForAlternateUse(SaxEventInterpretationContext seic, String name, Attributes attributes,
101 String optionalStr) {
102 this.includeModel = new IncludeModel();
103 this.includeModel.setOptional(optionalStr);
104 fillInIncludeModelAttributes(includeModel, name, attributes);
105 if (!seic.isModelStackEmpty()) {
106 parentModel = seic.peekModel();
107 }
108 final int lineNumber = getLineNumber(seic);
109 this.includeModel.setLineNumber(lineNumber);
110 seic.pushModel(includeModel);
111 }
112
113 private void fillInIncludeModelAttributes(IncludeModel includeModel, String name, Attributes attributes) {
114 this.includeModel.setTag(name);
115 String fileAttribute = attributes.getValue(FILE_ATTR);
116 String urlAttribute = attributes.getValue(URL_ATTR);
117 String resourceAttribute = attributes.getValue(RESOURCE_ATTR);
118
119 this.includeModel.setFile(fileAttribute);
120 this.includeModel.setUrl(urlAttribute);
121 this.includeModel.setResource(resourceAttribute);
122
123 }
124
125 void close(InputStream in) {
126 if (in != null) {
127 try {
128 in.close();
129 } catch (IOException e) {
130 }
131 }
132 }
133
134 private boolean checkAttributes(Attributes attributes) {
135 String fileAttribute = attributes.getValue(FILE_ATTR);
136 String urlAttribute = attributes.getValue(URL_ATTR);
137 String resourceAttribute = attributes.getValue(RESOURCE_ATTR);
138
139 int count = 0;
140
141 if (!OptionHelper.isNullOrEmpty(fileAttribute)) {
142 count++;
143 }
144 if (!OptionHelper.isNullOrEmpty(urlAttribute)) {
145 count++;
146 }
147 if (!OptionHelper.isNullOrEmpty(resourceAttribute)) {
148 count++;
149 }
150
151 if (count == 0) {
152 addError("One of \"path\", \"resource\" or \"url\" attributes must be set.");
153 return false;
154 } else if (count > 1) {
155 addError("Only one of \"file\", \"url\" or \"resource\" attributes should be set.");
156 return false;
157 } else if (count == 1) {
158 return true;
159 }
160 throw new IllegalStateException("Count value [" + count + "] is not expected");
161 }
162
163 URL attributeToURL(String urlAttribute) {
164 try {
165 return new URL(urlAttribute);
166 } catch (MalformedURLException mue) {
167 String errMsg = "URL [" + urlAttribute + "] is not well formed.";
168 addError(errMsg, mue);
169 return null;
170 }
171 }
172
173 InputStream openURL(URL url) {
174 try {
175 return url.openStream();
176 } catch (IOException e) {
177 optionalWarning("Failed to open [" + url.toString() + "]");
178 return null;
179 }
180 }
181
182 URL resourceAsURL(String resourceAttribute) {
183 URL url = Loader.getResourceBySelfClassLoader(resourceAttribute);
184 if (url == null) {
185 optionalWarning("Could not find resource corresponding to [" + resourceAttribute + "]");
186 return null;
187 } else
188 return url;
189 }
190
191 private void optionalWarning(String msg) {
192 if (!optional) {
193 addWarn(msg);
194 }
195 }
196
197 URL filePathAsURL(String path) {
198 URI uri = new File(path).toURI();
199 try {
200 return uri.toURL();
201 } catch (MalformedURLException e) {
202
203 e.printStackTrace();
204 return null;
205 }
206 }
207
208 URL getInputURL(SaxEventInterpretationContext ec, Attributes attributes) {
209 String fileAttribute = attributes.getValue(FILE_ATTR);
210 String urlAttribute = attributes.getValue(URL_ATTR);
211 String resourceAttribute = attributes.getValue(RESOURCE_ATTR);
212
213 if (!OptionHelper.isNullOrEmpty(fileAttribute)) {
214 this.attributeInUse = ec.subst(fileAttribute);
215 return filePathAsURL(attributeInUse);
216 }
217
218 if (!OptionHelper.isNullOrEmpty(urlAttribute)) {
219 this.attributeInUse = ec.subst(urlAttribute);
220 return attributeToURL(attributeInUse);
221 }
222
223 if (!OptionHelper.isNullOrEmpty(resourceAttribute)) {
224 this.attributeInUse = ec.subst(resourceAttribute);
225 return resourceAsURL(attributeInUse);
226 }
227
228 throw new IllegalStateException("A URL stream should have been returned");
229
230 }
231
232 InputStream getInputStream(SaxEventInterpretationContext ec, Attributes attributes) {
233 URL inputURL = getInputURL(ec, attributes);
234 if (inputURL == null)
235 return null;
236
237 ConfigurationWatchListUtil.addToWatchList(context, inputURL);
238 return openURL(inputURL);
239 }
240
241 private void trimHeadAndTail(SaxEventRecorder recorder) {
242
243
244
245
246
247 List<SaxEvent> saxEventList = recorder.getSaxEventList();
248
249 if (saxEventList.size() == 0) {
250 return;
251 }
252
253 SaxEvent first = saxEventList.get(0);
254 if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) {
255 saxEventList.remove(0);
256 }
257
258 SaxEvent last = saxEventList.get(saxEventList.size() - 1);
259 if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) {
260 saxEventList.remove(saxEventList.size() - 1);
261 }
262 }
263
264 private void parseAndRecord(InputStream inputSource, SaxEventRecorder recorder) throws JoranException {
265 recorder.setContext(context);
266 recorder.recordEvents(inputSource);
267 }
268
269 @Override
270 public void end(SaxEventInterpretationContext seic, String name) throws ActionException {
271
272 if(inError)
273 return;
274
275 Model m = seic.peekModel();
276
277 if (m != includeModel) {
278 addWarn("The object at the of the stack is not the model [" + includeModel.idString()
279 + "] pushed earlier.");
280 addWarn("This is wholly unexpected.");
281 }
282
283
284 if (parentModel != null) {
285 parentModel.addSubModel(includeModel);
286 seic.popModel();
287 }
288 }
289 }