Skip to content

Commit

Permalink
Improve WebAppProvider scanning (#6090)
Browse files Browse the repository at this point in the history
* Cherry pick from jetty-9

Ensure that WebAppProvider Filter always canonicalises the file passed in from the Scanner. Thus, both the monitored directory is canonical as well as the file it is being compared against.
  • Loading branch information
gregw committed Mar 24, 2021
1 parent c6752b3 commit 54023f5
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -45,6 +46,7 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
private DeploymentManager _deploymentManager;
private FilenameFilter _filenameFilter;
private final List<Resource> _monitored = new CopyOnWriteArrayList<>();
private final List<Resource> _canonicalMonitored = new CopyOnWriteArrayList<>();
private int _scanInterval = 10;
private Scanner _scanner;

Expand Down Expand Up @@ -236,12 +238,35 @@ public void setMonitoredResources(List<Resource> resources)
{
_monitored.clear();
_monitored.addAll(resources);
_canonicalMonitored.clear();
for (Resource r : resources)
{
Resource canonical = r; //assume original is canonical
try
{
File f = r.getFile(); //if it has a file, ensure it is canonical
if (f != null)
canonical = Resource.newResource(f.getCanonicalFile());
}
catch (IOException e)
{
//use the original resource
if (LOG.isDebugEnabled())
LOG.debug("ignored", e);
}
_canonicalMonitored.add(canonical);
}
}

public List<Resource> getMonitoredResources()
{
return Collections.unmodifiableList(_monitored);
}

List<Resource> getCanonicalMonitoredResources()
{
return Collections.unmodifiableList(_canonicalMonitored);
}

public void setMonitoredDirResource(Resource resource)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Locale;

import org.eclipse.jetty.deploy.App;
Expand All @@ -25,9 +26,12 @@
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.LoggerFactory;

/**
* The webapps directory scanning provider.
Expand Down Expand Up @@ -57,6 +61,8 @@
@ManagedObject("Provider for start-up deployement of webapps based on presence in directory")
public class WebAppProvider extends ScanningAppProvider
{
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(WebAppProvider.class);

private boolean _extractWars = false;
private boolean _parentLoaderPriority = false;
private ConfigurationManager _configurationManager;
Expand All @@ -69,58 +75,62 @@ public class Filter implements FilenameFilter
@Override
public boolean accept(File dir, String name)
{
if (!dir.exists())
{
if (dir == null)
return false;
}
String lowername = name.toLowerCase(Locale.ENGLISH);

File file = new File(dir, name);
Resource r = Resource.newResource(file);
if (getMonitoredResources().contains(r) && r.isDirectory())
try
{
return false;
}

// ignore hidden files
if (lowername.startsWith("."))
return false;

// Ignore some directories
if (file.isDirectory())
{
// is it a nominated config directory
if (lowername.endsWith(".d"))
//Always work on canonical files because we need to
//check if the file passed in is one of the
//monitored resources
if (!dir.getCanonicalFile().exists())
return false;

String lowerName = name.toLowerCase(Locale.ENGLISH);

// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
File canonical = new File(dir, name).getCanonicalFile();
Resource r = Resource.newResource(canonical);
if (getCanonicalMonitoredResources().contains(r) && r.isDirectory())
return false;

// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
// ignore hidden files
if (lowerName.startsWith("."))
return false;

//is it a sccs dir?
if ("cvs".equals(lowername) || "cvsroot".equals(lowername))
return false;
// Ignore some directories
if (canonical.isDirectory())
{
// is it a nominated config directory
if (lowerName.endsWith(".d"))
return false;

// OK to deploy it then
return true;
}
// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
return false;

// else is it a war file
if (lowername.endsWith(".war"))
{
//defer deployment decision to fileChanged()
return true;
}
// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
return false;

// else is it a context XML file
if (lowername.endsWith(".xml"))
return true;
//is it a sccs dir?
return !"cvs".equals(lowerName) && !"cvsroot".equals(lowerName); // OK to deploy it then
}

return false;
// else is it a war file
if (lowerName.endsWith(".war"))
{
//defer deployment decision to fileChanged()
return true;
}

// else is it a context XML file
return lowerName.endsWith(".xml");
}
catch (IOException e)
{
LOG.warn("Cannot accept", e);
return false;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,36 @@
package org.eclipse.jetty.deploy.providers;

import java.io.File;
import java.net.URL;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.HandlerCollection;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

@Disabled("See issue #1200")
@ExtendWith(WorkDirExtension.class)
public class WebAppProviderTest
{
Expand All @@ -44,7 +54,8 @@ public class WebAppProviderTest
@BeforeEach
public void setupEnvironment() throws Exception
{
jetty = new XmlConfiguredJetty(testdir.getEmptyPathDir());
Path p = testdir.getEmptyPathDir();
jetty = new XmlConfiguredJetty(p);
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");
Expand Down Expand Up @@ -81,6 +92,7 @@ public void teardownEnvironment() throws Exception
jetty.stop();
}

@Disabled("See issue #1200")
@Test
public void testStartupContext()
{
Expand All @@ -96,7 +108,8 @@ public void testStartupContext()
// Test for correct behaviour
assertTrue(hasJettyGeneratedPath(workDir, "foo.war"), "Should have generated directory in work directory: " + workDir);
}


@Disabled("See issue #1200")
@Test
public void testStartupSymlinkContext()
{
Expand All @@ -114,7 +127,103 @@ public void testStartupSymlinkContext()
File workDir = jetty.getJettyDir("workish");
assertTrue(hasJettyGeneratedPath(workDir, "bar.war"), "Should have generated directory in work directory: " + workDir);
}

@Test
public void testWebappSymlinkDir() throws Exception
{
jetty.stop(); //reconfigure jetty

testdir.ensureEmpty();

jetty = new XmlConfiguredJetty(testdir.getEmptyPathDir());
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");

assumeTrue(symlinkSupported);

//delete the existing webapps directory
File webapps = jetty.getJettyDir("webapps");
assertTrue(IO.delete(webapps));

//make a different directory to contain webapps
File x = jetty.getJettyDir("x");
Files.createDirectory(x.toPath());

//Put a webapp into it
File srcDir = MavenTestingUtils.getTestResourceDir("webapps");
File fooWar = new File(x, "foo.war");
IO.copy(new File(srcDir, "foo-webapp-1.war"), fooWar);
assertTrue(Files.exists(fooWar.toPath()));

//make a link from x to webapps
Files.createSymbolicLink(jetty.getJettyDir("webapps").toPath(), x.toPath());
assertTrue(Files.exists(jetty.getJettyDir("webapps").toPath()));

jetty.load();
jetty.start();

//only webapp in x should be deployed, not x itself
jetty.assertWebAppContextsExists("/foo");
}

@Test
public void testBaseDirSymlink() throws Exception
{
jetty.stop(); //reconfigure jetty

testdir.ensureEmpty();

Path realBase = testdir.getEmptyPathDir();

//set jetty up on the real base
jetty = new XmlConfiguredJetty(realBase);
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
jetty.addConfiguration("jetty-deploy-wars.xml");

//Put a webapp into the base
jetty.copyWebapp("foo-webapp-1.war", "foo.war");

//create the jetty structure
jetty.load();
jetty.start();
Path jettyHome = jetty.getJettyHome().toPath();

jetty.stop();

//Make a symbolic link to the real base
File testsDir = MavenTestingUtils.getTargetTestingDir();
Path symlinkBase = Files.createSymbolicLink(testsDir.toPath().resolve("basedirsymlink-" + System.currentTimeMillis()), jettyHome);
Map<String, String> properties = new HashMap<>();
properties.put("jetty.home", jettyHome.toString());
//Start jetty, but this time running from the symlinked base
System.setProperty("jetty.home", properties.get("jetty.home"));

List<Resource> configurations = jetty.getConfigurations();
Server server = XmlConfiguredJetty.loadConfigurations(configurations, properties);

try
{
server.start();
HandlerCollection handlers = (HandlerCollection)server.getHandler();
Handler[] children = server.getChildHandlersByClass(WebAppContext.class);
assertEquals(1, children.length);
assertEquals("/foo", ((WebAppContext)children[0]).getContextPath());
}
finally
{
server.stop();
}
}

private Map<String, String> setupJettyProperties(Path jettyHome)
{
Map<String, String> properties = new HashMap<>();
properties.put("jetty.home", jettyHome.toFile().getAbsolutePath());
return properties;
}

private static boolean hasJettyGeneratedPath(File basedir, String expectedWarFilename)
{
File[] paths = basedir.listFiles();
Expand Down

0 comments on commit 54023f5

Please sign in to comment.