| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core.config.xml; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.charset.Charset; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.stream.Stream; |
| |
| import javax.xml.XMLConstants; |
| import javax.xml.transform.sax.SAXSource; |
| import javax.xml.transform.stream.StreamSource; |
| import javax.xml.validation.Schema; |
| import javax.xml.validation.SchemaFactory; |
| import javax.xml.validation.Validator; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.mutable.MutableInt; |
| import org.junit.jupiter.api.Test; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.XMLFilterImpl; |
| import org.xml.sax.helpers.XMLReaderFactory; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| |
| public class XmlSchemaTest { |
| |
| private static final String TARGET_NAMESPACE = "http://logging.apache.org/log4j/2.0/config"; |
| |
| private static final List<String> IGNORE_CONFIGS = Arrays.asList( // |
| "log4j2-arbiters.xml", // Arbiters violate XML schema as they can appear anywhere |
| "log4j2-scriptArbiters.xml", |
| "log4j2-selectArbiters.xml", |
| "log4j-core-gctests/src/test/resources/gcFreeLogging.xml", // has 2 <Pattern> tags defined |
| "legacy-plugins.xml", // |
| "logback", // logback configs |
| "log4j-xinclude", // |
| "log4j12", // log4j 1.x configs |
| "perf-CountingNoOpAppender.xml", // uses test-appender CountingNoOp |
| "reconfiguration-deadlock.xml", // uses test-appender ReconfigurationDeadlockTestAppender |
| "XmlConfigurationSecurity.xml" // |
| ); |
| |
| private static String capitalizeTags(final String xml) { |
| final StringBuffer sb = new StringBuffer(); |
| final Matcher m = Pattern.compile("([<][/]?[a-z])").matcher(xml); |
| while (m.find()) { |
| m.appendReplacement(sb, m.group(1).toUpperCase()); |
| } |
| return m.appendTail(sb).toString(); |
| } |
| |
| private static String fixXml(String xml) { |
| xml = StringUtils.replace(xml, "JSONLayout", "JsonLayout"); |
| xml = StringUtils.replace(xml, "HTMLLayout", "HtmlLayout"); |
| xml = StringUtils.replace(xml, "XMLLayout", "XmlLayout"); |
| xml = StringUtils.replace(xml, "appender-ref", "AppenderRef"); |
| xml = StringUtils.replace(xml, " onmatch=", " onMatch="); |
| xml = StringUtils.replace(xml, " onMisMatch=", " onMismatch="); |
| xml = StringUtils.replace(xml, " onmismatch=", " onMismatch="); |
| xml = StringUtils.replace(xml, "<Marker ", "<MarkerFilter "); |
| xml = StringUtils.replace(xml, " Value=", " value="); |
| xml = capitalizeTags(xml); |
| return xml; |
| } |
| |
| @Test |
| public void testXmlSchemaValidation() throws SAXException, IOException { |
| final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| final Schema schema = factory.newSchema(new StreamSource(new File("src/main/resources/Log4j-config.xsd"))); |
| final Validator validator = schema.newValidator(); |
| |
| final XMLFilterImpl namespaceAdder = new XMLFilterImpl(XMLReaderFactory.createXMLReader()) { |
| @Override |
| public void startElement(final String namespace, final String localName, final String qName, |
| final Attributes atts) throws SAXException { |
| super.startElement(TARGET_NAMESPACE, localName, qName, atts); |
| } |
| }; |
| |
| final MutableInt configs = new MutableInt(); |
| final MutableInt failures = new MutableInt(); |
| |
| try (final Stream<Path> testResources = Files.list(Paths.get("src", "test", "resources"))) { |
| testResources |
| .filter(filePath -> { |
| final String fileName = filePath.getFileName().toString(); |
| if (!fileName.endsWith(".xml")) |
| return false; |
| for (final String ignore : IGNORE_CONFIGS) { |
| if (fileName.contains(ignore)) |
| return false; |
| } |
| return true; |
| }) // |
| .forEach(filePath -> { |
| System.out.println("Validating " + configs.incrementAndGet() + ". [" + filePath + "]..."); |
| System.out.flush(); |
| |
| try { |
| final String xml = fixXml( |
| FileUtils.readFileToString(filePath.toFile(), Charset.defaultCharset())); |
| validator.validate(new SAXSource(namespaceAdder, |
| new InputSource(new ByteArrayInputStream(xml.getBytes()))), null); |
| } catch (final Exception ex) { |
| System.err.println(ex); |
| System.err.flush(); |
| failures.increment(); |
| } |
| }); |
| } |
| |
| assertEquals(0, failures.intValue()); |
| } |
| } |