* Issue #10661 Allow jetty api to override servlets and mappings from webdefault --------- Co-authored-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
467f026d37
commit
14a5ba3489
|
@ -207,14 +207,14 @@ public class EmbeddedWeldTest
|
|||
webapp.getServerClassMatcher().add("-" + pkg + ".");
|
||||
webapp.getSystemClassMatcher().add(pkg + ".");
|
||||
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
webapp.addServlet(GreetingsServlet.class, "/greet");
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
String response = connector.getResponse("GET /greet HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
|
@ -245,12 +245,12 @@ public class EmbeddedWeldTest
|
|||
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
webapp.addServlet(GreetingsServlet.class, "/greet");
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
String response = connector.getResponse("GET /greet HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>@{argLine} ${jetty.surefire.argLine}
|
||||
--add-exports org.eclipse.jetty.ee10.webapp/org.acme.webapp=org.eclipse.jetty.ee10.servlet</argLine>
|
||||
--add-exports org.eclipse.jetty.ee10.webapp/org.acme.webapp=org.eclipse.jetty.ee10.servlet --add-reads org.eclipse.jetty.ee10.webapp=org.eclipse.jetty.logging</argLine>
|
||||
<useManifestOnlyJar>false</useManifestOnlyJar>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${basedir}/src/test/resources/mods/foo-bar-janb.jar</additionalClasspathElement>
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -213,9 +214,18 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
_servletHolderMap.put(name, holder);
|
||||
_servletHolders.add(holder);
|
||||
}
|
||||
else
|
||||
{
|
||||
//A servlet of the same name already exists. If it came from the jetty api
|
||||
//and we're parsing webdefaults, then we will stop visiting this Servlet to allow
|
||||
//the api to define the defaults rather than webdefaults
|
||||
if (Source.Origin.EMBEDDED == holder.getSource().getOrigin() && descriptor instanceof DefaultsDescriptor)
|
||||
return;
|
||||
}
|
||||
|
||||
// init params
|
||||
Iterator<?> iParamsIter = node.iterator("init-param");
|
||||
|
||||
while (iParamsIter.hasNext())
|
||||
{
|
||||
XmlParser.Node paramNode = (XmlParser.Node)iParamsIter.next();
|
||||
|
@ -1028,70 +1038,102 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve any duplicate servlet path mappings.
|
||||
* A path can be (re)mapped iff:
|
||||
* <ul>
|
||||
* <li>it is not already mapped</li>
|
||||
* <li>it is already mapped by webdefault.xml</li>
|
||||
* <li>it is already mapped by the embedded api, and the descriptor is not webdefault.xml</li>
|
||||
* </ul>
|
||||
* The effect of the above conditions is to produce the following precedence hierarchy:
|
||||
* <ol>
|
||||
* <li>jetty-override.xml (OverrideDescriptor)</li>
|
||||
* <li>web.xml (WebDescriptor)</li>
|
||||
* <li>web-fragment.xml (FragmentDescriptor)</li>
|
||||
* <li>embedded api</li>
|
||||
* <li>webdefault.xml (DefaultsDescriptor)</li>
|
||||
* </ol>
|
||||
* @param servletName the servlet target of the mapping
|
||||
* @param path the path to check
|
||||
* @param context the context of the mapping
|
||||
* @param descriptor the descriptor currently being parsed
|
||||
* @return true if the path can be mapped, false otherwise
|
||||
* @throws IllegalStateException if the duplicate mappings are illegal
|
||||
*/
|
||||
private boolean resolveAnyDuplicateServletPathMapping(String servletName, String path, WebAppContext context, Descriptor descriptor)
|
||||
{
|
||||
Objects.requireNonNull(servletName);
|
||||
Objects.requireNonNull(path);
|
||||
Objects.requireNonNull(context);
|
||||
Objects.requireNonNull(descriptor);
|
||||
|
||||
//Find any duplicate mapping
|
||||
ServletMapping existing = null;
|
||||
loop: for (ServletMapping existingMapping: _servletMappings)
|
||||
{
|
||||
for (String pathSpec : existingMapping.getPathSpecs())
|
||||
{
|
||||
if (Objects.equals(pathSpec, path))
|
||||
{
|
||||
existing = existingMapping;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If there is no duplicate we can add it
|
||||
if (existing == null)
|
||||
return true;
|
||||
|
||||
//If it is the same name we can ignore it
|
||||
if (Objects.equals(existing.getServletName(), servletName))
|
||||
return false;
|
||||
|
||||
//A defaults descriptor cannot override embedded.
|
||||
if (existing.getSource().getOrigin() == Source.Origin.EMBEDDED && descriptor instanceof DefaultsDescriptor)
|
||||
return false;
|
||||
|
||||
//If the descriptor of the original mapping and the new mapping are the same, that is an error
|
||||
if (existing.getSource().getOrigin() == Source.Origin.DESCRIPTOR && existing.getSource().getResource().equals(descriptor.getResource()))
|
||||
throw new IllegalStateException("Duplicate mappings for " + path);
|
||||
|
||||
//Remove existing duplicate mapping and tidy up by removing the originMapping if it now has no paths
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed path {} from mapping {}", path, existing);
|
||||
existing.setPathSpecs(ArrayUtil.removeFromArray(existing.getPathSpecs(), path));
|
||||
if (existing.getPathSpecs() == null || existing.getPathSpecs().length == 0)
|
||||
_servletMappings.remove(existing);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ServletMapping addServletMapping(String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
|
||||
{
|
||||
List<String> paths = new ArrayList<String>();
|
||||
Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
|
||||
//For each url pattern, check if that has already been mapped or not
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String path = iter.next().toString(false, true);
|
||||
path = ServletPathSpec.normalize(path);
|
||||
|
||||
//check if there is already a mapping for this path
|
||||
if (resolveAnyDuplicateServletPathMapping(servletName, path, context, descriptor))
|
||||
{
|
||||
paths.add(path);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + path, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (paths.isEmpty())
|
||||
return null; //no paths were added, skip adding a ServletMapping
|
||||
|
||||
ServletMapping mapping = new ServletMapping(new Source(Source.Origin.DESCRIPTOR, descriptor.getResource()));
|
||||
mapping.setServletName(servletName);
|
||||
mapping.setFromDefaultDescriptor(descriptor instanceof DefaultsDescriptor);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), descriptor);
|
||||
|
||||
List<String> paths = new ArrayList<String>();
|
||||
Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String p = iter.next().toString(false, true);
|
||||
p = ServletPathSpec.normalize(p);
|
||||
|
||||
//check if there is already a mapping for this path
|
||||
ListIterator<ServletMapping> listItor = _servletMappings.listIterator();
|
||||
boolean found = false;
|
||||
while (listItor.hasNext() && !found)
|
||||
{
|
||||
ServletMapping sm = listItor.next();
|
||||
if (sm.getPathSpecs() != null)
|
||||
{
|
||||
for (String ps : sm.getPathSpecs())
|
||||
{
|
||||
//The same path has been mapped multiple times, either to a different servlet or the same servlet.
|
||||
//If its a different servlet, this is only valid to do if the old mapping was from a default descriptor.
|
||||
if (p.equals(ps) && (sm.isFromDefaultDescriptor() || servletName.equals(sm.getServletName())))
|
||||
{
|
||||
if (sm.isFromDefaultDescriptor())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} in mapping {} from defaults descriptor is overridden by {}", ps, sm, servletName);
|
||||
}
|
||||
else
|
||||
LOG.warn("Duplicate mapping from {} to {}", p, servletName);
|
||||
|
||||
//remove ps from the path specs on the existing mapping
|
||||
//if the mapping now has no pathspecs, remove it
|
||||
String[] updatedPaths = ArrayUtil.removeFromArray(sm.getPathSpecs(), ps);
|
||||
|
||||
if (updatedPaths == null || updatedPaths.length == 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed empty mapping {}", sm);
|
||||
listItor.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
sm.setPathSpecs(updatedPaths);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed path {} from mapping {}", p, sm);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths.add(p);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + p, descriptor);
|
||||
}
|
||||
|
||||
mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
|
||||
mapping.setPathSpecs(paths.toArray(new String[0]));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Added mapping {} ", mapping);
|
||||
_servletMappings.add(mapping);
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
package org.eclipse.jetty.ee10.webapp;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.ee10.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.ee10.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.ee10.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
|
@ -31,8 +36,12 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalToIgnoringCase;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class StandardDescriptorProcessorTest
|
||||
|
@ -53,6 +62,126 @@ public class StandardDescriptorProcessorTest
|
|||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJettyApiDefaults(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the DefaultServlet named "default" defined by jetty api is not redefined by webdefault-ee10.xml
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
|
||||
defaultServlet.setName("default");
|
||||
defaultServlet.setInitParameter("acceptRanges", "false");
|
||||
defaultServlet.setInitParameter("dirAllowed", "false");
|
||||
defaultServlet.setInitParameter("welcomeServlets", "true");
|
||||
defaultServlet.setInitParameter("redirectWelcome", "true");
|
||||
defaultServlet.setInitParameter("maxCacheSize", "10");
|
||||
defaultServlet.setInitParameter("maxCachedFileSize", "1");
|
||||
defaultServlet.setInitParameter("maxCacheFiles", "10");
|
||||
defaultServlet.setInitParameter("etags", "true");
|
||||
defaultServlet.setInitParameter("useFileMappedBuffer", "false");
|
||||
defaultServlet.setInitOrder(2);
|
||||
defaultServlet.setRunAsRole("foo");
|
||||
wac.getServletHandler().addServletWithMapping(defaultServlet, "/");
|
||||
wac.start();
|
||||
|
||||
ServletHolder[] holders = wac.getServletHandler().getServlets();
|
||||
ServletHolder holder = null;
|
||||
for (ServletHolder h:holders)
|
||||
{
|
||||
if ("default".equals(h.getName()))
|
||||
{
|
||||
assertThat(holder, nullValue());
|
||||
holder = h;
|
||||
}
|
||||
}
|
||||
assertNotNull(holder);
|
||||
assertEquals("false", holder.getInitParameter("acceptRanges"));
|
||||
assertEquals("false", holder.getInitParameter("dirAllowed"));
|
||||
assertEquals("true", holder.getInitParameter("welcomeServlets"));
|
||||
assertEquals("true", holder.getInitParameter("redirectWelcome"));
|
||||
assertEquals("10", holder.getInitParameter("maxCacheSize"));
|
||||
assertEquals("1", holder.getInitParameter("maxCachedFileSize"));
|
||||
assertEquals("10", holder.getInitParameter("maxCacheFiles"));
|
||||
assertEquals("true", holder.getInitParameter("etags"));
|
||||
assertEquals("false", holder.getInitParameter("useFileMappedBuffer"));
|
||||
assertEquals(2, holder.getInitOrder());
|
||||
assertEquals("foo", holder.getRunAsRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateServletMappingsFromJettyApi(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that an embedded mapping overrides one from webdefault-ee10.xml
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setThrowUnavailableOnStartupException(true);
|
||||
ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
|
||||
defaultServlet.setName("noname");
|
||||
wac.getServletHandler().addServletWithMapping(defaultServlet, "/");
|
||||
wac.start();
|
||||
|
||||
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
|
||||
ServletMapping mapping = null;
|
||||
for (ServletMapping m : mappings)
|
||||
{
|
||||
assertThat(m.getServletName(), not(equals("default")));
|
||||
if (m.containsPathSpec("/"))
|
||||
{
|
||||
assertThat(mapping, nullValue());
|
||||
mapping = m;
|
||||
}
|
||||
}
|
||||
assertNotNull(mapping);
|
||||
assertEquals("noname", mapping.getServletName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateServletMappingsFromDescriptors(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the DefaultServlet mapping from webdefault-ee10.xml can be overridden in web.xml
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
File webXml = MavenTestingUtils.getTestResourceFile("web-redefine-mapping.xml");
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setDescriptor(webXml.toURI().toURL().toString());
|
||||
wac.start();
|
||||
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
|
||||
ServletMapping mapping = null;
|
||||
for (ServletMapping m : mappings)
|
||||
{
|
||||
assertThat(m.getServletName(), not(equals("default")));
|
||||
if (m.containsPathSpec("/"))
|
||||
{
|
||||
assertThat(mapping, nullValue());
|
||||
mapping = m;
|
||||
}
|
||||
}
|
||||
assertNotNull(mapping);
|
||||
assertEquals("other", mapping.getServletName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadDuplicateServletMappingsFromDescriptors(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the same mapping cannot be redefined to a different servlet in the same (non-default) descriptor
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
File webXml = MavenTestingUtils.getTestResourceFile("web-redefine-mapping-fail.xml");
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setDescriptor(webXml.toURI().toURL().toString());
|
||||
wac.setThrowUnavailableOnStartupException(true);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebAppContext.class))
|
||||
{
|
||||
assertThrows(InvocationTargetException.class, () -> wac.start());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisitSessionConfig(WorkDir workDir) throws Exception
|
||||
{
|
||||
|
@ -118,7 +247,6 @@ public class StandardDescriptorProcessorTest
|
|||
|
||||
//test the attributes on SessionHandler do NOT contain the name
|
||||
Map<String, String> sessionAttributes = wac.getSessionHandler().getSessionCookieAttributes();
|
||||
sessionAttributes.keySet().forEach(System.err::println);
|
||||
assertThat(sessionAttributes.keySet(),
|
||||
containsInAnyOrder(Arrays.asList(
|
||||
equalToIgnoringCase("comment"),
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
|
||||
<display-name>Bad Redefine Default Servlet Mapping WebApp</display-name>
|
||||
<servlet>
|
||||
<servlet-name>first</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourceServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>first</servlet-name>
|
||||
<url-pattern>/x</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>second</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourceServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>second</servlet-name>
|
||||
<url-pattern>/x</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
|
||||
<display-name>Redefine Default Servlet Mapping WebApp</display-name>
|
||||
<servlet>
|
||||
<servlet-name>other</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourceServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>other</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
|
@ -122,7 +122,8 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>@{argLine} ${jetty.surefire.argLine}</argLine>
|
||||
<argLine>@{argLine} ${jetty.surefire.argLine}
|
||||
--add-reads org.eclipse.jetty.ee8.webapp=org.eclipse.jetty.logging</argLine>
|
||||
<useManifestOnlyJar>false</useManifestOnlyJar>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${project.build.testOutputDirectory}/mods/foo-bar-janb.jar</additionalClasspathElement>
|
||||
|
|
|
@ -205,14 +205,14 @@ public class EmbeddedWeldTest
|
|||
webapp.getServerClassMatcher().add("-" + pkg + ".");
|
||||
webapp.getSystemClassMatcher().add(pkg + ".");
|
||||
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
webapp.addServlet(GreetingsServlet.class, "/greet");
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
String response = connector.getResponse("GET /greet HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
|
@ -243,12 +243,12 @@ public class EmbeddedWeldTest
|
|||
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
webapp.addServlet(GreetingsServlet.class, "/greet");
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
String response = connector.getResponse("GET /greet HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
|
|
|
@ -101,7 +101,8 @@
|
|||
<configuration>
|
||||
<argLine>@{argLine} ${jetty.surefire.argLine}
|
||||
--add-exports org.eclipse.jetty.ee9.webapp/org.acme.webapp=org.eclipse.jetty.ee9.servlet
|
||||
--add-exports org.eclipse.jetty.ee9.webapp/org.acme.webapp=org.eclipse.jetty.ee9.nested</argLine>
|
||||
--add-exports org.eclipse.jetty.ee9.webapp/org.acme.webapp=org.eclipse.jetty.ee9.nested
|
||||
--add-reads org.eclipse.jetty.ee9.webapp=org.eclipse.jetty.logging</argLine>
|
||||
<useManifestOnlyJar>false</useManifestOnlyJar>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${project.build.testOutputDirectory}/mods/foo-bar-janb.jar</additionalClasspathElement>
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -211,6 +212,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
_servletHolderMap.put(name, holder);
|
||||
_servletHolders.add(holder);
|
||||
}
|
||||
else
|
||||
{
|
||||
//A servlet of the same name already exists. If it came from the jetty api
|
||||
//and we're parsing webdefaults, then we will stop reading the descriptor to allow
|
||||
//the api to define the default
|
||||
if (Source.Origin.EMBEDDED == holder.getSource().getOrigin() && descriptor instanceof DefaultsDescriptor)
|
||||
return;
|
||||
}
|
||||
|
||||
// init params
|
||||
Iterator<?> iParamsIter = node.iterator("init-param");
|
||||
|
@ -1195,69 +1204,134 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
}
|
||||
}
|
||||
|
||||
public ServletMapping addServletMapping(String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
|
||||
/**
|
||||
* Check if a path is eligible to be mapped to the servlet.
|
||||
* A path can be (re)mapped iff:
|
||||
* <ul>
|
||||
* <li>it is not already mapped</li>
|
||||
* <li>it is already mapped by webdefault.xml</li>
|
||||
* <li>it is already mapped by the embedded api, and the descriptor is not webdefault.xml</li>
|
||||
* </ul>
|
||||
* The effect of the above conditions is to produce the following precedence hierarchy:
|
||||
* <ol>
|
||||
* <li>jetty-override.xml (OverrideDescriptor)</li>
|
||||
* <li>web.xml (WebDescriptor)</li>
|
||||
* <li>web-fragment.xml (FragmentDescriptor)</li>
|
||||
* <li>embedded api</li>
|
||||
* <li>webdefault.xml (DefaultsDescriptor)</li>
|
||||
* </ol>
|
||||
* @param servletName the servlet target of the mapping
|
||||
* @param path the path to check
|
||||
* @param context the context of the mapping
|
||||
* @param descriptor the descriptor currently being parsed
|
||||
* @return true if the path can be (re)mapped, false otherwise
|
||||
*/
|
||||
private boolean checkServletMappingPath(String servletName, String path, WebAppContext context, Descriptor descriptor)
|
||||
{
|
||||
ServletMapping mapping = new ServletMapping(new Source(Source.Origin.DESCRIPTOR, descriptor.getResource()));
|
||||
mapping.setServletName(servletName);
|
||||
mapping.setFromDefaultDescriptor(descriptor instanceof DefaultsDescriptor);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), descriptor);
|
||||
|
||||
List<String> paths = new ArrayList<String>();
|
||||
Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
|
||||
while (iter.hasNext())
|
||||
Objects.requireNonNull(servletName);
|
||||
Objects.requireNonNull(path);
|
||||
Objects.requireNonNull(context);
|
||||
Objects.requireNonNull(descriptor);
|
||||
|
||||
ListIterator<ServletMapping> listItor = _servletMappings.listIterator();
|
||||
boolean found = false;
|
||||
while (listItor.hasNext() && !found)
|
||||
{
|
||||
String p = iter.next().toString(false, true);
|
||||
p = ServletPathSpec.normalize(p);
|
||||
ServletMapping originalMapping = listItor.next();
|
||||
String[] originalPathSpecs = originalMapping.getPathSpecs();
|
||||
if (originalPathSpecs == null || (originalPathSpecs.length == 0))
|
||||
continue;
|
||||
|
||||
//check if there is already a mapping for this path
|
||||
ListIterator<ServletMapping> listItor = _servletMappings.listIterator();
|
||||
boolean found = false;
|
||||
while (listItor.hasNext() && !found)
|
||||
for (String originalPath : originalPathSpecs)
|
||||
{
|
||||
ServletMapping sm = listItor.next();
|
||||
if (sm.getPathSpecs() != null)
|
||||
if (!originalPath.equals(path))
|
||||
continue;
|
||||
|
||||
if (servletName.equals(originalMapping.getServletName()))
|
||||
{
|
||||
for (String ps : sm.getPathSpecs())
|
||||
//Same path mapped to same servlet, ignore
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Same path mapped to different servlet
|
||||
switch (originalMapping.getSource().getOrigin())
|
||||
{
|
||||
//The same path has been mapped multiple times, either to a different servlet or the same servlet.
|
||||
//If its a different servlet, this is only valid to do if the old mapping was from a default descriptor.
|
||||
if (p.equals(ps) && (sm.isFromDefaultDescriptor() || servletName.equals(sm.getServletName())))
|
||||
case EMBEDDED ->
|
||||
{
|
||||
if (sm.isFromDefaultDescriptor())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} in mapping {} from defaults descriptor is overridden by {}", ps, sm, servletName);
|
||||
}
|
||||
else
|
||||
LOG.warn("Duplicate mapping from {} to {}", p, servletName);
|
||||
if (descriptor instanceof DefaultsDescriptor)
|
||||
return false; //webdefault.xml cannot override a path mapping
|
||||
|
||||
//remove ps from the path specs on the existing mapping
|
||||
//if the mapping now has no pathspecs, remove it
|
||||
String[] updatedPaths = ArrayUtil.removeFromArray(sm.getPathSpecs(), ps);
|
||||
|
||||
if (updatedPaths == null || updatedPaths.length == 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed empty mapping {}", sm);
|
||||
//Remove existing mapping
|
||||
removeMapping(originalMapping, path);
|
||||
//Tidy up by removing the originMapping if it now has no paths
|
||||
if (originalMapping.getPathSpecs() == null || originalMapping.getPathSpecs().length == 0)
|
||||
listItor.remove();
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
case DESCRIPTOR ->
|
||||
{
|
||||
//if the descriptor of the original mapping and the new mapping are the same, that is an error
|
||||
if (originalMapping.getSource().getResource().equals(descriptor.getResource()))
|
||||
throw new IllegalStateException("Duplicate mappings for " + path);
|
||||
|
||||
//if the original mapping came from the defaults descriptor it can be overridden by any other descriptor
|
||||
if (originalMapping.isFromDefaultDescriptor())
|
||||
{
|
||||
sm.setPathSpecs(updatedPaths);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed path {} from mapping {}", p, sm);
|
||||
//Remove existing mapping
|
||||
removeMapping(originalMapping, path);
|
||||
//Tidy up by removing the originMapping if it now has no paths
|
||||
if (originalMapping.getPathSpecs() == null || originalMapping.getPathSpecs().length == 0)
|
||||
listItor.remove();
|
||||
return true;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
paths.add(p);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + p, descriptor);
|
||||
/**
|
||||
* Remove a path from the pathSpecs for a ServletMapping
|
||||
* @param mapping the ServletMapping
|
||||
* @param path the path to remove
|
||||
*/
|
||||
private void removeMapping(ServletMapping mapping, String path)
|
||||
{
|
||||
//Remove the path from the path specs on the existing mapping so it can be remapped
|
||||
String[] updatedPaths = ArrayUtil.removeFromArray(mapping.getPathSpecs(), path);
|
||||
mapping.setPathSpecs(updatedPaths);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed path {} from mapping {}", path, mapping);
|
||||
}
|
||||
|
||||
public ServletMapping addServletMapping(String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
|
||||
{
|
||||
List<String> paths = new ArrayList<String>();
|
||||
Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
|
||||
//For each url pattern, check if that has already been mapped or not
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String path = iter.next().toString(false, true);
|
||||
path = ServletPathSpec.normalize(path);
|
||||
|
||||
//check if there is already a mapping for this path
|
||||
if (checkServletMappingPath(servletName, path, context, descriptor))
|
||||
{
|
||||
paths.add(path);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + path, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (paths.isEmpty())
|
||||
return null; //no paths were added, skip adding a ServletMapping
|
||||
|
||||
ServletMapping mapping = new ServletMapping(new Source(Source.Origin.DESCRIPTOR, descriptor.getResource()));
|
||||
mapping.setServletName(servletName);
|
||||
mapping.setFromDefaultDescriptor(descriptor instanceof DefaultsDescriptor);
|
||||
context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), descriptor);
|
||||
mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Added mapping {} ", mapping);
|
||||
|
|
|
@ -13,22 +13,29 @@
|
|||
|
||||
package org.eclipse.jetty.ee9.webapp;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.ee9.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.ee9.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.ee9.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.MavenPaths;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.resource.FileSystemPool;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class StandardDescriptorProcessorTest
|
||||
|
@ -49,6 +56,127 @@ public class StandardDescriptorProcessorTest
|
|||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJettyApiDefaults(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the DefaultServlet named "default" defined by jetty api is not redefined by webdefault-ee10.xml
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
|
||||
defaultServlet.setName("default");
|
||||
defaultServlet.setInitParameter("acceptRanges", "false");
|
||||
defaultServlet.setInitParameter("dirAllowed", "false");
|
||||
defaultServlet.setInitParameter("welcomeServlets", "true");
|
||||
defaultServlet.setInitParameter("redirectWelcome", "true");
|
||||
defaultServlet.setInitParameter("maxCacheSize", "10");
|
||||
defaultServlet.setInitParameter("maxCachedFileSize", "1");
|
||||
defaultServlet.setInitParameter("maxCacheFiles", "10");
|
||||
defaultServlet.setInitParameter("etags", "true");
|
||||
defaultServlet.setInitParameter("useFileMappedBuffer", "false");
|
||||
defaultServlet.setInitOrder(2);
|
||||
defaultServlet.setRunAsRole("foo");
|
||||
wac.getServletHandler().addServletWithMapping(defaultServlet, "/");
|
||||
wac.start();
|
||||
|
||||
ServletHolder[] holders = wac.getServletHandler().getServlets();
|
||||
ServletHolder holder = null;
|
||||
for (ServletHolder h:holders)
|
||||
{
|
||||
if ("default".equals(h.getName()))
|
||||
{
|
||||
assertThat(holder, nullValue());
|
||||
holder = h;
|
||||
}
|
||||
}
|
||||
assertNotNull(holder);
|
||||
assertEquals("false", holder.getInitParameter("acceptRanges"));
|
||||
assertEquals("false", holder.getInitParameter("dirAllowed"));
|
||||
assertEquals("true", holder.getInitParameter("welcomeServlets"));
|
||||
assertEquals("true", holder.getInitParameter("redirectWelcome"));
|
||||
assertEquals("10", holder.getInitParameter("maxCacheSize"));
|
||||
assertEquals("1", holder.getInitParameter("maxCachedFileSize"));
|
||||
assertEquals("10", holder.getInitParameter("maxCacheFiles"));
|
||||
assertEquals("true", holder.getInitParameter("etags"));
|
||||
assertEquals("false", holder.getInitParameter("useFileMappedBuffer"));
|
||||
assertEquals(2, holder.getInitOrder());
|
||||
assertEquals("foo", holder.getRunAsRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateServletMappingsFromJettyApi(WorkDir workDir) throws Exception
|
||||
{
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setThrowUnavailableOnStartupException(true);
|
||||
//add a mapping that will conflict with the webdefault-ee10.xml mapping
|
||||
//for the default servlet
|
||||
ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
|
||||
defaultServlet.setName("noname");
|
||||
wac.getServletHandler().addServletWithMapping(defaultServlet, "/");
|
||||
wac.start();
|
||||
|
||||
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
|
||||
ServletMapping mapping = null;
|
||||
for (ServletMapping m : mappings)
|
||||
{
|
||||
assertThat(m.getServletName(), not(equals("default")));
|
||||
if (m.containsPathSpec("/"))
|
||||
{
|
||||
assertThat(mapping, nullValue());
|
||||
mapping = m;
|
||||
}
|
||||
}
|
||||
assertNotNull(mapping);
|
||||
assertEquals("noname", mapping.getServletName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateServletMappingsFromDescriptors(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the DefaultServlet mapping from webdefault-ee10.xml can be overridden in web.xml
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
Path webXml = MavenPaths.findTestResourceFile("web-redefine-mapping.xml");
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setDescriptor(webXml.toUri().toURL().toString());
|
||||
wac.start();
|
||||
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
|
||||
ServletMapping mapping = null;
|
||||
for (ServletMapping m : mappings)
|
||||
{
|
||||
assertThat(m.getServletName(), not(equals("default")));
|
||||
if (m.containsPathSpec("/"))
|
||||
{
|
||||
assertThat(mapping, nullValue());
|
||||
mapping = m;
|
||||
}
|
||||
}
|
||||
assertNotNull(mapping);
|
||||
assertEquals("other", mapping.getServletName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadDuplicateServletMappingsFromDescriptors(WorkDir workDir) throws Exception
|
||||
{
|
||||
//Test that the same mapping cannot be redefined to a different servlet in the same (non-default) descriptor
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
Path webXml = MavenPaths.findTestResourceFile("web-redefine-mapping-fail.xml");
|
||||
WebAppContext wac = new WebAppContext();
|
||||
wac.setServer(_server);
|
||||
wac.setBaseResourceAsPath(docroot);
|
||||
wac.setDescriptor(webXml.toUri().toURL().toString());
|
||||
wac.setThrowUnavailableOnStartupException(true);
|
||||
try (StacklessLogging ignored = new StacklessLogging(WebAppContext.class))
|
||||
{
|
||||
assertThrows(RuntimeException.class, () -> wac.start());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisitSessionConfig(WorkDir workDir) throws Exception
|
||||
{
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
|
||||
<display-name>Bad Redefine Default Servlet Mapping WebApp</display-name>
|
||||
<servlet>
|
||||
<servlet-name>first</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourcePathsServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>first</servlet-name>
|
||||
<url-pattern>/x</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>second</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourcePathsServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>second</servlet-name>
|
||||
<url-pattern>/x</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
|
||||
<display-name>Redefine Default Servlet Mapping WebApp</display-name>
|
||||
<servlet>
|
||||
<servlet-name>other</servlet-name>
|
||||
<servlet-class>org.acme.webapp.GetResourcePathsServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>other</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
Loading…
Reference in New Issue