Jetty 12 temp directory cleanup (#9153)

+ `Server` now has setter/getter for a temp directory, which can be null
 + `Context` now has a getter for a temp directory, which is never null 
 + Server root Context temp directory is either whatever is set, else a work directory, else java.io.tmpdir
 + WebInfConfiguration will still create a temp directory name, but defers to ContextHandler for creation/persistence of the temp directory
 + temp directory (and BASE) removed from the deployer, as it is now the server temp directory.
This commit is contained in:
Greg Wilkins 2023-01-17 11:44:09 +11:00 committed by GitHub
parent be3b8f0b9d
commit 94991c60c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 520 additions and 939 deletions

View File

@ -33,7 +33,6 @@
</Call>
</Set>
<Set name="scanInterval" property="jetty.deploy.scanInterval"/>
<Set name="tempDir" property="jetty.deploy.tempDir" />
</New>
</Arg>
</Call>

View File

@ -28,8 +28,5 @@ contextHandlerClass?=org.eclipse.jetty.server.handler.ResourceHandler$ResourceCo
## Monitored directory scan period (seconds)
# jetty.deploy.scanInterval=1
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Default ContextHandler class for core deployments
# contextHandlerClass=org.eclipse.jetty.server.handler.ResourceHandler$ResourceContext

View File

@ -272,46 +272,6 @@ public class ContextProvider extends ScanningAppProvider
return cc == null ? new String[0] : cc.split(",");
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(String directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory);
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(File directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory.getAbsolutePath());
}
/**
* Get the temporary directory for deployment.
* <p>
* This is equivalent to getting the {@link Deployable#BASE_TEMP_DIR} property.
*
* @return the user supplied work directory (null if user has not set Temp Directory yet)
*/
@ManagedAttribute("temp directory for use, null if no user set temp directory")
public File getTempDir()
{
String tmpDir = _properties.get(Deployable.BASE_TEMP_DIR);
return tmpDir == null ? null : new File(tmpDir);
}
protected ContextHandler initializeContextHandler(Object context, Path path, Map<String, String> properties)
{
if (LOG.isDebugEnabled())

View File

@ -24,7 +24,6 @@
</Call>
</Set>
<Set name="scanInterval" property="jetty.deploy.scanInterval"/>
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Call name="loadPropertiesFromString">
<Arg>
<Property name="jetty.deploy.common.properties"/>

View File

@ -40,7 +40,7 @@ public interface Deployable
};
String ATTRIBUTE_PREFIX = "jetty.deploy.attribute.";
String BASE_TEMP_DIR = "jetty.deploy.tempDir";
String TEMP_DIR = "jetty.deploy.tempDir";
String CONFIGURATION_CLASSES = "jetty.deploy.configurationClasses";
String CONTAINER_SCAN_JARS = "jetty.deploy.containerScanJarPattern";
String CONTEXT_PATH = "jetty.deploy.contextPath";

View File

@ -13,11 +13,6 @@
package org.eclipse.jetty.osgi;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -26,15 +21,10 @@ import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.OSGiClassLoader;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
@ -199,45 +189,6 @@ public abstract class AbstractContextProvider extends AbstractLifeCycle implemen
return cc == null ? new String[0] : cc.split(",");
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(String directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory);
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(File directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory.getAbsolutePath());
}
/**
* Get the temporary directory for deployment.
* <p>
* This is equivalent to getting the {@link Deployable#BASE_TEMP_DIR} property.
*
* @return the user supplied work directory (null if user has not set Temp Directory yet)
*/
public File getTempDir()
{
String tmpDir = _properties.get(Deployable.BASE_TEMP_DIR);
return tmpDir == null ? null : new File(tmpDir);
}
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the context instances created here.

View File

@ -109,5 +109,6 @@
<Set name="stopTimeout"><Property name="jetty.server.stopTimeout" default="5000"/></Set>
<Set name="dumpAfterStart" property="jetty.server.dumpAfterStart"/>
<Set name="dumpBeforeStop" property="jetty.server.dumpBeforeStop"/>
<Set name="tempDirectory" property="jetty.server.tempDirectory"/>
</Configure>

View File

@ -93,6 +93,9 @@ etc/jetty.xml
## Dump the state of the Jetty server, components, and webapps after startup
# jetty.server.dumpAfterStart=false
## The temporary directory used by the Jetty server and as a root for its contexts
# jetty.server.tempDirectory=
## Dump the state of the Jetty server, components, and webapps before shutdown
# jetty.server.dumpBeforeStop=false
# end::documentation-server-config[]

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.server;
import java.io.File;
import java.util.List;
import java.util.concurrent.Executor;
@ -70,4 +71,9 @@ public interface Context extends Attributes, Decorator, Executor
* The empty string is returned if the full path is exactly the context path.
*/
String getPathInContext(String fullPath);
/**
* @return A temporary directory, configured either for the context, the server or the JVM. Never null.
*/
File getTempDirectory();
}

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.server;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -22,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@ -39,6 +41,7 @@ import org.eclipse.jetty.server.handler.ErrorProcessor;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.Uptime;
@ -61,7 +64,6 @@ import org.slf4j.LoggerFactory;
public class Server extends Handler.Wrapper implements Attributes
{
public static final String BASE_TEMP_DIR_ATTR = "org.eclipse.jetty.server.BaseTempDir";
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
private static final String __serverInfo = "jetty/" + Server.getVersion();
@ -81,6 +83,7 @@ public class Server extends Handler.Wrapper implements Attributes
private volatile DateField _dateField;
private long _stopTimeout;
private InvocationType _invocationType = InvocationType.NON_BLOCKING;
private File _tempDirectory;
public Server()
{
@ -132,6 +135,48 @@ public class Server extends Handler.Wrapper implements Attributes
return _serverInfo;
}
/**
* <p>Convenience method to call {@link #setTempDirectory(File)} from a String representation
* of the temporary directory.</p>
* @param temp A string representation of the temporary directory.
* @see #setTempDirectory(File)
*/
public void setTempDirectory(String temp)
{
setTempDirectory(new File(temp));
}
/**
* <p>Set the temporary directory returned by {@link Context#getTempDirectory()} for the root
* {@link Context} returned {@link #getContext()}. If not set explicitly here, then the root
* {@link Context#getTempDirectory()} will return either the directory found at
* {@code new File(IO.asFile(System.getProperty("jetty.base")), "work")} if it exists,
* else the JVMs temporary directory as {@code IO.asFile(System.getProperty("java.io.tmpdir"))}.
* @param temp A directory that must exist and be writable or null to get the default.
*/
public void setTempDirectory(File temp)
{
if (isStarted())
throw new IllegalStateException(getState());
if (temp != null && !temp.exists())
throw new IllegalArgumentException("Does not exist: " + temp);
if (temp != null && !temp.canWrite())
throw new IllegalArgumentException("Cannot write: " + temp);
_tempDirectory = temp;
}
/**
* @return The server temporary directory if set, else null. To always obtain a non-null
* temporary directory use {@link Context#getTempDirectory()} on {@link #getContext()}.
* @see #getContext()
* @see Context#getTempDirectory()
*/
@ManagedAttribute("temporary directory")
public File getTempDirectory()
{
return _tempDirectory;
}
public void setServerInfo(String serverInfo)
{
_serverInfo = serverInfo;
@ -721,6 +766,10 @@ public class Server extends Handler.Wrapper implements Attributes
class ServerContext extends Attributes.Wrapper implements Context
{
private final File jettyBase = IO.asFile(System.getProperty("jetty.base"));
private final File workDir = jettyBase != null && jettyBase.isDirectory() && jettyBase.canWrite() ? new File(jettyBase, "work") : null;
private final File tempDir = workDir != null && workDir.isDirectory() && workDir.canWrite() ? workDir : IO.asFile(System.getProperty("java.io.tmpdir"));
private ServerContext()
{
super(Server.this);
@ -804,6 +853,12 @@ public class Server extends Handler.Wrapper implements Attributes
{
return fullPath;
}
@Override
public File getTempDirectory()
{
return Objects.requireNonNullElse(Server.this.getTempDirectory(), tempDir);
}
}
private class ServerEnvironment extends Attributes.Wrapper implements Environment

View File

@ -46,6 +46,7 @@ import org.eclipse.jetty.server.SymlinkAllowedResourceAliasChecker;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
@ -122,6 +123,9 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
private boolean _allowNullPathInContext;
private Index<ProtectedTargetType> _protectedTargets = Index.empty(false);
private final List<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<>();
private File _tempDirectory;
private boolean _tempDirectoryPersisted = false;
private boolean _tempDirectoryCreated = false;
public enum Availability
{
@ -179,6 +183,64 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
return new ScopedContext();
}
/**
* @return The temporary directory configured for the context, or null if none configured.
* @see Context#getTempDirectory()
*/
@ManagedAttribute(value = "temporary directory location", readonly = true)
public File getTempDirectory()
{
return _tempDirectory;
}
/**
* <p>Set the temporary directory returned by {@link ScopedContext#getTempDirectory()}. If not set here,
* then the {@link Server#getTempDirectory()} is returned by {@link ScopedContext#getTempDirectory()}.</p>
* <p>If {@link #isTempDirectoryPersistent()} is true, the directory set here is used directly but may
* be created if it does not exist. If {@link #isTempDirectoryPersistent()} is false, then any {@code File} set
* here will be deleted and recreated as a directory during {@link #start()} and will be deleted during
* {@link #stop()}.</p>
* @see #setTempDirectoryPersistent(boolean)
* @param tempDirectory A directory. If it does not exist, it must be able to be created during start.
*/
public void setTempDirectory(File tempDirectory)
{
if (isStarted())
throw new IllegalStateException("Started");
if (tempDirectory != null)
{
try
{
tempDirectory = new File(tempDirectory.getCanonicalPath());
}
catch (IOException e)
{
LOG.warn("Unable to find canonical path for {}", tempDirectory, e);
}
}
_tempDirectory = tempDirectory;
}
/**
* <p>Set if the temp directory for this context will be kept over a stop and start cycle.</p>
*
* @see #setTempDirectory(File)
* @param persist true to persist the temp directory on shutdown / exit of the context
*/
public void setTempDirectoryPersistent(boolean persist)
{
_tempDirectoryPersisted = persist;
}
/**
* @return true if tmp directory will persist between startups of the context
*/
public boolean isTempDirectoryPersistent()
{
return _tempDirectoryPersisted;
}
/**
* @return A mutable MimeTypes that wraps the {@link Server#getMimeTypes()}
* once {@link ContextHandler#setServer(Server)} has been called.
@ -585,6 +647,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
_availability.set(Availability.STARTING);
try
{
createTempDirectory();
_context.call(super::doStart, null);
_availability.compareAndSet(Availability.STARTING, Availability.AVAILABLE);
LOG.info("Started {}", this);
@ -595,10 +658,53 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
}
}
/**
* <p>Create the temporary directory. If the directory exists, but is not persistent, then it is
* first deleted and then recreated. Once created, this method is a noop if called again before
* stopping the context.</p>
*/
protected void createTempDirectory()
{
File tempDirectory = getTempDirectory();
if (tempDirectory != null && !_tempDirectoryCreated)
{
_tempDirectoryCreated = true;
if (isTempDirectoryPersistent())
{
// Create the directory if it doesn't exist
if (!tempDirectory.exists() && !tempDirectory.mkdirs())
throw new IllegalArgumentException("Unable to create temp dir: " + tempDirectory);
}
else
{
// Delete and recreate it to ensure it is empty
if (tempDirectory.exists() && !IO.delete(tempDirectory))
throw new IllegalArgumentException("Failed to delete temp dir: " + tempDirectory);
if (!tempDirectory.mkdirs())
throw new IllegalArgumentException("Unable to create temp dir: " + tempDirectory);
// ensure it is removed on exist
tempDirectory.deleteOnExit();
}
// is it usable
if (!tempDirectory.canWrite() || !tempDirectory.isDirectory())
throw new IllegalArgumentException("Temp dir " + tempDirectory + " not useable: writeable=" + tempDirectory.canWrite() + ", dir=" + tempDirectory.isDirectory());
}
}
@Override
protected void doStop() throws Exception
{
_context.call(super::doStop, null);
File tempDirectory = getTempDirectory();
// if we're not persisting the temp dir contents delete it
if (tempDirectory != null && tempDirectory.exists() && !isTempDirectoryPersistent())
IO.delete(tempDirectory);
_tempDirectoryCreated = false;
}
public boolean checkVirtualHost(Request request)
@ -787,7 +893,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
if (path == null)
{
// allow user to unset variable
setBaseResource((Resource)null);
setBaseResource(null);
return;
}
@ -806,7 +912,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
*/
public void setBaseResourceAsString(String base)
{
setBaseResource((Resource)(base == null ? null : ResourceFactory.of(this).newResource(base)));
setBaseResource((base == null ? null : ResourceFactory.of(this).newResource(base)));
}
/**
@ -1076,6 +1182,15 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
return _baseResource;
}
@Override
public File getTempDirectory()
{
File tempDirectory = ContextHandler.this.getTempDirectory();
if (tempDirectory == null)
tempDirectory = getServer().getTempDirectory();
return tempDirectory;
}
@Override
public List<String> getVirtualHosts()
{

View File

@ -32,10 +32,8 @@ import org.eclipse.jetty.server.FormFields;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
public class DelayedHandler extends Handler.Wrapper
@ -310,8 +308,7 @@ public class DelayedHandler extends Handler.Wrapper
}
else
{
Object baseTempDirectory = getRequest().getContext().getAttribute(Server.BASE_TEMP_DIR_ATTR);
_formData.setFilesDirectory(IO.asFile(baseTempDirectory == null ? System.getProperty("java.io.tmpdir") : baseTempDirectory).toPath());
_formData.setFilesDirectory(getRequest().getContext().getTempDirectory().toPath());
readAndParse();
// if we are done already, then we are still in the scope of the original process call and can
// process directly, otherwise we must execute a call to process as we are within a serialized

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.server.handler;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
@ -23,6 +24,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@ -45,11 +47,20 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.internal.HttpChannelState;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.AfterAll;
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.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@ -58,6 +69,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
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.assertThrows;
@ -65,6 +77,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class ContextHandlerTest
{
public static final File TEST_BAD = MavenTestingUtils.getTargetTestingPath("testBad").toFile();
public static final File TEST_OK = MavenTestingUtils.getTargetTestingPath("testOK").toFile();
Server _server;
ClassLoader _loader;
ContextHandler _contextHandler;
@ -93,6 +107,15 @@ public class ContextHandlerTest
_server.stop();
}
@AfterAll
public static void afterAll()
{
ensureWritable(TEST_OK);
FS.ensureDeleted(TEST_OK.toPath());
ensureWritable(TEST_BAD);
FS.ensureDeleted(TEST_BAD.toPath());
}
@Test
public void testMiss() throws Exception
{
@ -283,7 +306,7 @@ public class ContextHandlerTest
Handler handler = new Handler.Abstract()
{
@Override
public boolean process(Request request, Response response, Callback callback) throws Exception
public boolean process(Request request, Response response, Callback callback)
{
assertInContext(request);
scopeListener.assertInContext(request.getContext(), request);
@ -649,6 +672,195 @@ public class ContextHandlerTest
assertThrows(IllegalStateException.class, () -> handlerCollection.addHandler(contextHandlerA));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testSetTempDirectoryNotExists(boolean persistTempDir) throws Exception
{
Server server = new Server();
ContextHandler context = new ContextHandler();
server.setHandler(context);
context.setTempDirectoryPersistent(persistTempDir);
// The temp directory is defined but has not been created.
File tempDir = MavenTestingUtils.getTargetTestingPath("tempDir").toFile();
IO.delete(tempDir);
context.setTempDirectory(tempDir);
assertThat(context.getTempDirectory(), is(tempDir));
assertFalse(context.getTempDirectory().exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = context.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = context.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testSetTempDirectoryExists(boolean persistTempDir) throws Exception
{
Server server = new Server();
ContextHandler context = new ContextHandler();
server.setHandler(context);
context.setTempDirectoryPersistent(persistTempDir);
// The temp directory is defined and has already been created.
File tempDir = MavenTestingUtils.getTargetTestingPath("tempDir").toFile();
IO.delete(tempDir);
assertFalse(tempDir.exists());
assertTrue(tempDir.mkdir());
context.setTempDirectory(tempDir);
assertThat(context.getTempDirectory(), is(tempDir));
assertTrue(tempDir.exists());
// create some content
File someFile = new File(tempDir, "somefile.txt");
assertTrue(someFile.createNewFile());
assertTrue(someFile.exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = context.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Contents exists if persistent else it was deleted
if (persistTempDir)
assertTrue(someFile.exists());
else
assertFalse(someFile.exists());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = context.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
private static void ensureWritable(File file)
{
if (file.exists())
{
assertTrue(file.setWritable(true));
if (file.isDirectory())
{
File[] files = file.listFiles();
if (files != null)
for (File child : files)
ensureWritable(child);
}
}
}
public static Stream<Arguments> okTempDirs() throws Exception
{
ensureWritable(TEST_OK);
FS.ensureDeleted(TEST_OK.toPath());
assertFalse(TEST_OK.exists());
assertTrue(TEST_OK.mkdir());
TEST_OK.deleteOnExit();
File notDirectory = new File(TEST_OK, "notDirectory.txt");
assertTrue(notDirectory.createNewFile());
File notWritable = new File(TEST_OK, "notWritable");
assertTrue(notWritable.mkdir());
assertTrue(notWritable.setWritable(false));
File notWriteableParent = new File(TEST_OK, "notWritableParent");
assertTrue(notWriteableParent.mkdir());
File cantDelete = new File(notWriteableParent, "cantDelete");
assertTrue(cantDelete.mkdirs());
assertTrue(notWriteableParent.setWritable(false));
return Stream.of(
Arguments.of(false, notDirectory),
Arguments.of(false, notWritable),
Arguments.of(true, cantDelete)
);
}
@ParameterizedTest
@MethodSource("okTempDirs")
public void testSetTempDirectoryOK(boolean persistent, File okTempDir) throws Exception
{
Server server = new Server();
ContextHandler context = new ContextHandler();
server.setHandler(context);
context.setTempDirectory(okTempDir);
context.setTempDirectoryPersistent(persistent);
server.start();
assertTrue(context.getTempDirectory().exists());
assertTrue(context.getTempDirectory().isDirectory());
assertThat(context.getTempDirectory().getAbsolutePath(), equalTo(okTempDir.getAbsolutePath()));
server.stop();
if (persistent)
{
assertTrue(context.getTempDirectory().exists());
assertTrue(context.getTempDirectory().isDirectory());
assertThat(context.getTempDirectory().getAbsolutePath(), equalTo(okTempDir.getAbsolutePath()));
}
else
{
assertFalse(context.getTempDirectory().exists());
}
}
public static Stream<Arguments> badTempDirs() throws Exception
{
ensureWritable(TEST_BAD);
FS.ensureDeleted(TEST_BAD.toPath());
assertFalse(TEST_BAD.exists());
assertTrue(TEST_BAD.mkdir());
TEST_BAD.deleteOnExit();
File notDirectory = new File(TEST_BAD, "notDirectory.txt");
assertTrue(notDirectory.createNewFile());
File notWritable = new File(TEST_BAD, "notWritable");
assertTrue(notWritable.mkdir());
assertTrue(notWritable.setWritable(false));
File notWriteableParent = new File(TEST_BAD, "notWritableParent");
assertTrue(notWriteableParent.mkdir());
File cantCreate = new File(notWriteableParent, "temp");
File cantDelete = new File(notWriteableParent, "cantDelete");
assertTrue(cantDelete.mkdirs());
assertTrue(notWriteableParent.setWritable(false));
return Stream.of(
Arguments.of(true, notDirectory),
Arguments.of(true, notWritable),
Arguments.of(true, cantCreate),
Arguments.of(false, cantCreate),
Arguments.of(false, cantDelete)
);
}
@Disabled // TODO doesn't work on jenkins?
@ParameterizedTest
@MethodSource("badTempDirs")
public void testSetTempDirectoryBad(boolean persistent, File badTempDir)
{
Server server = new Server();
ContextHandler context = new ContextHandler();
server.setHandler(context);
context.setTempDirectory(badTempDir);
context.setTempDirectoryPersistent(persistent);
assertThrows(IllegalArgumentException.class, server::start);
}
private static class ScopeListener implements ContextHandler.ContextScopeListener
{
private static final Request NULL = new Request.Wrapper(null);

View File

@ -39,7 +39,7 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
public void deconfigure(WebAppContext context) throws Exception
{
//if we're not persisting the temp dir, get rid of any overlays
if (!context.isPersistTempDirectory())
if (!context.isTempDirectoryPersistent())
{
Resource originalBases = (Resource)context.getAttribute("org.eclipse.jetty.resources.originalBases");
String originalBaseStr = originalBases.toString();

View File

@ -153,7 +153,7 @@ public class QuickStartGenerator
ServerSupport.addWebApplication(server, webApp);
//leave everything unpacked for the forked process to use
webApp.setPersistTempDirectory(true);
webApp.setTempDirectoryPersistent(true);
}
try

View File

@ -102,7 +102,7 @@ public class WebAppPropertyConverter
//tmp dir
props.put(TMP_DIR, webApp.getTempDirectory().getAbsolutePath());
//props.put("tmp.dir.persist", Boolean.toString(originalPersistTemp));
props.put(TMP_DIR_PERSIST, Boolean.toString(webApp.isPersistTempDirectory()));
props.put(TMP_DIR_PERSIST, Boolean.toString(webApp.isTempDirectoryPersistent()));
//send over the calculated resource bases that includes unpacked overlays
Resource baseResource = webApp.getBaseResource();
@ -219,7 +219,7 @@ public class WebAppPropertyConverter
str = webAppProperties.getProperty(TMP_DIR_PERSIST);
if (!StringUtil.isBlank(str))
webApp.setPersistTempDirectory(Boolean.valueOf(str));
webApp.setTempDirectoryPersistent(Boolean.valueOf(str));
//Get the calculated base dirs which includes the overlays
str = webAppProperties.getProperty(BASE_DIRS);

View File

@ -96,7 +96,7 @@ public class TestWebAppPropertyConverter
webApp.setContextPath("/foo");
webApp.setBaseResourceAsPath(MavenTestingUtils.getTestResourcePathDir("root"));
webApp.setTempDirectory(tmpDir);
webApp.setPersistTempDirectory(false);
webApp.setTempDirectoryPersistent(false);
webApp.setClasses(classesDir);
webApp.setTestClasses(testClassesDir);
webApp.setWebInfLib(Arrays.asList(jar1, jar2));
@ -149,7 +149,7 @@ public class TestWebAppPropertyConverter
assertThat(webApp.getWebInfLib(), Matchers.contains(jar1, jar2));
assertThat(webApp.getOverrideDescriptors(), Matchers.contains(override1.getAbsolutePath(), override2.getAbsolutePath()));
assertEquals(tmpDir, webApp.getTempDirectory());
assertEquals(true, webApp.isPersistTempDirectory());
assertEquals(true, webApp.isTempDirectoryPersistent());
assertEquals(war.getAbsolutePath(), webApp.getWar());
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
assertThat(webApp.getBaseResource(), instanceOf(CombinedResource.class));

View File

@ -818,7 +818,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
{
generateQuickStartWebXml(context, os);
LOG.info("Generated {}", _quickStartWebXml);
if (context.getAttribute(WebInfConfiguration.TEMPORARY_RESOURCE_BASE) != null && !context.isPersistTempDirectory())
if (context.getAttribute(WebInfConfiguration.TEMPORARY_RESOURCE_BASE) != null && !context.isTempDirectoryPersistent())
LOG.warn("Generated to non persistent location: {}", _quickStartWebXml);
}
}

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.ee10.servlet;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@ -103,6 +104,8 @@ import org.eclipse.jetty.util.resource.Resources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jakarta.servlet.ServletContext.TEMPDIR;
/**
* Servlet Context.
* <p>
@ -284,7 +287,14 @@ public class ServletContextHandler extends ContextHandler implements Graceful
// Link the handlers
relinkHandlers();
}
@Override
public void setTempDirectory(File tempDirectory)
{
super.setTempDirectory(tempDirectory);
setAttribute(TEMPDIR, getTempDirectory());
}
public ServletContextApi newServletContextApi()
{
return new ServletContextApi();

View File

@ -45,7 +45,6 @@
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">

View File

@ -38,9 +38,6 @@ etc/jetty-ee10-deploy.xml
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$

View File

@ -13,7 +13,6 @@
package org.eclipse.jetty.ee10.webapp;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
@ -53,6 +52,7 @@ import org.eclipse.jetty.ee10.servlet.security.SecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@ -82,10 +82,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
{
static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
public static final String TEMPDIR = ServletContext.TEMPDIR;
public static final String BASETEMPDIR = Server.BASE_TEMP_DIR_ATTR;
public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee10/webapp/webdefault-ee10.xml";
public static final String ERROR_PAGE = "org.eclipse.jetty.server.error_page";
public static final String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
public static final String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
@ -128,9 +125,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private String[] _contextWhiteList = null;
private File _tmpDir;
private boolean _persistTmpDir = false;
private String _war;
private List<Resource> _extraClasspath;
private Throwable _unavailableException;
@ -244,7 +238,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
switch (property)
{
case Deployable.WAR -> setWar(value);
case Deployable.BASE_TEMP_DIR -> setAttribute(BASETEMPDIR, value);
case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value));
case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses(value == null ? null : value.split(","));
case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value);
case Deployable.EXTRACT_WARS -> setExtractWAR(Boolean.parseBoolean(value));
@ -490,6 +484,12 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
_configurations.preConfigure(this);
}
@Override
protected void createTempDirectory()
{
super.createTempDirectory();
}
public boolean configure() throws Exception
{
return _configurations.configure(this);
@ -583,13 +583,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private void dumpUrl()
{
Connector[] connectors = getServer().getConnectors();
for (int i = 0; i < connectors.length; i++)
for (Connector connector : connectors)
{
String displayName = getDisplayName();
if (displayName == null)
displayName = "WebApp@" + Arrays.hashCode(connectors);
LOG.info("{} at http://{}{}", displayName, connectors[i].toString(), getContextPath());
LOG.info("{} at http://{}{}", displayName, connector.toString(), getContextPath());
}
}
@ -1152,59 +1152,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
_contextWhiteList = contextWhiteList;
}
/**
* Set temporary directory for context.
* The jakarta.servlet.context.tempdir attribute is also set.
*
* @param dir Writable temporary directory.
*/
public void setTempDirectory(File dir)
{
if (isStarted())
throw new IllegalStateException("Started");
if (dir != null)
{
try
{
dir = new File(dir.getCanonicalPath());
}
catch (IOException e)
{
LOG.warn("Unable to find canonical path for {}", dir, e);
}
}
_tmpDir = dir;
setAttribute(TEMPDIR, _tmpDir);
}
@ManagedAttribute(value = "temporary directory location", readonly = true)
public File getTempDirectory()
{
return _tmpDir;
}
/**
* If true the temp directory for this
* webapp will be kept when the webapp stops. Otherwise,
* it will be deleted.
*
* @param persist true to persist the temp directory on shutdown / exit of the webapp
*/
public void setPersistTempDirectory(boolean persist)
{
_persistTmpDir = persist;
}
/**
* @return true if tmp directory will persist between startups of the webapp
*/
public boolean isPersistTempDirectory()
{
return _persistTmpDir;
}
/**
* Set the war of the webapp. From this value a {@link #setBaseResource(Resource)}
* value is computed by {@link WebInfConfiguration}, which may be changed from

View File

@ -20,6 +20,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import jakarta.servlet.ServletContext;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
@ -53,6 +54,9 @@ public class WebInfConfiguration extends AbstractConfiguration
// Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
// Force early configuration (clearing) of the temporary directory so we can unpack into it.
context.createTempDirectory();
// Extract webapp if necessary
unpack(context);
}
@ -80,14 +84,6 @@ public class WebInfConfiguration extends AbstractConfiguration
@Override
public void deconfigure(WebAppContext context) throws Exception
{
File tempDirectory = context.getTempDirectory();
// if we're not persisting the temp dir contents delete it
if (!context.isPersistTempDirectory())
{
IO.delete(tempDirectory);
}
//if it wasn't explicitly configured by the user, then unset it
Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
if (tmpdirConfigured != null && !tmpdirConfigured)
@ -128,10 +124,9 @@ public class WebInfConfiguration extends AbstractConfiguration
* <p>
* B. Create a directory based on global settings. The new directory
* will be called <code>"Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"</code>
* <p>
* If the user has specified the context attribute {@link Server#BASE_TEMP_DIR_ATTR}, the
* directory specified by this attribute will be the parent of the temp dir created. Otherwise,
* the parent dir is <code>${java.io.tmpdir}</code>. Set delete on exit depends on value of persistTempDirectory.
* If the temporary directory is persistent, then the random digits are not added to the name.
* The {@link Server#getTempDirectory()} is used for the parent of a created temporary directory.
* </p>
*
* @param context the context to resolve the temp directory from
* @throws Exception if unable to resolve the temp directory
@ -140,69 +135,25 @@ public class WebInfConfiguration extends AbstractConfiguration
throws Exception
{
//If a tmp directory is already set we should use it
File tmpDir = context.getTempDirectory();
if (tmpDir != null)
File tempDirectory = context.getTempDirectory();
if (tempDirectory != null)
{
configureTempDirectory(tmpDir, context);
context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE); //the tmp dir was set explicitly
return;
}
// No temp directory configured, try to establish one via the jakarta.servlet.context.tempdir.
File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
File servletTmpDir = IO.asFile(context.getAttribute(ServletContext.TEMPDIR));
if (servletTmpDir != null)
{
// Use as tmpDir
tmpDir = servletTmpDir;
configureTempDirectory(tmpDir, context);
// Ensure Attribute has File object
context.setAttribute(WebAppContext.TEMPDIR, tmpDir);
tempDirectory = servletTmpDir;
// Set as TempDir in context.
context.setTempDirectory(tmpDir);
context.setTempDirectory(tempDirectory);
return;
}
//We need to make a temp dir. Check if the user has set a directory to use instead
//of java.io.tmpdir as the parent of the dir
File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
if (baseTemp != null)
{
if (!baseTemp.isDirectory() || !baseTemp.canWrite())
throw new IllegalStateException(WebAppContext.BASETEMPDIR + " is not a writable directory");
//Make a temp directory as a child of the given base dir
makeTempDirectory(baseTemp, context);
return;
}
//Look for a directory named "work" in ${jetty.base} and
//treat it as parent of a new temp dir (which we will persist)
File jettyBase = asFile(System.getProperty("jetty.base"));
if (jettyBase != null)
{
File work = new File(jettyBase, "work");
if (work.exists() && work.isDirectory() && work.canWrite())
{
context.setPersistTempDirectory(true);
makeTempDirectory(work, context);
return;
}
}
//Make a temp directory in java.io.tmpdir
makeTempDirectory(new File(System.getProperty("java.io.tmpdir")), context);
}
/**
* Given an Object, return File reference for object.
* Typically used to convert anonymous Object from getAttribute() calls to a File object.
*
* @param fileObject the file object to analyze and return from (supports type File, Path, and String).
* @return the File object if it can be converted otherwise null.
*/
private File asFile(Object fileObject)
{
return IO.asFile(fileObject);
makeTempDirectory(context.getServer().getContext().getTempDirectory(), context);
}
public void makeTempDirectory(File parent, WebAppContext context)
@ -211,60 +162,28 @@ public class WebInfConfiguration extends AbstractConfiguration
if (parent == null || !parent.exists() || !parent.canWrite() || !parent.isDirectory())
throw new IllegalStateException("Parent for temp dir not configured correctly: " + (parent == null ? "null" : "writeable=" + parent.canWrite()));
boolean persistent = context.isTempDirectoryPersistent() || "work".equals(parent.toPath().getFileName().toString());
//Create a name for the webapp
String temp = getCanonicalNameForWebAppTmpDir(context);
File tmpDir = null;
if (context.isPersistTempDirectory())
File tmpDir;
if (persistent)
{
//if it is to be persisted, make sure it will be the same name
//by not using File.createTempFile, which appends random digits
tmpDir = new File(parent, temp);
configureTempDirectory(tmpDir, context);
}
else
{
// ensure dir will always be unique by having classlib generate random path name
tmpDir = Files.createTempDirectory(parent.toPath(), temp).toFile();
tmpDir.deleteOnExit();
ensureTempDirUsable(tmpDir);
}
if (LOG.isDebugEnabled())
LOG.debug("Set temp dir {}", tmpDir);
context.setTempDirectory(tmpDir);
}
public void configureTempDirectory(File dir, WebAppContext context)
{
if (dir == null)
throw new IllegalArgumentException("Null temp dir");
// if dir exists and we don't want it persisted, delete it
if (!context.isPersistTempDirectory() && dir.exists() && !IO.delete(dir))
{
throw new IllegalStateException("Failed to delete temp dir " + dir);
}
// if it doesn't exist make it
if (!dir.exists())
{
if (!dir.mkdirs())
{
throw new IllegalStateException("Unable to create temp dir " + dir);
}
}
if (!context.isPersistTempDirectory())
dir.deleteOnExit();
ensureTempDirUsable(dir);
}
private void ensureTempDirUsable(File dir)
{
// is it useable
if (!dir.canWrite() || !dir.isDirectory())
throw new IllegalStateException("Temp dir " + dir + " not useable: writeable=" + dir.canWrite() + ", dir=" + dir.isDirectory());
context.setTempDirectoryPersistent(persistent);
}
public void unpack(WebAppContext context) throws IOException
@ -474,23 +393,22 @@ public class WebInfConfiguration extends AbstractConfiguration
*/
public static String getCanonicalNameForWebAppTmpDir(WebAppContext context)
{
StringBuffer canonicalName = new StringBuffer();
StringBuilder canonicalName = new StringBuilder();
canonicalName.append("jetty-");
//get the host and the port from the first connector
Server server = context.getServer();
if (server != null)
{
Connector[] connectors = context.getServer().getConnectors();
Connector[] connectors = server.getConnectors();
if (connectors.length > 0)
{
//Get the host
String host = null;
int port = 0;
if (connectors != null && (connectors[0] instanceof NetworkConnector))
if (connectors[0] instanceof NetworkConnector connector)
{
NetworkConnector connector = (NetworkConnector)connectors[0];
host = connector.getHost();
port = connector.getLocalPort();
if (port < 0)
@ -499,12 +417,7 @@ public class WebInfConfiguration extends AbstractConfiguration
if (host == null)
host = "0.0.0.0";
canonicalName.append(host);
//Get the port
canonicalName.append("-");
//if not available (eg no connectors or connector not started),
//try getting one that was configured.
canonicalName.append(port);
canonicalName.append("-");
}

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.ee10.webapp;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import jakarta.servlet.ServletContext;
import org.eclipse.jetty.server.Server;
@ -25,14 +24,10 @@ import org.eclipse.jetty.toolchain.test.FS;
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.FileSystemPool;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@ -42,9 +37,6 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
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.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(WorkDirExtension.class)
@ -92,35 +84,6 @@ public class TempDirTest
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
}
/**
* ServletContext.TEMPDIR has <code>null</code> value
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
*/
@Test
public void attributeWithNullValue() throws Exception
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, null);
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTempDir = webAppContext.getTempDirectory().toPath();
Path javaIoTmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
assertEquals(javaIoTmpDir, webappTempDir.getParent());
}
/**
* ServletContext.TEMPDIR has <code>""</code> value
* IllegalStateException
*/
@Test
public void attributeWithEmptyStringValue()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, "");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
/**
* Test ServletContext.TEMPDIR as valid directory with types File, String and Path.
*/
@ -133,17 +96,10 @@ public class TempDirTest
FS.ensureDirExists(tmpDir);
switch (type)
{
case "File":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
case "File" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
case "String" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
case "Path" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
default -> throw new IllegalStateException();
}
// Test we have correct value as the webapp temp directory.
@ -160,23 +116,18 @@ public class TempDirTest
@ValueSource(strings = {"File", "String", "Path"})
public void attributeWithNonExistentDirectory(String type) throws Exception
{
Server server = new Server();
WebAppContext webAppContext = new WebAppContext();
Path tmpDir = workDir.getPath().resolve("foo_does_not_exist");
server.setHandler(webAppContext);
Path tmpDir = workDir.getPath().resolve("foo_did_not_exist");
assertFalse(Files.exists(tmpDir));
switch (type)
{
case "File":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
case "File" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
case "String" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
case "Path" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
default -> throw new IllegalStateException();
}
// Test we have correct value as the webapp temp directory.
@ -184,60 +135,20 @@ public class TempDirTest
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTmpDir = webAppContext.getTempDirectory().toPath();
assertThat(webappTmpDir, is(tmpDir));
assertTrue(Files.exists(webappTmpDir));
}
/**
* WebAppContext.BASETEMPDIR has <code>null</code> value
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
* Test Server.setTempDirectory as valid directory
*/
@Test
public void baseTempDirAttributeWithNullValue() throws Exception
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, null);
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(System.getProperty("java.io.tmpdir")));
}
/**
* WebAppContext.BASETEMPDIR has <code>""</code> value
* IllegalStateException
*/
@Test
public void baseTempDirAttributeWithEmptyStringValue()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, "");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
/**
* Test WebAppContext.BASETEMPDIR as valid directory with types File, String and Path.
*/
@ParameterizedTest
@ValueSource(strings = {"File", "String", "Path"})
public void baseTempDirAttributeWithValidDirectory(String type) throws Exception
public void serverTempDirAttributeWithValidDirectory() throws Exception
{
WebAppContext webAppContext = new WebAppContext();
Server server = new Server();
webAppContext.setServer(server);
Path tmpDir = workDir.getPath().resolve("temp_test");
FS.ensureDirExists(tmpDir);
switch (type)
{
case "File":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
}
server.setTempDirectory(tmpDir.toFile());
// Test we have correct value as the webapp temp directory.
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
@ -247,53 +158,6 @@ public class TempDirTest
assertThat(tempDirectory.getParentFile().toPath(), is(tmpDir));
}
/**
* WebAppContext.BASETEMPDIR as File to a non existent directory.
*/
@ParameterizedTest
@ValueSource(strings = {"File", "String", "Path"})
public void baseTempDirAttributeWithNonExistentDirectory(String type) throws Exception
{
WebAppContext webAppContext = new WebAppContext();
Path tmpDir = workDir.getPath().resolve("does_not_exists");
Files.deleteIfExists(tmpDir);
assertFalse(Files.exists(tmpDir));
switch (type)
{
case "File":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
}
// The base temp directory must exist for it to be used, if it does not exist or is not writable it will throw ISE.
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
assertFalse(Files.exists(tmpDir));
}
/**
* <code>${jetty.base}</code> exists but has no work subdirectory called work
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
*/
@Test
public void jettyBaseWorkDoesNotExist() throws Exception
{
Path workDir = jettyBase.resolve("work");
FS.ensureDeleted(workDir);
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(System.getProperty("java.io.tmpdir")));
}
/**
* <code>${jetty.base}</code> directory exists and has a subdirectory called work
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
@ -304,109 +168,10 @@ public class TempDirTest
Path workDir = jettyBase.resolve("work");
FS.ensureDirExists(workDir);
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
Server server = new Server();
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(workDir.toString()));
}
/**
* ServletContext.TEMPDIR has invalid <code>String</code> directory value (wrong permission to write into it)
*
* Note that if run in the CI environment, the test will fail, because it runs as root,
* so we _will_ have permission to write to this directory.
*/
@Tag("not-on-ci")
@DisabledOnOs(value = OS.WINDOWS, disabledReason = "Test/Temp directory is always writable")
@Test
public void attributeWithInvalidPermissions()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, "/var/foo_jetty");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testDefaultTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// Temp Directory Initially isn't set until started.
File tempDirectory = webapp.getTempDirectory();
assertNull(tempDirectory);
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory, "Temp Directory");
if (persistTempDir)
{
assertTrue(tempDirectory.exists(), "Temp Directory should exist");
}
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testPreDefinedTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// The temp directory is defined but has not been created.
File webappTempDir = MavenTestingUtils.getTargetTestingPath("webappTempDir").toFile();
IO.delete(webappTempDir);
webapp.setTempDirectory(webappTempDir);
assertThat(webapp.getTempDirectory(), is(webappTempDir));
assertFalse(webappTempDir.exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testPreExistingTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// The temp directory is defined and has already been created.
File webappTempDir = MavenTestingUtils.getTargetTestingPath("webappTempDir").toFile();
IO.delete(webappTempDir);
if (!webappTempDir.exists())
assertTrue(webappTempDir.mkdir());
webapp.setTempDirectory(webappTempDir);
assertThat(webapp.getTempDirectory(), is(webappTempDir));
assertTrue(webappTempDir.exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
}

View File

@ -37,7 +37,6 @@
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">

View File

@ -38,9 +38,6 @@ etc/jetty-ee8-deploy.xml
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*/jetty-servlet-api-[^/]*\.jar$|.*/javax.servlet.jsp.jstl-.*\.jar$

View File

@ -252,7 +252,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return _coreContextHandler;
}
public org.eclipse.jetty.server.handler.ContextHandler getCoreContextHandler()
public CoreContextHandler getCoreContextHandler()
{
return _coreContextHandler;
}
@ -2390,6 +2390,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
}
/*
* Expose configureTempDirectory so it can be triggered early by WebInfConfiguration#preConfigure
*/
@Override
public void createTempDirectory()
{
super.createTempDirectory();
}
@Override
protected void doStop() throws Exception
{

View File

@ -37,7 +37,6 @@
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">

View File

@ -38,9 +38,6 @@ etc/jetty-ee9-deploy.xml
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$

View File

@ -56,6 +56,7 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@ -86,8 +87,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
{
static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
public static final String TEMPDIR = ServletContext.TEMPDIR;
public static final String BASETEMPDIR = Server.BASE_TEMP_DIR_ATTR;
public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee9/webapp/webdefault-ee9.xml";
public static final String ERROR_PAGE = "org.eclipse.jetty.server.error_page";
public static final String SERVER_SYS_CLASSES = "org.eclipse.jetty.ee9.webapp.systemClasses";
@ -131,9 +130,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private String[] _contextWhiteList = null;
private File _tmpDir;
private boolean _persistTmpDir = false;
private String _war;
private List<Resource> _extraClasspath;
private Throwable _unavailableException;
@ -253,7 +249,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
switch (property)
{
case Deployable.WAR -> setWar(value);
case Deployable.BASE_TEMP_DIR -> setAttribute(BASETEMPDIR, value);
case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value));
case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses(value == null ? null : value.split(","));
case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value);
case Deployable.EXTRACT_WARS -> setExtractWAR(Boolean.parseBoolean(value));
@ -1158,29 +1154,14 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
*/
public void setTempDirectory(File dir)
{
if (isStarted())
throw new IllegalStateException("Started");
if (dir != null)
{
try
{
dir = new File(dir.getCanonicalPath());
}
catch (IOException e)
{
LOG.warn("Unable to find canonical path for {}", dir, e);
}
}
_tmpDir = dir;
setAttribute(TEMPDIR, _tmpDir);
getCoreContextHandler().setTempDirectory(dir);
setAttribute(ServletContext.TEMPDIR, getCoreContextHandler().getTempDirectory());
}
@ManagedAttribute(value = "temporary directory location", readonly = true)
public File getTempDirectory()
{
return _tmpDir;
return getCoreContextHandler().getTempDirectory();
}
/**
@ -1192,7 +1173,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
*/
public void setPersistTempDirectory(boolean persist)
{
_persistTmpDir = persist;
getCoreContextHandler().setTempDirectoryPersistent(persist);
}
/**
@ -1200,7 +1181,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
*/
public boolean isPersistTempDirectory()
{
return _persistTmpDir;
return getCoreContextHandler().isTempDirectoryPersistent();
}
/**

View File

@ -19,6 +19,7 @@ import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import jakarta.servlet.ServletContext;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
@ -38,7 +39,7 @@ public class WebInfConfiguration extends AbstractConfiguration
private static final Logger LOG = LoggerFactory.getLogger(WebInfConfiguration.class);
public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured";
public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.ee9.webapp.tmpResourceBase";
public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.webapp.tmpResourceBase";
protected Resource _preUnpackBaseResource;
@ -52,6 +53,9 @@ public class WebInfConfiguration extends AbstractConfiguration
// Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
// Force early configuration (clearing) of the temporary directory so we can unpack into it.
context.getCoreContextHandler().createTempDirectory();
// Extract webapp if necessary
unpack(context);
}
@ -79,14 +83,6 @@ public class WebInfConfiguration extends AbstractConfiguration
@Override
public void deconfigure(WebAppContext context) throws Exception
{
File tempDirectory = context.getTempDirectory();
// if we're not persisting the temp dir contents delete it
if (!context.isPersistTempDirectory())
{
IO.delete(tempDirectory);
}
//if it wasn't explicitly configured by the user, then unset it
Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
if (tmpdirConfigured != null && !tmpdirConfigured)
@ -126,10 +122,9 @@ public class WebInfConfiguration extends AbstractConfiguration
* <p>
* B. Create a directory based on global settings. The new directory
* will be called <code>"Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"</code>
* <p>
* If the user has specified the context attribute org.eclipse.jetty.ee9.webapp.basetempdir, the
* directory specified by this attribute will be the parent of the temp dir created. Otherwise,
* the parent dir is <code>${java.io.tmpdir}</code>. Set delete on exit depends on value of persistTempDirectory.
* If the temporary directory is persistent, then the random digits are not added to the name.
* The {@link Server#getTempDirectory()} is used for the parent of a created temporary directory.
* </p>
*
* @param context the context to resolve the temp directory from
* @throws Exception if unable to resolve the temp directory
@ -138,78 +133,25 @@ public class WebInfConfiguration extends AbstractConfiguration
throws Exception
{
//If a tmp directory is already set we should use it
File tmpDir = context.getTempDirectory();
if (tmpDir != null)
File tempDirectory = context.getTempDirectory();
if (tempDirectory != null)
{
configureTempDirectory(tmpDir, context);
context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE); //the tmp dir was set explicitly
return;
}
// No temp directory configured, try to establish one via the jakarta.servlet.context.tempdir.
File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
File servletTmpDir = IO.asFile(context.getAttribute(ServletContext.TEMPDIR));
if (servletTmpDir != null)
{
// Use as tmpDir
tmpDir = servletTmpDir;
configureTempDirectory(tmpDir, context);
// Ensure Attribute has File object
context.setAttribute(WebAppContext.TEMPDIR, tmpDir);
tempDirectory = servletTmpDir;
// Set as TempDir in context.
context.setTempDirectory(tmpDir);
context.setTempDirectory(tempDirectory);
return;
}
//We need to make a temp dir. Check if the user has set a directory to use instead
//of java.io.tmpdir as the parent of the dir
File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
if (baseTemp != null)
{
if (!baseTemp.isDirectory() || !baseTemp.canWrite())
throw new IllegalStateException(WebAppContext.BASETEMPDIR + " is not a writable directory");
//Make a temp directory as a child of the given base dir
makeTempDirectory(baseTemp, context);
return;
}
//Look for a directory named "work" in ${jetty.base} and
//treat it as parent of a new temp dir (which we will persist)
File jettyBase = asFile(System.getProperty("jetty.base"));
if (jettyBase != null)
{
File work = new File(jettyBase, "work");
if (work.exists() && work.isDirectory() && work.canWrite())
{
context.setPersistTempDirectory(true);
makeTempDirectory(work, context);
return;
}
}
//Make a temp directory in java.io.tmpdir
makeTempDirectory(new File(System.getProperty("java.io.tmpdir")), context);
}
/**
* Given an Object, return File reference for object.
* Typically used to convert anonymous Object from getAttribute() calls to a File object.
*
* @param fileattr the file attribute to analyze and return from (supports type File, Path, and String).
* @return the File object if it can be converted otherwise null.
*/
private File asFile(Object fileattr)
{
if (fileattr == null)
return null;
if (fileattr instanceof File)
return (File)fileattr;
if (fileattr instanceof String)
return new File((String)fileattr);
if (fileattr instanceof Path)
return ((Path)fileattr).toFile();
return null;
makeTempDirectory(context.getServer().getContext().getTempDirectory(), context);
}
public void makeTempDirectory(File parent, WebAppContext context)
@ -218,60 +160,28 @@ public class WebInfConfiguration extends AbstractConfiguration
if (parent == null || !parent.exists() || !parent.canWrite() || !parent.isDirectory())
throw new IllegalStateException("Parent for temp dir not configured correctly: " + (parent == null ? "null" : "writeable=" + parent.canWrite()));
//Create a name for the webapp
boolean persistent = context.isPersistTempDirectory() || "work".equals(parent.toPath().getFileName().toString());
//Create a name for the webapp
String temp = getCanonicalNameForWebAppTmpDir(context);
File tmpDir = null;
if (context.isPersistTempDirectory())
File tmpDir;
if (persistent)
{
//if it is to be persisted, make sure it will be the same name
//by not using File.createTempFile, which appends random digits
tmpDir = new File(parent, temp);
configureTempDirectory(tmpDir, context);
}
else
{
// ensure dir will always be unique by having classlib generate random path name
tmpDir = Files.createTempDirectory(parent.toPath(), temp).toFile();
tmpDir.deleteOnExit();
ensureTempDirUsable(tmpDir);
}
if (LOG.isDebugEnabled())
LOG.debug("Set temp dir {}", tmpDir);
context.setTempDirectory(tmpDir);
}
public void configureTempDirectory(File dir, WebAppContext context)
{
if (dir == null)
throw new IllegalArgumentException("Null temp dir");
// if dir exists and we don't want it persisted, delete it
if (!context.isPersistTempDirectory() && dir.exists() && !IO.delete(dir))
{
throw new IllegalStateException("Failed to delete temp dir " + dir);
}
// if it doesn't exist make it
if (!dir.exists())
{
if (!dir.mkdirs())
{
throw new IllegalStateException("Unable to create temp dir " + dir);
}
}
if (!context.isPersistTempDirectory())
dir.deleteOnExit();
ensureTempDirUsable(dir);
}
private void ensureTempDirUsable(File dir)
{
// is it useable
if (!dir.canWrite() || !dir.isDirectory())
throw new IllegalStateException("Temp dir " + dir + " not useable: writeable=" + dir.canWrite() + ", dir=" + dir.isDirectory());
context.setPersistTempDirectory(persistent);
}
public void unpack(WebAppContext context) throws IOException
@ -313,7 +223,6 @@ public class WebInfConfiguration extends AbstractConfiguration
// Is the WAR usable directly?
if (Resources.isReadableFile(webApp) && FileID.isJavaArchive(webApp.getURI()) && !webApp.getURI().getScheme().equalsIgnoreCase("jar"))
{
// No - then lets see if it can be turned into a jar URL.
// Turned this into a jar URL.
Resource jarWebApp = context.getResourceFactory().newJarFileResource(webApp.getURI());
if (Resources.isReadableFile(jarWebApp)) // but only if it is readable
@ -452,7 +361,6 @@ public class WebInfConfiguration extends AbstractConfiguration
LOG.debug("Copying WEB-INF/classes from {} to {}", webInfClasses, webInfClassesDir.getAbsolutePath());
webInfClasses.copyTo(webInfClassesDir.toPath());
}
webInf = context.getResourceFactory().newResource(extractedWebInfDir.getCanonicalPath());
}

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.ee9.webapp;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import jakarta.servlet.ServletContext;
import org.eclipse.jetty.server.Server;
@ -25,14 +24,10 @@ import org.eclipse.jetty.toolchain.test.FS;
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.FileSystemPool;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@ -42,9 +37,6 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
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.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(WorkDirExtension.class)
@ -92,35 +84,6 @@ public class TempDirTest
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
}
/**
* ServletContext.TEMPDIR has <code>null</code> value
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
*/
@Test
public void attributeWithNullValue() throws Exception
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, null);
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTempDir = webAppContext.getTempDirectory().toPath();
Path javaIoTmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
assertEquals(javaIoTmpDir, webappTempDir.getParent());
}
/**
* ServletContext.TEMPDIR has <code>""</code> value
* IllegalStateException
*/
@Test
public void attributeWithEmptyStringValue()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, "");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
/**
* Test ServletContext.TEMPDIR as valid directory with types File, String and Path.
*/
@ -133,17 +96,10 @@ public class TempDirTest
FS.ensureDirExists(tmpDir);
switch (type)
{
case "File":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
case "File" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
case "String" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
case "Path" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
default -> throw new IllegalStateException();
}
// Test we have correct value as the webapp temp directory.
@ -160,23 +116,18 @@ public class TempDirTest
@ValueSource(strings = {"File", "String", "Path"})
public void attributeWithNonExistentDirectory(String type) throws Exception
{
Server server = new Server();
WebAppContext webAppContext = new WebAppContext();
Path tmpDir = workDir.getPath().resolve("foo_does_not_exist");
server.setHandler(webAppContext);
Path tmpDir = workDir.getPath().resolve("foo_did_not_exist");
assertFalse(Files.exists(tmpDir));
switch (type)
{
case "File":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
case "File" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toFile());
case "String" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir.toString());
case "Path" -> webAppContext.setAttribute(ServletContext.TEMPDIR, tmpDir);
default -> throw new IllegalStateException();
}
// Test we have correct value as the webapp temp directory.
@ -184,60 +135,20 @@ public class TempDirTest
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTmpDir = webAppContext.getTempDirectory().toPath();
assertThat(webappTmpDir, is(tmpDir));
assertTrue(Files.exists(webappTmpDir));
}
/**
* WebAppContext.BASETEMPDIR has <code>null</code> value
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
* Test Server.setTempDirectory as valid directory
*/
@Test
public void baseTempDirAttributeWithNullValue() throws Exception
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, null);
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(System.getProperty("java.io.tmpdir")));
}
/**
* WebAppContext.BASETEMPDIR has <code>""</code> value
* IllegalStateException
*/
@Test
public void baseTempDirAttributeWithEmptyStringValue()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, "");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
/**
* Test WebAppContext.BASETEMPDIR as valid directory with types File, String and Path.
*/
@ParameterizedTest
@ValueSource(strings = {"File", "String", "Path"})
public void baseTempDirAttributeWithValidDirectory(String type) throws Exception
public void serverTempDirAttributeWithValidDirectory() throws Exception
{
WebAppContext webAppContext = new WebAppContext();
Server server = new Server();
webAppContext.setServer(server);
Path tmpDir = workDir.getPath().resolve("temp_test");
FS.ensureDirExists(tmpDir);
switch (type)
{
case "File":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
}
server.setTempDirectory(tmpDir.toFile());
// Test we have correct value as the webapp temp directory.
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
@ -247,53 +158,6 @@ public class TempDirTest
assertThat(tempDirectory.getParentFile().toPath(), is(tmpDir));
}
/**
* WebAppContext.BASETEMPDIR as File to a non existent directory.
*/
@ParameterizedTest
@ValueSource(strings = {"File", "String", "Path"})
public void baseTempDirAttributeWithNonExistentDirectory(String type) throws Exception
{
WebAppContext webAppContext = new WebAppContext();
Path tmpDir = workDir.getPath().resolve("does_not_exists");
Files.deleteIfExists(tmpDir);
assertFalse(Files.exists(tmpDir));
switch (type)
{
case "File":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toFile());
break;
case "String":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir.toString());
break;
case "Path":
webAppContext.setAttribute(WebAppContext.BASETEMPDIR, tmpDir);
break;
default:
throw new IllegalStateException();
}
// The base temp directory must exist for it to be used, if it does not exist or is not writable it will throw ISE.
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
assertFalse(Files.exists(tmpDir));
}
/**
* <code>${jetty.base}</code> exists but has no work subdirectory called work
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
*/
@Test
public void jettyBaseWorkDoesNotExist() throws Exception
{
Path workDir = jettyBase.resolve("work");
FS.ensureDeleted(workDir);
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(System.getProperty("java.io.tmpdir")));
}
/**
* <code>${jetty.base}</code> directory exists and has a subdirectory called work
* so webappContent#tempDirectory is created under <code>java.io.tmpdir</code>
@ -304,109 +168,10 @@ public class TempDirTest
Path workDir = jettyBase.resolve("work");
FS.ensureDirExists(workDir);
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
Server server = new Server();
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
webInfConfiguration.resolveTempDirectory(webAppContext);
assertThat(webAppContext.getTempDirectory().getParent(), is(workDir.toString()));
}
/**
* ServletContext.TEMPDIR has invalid <code>String</code> directory value (wrong permission to write into it)
*
* Note that if run in the CI environment, the test will fail, because it runs as root,
* so we _will_ have permission to write to this directory.
*/
@Tag("not-on-ci")
@DisabledOnOs(value = OS.WINDOWS, disabledReason = "Test/Temp directory is always writable")
@Test
public void attributeWithInvalidPermissions()
{
WebInfConfiguration webInfConfiguration = new WebInfConfiguration();
WebAppContext webAppContext = new WebAppContext();
webAppContext.setAttribute(ServletContext.TEMPDIR, "/var/foo_jetty");
assertThrows(IllegalStateException.class, () -> webInfConfiguration.resolveTempDirectory(webAppContext));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testDefaultTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// Temp Directory Initially isn't set until started.
File tempDirectory = webapp.getTempDirectory();
assertNull(tempDirectory);
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory, "Temp Directory");
if (persistTempDir)
{
assertTrue(tempDirectory.exists(), "Temp Directory should exist");
}
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testPreDefinedTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// The temp directory is defined but has not been created.
File webappTempDir = MavenTestingUtils.getTargetTestingPath("webappTempDir").toFile();
IO.delete(webappTempDir);
webapp.setTempDirectory(webappTempDir);
assertThat(webapp.getTempDirectory(), is(webappTempDir));
assertFalse(webappTempDir.exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testPreExistingTempDirectory(boolean persistTempDir) throws Exception
{
setupServer();
webapp.setPersistTempDirectory(persistTempDir);
// The temp directory is defined and has already been created.
File webappTempDir = MavenTestingUtils.getTargetTestingPath("webappTempDir").toFile();
IO.delete(webappTempDir);
if (!webappTempDir.exists())
assertTrue(webappTempDir.mkdir());
webapp.setTempDirectory(webappTempDir);
assertThat(webapp.getTempDirectory(), is(webappTempDir));
assertTrue(webappTempDir.exists());
// Once server is started the WebApp temp directory exists and is valid directory.
server.start();
File tempDirectory = webapp.getTempDirectory();
assertNotNull(tempDirectory);
assertTrue(tempDirectory.exists());
assertTrue(tempDirectory.isDirectory());
// Once server is stopped the WebApp temp should be deleted if persistTempDir is false.
server.stop();
tempDirectory = webapp.getTempDirectory();
assertThat(tempDirectory != null && tempDirectory.exists(), is(persistTempDir));
}
}