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.status.StatusConsoleListener;
24 import org.apache.logging.log4j.status.StatusListener;
25 import org.apache.logging.log4j.status.StatusLogger;
26 import org.w3c.dom.Attr;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.Element;
29 import org.w3c.dom.NamedNodeMap;
30 import org.w3c.dom.NodeList;
31 import org.w3c.dom.Text;
32 import org.xml.sax.InputSource;
33 import org.xml.sax.SAXException;
34
35 import javax.xml.XMLConstants;
36 import javax.xml.parsers.DocumentBuilder;
37 import javax.xml.parsers.DocumentBuilderFactory;
38 import javax.xml.parsers.ParserConfigurationException;
39 import javax.xml.transform.Source;
40 import javax.xml.transform.stream.StreamSource;
41 import javax.xml.validation.Schema;
42 import javax.xml.validation.SchemaFactory;
43 import javax.xml.validation.Validator;
44 import java.io.ByteArrayInputStream;
45 import java.io.ByteArrayOutputStream;
46 import java.io.File;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.util.ArrayList;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Map;
53
54
55
56
57 public class XMLConfiguration extends BaseConfiguration {
58
59 private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
60
61 private static final String LOG4J_XSD = "Log4J-V2.0.xsd";
62
63 private static final int BUF_SIZE = 16384;
64
65 private List<Status> status = new ArrayList<Status>();
66
67 private Element rootElement = null;
68
69 private boolean strict = false;
70
71 private String schema = null;
72
73 private Validator validator;
74
75 public XMLConfiguration(InputSource source, File configFile) {
76 byte[] buffer = null;
77
78 try {
79 buffer = toByteArray(source.getByteStream());
80 source = new InputSource(new ByteArrayInputStream(buffer));
81 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
82 Document document = builder.parse(source);
83 rootElement = document.getDocumentElement();
84 Map<String, String> attrs = processAttributes(rootNode, rootElement);
85 Level status = Level.OFF;
86 boolean verbose = false;
87
88 for (Map.Entry<String, String> entry : attrs.entrySet()) {
89 if ("status".equalsIgnoreCase(entry.getKey())) {
90 status = Level.toLevel(entry.getValue().toUpperCase(), Level.OFF);
91 } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
92 verbose = Boolean.parseBoolean(entry.getValue());
93 } else if ("packages".equalsIgnoreCase(entry.getKey())) {
94 String[] packages = entry.getValue().split(",");
95 for (String p : packages) {
96 PluginManager.addPackage(p);
97 }
98 } else if ("name".equalsIgnoreCase(entry.getKey())) {
99 setName(entry.getValue());
100 } else if ("strict".equalsIgnoreCase(entry.getKey())) {
101 strict = Boolean.parseBoolean(entry.getValue());
102 } else if ("schema".equalsIgnoreCase(entry.getKey())) {
103 schema = entry.getValue();
104 } else if ("monitorInterval".equalsIgnoreCase(entry.getKey())) {
105 int interval = Integer.parseInt(entry.getValue());
106 if (interval > 0 && configFile != null) {
107 monitor = new FileConfigurationMonitor(configFile, listeners, interval);
108 }
109 }
110 }
111 Iterator<StatusListener> iter = ((StatusLogger) LOGGER).getListeners();
112 boolean found = false;
113 while (iter.hasNext()) {
114 StatusListener listener = iter.next();
115 if (listener instanceof StatusConsoleListener) {
116 found = true;
117 ((StatusConsoleListener) listener).setLevel(status);
118 if (!verbose) {
119 ((StatusConsoleListener) listener).setFilters(VERBOSE_CLASSES);
120 }
121 }
122 }
123 if (!found && status != Level.OFF) {
124 StatusConsoleListener listener = new StatusConsoleListener(status);
125 if (!verbose) {
126 listener.setFilters(VERBOSE_CLASSES);
127 }
128 ((StatusLogger) LOGGER).registerListener(listener);
129 }
130
131 } catch (SAXException domEx) {
132 LOGGER.error("Error parsing " + source.getSystemId(), domEx);
133 } catch (IOException ioe) {
134 LOGGER.error("Error parsing " + source.getSystemId(), ioe);
135 } catch (ParserConfigurationException pex) {
136 LOGGER.error("Error parsing " + source.getSystemId(), pex);
137 }
138 if (strict && schema != null && buffer != null) {
139 InputStream is = null;
140 try {
141 is = getClass().getClassLoader().getResourceAsStream(schema);
142 } catch (Exception ex) {
143 LOGGER.error("Unable to access schema " + schema);
144 }
145 if (is != null) {
146 Source src = new StreamSource(is, LOG4J_XSD);
147 SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
148 Schema schema = null;
149 try {
150 schema = factory.newSchema(src);
151 } catch (SAXException ex) {
152 LOGGER.error("Error parsing Log4j schema", ex);
153 }
154 if (schema != null) {
155 validator = schema.newValidator();
156 try {
157 validator.validate(new StreamSource(new ByteArrayInputStream(buffer)));
158 } catch (IOException ioe) {
159 LOGGER.error("Error reading configuration for validation", ioe);
160 } catch (SAXException ex) {
161 LOGGER.error("Error validating configuration", ex);
162 }
163 }
164 }
165 }
166
167 if (getName() == null) {
168 setName(source.getSystemId());
169 }
170 }
171
172 public void setup() {
173 constructHierarchy(rootNode, rootElement);
174 if (status.size() > 0) {
175 for (Status s : status) {
176 LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
177 }
178 return;
179 }
180 rootElement = null;
181 }
182
183 private void constructHierarchy(Node node, Element element) {
184 processAttributes(node, element);
185 StringBuffer buffer = new StringBuffer();
186 NodeList list = element.getChildNodes();
187 List<Node> children = node.getChildren();
188 for (int i = 0; i < list.getLength(); i++) {
189 org.w3c.dom.Node w3cNode = list.item(i);
190 if (w3cNode instanceof Element) {
191 Element child = (Element) w3cNode;
192 String name = getType(child);
193 PluginType type = getPluginManager().getPluginType(name);
194 Node childNode = new Node(node, name, type);
195 constructHierarchy(childNode, child);
196 if (type == null) {
197 String value = childNode.getValue();
198 if (!childNode.hasChildren() && value != null) {
199 node.getAttributes().put(name, value);
200 } else {
201 status.add(new Status(name, element, ErrorType.CLASS_NOT_FOUND));
202 }
203 } else {
204 children.add(childNode);
205 }
206 } else if (w3cNode instanceof Text) {
207 Text data = (Text) w3cNode;
208 buffer.append(data.getData());
209 }
210 }
211
212 String text = buffer.toString().trim();
213 if (text.length() > 0 || (!node.hasChildren() && !node.isRoot())) {
214 node.setValue(text);
215 }
216 }
217
218 private String getType(Element element) {
219 if (strict) {
220 NamedNodeMap attrs = element.getAttributes();
221 for (int i = 0; i < attrs.getLength(); ++i) {
222 org.w3c.dom.Node w3cNode = attrs.item(i);
223 if (w3cNode instanceof Attr) {
224 Attr attr = (Attr) w3cNode;
225 if (attr.getName().equalsIgnoreCase("type")) {
226 String type = attr.getValue();
227 attrs.removeNamedItem(attr.getName());
228 return type;
229 }
230 }
231 }
232 }
233 return element.getTagName();
234 }
235
236 private byte[] toByteArray(InputStream is) throws IOException {
237 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
238
239 int nRead;
240 byte[] data = new byte[BUF_SIZE];
241
242 while ((nRead = is.read(data, 0, data.length)) != -1) {
243 buffer.write(data, 0, nRead);
244 }
245
246 return buffer.toByteArray();
247 }
248
249 private Map<String, String> processAttributes(Node node, Element element) {
250 NamedNodeMap attrs = element.getAttributes();
251 Map<String, String> attributes = node.getAttributes();
252
253 for (int i = 0; i < attrs.getLength(); ++i) {
254 org.w3c.dom.Node w3cNode = attrs.item(i);
255 if (w3cNode instanceof Attr) {
256 Attr attr = (Attr) w3cNode;
257 attributes.put(attr.getName(), attr.getValue());
258 }
259 }
260 return attributes;
261 }
262
263
264
265
266 private enum ErrorType {
267 CLASS_NOT_FOUND
268 }
269
270
271
272
273 private class Status {
274 private Element element;
275 private String name;
276 private ErrorType errorType;
277
278 public Status(String name, Element element, ErrorType errorType) {
279 this.name = name;
280 this.element = element;
281 this.errorType = errorType;
282 }
283 }
284
285 }