diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaData.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaData.java index 2e4042fbe471..fcb983f00c2c 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaData.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaData.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resources; import org.eclipse.jetty.util.thread.AutoLock; +import org.eclipse.jetty.xml.XmlParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +40,7 @@ public class MetaData private final AutoLock _lock = new AutoLock(); protected Map _origins = new HashMap<>(); + protected XmlParser _xmlParser; protected WebDescriptor _webDefaultsRoot; protected WebDescriptor _webXmlRoot; protected final List _webOverrideRoots = new ArrayList<>(); @@ -185,7 +187,7 @@ public void setDefaultsDescriptor(DefaultsDescriptor descriptor) throws Exception { _webDefaultsRoot = descriptor; - _webDefaultsRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webDefaultsRoot.parse(getXmlParser()); if (_webDefaultsRoot.isOrdered()) { Ordering ordering = getOrdering(); @@ -214,7 +216,7 @@ public void setWebDescriptor(WebDescriptor descriptor) throws Exception { _webXmlRoot = descriptor; - _webXmlRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webXmlRoot.parse(getXmlParser()); _metaDataComplete = WebDescriptor.isMetaDataComplete(_webXmlRoot); if (_webXmlRoot.isOrdered()) @@ -245,7 +247,7 @@ public void setWebDescriptor(WebDescriptor descriptor) public void addOverrideDescriptor(OverrideDescriptor descriptor) throws Exception { - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); Boolean metaDataComplete = descriptor.getMetaDataComplete(); if (metaDataComplete != null) @@ -292,7 +294,7 @@ public void addFragmentDescriptor(Resource jarResource, FragmentDescriptor descr //Metadata-complete is not set, or there is no web.xml _webFragmentResourceMap.put(jarResource, descriptor); _webFragmentRoots.add(descriptor); - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); if (descriptor.getName() != null) { @@ -638,9 +640,40 @@ public boolean isValidateXml() */ public void setValidateXml(boolean validateXml) { + if (_xmlParser != null && _xmlParser.isValidating() != _validateXml) + throw new IllegalStateException("XmlParser previously set"); + _validateXml = validateXml; } + /** + * Set the XmlParser to use for handling metadata, this will + * also add an environment specific catalog to the XmlParser. + * + *

This is useful when you want to configure a custom XML Parser + * with a variety of custom attributes and configurations.

+ * + * @param xmlParser the XML parser to use. + */ + public void setXmlParser(XmlParser xmlParser) + { + _xmlParser = Objects.requireNonNull(xmlParser); + + WebDescriptor.addDescriptorCatalog(_xmlParser); + } + + /** + * The XmlParser in use for this metadata. + * + * @return the in use XML Parser + */ + public XmlParser getXmlParser() + { + if (_xmlParser == null) + setXmlParser(new XmlParser(isValidateXml())); + return _xmlParser; + } + public Map getOrigins() { return Collections.unmodifiableMap(_origins); diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 844cdee7b7c7..e7ef3ec1e534 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -515,7 +515,9 @@ protected void doStart() throws Exception { _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); Boolean validate = (Boolean)getAttribute(MetaData.VALIDATE_XML); - _metadata.setValidateXml((validate != null && validate)); + // Don't set validate unless it is declared. + if (validate != null) + _metadata.setValidateXml(validate); preConfigure(); super.doStart(); postConfigure(); @@ -602,7 +604,7 @@ public Configurations getConfigurations() * * @return Returns the defaultsDescriptor. */ - @ManagedAttribute(value = "default web.xml deascriptor applied before standard web.xml", readonly = true) + @ManagedAttribute(value = "default web.xml descriptor applied before standard web.xml", readonly = true) public String getDefaultsDescriptor() { return _defaultsDescriptor; @@ -625,7 +627,7 @@ public String getOverrideDescriptor() * * @return Returns the Override Descriptor list */ - @ManagedAttribute(value = "web.xml deascriptors applied after standard web.xml", readonly = true) + @ManagedAttribute(value = "web.xml descriptors applied after standard web.xml", readonly = true) public List getOverrideDescriptors() { return Collections.unmodifiableList(_overrideDescriptors); @@ -1400,8 +1402,7 @@ protected void stopContext() throws Exception _configurations.get(i).deconfigure(this); } - if (_metadata != null) - _metadata.clear(); + _metadata.clear(); _metadata = new MetaData(); } finally diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java index 3923f75b4e07..927925af14ee 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java @@ -34,6 +34,10 @@ public class WebDescriptor extends Descriptor { private static final Logger LOG = LoggerFactory.getLogger(WebDescriptor.class); + /** + * @deprecated no direct replacement, use of {@link MetaData#getXmlParser()} is encouraged. + */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser __nonValidatingStaticParser = newParser(false); protected Boolean _metaDataComplete; protected int _majorVersion = 4; //default to container version @@ -61,13 +65,12 @@ public static boolean isMetaDataComplete(WebDescriptor d) * * @param validating true if the parser should validate syntax, false otherwise * @return an XmlParser for web descriptors + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser getParser(boolean validating) { - if (!validating) - return __nonValidatingStaticParser; - else - return newParser(true); + return newParser(validating); } /** @@ -75,29 +78,29 @@ public static XmlParser getParser(boolean validating) * * @param validating if true, the parser will validate syntax * @return an XmlParser + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser newParser(boolean validating) { + XmlParser xmlParser = new XmlParser(validating); + addDescriptorCatalog(xmlParser); + return xmlParser; + } + + protected static void addDescriptorCatalog(XmlParser xmlParser) throws IllegalStateException + { + String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.ENVIRONMENT.getName()); + URL url = WebDescriptor.class.getResource(catalogName); + if (url == null) + throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); try { - return new WebDescriptorParser(validating); + xmlParser.addCatalog(URI.create(url.toExternalForm()), Servlet.class); } catch (IOException e) { - throw new IllegalStateException("Unable to instantiate WebDescriptorParser", e); - } - } - - private static class WebDescriptorParser extends XmlParser - { - public WebDescriptorParser(boolean validating) throws IOException - { - super(validating); - String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.ENVIRONMENT.getName()); - URL url = WebDescriptor.class.getResource(catalogName); - if (url == null) - throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); - addCatalog(URI.create(url.toExternalForm()), Servlet.class); + throw new IllegalStateException("Unable to add catalog: " + url, e); } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java index e7e740ba11ee..014052401054 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java @@ -78,6 +78,7 @@ import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlParser; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -110,6 +111,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; 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.assertTrue; @Isolated() @@ -1295,6 +1297,29 @@ public void testAddProtectedClasses() throws Exception assertThat("deprecated API", protectedClasses, hasItem("org.deprecated.api.")); } + @Test + public void testCustomXmlParser(WorkDir workDir) throws Exception + { + Server server = newServer(); + + MyXmlParser xmlParser = new MyXmlParser(); + WebAppContext context = new WebAppContext(); + context.getMetaData().setXmlParser(xmlParser); + context.setBaseResourceAsPath(workDir.getEmptyPathDir()); + + server.setHandler(context); + server.start(); + + assertTrue(context.isStarted()); + assertTrue(context.isAvailable()); + XmlParser startedXmlParser = context.getMetaData().getXmlParser(); + assertSame(startedXmlParser, xmlParser); + } + + public static class MyXmlParser extends XmlParser + { + } + public static class OkServlet extends HttpServlet { @Override diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebDescriptorTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebDescriptorTest.java index bdd1366d189a..6fc99cfdc135 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebDescriptorTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebDescriptorTest.java @@ -47,7 +47,7 @@ public void testXmlWithXsd(WorkDir workDir) throws Exception """, StandardCharsets.UTF_8); WebDescriptor webDescriptor = new WebDescriptor(ResourceFactory.root().newResource(xml)); - XmlParser xmlParser = WebDescriptor.newParser(true); + XmlParser xmlParser = new XmlParser(true); // This should not throw an exception, if it does then you have a bad state. // Such as missing required XML resource entities. webDescriptor.parse(xmlParser); diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/MetaData.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/MetaData.java index f95aeabc041c..022d095f9ba1 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/MetaData.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/MetaData.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resources; import org.eclipse.jetty.util.thread.AutoLock; +import org.eclipse.jetty.xml.XmlParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +40,7 @@ public class MetaData private final AutoLock _lock = new AutoLock(); protected Map _origins = new HashMap<>(); + protected XmlParser _xmlParser; protected WebDescriptor _webDefaultsRoot; protected WebDescriptor _webXmlRoot; protected final List _webOverrideRoots = new ArrayList<>(); @@ -185,7 +187,7 @@ public void setDefaultsDescriptor(DefaultsDescriptor descriptor) throws Exception { _webDefaultsRoot = descriptor; - _webDefaultsRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webDefaultsRoot.parse(getXmlParser()); if (_webDefaultsRoot.isOrdered()) { Ordering ordering = getOrdering(); @@ -214,7 +216,7 @@ public void setWebDescriptor(WebDescriptor descriptor) throws Exception { _webXmlRoot = descriptor; - _webXmlRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webXmlRoot.parse(getXmlParser()); _metaDataComplete = WebDescriptor.isMetaDataComplete(_webXmlRoot); if (_webXmlRoot.isOrdered()) @@ -245,7 +247,7 @@ public void setWebDescriptor(WebDescriptor descriptor) public void addOverrideDescriptor(OverrideDescriptor descriptor) throws Exception { - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); Boolean metaDataComplete = descriptor.getMetaDataComplete(); if (metaDataComplete != null) @@ -292,7 +294,7 @@ public void addFragmentDescriptor(Resource jarResource, FragmentDescriptor descr //Metadata-complete is not set, or there is no web.xml _webFragmentResourceMap.put(jarResource, descriptor); _webFragmentRoots.add(descriptor); - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); if (descriptor.getName() != null) { @@ -638,9 +640,40 @@ public boolean isValidateXml() */ public void setValidateXml(boolean validateXml) { + if (_xmlParser != null && _xmlParser.isValidating() != _validateXml) + throw new IllegalStateException("XmlParser previously set"); + _validateXml = validateXml; } + /** + * Set the XmlParser to use for handling metadata, this will + * also add an environment specific catalog to the XmlParser. + * + *

This is useful when you want to configure a custom XML Parser + * with a variety of custom attributes and configurations.

+ * + * @param xmlParser the XML parser to use. + */ + public void setXmlParser(XmlParser xmlParser) + { + _xmlParser = Objects.requireNonNull(xmlParser); + + WebDescriptor.addDescriptorCatalog(_xmlParser); + } + + /** + * The XmlParser in use for this metadata. + * + * @return the in use XML Parser + */ + public XmlParser getXmlParser() + { + if (_xmlParser == null) + setXmlParser(new XmlParser(isValidateXml())); + return _xmlParser; + } + public Map getOrigins() { return Collections.unmodifiableMap(_origins); diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java index 2f7705943f38..7d2ecbce6916 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee11.webapp; -import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; @@ -494,7 +493,9 @@ protected void doStart() throws Exception { _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); Boolean validate = (Boolean)getAttribute(MetaData.VALIDATE_XML); - _metadata.setValidateXml((validate != null && validate)); + // Don't set validate unless it is declared. + if (validate != null) + _metadata.setValidateXml(validate); preConfigure(); super.doStart(); postConfigure(); @@ -581,7 +582,7 @@ public Configurations getConfigurations() * * @return Returns the defaultsDescriptor. */ - @ManagedAttribute(value = "default web.xml deascriptor applied before standard web.xml", readonly = true) + @ManagedAttribute(value = "default web.xml descriptor applied before standard web.xml", readonly = true) public String getDefaultsDescriptor() { return _defaultsDescriptor; @@ -604,7 +605,7 @@ public String getOverrideDescriptor() * * @return Returns the Override Descriptor list */ - @ManagedAttribute(value = "web.xml deascriptors applied after standard web.xml", readonly = true) + @ManagedAttribute(value = "web.xml descriptors applied after standard web.xml", readonly = true) public List getOverrideDescriptors() { return Collections.unmodifiableList(_overrideDescriptors); @@ -1286,8 +1287,7 @@ protected void stopContext() throws Exception _configurations.get(i).deconfigure(this); } - if (_metadata != null) - _metadata.clear(); + _metadata.clear(); _metadata = new MetaData(); } finally diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebDescriptor.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebDescriptor.java index cd3d2ce8a2e6..4a9d20c12459 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebDescriptor.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebDescriptor.java @@ -41,6 +41,10 @@ public class WebDescriptor extends Descriptor """; private static final Logger LOG = LoggerFactory.getLogger(WebDescriptor.class); + /** + * @deprecated no direct replacement, use of {@link MetaData#getXmlParser()} is encouraged. + */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser __nonValidatingStaticParser = newParser(false); protected Boolean _metaDataComplete; protected int _majorVersion = 4; //default to container version @@ -68,13 +72,12 @@ public static boolean isMetaDataComplete(WebDescriptor d) * * @param validating true if the parser should validate syntax, false otherwise * @return an XmlParser for web descriptors + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser getParser(boolean validating) { - if (!validating) - return __nonValidatingStaticParser; - else - return newParser(true); + return newParser(validating); } /** @@ -82,29 +85,29 @@ public static XmlParser getParser(boolean validating) * * @param validating if true, the parser will validate syntax * @return an XmlParser + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser newParser(boolean validating) { + XmlParser xmlParser = new XmlParser(validating); + addDescriptorCatalog(xmlParser); + return xmlParser; + } + + protected static void addDescriptorCatalog(XmlParser xmlParser) throws IllegalStateException + { + String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.ENVIRONMENT.getName()); + URL url = WebDescriptor.class.getResource(catalogName); + if (url == null) + throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); try { - return new WebDescriptorParser(validating); + xmlParser.addCatalog(URI.create(url.toExternalForm()), Servlet.class); } catch (IOException e) { - throw new IllegalStateException("Unable to instantiate WebDescriptorParser", e); - } - } - - private static class WebDescriptorParser extends XmlParser - { - public WebDescriptorParser(boolean validating) throws IOException - { - super(validating); - String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.ENVIRONMENT.getName()); - URL url = WebDescriptor.class.getResource(catalogName); - if (url == null) - throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); - addCatalog(URI.create(url.toExternalForm()), Servlet.class); + throw new IllegalStateException("Unable to add catalog: " + url, e); } } diff --git a/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebAppContextTest.java b/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebAppContextTest.java index e7e0f569f6f7..d446a73ddefc 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebAppContextTest.java +++ b/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebAppContextTest.java @@ -78,6 +78,7 @@ import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlParser; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -110,6 +111,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; 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.assertTrue; @Isolated() @@ -1289,6 +1291,29 @@ public void testAddProtectedClasses() throws Exception assertThat("context API", protectedClasses, hasItem("org.context.specific.")); } + @Test + public void testCustomXmlParser(WorkDir workDir) throws Exception + { + Server server = newServer(); + + MyXmlParser xmlParser = new MyXmlParser(); + WebAppContext context = new WebAppContext(); + context.getMetaData().setXmlParser(xmlParser); + context.setBaseResourceAsPath(workDir.getEmptyPathDir()); + + server.setHandler(context); + server.start(); + + assertTrue(context.isStarted()); + assertTrue(context.isAvailable()); + XmlParser startedXmlParser = context.getMetaData().getXmlParser(); + assertSame(startedXmlParser, xmlParser); + } + + public static class MyXmlParser extends XmlParser + { + } + public static class OkServlet extends HttpServlet { @Override diff --git a/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebDescriptorTest.java b/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebDescriptorTest.java index 71b47e86807c..cad85da55560 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebDescriptorTest.java +++ b/jetty-ee11/jetty-ee11-webapp/src/test/java/org/eclipse/jetty/ee11/webapp/WebDescriptorTest.java @@ -47,7 +47,7 @@ public void testXmlWithXsd(WorkDir workDir) throws Exception """, StandardCharsets.UTF_8); WebDescriptor webDescriptor = new WebDescriptor(ResourceFactory.root().newResource(xml)); - XmlParser xmlParser = WebDescriptor.newParser(true); + XmlParser xmlParser = new XmlParser(true); // This should not throw an exception, if it does then you have a bad state. // Such as missing required XML resource entities. webDescriptor.parse(xmlParser); diff --git a/jetty-ee8/pom.xml b/jetty-ee8/pom.xml index 89ced71dd3e0..3fda8218acff 100644 --- a/jetty-ee8/pom.xml +++ b/jetty-ee8/pom.xml @@ -48,7 +48,7 @@ 1.0.0.v201108011116 1.2.5 - 4.0.6 + 4.0.8 9.0.111 3.1.9.Final diff --git a/jetty-ee9/jetty-ee9-plus/src/test/java/org/eclipse/jetty/ee9/plus/webapp/PlusDescriptorProcessorTest.java b/jetty-ee9/jetty-ee9-plus/src/test/java/org/eclipse/jetty/ee9/plus/webapp/PlusDescriptorProcessorTest.java index 32650afcce7a..bc7742a65f3b 100644 --- a/jetty-ee9/jetty-ee9-plus/src/test/java/org/eclipse/jetty/ee9/plus/webapp/PlusDescriptorProcessorTest.java +++ b/jetty-ee9/jetty-ee9-plus/src/test/java/org/eclipse/jetty/ee9/plus/webapp/PlusDescriptorProcessorTest.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.plus.jndi.Resource; import org.eclipse.jetty.util.IntrospectionUtil; import org.eclipse.jetty.util.jndi.NamingUtil; +import org.eclipse.jetty.xml.XmlParser; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -167,21 +168,23 @@ public void setUp() throws Exception Resource res2 = new Resource(ServletContextHandler.ENVIRONMENT.getName(), "eeObject2", eeObject2); URL webXml = Thread.currentThread().getContextClassLoader().getResource("web.xml"); + XmlParser xmlParser = new XmlParser(false); + webDescriptor = new WebDescriptor(context.getResourceFactory().newResource(webXml)); - webDescriptor.parse(WebDescriptor.getParser(false)); + webDescriptor.parse(xmlParser); URL frag1Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-1.xml"); fragDescriptor1 = new FragmentDescriptor(context.getResourceFactory().newResource(frag1Xml)); - fragDescriptor1.parse(WebDescriptor.getParser(false)); + fragDescriptor1.parse(xmlParser); URL frag2Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-2.xml"); fragDescriptor2 = new FragmentDescriptor(context.getResourceFactory().newResource(frag2Xml)); - fragDescriptor2.parse(WebDescriptor.getParser(false)); + fragDescriptor2.parse(xmlParser); URL frag3Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-3.xml"); fragDescriptor3 = new FragmentDescriptor(context.getResourceFactory().newResource(frag3Xml)); - fragDescriptor3.parse(WebDescriptor.getParser(false)); + fragDescriptor3.parse(xmlParser); URL frag4Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-4.xml"); fragDescriptor4 = new FragmentDescriptor(context.getResourceFactory().newResource(frag4Xml)); - fragDescriptor4.parse(WebDescriptor.getParser(false)); + fragDescriptor4.parse(xmlParser); Thread.currentThread().setContextClassLoader(oldLoader); } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-quickstart/src/test/java/org/eclipse/jetty/ee9/quickstart/QuickStartTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-quickstart/src/test/java/org/eclipse/jetty/ee9/quickstart/QuickStartTest.java index 4d422feb08e4..ea012d423543 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-quickstart/src/test/java/org/eclipse/jetty/ee9/quickstart/QuickStartTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-quickstart/src/test/java/org/eclipse/jetty/ee9/quickstart/QuickStartTest.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; +import org.eclipse.jetty.xml.XmlParser; import org.eclipse.jetty.xml.XmlParser.Node; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; @@ -99,7 +100,7 @@ public void testStandardTestWar() throws Exception Path webXmlPath = targetDir.resolve("WEB-INF/quickstart-web.xml"); WebDescriptor descriptor = new WebDescriptor(webapp.getResourceFactory().newResource(webXmlPath)); - descriptor.parse(WebDescriptor.getParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); + descriptor.parse(new XmlParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); Node node = descriptor.getRoot(); assertNotNull(node); @@ -153,7 +154,7 @@ public void testSpecWar() throws Exception assertTrue(Files.exists(webXmlPath), "Path should exist:" + webXmlPath); WebDescriptor descriptor = new WebDescriptor(webapp.getResourceFactory().newResource(webXmlPath)); - descriptor.parse(WebDescriptor.getParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); + descriptor.parse(new XmlParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); Node node = descriptor.getRoot(); assertNotNull(node); @@ -217,7 +218,7 @@ public void testJNDIWar() throws Exception Path webXmlPath = targetDir.resolve("WEB-INF/quickstart-web.xml"); assertTrue(Files.exists(webXmlPath), "Exists: " + webXmlPath); WebDescriptor descriptor = new WebDescriptor(webapp.getResourceFactory().newResource(webXmlPath)); - descriptor.parse(WebDescriptor.getParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); + descriptor.parse(new XmlParser(!QuickStartGeneratorConfiguration.LOG.isDebugEnabled())); Node node = descriptor.getRoot(); assertNotNull(node); diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaData.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaData.java index be8aa42a19ce..0a428e9c78be 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaData.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaData.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resources; import org.eclipse.jetty.util.thread.AutoLock; +import org.eclipse.jetty.xml.XmlParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +43,7 @@ public class MetaData private final AutoLock _lock = new AutoLock(); protected Map _origins = new HashMap<>(); + protected XmlParser _xmlParser; protected WebDescriptor _webDefaultsRoot; protected WebDescriptor _webXmlRoot; protected final List _webOverrideRoots = new ArrayList<>(); @@ -193,7 +195,7 @@ public void setDefaultsDescriptor(DefaultsDescriptor descriptor) throws Exception { _webDefaultsRoot = descriptor; - _webDefaultsRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webDefaultsRoot.parse(getXmlParser()); if (_webDefaultsRoot.isOrdered()) { Ordering ordering = getOrdering(); @@ -222,7 +224,7 @@ public void setWebDescriptor(WebDescriptor descriptor) throws Exception { _webXmlRoot = descriptor; - _webXmlRoot.parse(WebDescriptor.getParser(isValidateXml())); + _webXmlRoot.parse(getXmlParser()); _metaDataComplete = WebDescriptor.isMetaDataComplete(_webXmlRoot); if (_webXmlRoot.isOrdered()) @@ -253,7 +255,7 @@ public void setWebDescriptor(WebDescriptor descriptor) public void addOverrideDescriptor(OverrideDescriptor descriptor) throws Exception { - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); switch (descriptor.getMetaDataComplete()) { @@ -308,7 +310,7 @@ public void addFragmentDescriptor(Resource jarResource, FragmentDescriptor descr //Metadata-complete is not set, or there is no web.xml _webFragmentResourceMap.put(jarResource, descriptor); _webFragmentRoots.add(descriptor); - descriptor.parse(WebDescriptor.getParser(isValidateXml())); + descriptor.parse(getXmlParser()); if (descriptor.getName() != null) { @@ -757,9 +759,40 @@ public boolean isValidateXml() */ public void setValidateXml(boolean validateXml) { + if (_xmlParser != null && _xmlParser.isValidating() != _validateXml) + throw new IllegalStateException("XmlParser previously set"); + _validateXml = validateXml; } + /** + * Set the XmlParser to use for handling metadata, this will + * also add an environment specific catalog to the XmlParser. + * + *

This is useful when you want to configure a custom XML Parser + * with a variety of custom attributes and configurations.

+ * + * @param xmlParser the XML parser to use. + */ + public void setXmlParser(XmlParser xmlParser) + { + _xmlParser = Objects.requireNonNull(xmlParser); + + WebDescriptor.addDescriptorCatalog(_xmlParser); + } + + /** + * The XmlParser in use for this metadata. + * + * @return the in use XML Parser + */ + public XmlParser getXmlParser() + { + if (_xmlParser == null) + setXmlParser(new XmlParser(isValidateXml())); + return _xmlParser; + } + public Map getOrigins() { return Collections.unmodifiableMap(_origins); diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index 20b1f070c428..2de6dc49dedf 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -577,7 +577,9 @@ protected void doStart() throws Exception { _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); Boolean validate = (Boolean)getAttribute(MetaData.VALIDATE_XML); - _metadata.setValidateXml((validate != null && validate)); + // Don't set validate unless it is declared. + if (validate != null) + _metadata.setValidateXml(validate); wrapConfigurations(); preConfigure(); Thread.currentThread().setContextClassLoader(getClassLoader()); @@ -686,7 +688,7 @@ public Configurations getConfigurations() * * @return Returns the defaultsDescriptor. */ - @ManagedAttribute(value = "default web.xml deascriptor applied before standard web.xml", readonly = true) + @ManagedAttribute(value = "default web.xml descriptor applied before standard web.xml", readonly = true) public String getDefaultsDescriptor() { return _defaultsDescriptor; @@ -709,7 +711,7 @@ public String getOverrideDescriptor() * * @return Returns the Override Descriptor list */ - @ManagedAttribute(value = "web.xml deascriptors applied after standard web.xml", readonly = true) + @ManagedAttribute(value = "web.xml descriptors applied after standard web.xml", readonly = true) public List getOverrideDescriptors() { return Collections.unmodifiableList(_overrideDescriptors); @@ -1389,8 +1391,7 @@ protected void stopContext() throws Exception _configurations.get(i).deconfigure(this); } - if (_metadata != null) - _metadata.clear(); + _metadata.clear(); _metadata = new MetaData(); } finally diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebDescriptor.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebDescriptor.java index 59e5f48aae92..2957df32a3e1 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebDescriptor.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebDescriptor.java @@ -34,6 +34,10 @@ public class WebDescriptor extends Descriptor { private static final Logger LOG = LoggerFactory.getLogger(WebDescriptor.class); + /** + * @deprecated no direct replacement, use of {@link MetaData#getXmlParser()} is encouraged. + */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser __nonValidatingStaticParser = newParser(false); protected MetaData.Complete _metaDataComplete; protected int _majorVersion = 4; //default to container version @@ -61,13 +65,12 @@ public static boolean isMetaDataComplete(WebDescriptor d) * * @param validating true if the parser should validate syntax, false otherwise * @return an XmlParser for web descriptors + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser getParser(boolean validating) { - if (!validating) - return __nonValidatingStaticParser; - else - return newParser(true); + return newParser(validating); } /** @@ -75,29 +78,29 @@ public static XmlParser getParser(boolean validating) * * @param validating if true, the parser will validate syntax * @return an XmlParser + * @deprecated use {@link MetaData#getXmlParser()} to control parser behavior. */ + @Deprecated(since = "12.1.6", forRemoval = true) public static XmlParser newParser(boolean validating) { + XmlParser xmlParser = new XmlParser(validating); + addDescriptorCatalog(xmlParser); + return xmlParser; + } + + protected static void addDescriptorCatalog(XmlParser xmlParser) throws IllegalStateException + { + String catalogName = "catalog-%s.xml".formatted(ContextHandler.ENVIRONMENT.getName()); + URL url = WebDescriptor.class.getResource(catalogName); + if (url == null) + throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); try { - return new WebDescriptorParser(validating); + xmlParser.addCatalog(URI.create(url.toExternalForm()), Servlet.class); } catch (IOException e) { - throw new IllegalStateException("Unable to instantiate WebDescriptorParser", e); - } - } - - private static class WebDescriptorParser extends XmlParser - { - public WebDescriptorParser(boolean validating) throws IOException - { - super(validating); - String catalogName = "catalog-%s.xml".formatted(ContextHandler.ENVIRONMENT.getName()); - URL url = WebDescriptor.class.getResource(catalogName); - if (url == null) - throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); - addCatalog(URI.create(url.toExternalForm()), Servlet.class); + throw new IllegalStateException("Unable to add catalog: " + url, e); } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java index ad77243df238..501ef7eba2d5 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java @@ -72,6 +72,7 @@ import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlParser; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -100,6 +101,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; 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.assertTrue; @Isolated @@ -1079,6 +1081,33 @@ public void testAddSystemClasses() throws Exception assertThat("deprecated API", systemClasses, hasItem("org.deprecated.api.")); } + @Test + public void testCustomXmlParser(WorkDir workDir) throws Exception + { + Server server = newServer(); + + MyXmlParser xmlParser = new MyXmlParser(); + WebAppContext context = new WebAppContext(); + context.getMetaData().setXmlParser(xmlParser); + context.setBaseResourceAsPath(workDir.getEmptyPathDir()); + + server.setHandler(context); + server.start(); + + assertTrue(context.isStarted()); + assertTrue(context.isAvailable()); + XmlParser startedXmlParser = context.getMetaData().getXmlParser(); + assertSame(startedXmlParser, xmlParser); + } + + public static class MyXmlParser extends XmlParser + { + public MyXmlParser() + { + super(true); + } + } + private static URI toURI(URL url) { try diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebDescriptorTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebDescriptorTest.java index f4325e98c92c..bd86d31f9dc6 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebDescriptorTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebDescriptorTest.java @@ -48,7 +48,7 @@ public void testXmlWithXsd(WorkDir workDir) throws Exception Resource xmlRes = ResourceFactory.root().newResource(xml); WebDescriptor webDescriptor = new WebDescriptor(xmlRes); - XmlParser xmlParser = WebDescriptor.newParser(true); + XmlParser xmlParser = new XmlParser(true); // This should not throw an exception, if it does then you have a bad state. // Such as missing required XML resource entities. webDescriptor.parse(xmlParser);