1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.config;
18
19 import org.apache.logging.log4j.Level;
20 import org.apache.logging.log4j.core.config.plugins.PluginManager;
21 import org.apache.logging.log4j.core.config.plugins.PluginType;
22 import org.apache.logging.log4j.core.config.plugins.ResolverUtil;
23 import org.apache.logging.log4j.core.helpers.FileUtils;
24 import org.apache.logging.log4j.core.net.Advertiser;
25 import org.apache.logging.log4j.status.StatusConsoleListener;
26 import org.apache.logging.log4j.status.StatusListener;
27 import org.apache.logging.log4j.status.StatusLogger;
28 import org.w3c.dom.Attr;
29 import org.w3c.dom.Document;
30 import org.w3c.dom.Element;
31 import org.w3c.dom.NamedNodeMap;
32 import org.w3c.dom.NodeList;
33 import org.w3c.dom.Text;
34 import org.xml.sax.InputSource;
35 import org.xml.sax.SAXException;
36
37 import javax.xml.XMLConstants;
38 import javax.xml.parsers.DocumentBuilder;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.parsers.ParserConfigurationException;
41 import javax.xml.transform.Source;
42 import javax.xml.transform.stream.StreamSource;
43 import javax.xml.validation.Schema;
44 import javax.xml.validation.SchemaFactory;
45 import javax.xml.validation.Validator;
46 import java.io.ByteArrayInputStream;
47 import java.io.ByteArrayOutputStream;
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.FileNotFoundException;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.PrintStream;
55 import java.net.URI;
56 import java.net.URISyntaxException;
57 import java.util.ArrayList;
58 import java.util.Iterator;
59 import java.util.List;
60 import java.util.Map;
61
62
63
64
65 public class XMLConfiguration extends BaseConfiguration implements Reconfigurable {
66
67 private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
68
69 private static final String LOG4J_XSD = "Log4J-V2.0.xsd";
70
71 private static final int BUF_SIZE = 16384;
72
73 private final List<Status> status = new ArrayList<Status>();
74
75 private Element rootElement;
76
77 private boolean strict;
78
79 private String schema;
80
81 private Validator validator;
82
83 private final List<String> messages = new ArrayList<String>();
84
85 private final File configFile;
86
87 public XMLConfiguration(final ConfigurationFactory.ConfigurationSource configSource) {
88 this.configFile = configSource.getFile();
89 byte[] buffer = null;
90
91 try {
92 final InputStream configStream = configSource.getInputStream();
93 buffer = toByteArray(configStream);
94 configStream.close();
95 final InputSource source = new InputSource(new ByteArrayInputStream(buffer));
96 final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
97 final Document document = builder.parse(source);
98 rootElement = document.getDocumentElement();
99 final Map<String, String> attrs = processAttributes(rootNode, rootElement);
100 Level status = Level.OFF;
101 boolean verbose = false;
102 PrintStream stream = System.out;
103
104 for (final Map.Entry<String, String> entry : attrs.entrySet()) {
105 if ("status".equalsIgnoreCase(entry.getKey())) {
106 status = Level.toLevel(getSubst().replace(entry.getValue()), null);
107 if (status == null) {
108 status = Level.ERROR;
109 messages.add("Invalid status specified: " + entry.getValue() + ". Defaulting to ERROR");
110 }
111 } else if ("dest".equalsIgnoreCase(entry.getKey())) {
112 final String dest = entry.getValue();
113 if (dest != null) {
114 if (dest.equalsIgnoreCase("err")) {
115 stream = System.err;
116 } else {
117 try {
118 final File destFile = FileUtils.fileFromURI(new URI(dest));
119 stream = new PrintStream(new FileOutputStream(destFile));
120 } catch (final URISyntaxException use) {
121 System.err.println("Unable to write to " + dest + ". Writing to stdout");
122 }
123 }
124 }
125 } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
126 verbose = Boolean.parseBoolean(getSubst().replace(entry.getValue()));
127 } else if ("packages".equalsIgnoreCase(getSubst().replace(entry.getKey()))) {
128 final String[] packages = entry.getValue().split(",");
129 for (final String p : packages) {
130 PluginManager.addPackage(p);
131 }
132 } else if ("name".equalsIgnoreCase(entry.getKey())) {
133 setName(getSubst().replace(entry.getValue()));
134 } else if ("strict".equalsIgnoreCase(entry.getKey())) {
135 strict = Boolean.parseBoolean(getSubst().replace(entry.getValue()));
136 } else if ("schema".equalsIgnoreCase(entry.getKey())) {
137 schema = getSubst().replace(entry.getValue());
138 } else if ("monitorInterval".equalsIgnoreCase(entry.getKey())) {
139 final int interval = Integer.parseInt(getSubst().replace(entry.getValue()));
140 if (interval > 0 && configFile != null) {
141 monitor = new FileConfigurationMonitor(this, configFile, listeners, interval);
142 }
143 } else if ("advertiser".equalsIgnoreCase(entry.getKey())) {
144 final String advertiserString = getSubst().replace(entry.getValue());
145 if (advertiserString != null)
146 {
147 final PluginType type = getPluginManager().getPluginType(advertiserString);
148 if (type != null)
149 {
150 final Class<Advertiser> clazz = type.getPluginClass();
151 try {
152 advertiser = clazz.newInstance();
153 } catch (InstantiationException e) {
154 System.err.println("InstantiationException attempting to instantiate advertiser: " + advertiserString);
155 } catch (IllegalAccessException e) {
156 System.err.println("IllegalAccessException attempting to instantiate advertiser: " + advertiserString);
157 }
158 }
159 }
160 }
161 }
162 final Iterator<StatusListener> iter = ((StatusLogger) LOGGER).getListeners();
163 boolean found = false;
164 while (iter.hasNext()) {
165 final StatusListener listener = iter.next();
166 if (listener instanceof StatusConsoleListener) {
167 found = true;
168 ((StatusConsoleListener) listener).setLevel(status);
169 if (!verbose) {
170 ((StatusConsoleListener) listener).setFilters(VERBOSE_CLASSES);
171 }
172 }
173 }
174 if (!found && status != Level.OFF) {
175 final StatusConsoleListener listener = new StatusConsoleListener(status, stream);
176 if (!verbose) {
177 listener.setFilters(VERBOSE_CLASSES);
178 }
179 ((StatusLogger) LOGGER).registerListener(listener);
180 for (final String msg : messages) {
181 LOGGER.error(msg);
182 }
183 }
184
185 } catch (final SAXException domEx) {
186 LOGGER.error("Error parsing " + configSource.getLocation(), domEx);
187 } catch (final IOException ioe) {
188 LOGGER.error("Error parsing " + configSource.getLocation(), ioe);
189 } catch (final ParserConfigurationException pex) {
190 LOGGER.error("Error parsing " + configSource.getLocation(), pex);
191 }
192 if (strict && schema != null && buffer != null) {
193 InputStream is = null;
194 try {
195 is = getClass().getClassLoader().getResourceAsStream(schema);
196 } catch (final Exception ex) {
197 LOGGER.error("Unable to access schema " + schema);
198 }
199 if (is != null) {
200 final Source src = new StreamSource(is, LOG4J_XSD);
201 final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
202 Schema schema = null;
203 try {
204 schema = factory.newSchema(src);
205 } catch (final SAXException ex) {
206 LOGGER.error("Error parsing Log4j schema", ex);
207 }
208 if (schema != null) {
209 validator = schema.newValidator();
210 try {
211 validator.validate(new StreamSource(new ByteArrayInputStream(buffer)));
212 } catch (final IOException ioe) {
213 LOGGER.error("Error reading configuration for validation", ioe);
214 } catch (final SAXException ex) {
215 LOGGER.error("Error validating configuration", ex);
216 }
217 }
218 }
219 }
220
221 if (getName() == null) {
222 setName(configSource.getLocation());
223 }
224 }
225
226 @Override
227 public void setup() {
228 constructHierarchy(rootNode, rootElement);
229 if (status.size() > 0) {
230 for (final Status s : status) {
231 LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
232 }
233 return;
234 }
235 rootElement = null;
236 }
237
238 public Configuration reconfigure() {
239 if (configFile != null) {
240 try {
241 final ConfigurationFactory.ConfigurationSource source =
242 new ConfigurationFactory.ConfigurationSource(new FileInputStream(configFile), configFile);
243 return new XMLConfiguration(source);
244 } catch (final FileNotFoundException ex) {
245 LOGGER.error("Cannot locate file " + configFile, ex);
246 }
247 }
248 return null;
249 }
250
251 private void constructHierarchy(final Node node, final Element element) {
252 processAttributes(node, element);
253 final StringBuffer buffer = new StringBuffer();
254 final NodeList list = element.getChildNodes();
255 final List<Node> children = node.getChildren();
256 for (int i = 0; i < list.getLength(); i++) {
257 final org.w3c.dom.Node w3cNode = list.item(i);
258 if (w3cNode instanceof Element) {
259 final Element child = (Element) w3cNode;
260 final String name = getType(child);
261 final PluginType type = getPluginManager().getPluginType(name);
262 final Node childNode = new Node(node, name, type);
263 constructHierarchy(childNode, child);
264 if (type == null) {
265 final String value = childNode.getValue();
266 if (!childNode.hasChildren() && value != null) {
267 node.getAttributes().put(name, value);
268 } else {
269 status.add(new Status(name, element, ErrorType.CLASS_NOT_FOUND));
270 }
271 } else {
272 children.add(childNode);
273 }
274 } else if (w3cNode instanceof Text) {
275 final Text data = (Text) w3cNode;
276 buffer.append(data.getData());
277 }
278 }
279
280 final String text = buffer.toString().trim();
281 if (text.length() > 0 || (!node.hasChildren() && !node.isRoot())) {
282 node.setValue(text);
283 }
284 }
285
286 private String getType(final Element element) {
287 if (strict) {
288 final NamedNodeMap attrs = element.getAttributes();
289 for (int i = 0; i < attrs.getLength(); ++i) {
290 final org.w3c.dom.Node w3cNode = attrs.item(i);
291 if (w3cNode instanceof Attr) {
292 final Attr attr = (Attr) w3cNode;
293 if (attr.getName().equalsIgnoreCase("type")) {
294 final String type = attr.getValue();
295 attrs.removeNamedItem(attr.getName());
296 return type;
297 }
298 }
299 }
300 }
301 return element.getTagName();
302 }
303
304 private byte[] toByteArray(final InputStream is) throws IOException {
305 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
306
307 int nRead;
308 final byte[] data = new byte[BUF_SIZE];
309
310 while ((nRead = is.read(data, 0, data.length)) != -1) {
311 buffer.write(data, 0, nRead);
312 }
313
314 return buffer.toByteArray();
315 }
316
317 private Map<String, String> processAttributes(final Node node, final Element element) {
318 final NamedNodeMap attrs = element.getAttributes();
319 final Map<String, String> attributes = node.getAttributes();
320
321 for (int i = 0; i < attrs.getLength(); ++i) {
322 final org.w3c.dom.Node w3cNode = attrs.item(i);
323 if (w3cNode instanceof Attr) {
324 final Attr attr = (Attr) w3cNode;
325 attributes.put(attr.getName(), attr.getValue());
326 }
327 }
328 return attributes;
329 }
330
331
332
333
334 private enum ErrorType {
335 CLASS_NOT_FOUND
336 }
337
338
339
340
341 private class Status {
342 private final Element element;
343 private final String name;
344 private final ErrorType errorType;
345
346 public Status(final String name, final Element element, final ErrorType errorType) {
347 this.name = name;
348 this.element = element;
349 this.errorType = errorType;
350 }
351 }
352
353 }