diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java index 65a3ece5b98..16439a35ae2 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java @@ -26,11 +26,8 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; -import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; @@ -51,6 +48,7 @@ import org.springframework.boot.build.bom.ResolvedBom.Id; import org.springframework.boot.build.bom.ResolvedBom.JavadocLink; import org.springframework.boot.build.bom.ResolvedBom.Links; import org.springframework.boot.build.bom.ResolvedBom.ResolvedLibrary; +import org.springframework.boot.build.xml.XmlDocument; /** * Creates a {@link ResolvedBom resolved bom}. @@ -68,15 +66,7 @@ class BomResolver { BomResolver(ConfigurationContainer configurations, DependencyHandler dependencies) { this.configurations = configurations; this.dependencies = dependencies; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - this.documentBuilder = factory.newDocumentBuilder(); - } - catch (ParserConfigurationException ex) { - throw new RuntimeException(ex); - } + this.documentBuilder = XmlDocument.builder(); } ResolvedBom resolve(BomExtension bomExtension) { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java index 50e155da8bc..0e1b335772d 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java @@ -35,9 +35,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; @@ -52,6 +49,7 @@ import org.w3c.dom.Document; import org.springframework.boot.build.bom.ResolvedBom.Id; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; +import org.springframework.boot.build.xml.XmlDocument; /** * A collection of modules, Maven plugins, and Maven boms that are versioned and released @@ -677,11 +675,7 @@ public class Library { private String propertyFrom(File pomFile) { try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); - Document document = documentBuilder.parse(pomFile); + Document document = XmlDocument.parse(pomFile); XPath xpath = XPathFactory.newInstance().newXPath(); return xpath.evaluate("/project/properties/" + this.name + "/text()", document); } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java index 2ca32574299..ecc70b4314e 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java @@ -16,7 +16,6 @@ package org.springframework.boot.build.bom.bomr; -import java.io.StringReader; import java.net.URI; import java.util.Collection; import java.util.Collections; @@ -26,8 +25,6 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; @@ -37,9 +34,9 @@ import org.gradle.api.credentials.Credentials; import org.gradle.internal.artifacts.repositories.AuthenticationSupportedInternal; import org.w3c.dom.Document; import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; +import org.springframework.boot.build.xml.XmlDocument; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -94,10 +91,7 @@ final class MavenMetadataVersionResolver implements VersionResolver { } HttpEntity request = new HttpEntity<>(headers); String metadata = this.rest.exchange(url, HttpMethod.GET, request, String.class).getBody(); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - Document metadataDocument = factory.newDocumentBuilder().parse(new InputSource(new StringReader(metadata))); + Document metadataDocument = XmlDocument.parseContent(metadata); NodeList versionNodes = (NodeList) XPathFactory.newInstance() .newXPath() .evaluate("/metadata/versioning/versions/version", metadataDocument, XPathConstants.NODESET); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java index 1aec708d0d8..53c3c6deb86 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java @@ -23,8 +23,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; @@ -33,6 +31,8 @@ import javax.xml.xpath.XPathFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.springframework.boot.build.xml.XmlDocument; + /** * A parser for a Maven plugin's {@code plugin.xml} file. * @@ -49,10 +49,7 @@ class PluginXmlParser { Plugin parse(File pluginXml) { try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - Node root = factory.newDocumentBuilder().parse(pluginXml); + Node root = XmlDocument.parse(pluginXml); List mojos = parseMojos(root); return new Plugin(textAt("//plugin/groupId", root), textAt("//plugin/artifactId", root), textAt("//plugin/version", root), textAt("//plugin/goalPrefix", root), mojos); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/xml/XmlDocument.java b/buildSrc/src/main/java/org/springframework/boot/build/xml/XmlDocument.java new file mode 100644 index 00000000000..925d7c87713 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/xml/XmlDocument.java @@ -0,0 +1,72 @@ +/* + * Copyright 2026 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.boot.build.xml; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * XML {@link Document} builder and parsing. + * + * @author Phillip Webb + * @author Sebastien Tardif + */ +public final class XmlDocument { + + private static final DocumentBuilderFactory factory; + static { + try { + factory = DocumentBuilderFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + } + catch (ParserConfigurationException ex) { + throw new IllegalStateException(ex); + } + } + + private XmlDocument() { + } + + public static Document parseContent(String content) throws SAXException, IOException { + return builder().parse(new InputSource(new StringReader(content))); + } + + public static Document parse(File file) throws SAXException, IOException { + return builder().parse(file); + } + + public static DocumentBuilder builder() { + try { + return factory.newDocumentBuilder(); + } + catch (ParserConfigurationException ex) { + throw new IllegalStateException(ex); + } + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java b/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java index 791e3213d6a..703f687e38c 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java @@ -18,7 +18,6 @@ package org.springframework.boot.build.assertj; import java.io.File; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; @@ -30,6 +29,8 @@ import org.assertj.core.api.StringAssert; import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.springframework.boot.build.xml.XmlDocument; + /** * AssertJ {@link AssertProvider} for {@link Node} assertions. * @@ -37,8 +38,6 @@ import org.w3c.dom.Node; */ public class NodeAssert extends AbstractAssert implements AssertProvider { - private static final DocumentBuilderFactory FACTORY = DocumentBuilderFactory.newInstance(); - private final XPathFactory xpathFactory = XPathFactory.newInstance(); private final XPath xpath = this.xpathFactory.newXPath(); @@ -53,7 +52,7 @@ public class NodeAssert extends AbstractAssert implements Asse private static Document read(File xmlFile) { try { - return FACTORY.newDocumentBuilder().parse(xmlFile); + return XmlDocument.parse(xmlFile); } catch (Exception ex) { throw new RuntimeException(ex);