From 4e3e99c5c5dfb7f2b6f7884c4c97cc071bb73d07 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 2 Aug 2021 18:16:34 +1000 Subject: [PATCH] Fix #6565 Deploy Symlinked applications (#6567) Fix #6565 Deploy Symlinked applications by treating extracting context name (which becomes the default context path) from the base resource and then following aliases, so that base resource will not be an alias. Added warning in ContextHandler if the base resource is an alias that we may not support this in future releases. Signed-off-by: Greg Wilkins --- .../deploy/providers/WebAppProvider.java | 99 ++++++++++++------- .../deploy/providers/WebAppProviderTest.java | 6 +- .../jetty/server/handler/ContextHandler.java | 8 +- 3 files changed, 72 insertions(+), 41 deletions(-) diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java index 24c6958c6674..b531ce1d1ea1 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java @@ -22,6 +22,7 @@ import org.eclipse.jetty.deploy.util.FileID; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -265,8 +266,18 @@ public ContextHandler createContextHandler(final App app) throws Exception if (!resource.exists()) throw new IllegalStateException("App resource does not exist " + resource); - String context = file.getName(); + final String contextName = file.getName(); + // Resource aliases (after getting name) to ensure baseResource is not an alias + if (resource.isAlias()) + { + file = new File(resource.getAlias()).toPath().toRealPath().toFile(); + resource = Resource.newResource(file); + if (!resource.exists()) + throw new IllegalStateException("App resource does not exist " + resource); + } + + // Handle a context XML file if (resource.exists() && FileID.isXmlFile(file)) { XmlConfiguration xmlc = new XmlConfiguration(resource) @@ -276,11 +287,15 @@ public void initializeDefaults(Object context) { super.initializeDefaults(context); + // If the XML created object is a ContextHandler + if (context instanceof ContextHandler) + // Initialize the context path prior to running context XML + initializeContextPath((ContextHandler)context, contextName, true); + + // If it is a webapp if (context instanceof WebAppContext) - { - WebAppContext webapp = (WebAppContext)context; - initializeWebAppContextDefaults(webapp); - } + // initialize other defaults prior to running context XML + initializeWebAppContextDefaults((WebAppContext)context); } }; @@ -290,54 +305,62 @@ public void initializeDefaults(Object context) xmlc.getProperties().putAll(getConfigurationManager().getProperties()); return (ContextHandler)xmlc.configure(); } - else if (file.isDirectory()) - { - // must be a directory - } - else if (FileID.isWebArchiveFile(file)) - { - // Context Path is the same as the archive. - context = context.substring(0, context.length() - 4); - } - else + // Otherwise it must be a directory or an archive + else if (!file.isDirectory() && !FileID.isWebArchiveFile(file)) { throw new IllegalStateException("unable to create ContextHandler for " + app); } - // Ensure "/" is Not Trailing in context paths. - if (context.endsWith("/") && context.length() > 0) - { - context = context.substring(0, context.length() - 1); - } - - // Start building the webapplication + // Build the web application WebAppContext webAppContext = new WebAppContext(); - webAppContext.setDisplayName(context); + webAppContext.setWar(file.getAbsolutePath()); + initializeContextPath(webAppContext, contextName, !file.isDirectory()); + initializeWebAppContextDefaults(webAppContext); + + return webAppContext; + } + + protected void initializeContextPath(ContextHandler context, String contextName, boolean stripExtension) + { + String contextPath = contextName; + + // Strip any 3 char extension from non directories + if (stripExtension && contextPath.length() > 4 && contextPath.charAt(contextPath.length() - 4) == '.') + contextPath = contextPath.substring(0, contextPath.length() - 4); + + // Ensure "/" is Not Trailing in context paths. + if (contextPath.endsWith("/") && contextPath.length() > 1) + contextPath = contextPath.substring(0, contextPath.length() - 1); // special case of archive (or dir) named "root" is / context - if (context.equalsIgnoreCase("root")) + if (contextPath.equalsIgnoreCase("root")) { - context = URIUtil.SLASH; + contextPath = URIUtil.SLASH; } - else if (context.toLowerCase(Locale.ENGLISH).startsWith("root-")) + // handle root with virtual host form + else if (StringUtil.startsWithIgnoreCase(contextPath, "root-")) { - int dash = context.toLowerCase(Locale.ENGLISH).indexOf('-'); - String virtual = context.substring(dash + 1); - webAppContext.setVirtualHosts(new String[]{virtual}); - context = URIUtil.SLASH; + int dash = contextPath.indexOf('-'); + String virtual = contextPath.substring(dash + 1); + context.setVirtualHosts(virtual.split(",")); + contextPath = URIUtil.SLASH; } // Ensure "/" is Prepended to all context paths. - if (context.charAt(0) != '/') + if (contextPath.charAt(0) != '/') + contextPath = "/" + contextPath; + + // Set the display name and context Path + context.setDisplayName(contextName); + if (context instanceof WebAppContext) { - context = "/" + context; + WebAppContext webAppContext = (WebAppContext)context; + webAppContext.setDefaultContextPath(contextPath); + } + else + { + context.setContextPath(contextPath); } - - webAppContext.setDefaultContextPath(context); - webAppContext.setWar(file.getAbsolutePath()); - initializeWebAppContextDefaults(webAppContext); - - return webAppContext; } @Override diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java index 90a687b9663f..e0ec2a8e8c4b 100644 --- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java +++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/WebAppProviderTest.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; @@ -124,9 +125,12 @@ public void testStartupSymlinkContext() // Check Server for expected Handlers jetty.assertWebAppContextsExists("/bar", "/foo", "/bob"); + // Check that baseResources are not aliases + jetty.getServer().getContainedBeans(ContextHandler.class).forEach(h -> assertFalse(h.getBaseResource().isAlias())); + // Test for expected work/temp directory behaviour File workDir = jetty.getJettyDir("workish"); - assertTrue(hasJettyGeneratedPath(workDir, "bar_war"), "Should have generated directory in work directory: " + workDir); + assertTrue(hasJettyGeneratedPath(workDir, "_war-_bar"), "Should have generated directory in work directory: " + workDir); } @Test diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 9e4735621c3a..414ff29d510a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -853,11 +853,15 @@ public void setLogger(Logger logger) @Override protected void doStart() throws Exception { - _availability.set(Availability.STARTING); - if (_contextPath == null) throw new IllegalStateException("Null contextPath"); + if (getBaseResource() != null && getBaseResource().isAlias()) + LOG.warn("BaseResource {} is aliased to {} in {}. May not be supported in future releases.", + getBaseResource(), getBaseResource().getAlias(), this); + + _availability.set(Availability.STARTING); + if (_logger == null) _logger = LoggerFactory.getLogger(ContextHandler.class.getName() + getLogNameSuffix());