This is an automated email from the ASF dual-hosted git repository. ppkarwasz pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/commons-xml.git
commit 9157c8e2971e3aeb08eb2124f1fb00bff635f610 Author: Piotr P. Karwasz <[email protected]> AuthorDate: Sat Apr 25 10:02:58 2026 +0200 feat: remove UnsupportedXmlImplementationException `XmlFactories` will throw `HardeningException` in both the following cases: - The implementation is unsupported, - A known implementation unexpectedly does not handle a feature. --- README.md | 2 +- .../UnsupportedXmlImplementationException.java | 53 ---------------------- .../apache/commons/xml/factory/XmlFactories.java | 18 +++++--- .../xml/factory/internal/HardeningException.java | 16 +++++-- .../xml/factory/internal/ProviderRegistry.java | 10 ++-- src/site/markdown/index.md | 2 +- .../factory/UnsupportedXmlImplementationTest.java | 10 ++-- .../xml/factory/internal/ProviderRegistryTest.java | 13 +++--- 8 files changed, 43 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 9bcec33..e169e91 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ stylesheet) is blocked, and DOCTYPE input is rejected wherever the underlying im Out of the box the library recognises the stock JDK JAXP implementations, Apache Xerces 2.x, Woodstox, and Saxon-HE. If a factory resolves to an implementation not covered by any bundled or registered provider, every `XmlFactories` method -throws `UnsupportedXmlImplementationException`. +throws `IllegalStateException` with a message naming the unsupported class. Support for additional implementations can be plugged in by publishing an `org.apache.commons.xml.factory.spi.XmlProvider` through the standard Java `ServiceLoader` mechanism. Bundled providers diff --git a/src/main/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationException.java b/src/main/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationException.java deleted file mode 100644 index 11ed207..0000000 --- a/src/main/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 - * - * 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.apache.commons.xml.factory; - -/** - * Thrown when no {@link org.apache.commons.xml.factory.spi.XmlProvider XmlProvider} is able to harden a JAXP factory of the given concrete class. - */ -public class UnsupportedXmlImplementationException extends IllegalStateException { - - private static final long serialVersionUID = 1L; - - private final Class<?> unsupportedFactoryClass; - - /** - * Constructs a new exception naming the unsupported factory class. - * - * <p>The detail message identifies the class and suggests remediation steps.</p> - * - * @param unsupportedFactoryClass the concrete factory class that no provider recognises. - */ - public UnsupportedXmlImplementationException(final Class<?> unsupportedFactoryClass) { - super(buildMessage(unsupportedFactoryClass)); - this.unsupportedFactoryClass = unsupportedFactoryClass; - } - - private static String buildMessage(final Class<?> factoryClass) { - return String.format("No XmlProvider supports JAXP factory class %s. Add a supported JAXP implementation (for example Apache Xerces) to the " + - "classpath, or register a custom XmlProvider via META-INF/services/org.apache.commons.xml.factory.spi.XmlProvider.", factoryClass.getName()); - } - - /** - * Gets the concrete factory class that no registered provider could configure. - * - * @return the unsupported factory class. - */ - public Class<?> getUnsupportedFactoryClass() { - return unsupportedFactoryClass; - } -} diff --git a/src/main/java/org/apache/commons/xml/factory/XmlFactories.java b/src/main/java/org/apache/commons/xml/factory/XmlFactories.java index 18d63c3..3733d0c 100644 --- a/src/main/java/org/apache/commons/xml/factory/XmlFactories.java +++ b/src/main/java/org/apache/commons/xml/factory/XmlFactories.java @@ -70,7 +70,8 @@ public final class XmlFactories { * <p>Together these block <strong>every</strong> external reference the parser would follow while reading a document through this factory.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying JAXP implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying JAXP implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static DocumentBuilderFactory newDocumentBuilderFactory() { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -91,7 +92,8 @@ public static DocumentBuilderFactory newDocumentBuilderFactory() { * <p>Together these block <strong>every</strong> external reference the parser would follow while reading a document through this factory.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying JAXP implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying JAXP implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static SAXParserFactory newSAXParserFactory() { final SAXParserFactory factory = SAXParserFactory.newInstance(); @@ -122,7 +124,8 @@ public static SAXParserFactory newSAXParserFactory() { * above.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying Schema implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying Schema implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static SchemaFactory newSchemaFactory() { final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); @@ -150,7 +153,8 @@ public static SchemaFactory newSchemaFactory() { * {@link javax.xml.transform.sax.SAXSource}.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying TrAX implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying TrAX implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static TransformerFactory newTransformerFactory() { final TransformerFactory factory = TransformerFactory.newInstance(); @@ -176,7 +180,8 @@ public static TransformerFactory newTransformerFactory() { * {@link XMLInputFactory#IS_VALIDATING}.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying StAX implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying StAX implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static XMLInputFactory newXMLInputFactory() { final XMLInputFactory factory = XMLInputFactory.newInstance(); @@ -196,7 +201,8 @@ public static XMLInputFactory newXMLInputFactory() { * underlying implementation honours the flag.</p> * * @return a hardened factory. - * @throws UnsupportedXmlImplementationException if the underlying XPath implementation is not recognised by any registered {@link XmlProvider}. + * @throws IllegalStateException if no registered {@link XmlProvider} recognises the underlying XPath implementation, or if the recognised provider cannot + * apply the hardening settings to it. */ public static XPathFactory newXPathFactory() { final XPathFactory factory = XPathFactory.newInstance(); diff --git a/src/main/java/org/apache/commons/xml/factory/internal/HardeningException.java b/src/main/java/org/apache/commons/xml/factory/internal/HardeningException.java index dbfaea9..804dcd1 100644 --- a/src/main/java/org/apache/commons/xml/factory/internal/HardeningException.java +++ b/src/main/java/org/apache/commons/xml/factory/internal/HardeningException.java @@ -17,10 +17,16 @@ package org.apache.commons.xml.factory.internal; /** - * Thrown when a provider cannot fully harden a factory. + * Thrown when a factory cannot be hardened. * - * <p>The message names the specific feature or property that failed; the cause is the original checked or unchecked exception from the JAXP - * implementation.</p> + * <p>Two failure modes share this type:</p> + * <ul> + * <li>No registered {@link org.apache.commons.xml.factory.spi.XmlProvider XmlProvider} recognises the concrete factory class.</li> + * <li>A recognised provider tried to apply a hardening setting and the implementation rejected it.</li> + * </ul> + * + * <p>The message names the unsupported factory class or the specific feature, attribute or property that failed; the cause, when present, is the original + * checked or unchecked exception from the JAXP implementation.</p> * * <p>Package-private by design: callers should catch {@link IllegalStateException}, which this extends.</p> */ @@ -28,6 +34,10 @@ class HardeningException extends IllegalStateException { private static final long serialVersionUID = 1L; + HardeningException(final String message) { + super(message); + } + HardeningException(final String message, final Throwable cause) { super(message, cause); } diff --git a/src/main/java/org/apache/commons/xml/factory/internal/ProviderRegistry.java b/src/main/java/org/apache/commons/xml/factory/internal/ProviderRegistry.java index 2adf613..5dad3b5 100644 --- a/src/main/java/org/apache/commons/xml/factory/internal/ProviderRegistry.java +++ b/src/main/java/org/apache/commons/xml/factory/internal/ProviderRegistry.java @@ -25,7 +25,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.commons.xml.factory.UnsupportedXmlImplementationException; import org.apache.commons.xml.factory.spi.XmlProvider; /** @@ -36,7 +35,7 @@ * <ol> * <li>Bundled providers in a fixed order: {@link StockJdkProvider}, {@link XercesProvider}, {@link WoodstoxProvider}, {@link SaxonProvider}.</li> * <li>Third-party providers discovered via {@link ServiceLoader}, in the order {@link ServiceLoader} yields them.</li> - * <li>If nothing matches, {@link UnsupportedXmlImplementationException}.</li> + * <li>If nothing matches, {@link HardeningException}.</li> * </ol> * * <p>Bundled providers are consulted first so that a third-party provider on the classpath cannot intercept hardening of a factory class this library already @@ -76,7 +75,7 @@ public static ProviderRegistry getInstance() { * * @param factoryClass the concrete factory class; never {@code null}. * @return the matching provider. - * @throws UnsupportedXmlImplementationException if no provider matches. + * @throws HardeningException if no provider matches. */ public XmlProvider providerFor(final Class<?> factoryClass) { Objects.requireNonNull(factoryClass); @@ -94,7 +93,10 @@ private XmlProvider lookup(final Class<?> factoryClass) { return provider; } } - throw new UnsupportedXmlImplementationException(factoryClass); + throw new HardeningException(String.format( + "No XmlProvider supports JAXP factory class %s. Add a supported JAXP implementation to the classpath, " + + "or register a custom XmlProvider via ServiceLoader.", + factoryClass.getName())); } private static List<XmlProvider> bundledProviders() { diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index a7e7878..4fe6566 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -60,7 +60,7 @@ stylesheet) is blocked, and DOCTYPE input is rejected wherever the underlying im Out of the box the library recognises the stock JDK JAXP implementations, Apache Xerces 2.x, Woodstox, and Saxon-HE. If a factory resolves to an implementation not covered by any bundled or registered provider, every `XmlFactories` method -throws `UnsupportedXmlImplementationException`. +throws `IllegalStateException` with a message naming the unsupported class. Support for additional implementations can be plugged in by publishing an `org.apache.commons.xml.factory.spi.XmlProvider` through the standard Java `ServiceLoader` mechanism. Bundled providers diff --git a/src/test/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationTest.java b/src/test/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationTest.java index d83ef82..a22b0c2 100644 --- a/src/test/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationTest.java +++ b/src/test/java/org/apache/commons/xml/factory/UnsupportedXmlImplementationTest.java @@ -16,10 +16,7 @@ */ package org.apache.commons.xml.factory; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -30,7 +27,7 @@ import org.junit.jupiter.api.Test; /** - * Verifies that an unknown factory class surfaces {@link UnsupportedXmlImplementationException} with the class attached. + * Verifies that an unknown factory class surfaces {@link IllegalStateException} with a message naming the class and pointing at the remediation path. */ class UnsupportedXmlImplementationTest { @@ -67,10 +64,9 @@ public boolean getFeature(final String name) { @Test void registryRejectsUnknownFactory() { - final UnsupportedXmlImplementationException thrown = assertThrows( - UnsupportedXmlImplementationException.class, + final IllegalStateException thrown = assertThrows( + IllegalStateException.class, () -> ProviderRegistry.getInstance().providerFor(FakeDocumentBuilderFactory.class)); - assertSame(FakeDocumentBuilderFactory.class, thrown.getUnsupportedFactoryClass()); assertNotNull(thrown.getMessage()); assertTrue(thrown.getMessage().contains(FakeDocumentBuilderFactory.class.getName()), "Exception message must name the unsupported class: " + thrown.getMessage()); diff --git a/src/test/java/org/apache/commons/xml/factory/internal/ProviderRegistryTest.java b/src/test/java/org/apache/commons/xml/factory/internal/ProviderRegistryTest.java index 684ad8f..318e627 100644 --- a/src/test/java/org/apache/commons/xml/factory/internal/ProviderRegistryTest.java +++ b/src/test/java/org/apache/commons/xml/factory/internal/ProviderRegistryTest.java @@ -18,11 +18,11 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.Collections; -import org.apache.commons.xml.factory.UnsupportedXmlImplementationException; import org.apache.commons.xml.factory.spi.XmlProvider; import org.junit.jupiter.api.Test; @@ -57,12 +57,13 @@ void providerForNullThrowsNpe() { } @Test - void providerForUnknownClassThrowsUnsupportedWithClassAttached() { + void providerForUnknownClassThrowsHardeningException() { final ProviderRegistry registry = new ProviderRegistry(Collections.emptyList()); - final UnsupportedXmlImplementationException thrown = assertThrows( - UnsupportedXmlImplementationException.class, + final HardeningException thrown = assertThrows( + HardeningException.class, () -> registry.providerFor(String.class)); - assertSame(String.class, thrown.getUnsupportedFactoryClass()); + assertTrue(thrown.getMessage().contains(String.class.getName()), + "Exception message must name the unsupported class: " + thrown.getMessage()); } @Test @@ -94,7 +95,7 @@ void nonMatchingBundledProviderFallsThroughToNext() { @Test void emptyBundledListWithNoServiceLoaderProvidersThrows() { final ProviderRegistry registry = new ProviderRegistry(Collections.emptyList()); - assertThrows(UnsupportedXmlImplementationException.class, + assertThrows(HardeningException.class, () -> registry.providerFor(Integer.class)); } }
