TOMEE-3222 support for default-context-path since addition in Servlet 4
This should really be implemented in Tomcat because it's a bit hacky to do it in TomEE
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/WebAppInfo.java b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/WebAppInfo.java
index 9dcdf3a..de6f3e3 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/WebAppInfo.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/WebAppInfo.java
@@ -32,6 +32,7 @@
     public String moduleId;
     public String host;
     public String contextRoot;
+    public String defaultContextPath;
     public int sessionTimeout;
     public final Set<String> watchedResources = new TreeSet<>();
     public final Set<String> restClass = new TreeSet<>();
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/AppInfoBuilder.java b/container/openejb-core/src/main/java/org/apache/openejb/config/AppInfoBuilder.java
index 868b9dd..23d4e12 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/AppInfoBuilder.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/AppInfoBuilder.java
@@ -437,6 +437,8 @@
                 webAppInfo.contextRoot = webModule.getContextRoot();
             }
 
+            webAppInfo.defaultContextPath = webModule.getDefaultContextPath();
+
             webAppInfo.sessionTimeout = 30;
             if (webModule.getWebApp() != null && webModule.getWebApp().getSessionConfig() != null) {
                 for (final SessionConfig sessionConfig : webModule.getWebApp().getSessionConfig()) {
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java b/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java
index 19872a0..c8d4434 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java
@@ -1075,6 +1075,7 @@
         webModule.setAddedUrls(addedUrls);
         webModule.setRarUrls(Arrays.asList(urls.get(RAR_URLS_KEY)));
         webModule.setScannableUrls(scannableUrls);
+        webModule.setDefaultContextPath(webApp.getDefaultContextPath());
         webModule.getAltDDs().putAll(descriptors);
         webModule.getWatchedResources().add(warPath);
         webModule.getWatchedResources().add(warFile.getAbsolutePath());
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java b/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java
index 0ed48a9..58936f3 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java
@@ -58,7 +58,6 @@
 import org.apache.openejb.sxc.FacesConfigXml;
 import org.apache.openejb.sxc.HandlerChainsXml;
 import org.apache.openejb.sxc.TldTaglibXml;
-import org.apache.openejb.sxc.WebXml;
 import org.apache.openejb.sxc.WebservicesXml;
 import org.apache.openejb.util.LengthInputStream;
 import org.apache.openejb.util.LogCategory;
@@ -833,7 +832,9 @@
     public static WebApp readWebApp(final URL url) throws OpenEJBException {
         final WebApp webApp;
         try {
-            webApp = WebXml.unmarshal(url);
+            webApp = (WebApp) JaxbJavaee.unmarshalJavaee(WebApp.class, IO.read(url));
+            // don't use the SXC version with the accessors as it's not up to date
+            // webApp = WebXml.unmarshal(url);
         } catch (final SAXException e) {
             throw new OpenEJBException("Cannot parse the web.xml file: " + url.toExternalForm(), e);
         } catch (final JAXBException e) {
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/WebModule.java b/container/openejb-core/src/main/java/org/apache/openejb/config/WebModule.java
index 3df658c..e026872 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/WebModule.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/WebModule.java
@@ -46,6 +46,7 @@
     private Webservices webservices;
     private String host;
     private String contextRoot;
+    private String defaultContextPath;
     private final List<TldTaglib> taglibs = new ArrayList<>();
     private final Set<String> watchedResources = new TreeSet<>();
     // List of all faces configuration files found in this web module
@@ -119,6 +120,14 @@
         return id.getLocation();
     }
 
+    public String getDefaultContextPath() {
+        return defaultContextPath;
+    }
+
+    public void setDefaultContextPath(final String defaultContextPath) {
+        this.defaultContextPath = defaultContextPath;
+    }
+
     @Override
     public URI getModuleUri() {
         return id.getUri();
diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java
index 90ac693..3f59ece 100644
--- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java
+++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java
@@ -70,6 +70,7 @@
     "listener",
     "servlet",
     "servletMapping",
+    "defaultContextPath",
     "sessionConfig",
     "mimeMapping",
     "welcomeFileList",
@@ -177,6 +178,8 @@
 
     @XmlElement(name = "module-name")
     protected String moduleName;
+    @XmlElement(name = "default-context-path")
+    protected String defaultContextPath;
     @XmlElement(name = "absolute-ordering")
     protected AbsoluteOrdering absoluteOrdering;
 
@@ -238,6 +241,14 @@
         return displayName.get();
     }
 
+    public String getDefaultContextPath() {
+        return defaultContextPath;
+    }
+
+    public void setDefaultContextPath(final String defaultContextPath) {
+        this.defaultContextPath = defaultContextPath;
+    }
+
     @Override
     public Collection<Icon> getIcons() {
         if (icon == null) {
@@ -774,6 +785,11 @@
         return this;
     }
 
+    public WebApp defaultContextPath(final String path) {
+        setDefaultContextPath(path);
+        return this;
+    }
+
     public WebApp addListener(final String classname) {
         final Listener l = new Listener();
         l.setListenerClass(classname);
diff --git a/container/openejb-jee/src/test/java/org/apache/openejb/jee/JeeTest.java b/container/openejb-jee/src/test/java/org/apache/openejb/jee/JeeTest.java
index 17d3fb5..bc1058d 100644
--- a/container/openejb-jee/src/test/java/org/apache/openejb/jee/JeeTest.java
+++ b/container/openejb-jee/src/test/java/org/apache/openejb/jee/JeeTest.java
@@ -217,6 +217,10 @@
         marshalAndUnmarshal(WebApp.class, "web_2.3-example.xml", "web_2.3-example-expected.xml");
     }
 
+    public void testWar5() throws Exception {
+        marshalAndUnmarshal(WebApp.class, "web_5-example.xml", "web_5-example-expected.xml");
+    }
+
     public void testTld() throws Exception {
         marshalAndUnmarshal(TldTaglib.class, "tld-example.xml", null);
     }
diff --git a/container/openejb-jee/src/test/resources/web_5-example-expected.xml b/container/openejb-jee/src/test/resources/web_5-example-expected.xml
new file mode 100644
index 0000000..a6d4f91
--- /dev/null
+++ b/container/openejb-jee/src/test/resources/web_5-example-expected.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    <!--
+
+        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.
+    -->
+
+<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5.0" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd">
+    <display-name>SerSpecDefaultContextPath</display-name>
+    <servlet>
+        <servlet-name>TestServlet</servlet-name>
+        <servlet-class>com.sun.ts.tests.servlet.spec.defaultcontextpath.TestServlet</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>TestServlet</servlet-name>
+        <url-pattern>/TestServlet</url-pattern>
+    </servlet-mapping>
+    <default-context-path>/defaultContextPath</default-context-path>
+    <session-config>
+        <session-timeout>54</session-timeout>
+    </session-config>
+</web-app>
diff --git a/container/openejb-jee/src/test/resources/web_5-example.xml b/container/openejb-jee/src/test/resources/web_5-example.xml
new file mode 100644
index 0000000..a6d4f91
--- /dev/null
+++ b/container/openejb-jee/src/test/resources/web_5-example.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    <!--
+
+        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.
+    -->
+
+<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5.0" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd">
+    <display-name>SerSpecDefaultContextPath</display-name>
+    <servlet>
+        <servlet-name>TestServlet</servlet-name>
+        <servlet-class>com.sun.ts.tests.servlet.spec.defaultcontextpath.TestServlet</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>TestServlet</servlet-name>
+        <url-pattern>/TestServlet</url-pattern>
+    </servlet-mapping>
+    <default-context-path>/defaultContextPath</default-context-path>
+    <session-config>
+        <session-timeout>54</session-timeout>
+    </session-config>
+</web-app>
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
index 84fc828..bc0c6cd 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java
@@ -124,7 +124,6 @@
 import org.apache.tomcat.util.descriptor.web.FilterDef;
 import org.apache.tomcat.util.descriptor.web.FilterMap;
 import org.apache.tomcat.util.descriptor.web.ResourceBase;
-import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
 import org.apache.tomcat.util.http.CookieProcessor;
 import org.apache.tomcat.util.scan.StandardJarScanFilter;
 import org.apache.tomee.catalina.cdi.ServletContextHandler;
@@ -149,8 +148,6 @@
 import javax.naming.NamingException;
 import javax.naming.Reference;
 import javax.naming.StringRefAddr;
-import javax.security.jacc.PolicyConfiguration;
-import javax.security.jacc.PolicyConfigurationFactory;
 import javax.servlet.ServletContext;
 import javax.servlet.SessionTrackingMode;
 import javax.servlet.http.HttpServletRequest;
@@ -158,6 +155,7 @@
 import javax.transaction.TransactionManager;
 import javax.transaction.TransactionSynchronizationRegistry;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.Serializable;
 import java.lang.reflect.Field;
@@ -519,6 +517,20 @@
                     standardContext.setConfigFile(contextXmlUrl);
                 }
 
+                // override path if needed - hack at this moment just to pass the TCK
+                // Tomcat should definitely implement it, but community does not seem to like the feature and therefore
+                // willing to implement it.
+                // default context path must start with / but not end with slash
+                try {
+                    if (webApp.defaultContextPath != null && webApp.defaultContextPath.matches("^/\\w*[^/]$")) {
+                        standardContext.setPath(webApp.defaultContextPath);
+                    }
+
+                } catch (final Exception e) {
+                    // don't fail because it's a hack, just output the exception
+                    e.printStackTrace();
+                }
+
                 if (standardContext.getPath() != null) {
                     webApp.contextRoot = standardContext.getPath();
                 }