diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/.asciidoctorconfig b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/.asciidoctorconfig
index 308cdcf2991d..142524ddd94e 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/.asciidoctorconfig
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/.asciidoctorconfig
@@ -2,4 +2,4 @@
// See https://github.com/asciidoctor/asciidoctor-intellij-plugin/wiki/Support-project-specific-configurations
:experimental:
:imagesdir: images
-:JETTY_HOME: ../../../../../../../jetty-home/target/jetty-home
+:jetty-home: ../../../../../../../jetty-home/target/jetty-home
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaas/chapter.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaas/chapter.adoc
index 0c80bb02a28b..95db3251e2ea 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaas/chapter.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaas/chapter.adoc
@@ -39,7 +39,7 @@ own custom link:https://docs.oracle.com/javase/7/docs/api/javax/security/auth/sp
Enable the `ee{8,9,10}-jaas` module appropriate for your EE platform:
----
-include::{JETTY_HOME}/modules/ee10-jaas.mod[]
+include::{jetty-home}/modules/ee10-jaas.mod[]
----
The configurable items in the resulting `$jetty.base/start.d/jaas.ini` file are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaspi/chapter.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaspi/chapter.adoc
index 63141006bde7..dcc0e629c7de 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaspi/chapter.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jaspi/chapter.adoc
@@ -27,7 +27,7 @@ Only modules conforming to the "Servlet Container Profile" with the ServerAuthMo
Enable the `jaspi` module:
----
-include::{JETTY_HOME}/modules/ee10-jaspi.mod[]
+include::{jetty-home}/modules/ee10-jaspi.mod[]
----
[[og-jaspi-xml]]
@@ -48,7 +48,7 @@ The following example uses Jetty's EE10 implementation of `AuthConfigProvider` t
[source, xml]
----
-include::{JETTY_HOME}/etc/jaspi/jetty-ee10-jaspi-demo.xml[]
+include::{jetty-home}/etc/jaspi/jetty-ee10-jaspi-demo.xml[]
----
Other custom or 3rd party modules that are compatible with the `ServerAuthModule` interface in JASPI can be registered in the same way.
@@ -66,5 +66,5 @@ This custom module must reference an XML file which sets a new instance of the `
For an example of this see the `ee{9,10}-jaspi-default-auth-config-factory` module, which provides the default implementation used by Jetty.
----
-include::{JETTY_HOME}/modules/ee10-jaspi-default-auth-config-factory.mod[]
+include::{jetty-home}/modules/ee10-jaspi-default-auth-config-factory.mod[]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jsp/chapter.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jsp/chapter.adoc
index 9590ac1f3737..9c9900d36b64 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jsp/chapter.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/jsp/chapter.adoc
@@ -17,7 +17,7 @@
Jetty supports JSP via the `ee{8,9,10}-jsp` modules, which are based on Apache Jasper:
----
-include::{JETTY_HOME}/modules/ee10-jsp.mod[]
+include::{jetty-home}/modules/ee10-jsp.mod[]
----
Logging has been bridged to Jetty logging, so you can enable logging for the `org.apache.jasper` package, subpackages and classes as usual.
@@ -175,7 +175,7 @@ If the value you set doesn't take effect, try using all lower case instead of ca
The JavaServer Pages Standard Tag Library (JSTL) is part of the Jetty distribution, and is available via the `ee{8,9,10}-jstl` modules:
----
-include::{JETTY_HOME}/modules/ee10-jstl.mod[]
+include::{jetty-home}/modules/ee10-jstl.mod[]
----
When enabled, Jetty will make the JSTL tags available for your webapps.
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-alpn.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-alpn.adoc
index fab002a7ecc1..2576050ccfa9 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-alpn.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-alpn.adoc
@@ -21,5 +21,5 @@ You can configure the list of application protocols negotiated by the ALPN mecha
The module properties are:
----
-include::{JETTY_HOME}/modules/alpn.mod[tags=documentation]
+include::{jetty-home}/modules/alpn.mod[tags=documentation]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-bytebufferpool.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-bytebufferpool.adoc
index dceabd3700a7..d5bac8277cc9 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-bytebufferpool.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-bytebufferpool.adoc
@@ -28,7 +28,7 @@ Excess buffers will not be pooled and will be eventually garbage collected.
The module file is `$JETTY_HOME/modules/bytebufferpool.mod`:
----
-include::{JETTY_HOME}/modules/bytebufferpool.mod[]
+include::{jetty-home}/modules/bytebufferpool.mod[]
----
Among the configurable properties, the most relevant are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-console-capture.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-console-capture.adoc
index 05696e3dece2..a92ccb772b39 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-console-capture.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-console-capture.adoc
@@ -22,5 +22,5 @@ Old, rolled files are kept for the number of days specified by the `jetty.consol
The module properties are:
----
-include::{JETTY_HOME}/modules/console-capture.mod[tags=documentation]
+include::{jetty-home}/modules/console-capture.mod[tags=documentation]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-deploy.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-deploy.adoc
index ff046831f577..481563776bbb 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-deploy.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-deploy.adoc
@@ -21,7 +21,7 @@ Files or directories added in this monitored directory cause the `DeploymentMana
The module file is `$JETTY_HOME/modules/deploy.mod`:
----
-include::{JETTY_HOME}/modules/deploy.mod[]
+include::{jetty-home}/modules/deploy.mod[]
----
Among the configurable properties, the most relevant are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http-forwarded.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http-forwarded.adoc
index 03f694d3dffb..5fbf60a3a1bf 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http-forwarded.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http-forwarded.adoc
@@ -19,5 +19,5 @@ The `http-forwarded` module provides support for processing the `Forwarded` HTTP
The module properties are:
----
-include::{JETTY_HOME}/modules/http-forwarded.mod[tags=documentation]
+include::{jetty-home}/modules/http-forwarded.mod[tags=documentation]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http.adoc
index b9e5d6156120..674528871ab1 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http.adoc
@@ -19,7 +19,7 @@ The `http` module provides the clear-text connector and support for the clear-te
The module properties to configure the clear-text connector are:
----
-include::{JETTY_HOME}/modules/http.mod[tags=documentation]
+include::{jetty-home}/modules/http.mod[tags=documentation]
----
Among the configurable properties, the most relevant are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2.adoc
index bf40de5235c5..93c99041a726 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2.adoc
@@ -19,7 +19,7 @@ The `http2` module enables support for the secure HTTP/2 protocol.
The module properties are:
----
-include::{JETTY_HOME}/modules/http2.mod[tags=documentation]
+include::{jetty-home}/modules/http2.mod[tags=documentation]
----
// tag::rate-control[]
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2c.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2c.adoc
index 75c6ffec0935..35adbf277b88 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2c.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http2c.adoc
@@ -19,7 +19,7 @@ The `http2c` module enables support for the clear-text HTTP/2 protocol.
The module properties are:
----
-include::{JETTY_HOME}/modules/http2c.mod[tags=documentation]
+include::{jetty-home}/modules/http2c.mod[tags=documentation]
----
include::module-http2.adoc[tags=rate-control]
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http3.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http3.adoc
index 4503c596ce5a..dcdc96727501 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http3.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-http3.adoc
@@ -19,5 +19,5 @@ The `http3` module enables support for the HTTP/3 protocol.
The module properties are:
----
-include::{JETTY_HOME}/modules/http3.mod[tags=documentation]
+include::{jetty-home}/modules/http3.mod[tags=documentation]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-https.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-https.adoc
index e519a9a28b64..8a75f46a2f24 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-https.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-https.adoc
@@ -19,5 +19,5 @@ The `https` module provides the HTTP/1.1 protocol to the xref:og-module-ssl[`ssl
The module file is `$JETTY_HOME/modules/https.mod`:
----
-include::{JETTY_HOME}/modules/https.mod[]
+include::{jetty-home}/modules/https.mod[]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-jmx-remote.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-jmx-remote.adoc
index 944c3eb76cbc..d42a2f467f75 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-jmx-remote.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-jmx-remote.adoc
@@ -19,7 +19,7 @@ The `jmx-remote` module provides remote access to JMX clients.
The module properties to configure remote JMX connector are:
----
-include::{JETTY_HOME}/modules/jmx-remote.mod[tags=documentation]
+include::{jetty-home}/modules/jmx-remote.mod[tags=documentation]
----
The system property `java.rmi.server.hostname` is specified with the usual notation, prepending a `-D` in front of the system property name.
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-requestlog.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-requestlog.adoc
index 5852ebd82f94..3fd66df256b2 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-requestlog.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-requestlog.adoc
@@ -19,7 +19,7 @@ The `requestlog` module provides HTTP request/response logging in the standard l
The module properties are:
----
-include::{JETTY_HOME}/modules/requestlog.mod[tags=documentation]
+include::{jetty-home}/modules/requestlog.mod[tags=documentation]
----
The property `jetty.requestlog.formatString` can be customized using format codes.
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-server.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-server.adoc
index 408594ee3b94..82f195c9bacc 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-server.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-server.adoc
@@ -31,7 +31,7 @@ See also the xref:og-protocols[protocols section] for more information about the
The module properties to configure generic HTTP properties are listed below. Mostly they frequently apply to HTTP/1, HTTP/2 and HTTP/3, but some parameters are version specific:
----
-include::{JETTY_HOME}/modules/server.mod[tags=documentation-http-config]
+include::{jetty-home}/modules/server.mod[tags=documentation-http-config]
----
Among the configurable properties, the most relevant are:
@@ -58,7 +58,7 @@ Server: Jetty({version})
The module properties to configure the Jetty server are:
----
-include::{JETTY_HOME}/modules/server.mod[tags=documentation-server-config]
+include::{jetty-home}/modules/server.mod[tags=documentation-server-config]
----
Among the configurable properties, the most relevant are:
@@ -83,7 +83,7 @@ The Jetty server strives to keep up with the latest link:https://en.wikipedia.or
The module properties to configure the Jetty server compliance are:
----
-include::{JETTY_HOME}/modules/server.mod[tags=documentation-server-compliance]
+include::{jetty-home}/modules/server.mod[tags=documentation-server-compliance]
----
Among the configurable properties, the most relevant are:
@@ -128,5 +128,5 @@ For more information about `CookieCompliance` see also xref:{prog-guide}#pg-serv
The module properties to configure the Jetty server scheduler are:
----
-include::{JETTY_HOME}/modules/server.mod[tags=documentation-scheduler-config]
+include::{jetty-home}/modules/server.mod[tags=documentation-scheduler-config]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl-reload.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl-reload.adoc
index b8621b8a5cc5..4d351c3fddad 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl-reload.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl-reload.adoc
@@ -20,5 +20,5 @@ When the scanning detects a change to the KeyStore file, the correspondent `SslC
The module properties are:
----
-include::{JETTY_HOME}/modules/ssl-reload.mod[tags=documentation]
+include::{jetty-home}/modules/ssl-reload.mod[tags=documentation]
----
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl.adoc
index 5c7a43eb9d60..eceb6f273d93 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-ssl.adoc
@@ -22,7 +22,7 @@ The `ssl` module provides the secure connector, and allows you to configure the
The module properties to configure the secure connector are:
----
-include::{JETTY_HOME}/modules/ssl.mod[tags=documentation-connector]
+include::{jetty-home}/modules/ssl.mod[tags=documentation-connector]
----
Among the configurable properties, the most relevant are:
@@ -41,7 +41,7 @@ Refer to xref:og-module-http-selectors[this section] for more information about
The module properties to configure the KeyStore and TLS parameters are:
----
-include::{JETTY_HOME}/modules/ssl.mod[tags=documentation-ssl-context]
+include::{jetty-home}/modules/ssl.mod[tags=documentation-ssl-context]
----
[[og-module-ssl-keystore-tls]]
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-test-keystore.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-test-keystore.adoc
index 2b264a77045a..ef8c0831567c 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-test-keystore.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-test-keystore.adoc
@@ -20,7 +20,7 @@ The KeyStore file is automatically deleted when the JVM exits, and re-created wh
The module file is `$JETTY_HOME/modules/test-keystore.mod`:
----
-include::{JETTY_HOME}/modules/test-keystore.mod[]
+include::{jetty-home}/modules/test-keystore.mod[]
----
Note how properties `jetty.sslContext.keyStorePath` and `jetty.sslContext.keyStorePassword` are configured, only if not already set (via the `?=` operator), directly in the module file, rather than in a `+*.ini+` file.
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool-virtual-preview.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool-virtual-preview.adoc
index 1e0323030065..0fd4ea4e87cb 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool-virtual-preview.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool-virtual-preview.adoc
@@ -23,7 +23,7 @@ Refer to the xref:og-module-threadpool[`threadpool`] Jetty module for the genera
The module properties to configure the thread pool are:
----
-include::{JETTY_HOME}/modules/threadpool-virtual-preview.mod[tags=documentation]
+include::{jetty-home}/modules/threadpool-virtual-preview.mod[tags=documentation]
----
The specific properties to configure virtual threads are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool.adoc
index fb28cf144e5d..d551a2090f22 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-threadpool.adoc
@@ -24,7 +24,7 @@ Start with the default value of `maxThreads`, and tune for larger values if need
The module properties to configure the thread pool are:
----
-include::{JETTY_HOME}/modules/threadpool.mod[tags=documentation]
+include::{jetty-home}/modules/threadpool.mod[tags=documentation]
----
Among the configurable properties, the most relevant are:
diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-well-known.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-well-known.adoc
index 1bb9024deda6..812c9f45864e 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-well-known.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/module-well-known.adoc
@@ -25,5 +25,5 @@ See link:https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml[
The module properties are:
----
-include::{JETTY_HOME}/modules/well-known.mod[tags=documentation]
+include::{jetty-home}/modules/well-known.mod[tags=documentation]
----
diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml
index 4a0205ac556f..036285fffc3f 100644
--- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml
+++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml
@@ -33,7 +33,6 @@
-
diff --git a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod
index a4e82cbd8421..61cf7a1748a4 100644
--- a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod
+++ b/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod
@@ -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
diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java
index 8db5ce253acc..34decea39488 100644
--- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java
+++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java
@@ -272,46 +272,6 @@ public String[] getConfigurationClasses()
return cc == null ? new String[0] : cc.split(",");
}
- /**
- * Set the temporary directory for deployment.
- *
- * This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
- * If not set, then the java.io.tmpdir
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.
- *
- * This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
- * If not set, then the java.io.tmpdir
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.
- *
- * 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 properties)
{
if (LOG.isDebugEnabled())
diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
index 9f8d7efb6ac8..2823256b483a 100644
--- a/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
+++ b/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml
@@ -24,7 +24,6 @@
-
diff --git a/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/Deployable.java b/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/Deployable.java
index da29548e5b50..1bfc63e02f2d 100644
--- a/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/Deployable.java
+++ b/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/Deployable.java
@@ -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";
diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java
index 3ef010e99d54..7746e2c248d6 100644
--- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java
+++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java
@@ -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.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 String[] getConfigurationClasses()
return cc == null ? new String[0] : cc.split(",");
}
- /**
- * Set the temporary directory for deployment.
- *
- * This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
- * If not set, then the java.io.tmpdir
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.
- *
- * This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
- * If not set, then the java.io.tmpdir
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.
- *
- * 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.
diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty.xml b/jetty-core/jetty-server/src/main/config/etc/jetty.xml
index 6535e6d4d1c8..4fb1c92b8395 100644
--- a/jetty-core/jetty-server/src/main/config/etc/jetty.xml
+++ b/jetty-core/jetty-server/src/main/config/etc/jetty.xml
@@ -109,5 +109,6 @@
+
diff --git a/jetty-core/jetty-server/src/main/config/modules/server.mod b/jetty-core/jetty-server/src/main/config/modules/server.mod
index 9e92a955ade8..fca754b7dfbf 100644
--- a/jetty-core/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-core/jetty-server/src/main/config/modules/server.mod
@@ -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[]
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Context.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Context.java
index 69ffa56d4193..aa2ef27c48ab 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Context.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Context.java
@@ -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();
}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index 540f9d73ab73..37d5b385dcca 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -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.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.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 @@
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 String getServerInfo()
return _serverInfo;
}
+ /**
+ *
Convenience method to call {@link #setTempDirectory(File)} from a String representation
+ * of the temporary directory.
+ * @param temp A string representation of the temporary directory.
+ * @see #setTempDirectory(File)
+ */
+ public void setTempDirectory(String temp)
+ {
+ setTempDirectory(new File(temp));
+ }
+
+ /**
+ * 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 @@ private static class DynamicErrorProcessor extends ErrorProcessor {}
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 String getPathInContext(String fullPath)
{
return fullPath;
}
+
+ @Override
+ public File getTempDirectory()
+ {
+ return Objects.requireNonNullElse(Server.this.getTempDirectory(), tempDir);
+ }
}
private class ServerEnvironment extends Attributes.Wrapper implements Environment
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index 6e1eacc9189e..252622d54a9a 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -46,6 +46,7 @@
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 static String getServerInfo()
private boolean _allowNullPathInContext;
private Index _protectedTargets = Index.empty(false);
private final List _aliasChecks = new CopyOnWriteArrayList<>();
+ private File _tempDirectory;
+ private boolean _tempDirectoryPersisted = false;
+ private boolean _tempDirectoryCreated = false;
public enum Availability
{
@@ -179,6 +183,64 @@ protected ScopedContext newContext()
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;
+ }
+
+ /**
+ * Set the temporary directory returned by {@link ScopedContext#getTempDirectory()}. If not set here,
+ * then the {@link Server#getTempDirectory()} is returned by {@link ScopedContext#getTempDirectory()}.
+ * 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()}.
+ * @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;
+ }
+
+ /**
+ * Set if the temp directory for this context will be kept over a stop and start cycle.
+ *
+ * @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 @@ protected void doStart() throws Exception
_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 @@ protected void doStart() throws Exception
}
}
+ /**
+ * 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.
+ */
+ 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 void setBaseResourceAsPath(Path path)
if (path == null)
{
// allow user to unset variable
- setBaseResource((Resource)null);
+ setBaseResource(null);
return;
}
@@ -806,7 +912,7 @@ public void setBaseResourceAsPath(Path path)
*/
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 Resource getBaseResource()
return _baseResource;
}
+ @Override
+ public File getTempDirectory()
+ {
+ File tempDirectory = ContextHandler.this.getTempDirectory();
+ if (tempDirectory == null)
+ tempDirectory = getServer().getTempDirectory();
+ return tempDirectory;
+ }
+
@Override
public List getVirtualHosts()
{
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java
index 016aaaa5f192..73f9cb5c9c2d 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java
@@ -32,10 +32,8 @@
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 void delay()
}
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
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
index e04fe7a663ca..aa8cf3456b5e 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
@@ -43,7 +43,6 @@
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.logging.StacklessLogging;
-import org.eclipse.jetty.server.handler.ContextRequest;
import org.eclipse.jetty.server.handler.DumpHandler;
import org.eclipse.jetty.server.internal.HttpChannelState;
import org.eclipse.jetty.server.internal.HttpConnection;
@@ -1036,7 +1035,7 @@ public void testUnconsumedException() throws Exception
"\r\n" +
"abcdefghij\r\n";
- try (StacklessLogging ignored = new StacklessLogging(ContextRequest.class))
+ try (StacklessLogging ignored = new StacklessLogging(Response.class))
{
LOG.info("EXPECTING: java.lang.IllegalStateException...");
String response = _connector.getResponse(requests);
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
index 08753fcece9e..36ea166e22aa 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
@@ -43,7 +43,6 @@
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RetainableByteBufferPool;
import org.eclipse.jetty.logging.StacklessLogging;
-import org.eclipse.jetty.server.handler.ContextRequest;
import org.eclipse.jetty.server.handler.EchoHandler;
import org.eclipse.jetty.server.handler.HelloHandler;
import org.eclipse.jetty.server.internal.HttpConnection;
@@ -406,7 +405,7 @@ public boolean process(Request request, Response response, Callback callback) th
Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
OutputStream os = client.getOutputStream();
- try (StacklessLogging ignored = new StacklessLogging(ContextRequest.class))
+ try (StacklessLogging ignored = new StacklessLogging(Response.class))
{
LOG.info("Expecting Exception: TEST handler exception...");
os.write(request.toString().getBytes());
@@ -435,7 +434,7 @@ public boolean process(Request request, Response response, Callback callback) th
Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
OutputStream os = client.getOutputStream();
- try (StacklessLogging ignored = new StacklessLogging(ContextRequest.class))
+ try (StacklessLogging ignored = new StacklessLogging(Response.class))
{
LOG.info("Expecting Exception: TEST handler exception...");
os.write(request.toString().getBytes());
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
index d1f4ef717dfc..b653d87a4d4a 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
@@ -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.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.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.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 @@
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 void afterEach() throws Exception
_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 void testCallbackInContext() throws Exception
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 void testAddHandlerLoopDeep()
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 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 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);
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResource.java
new file mode 100644
index 000000000000..690178aeeb71
--- /dev/null
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResource.java
@@ -0,0 +1,144 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.util.resource;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * GraalVM Native-Image {@link Path} Resource.
+ *
+ * @see Graal issue 5720
+ */
+final class GraalIssue5720PathResource extends PathResource
+{
+ private static final Logger LOG = LoggerFactory.getLogger(GraalIssue5720PathResource.class);
+ private static final String URI_BAD_RESOURCE_PREFIX = "file:///resources!";
+
+ GraalIssue5720PathResource(Path path, URI uri, boolean bypassAllowedSchemeCheck)
+ {
+ super(path, correctResourceURI(uri), (bypassAllowedSchemeCheck || isResourceScheme(uri)));
+ }
+
+ private static final boolean isResourceScheme(URI uri)
+ {
+ return "resource".equals(uri.getScheme());
+ }
+
+ /**
+ * Checks if the given resource URL is affected by Graal issue 5720.
+ *
+ * @param url
+ * The URL to check.
+ * @return {@code true} if affected.
+ */
+ static boolean isAffectedURL(URL url)
+ {
+ if (url == null || !"resource".equals(url.getProtocol()))
+ {
+ return false;
+ }
+
+ URI uri;
+ try
+ {
+ uri = url.toURI();
+ }
+ catch (URISyntaxException e)
+ {
+ return false;
+ }
+
+ try
+ {
+ try
+ {
+ uri = Path.of(uri).toUri();
+ }
+ catch (FileSystemNotFoundException e)
+ {
+ try
+ {
+ FileSystems.newFileSystem(uri, Collections.emptyMap());
+ }
+ catch (FileSystemAlreadyExistsException e2)
+ {
+ LOG.debug("Race condition upon calling FileSystems.newFileSystem for: {}", uri, e);
+ }
+ uri = Path.of(uri).toUri();
+ }
+ }
+ catch (IOException | RuntimeException e)
+ {
+ LOG.warn("Could not check URL: {}", url, e);
+ }
+
+ return uri.getSchemeSpecificPart().startsWith(URI_BAD_RESOURCE_PREFIX);
+ }
+
+ /**
+ * Corrects any bad {@code resource} based URIs, such as those starting with {@code resource:file:///resources!}.
+ *
+ * @param uri
+ * The URI to correct.
+ * @return the corrected URI, or the original URI.
+ * @see Graal issue 5720
+ */
+ static URI correctResourceURI(URI uri)
+ {
+ if (uri == null || !isResourceScheme(uri))
+ return uri;
+
+ String ssp = uri.getSchemeSpecificPart();
+ if (ssp.startsWith(URI_BAD_RESOURCE_PREFIX))
+ {
+ return URI.create("resource:" + ssp.substring(URI_BAD_RESOURCE_PREFIX.length()));
+ }
+ else
+ {
+ return uri;
+ }
+ }
+
+ @Override
+ public URI getRealURI()
+ {
+ Path realPath = getRealPath();
+ return (realPath == null) ? null : correctResourceURI(realPath.toUri());
+ }
+
+ @Override
+ protected URI toUri(Path path)
+ {
+ URI pathUri = correctResourceURI(path.toUri());
+ String rawUri = pathUri.toASCIIString();
+
+ if (Files.isDirectory(path) && !rawUri.endsWith("/"))
+ {
+ return URI.create(rawUri + '/');
+ }
+ return pathUri;
+ }
+}
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResourceFactory.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResourceFactory.java
new file mode 100644
index 000000000000..6660cf3831b3
--- /dev/null
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/GraalIssue5720PathResourceFactory.java
@@ -0,0 +1,39 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.util.resource;
+
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * GraalVM Native-Image {@link PathResourceFactory}.
+ *
+ * @see Graal issue 5720
+ * @see GraalIssue5720PathResource
+ */
+final class GraalIssue5720PathResourceFactory extends PathResourceFactory
+{
+ @Override
+ public Resource newResource(URI uri)
+ {
+ uri = GraalIssue5720PathResource.correctResourceURI(uri.normalize());
+ Path path = Path.of(uri);
+
+ if (!Files.exists(path))
+ return null;
+
+ return new GraalIssue5720PathResource(path, uri, false);
+ }
+}
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
index 997eb42b4d0b..440ab8c7bfd2 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
@@ -510,10 +510,10 @@ public void copyTo(Path destination) throws IOException
* @param path the path to convert to URI
* @return the appropriate URI for the path
*/
- private static URI toUri(Path path)
+ protected URI toUri(Path path)
{
URI pathUri = path.toUri();
- String rawUri = path.toUri().toASCIIString();
+ String rawUri = pathUri.toASCIIString();
if (Files.isDirectory(path) && !rawUri.endsWith("/"))
{
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
index d810e1853d61..9d3493b7279b 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
@@ -55,16 +55,27 @@ class ResourceFactoryInternals
RESOURCE_FACTORIES.put("file", pathResourceFactory);
RESOURCE_FACTORIES.put("jrt", pathResourceFactory);
- /* Best effort attempt to detect that an alternate FileSystem type that is in use.
- * We don't attempt to look up a Class, as not all runtimes and environments have the classes anymore
- * (eg: they were compiled into native code)
- * The build.properties is present in the jetty-util jar, so it's reasonably safe to look for that
- * as a resource
+ /* Best-effort attempt to support an alternate FileSystem type that is in use for classpath
+ * resources.
+ *
+ * The build.properties is present in the jetty-util jar, and explicitly included for reflection
+ * with native-image (unlike classes, which are not accessible by default), so we use that
+ * resource as a reference.
*/
URL url = ResourceFactoryInternals.class.getResource("/org/eclipse/jetty/version/build.properties");
if ((url != null) && !RESOURCE_FACTORIES.contains(url.getProtocol()))
{
- RESOURCE_FACTORIES.put(url.getProtocol(), mountedPathResourceFactory);
+ ResourceFactory resourceFactory;
+ if (GraalIssue5720PathResource.isAffectedURL(url))
+ {
+ resourceFactory = new GraalIssue5720PathResourceFactory();
+ }
+ else
+ {
+ resourceFactory = url.toString().contains("!/") ? mountedPathResourceFactory : pathResourceFactory;
+ }
+
+ RESOURCE_FACTORIES.put(url.getProtocol(), resourceFactory);
}
}
diff --git a/jetty-core/jetty-util/src/main/resources/META-INF/native-image/org.eclipse.jetty/jetty-core/resource-config.json b/jetty-core/jetty-util/src/main/resources/META-INF/native-image/org.eclipse.jetty/jetty-core/resource-config.json
new file mode 100644
index 000000000000..0d9bcb4fac5b
--- /dev/null
+++ b/jetty-core/jetty-util/src/main/resources/META-INF/native-image/org.eclipse.jetty/jetty-core/resource-config.json
@@ -0,0 +1,9 @@
+{
+ "resources":{
+ "includes":[
+ {
+ "pattern":"\\Qorg/eclipse/jetty/version/build.properties\\E"
+ }
+ ]},
+ "bundles":[]
+}
\ No newline at end of file
diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java
index cb9b915875bd..2a78b24fa7c3 100644
--- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java
+++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java
@@ -39,7 +39,7 @@ public Class extends Configuration> replaces()
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();
diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java
index a0c84b3c6f22..29b40f68b5b1 100644
--- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java
+++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java
@@ -153,7 +153,7 @@ public void generate() throws Exception
ServerSupport.addWebApplication(server, webApp);
//leave everything unpacked for the forked process to use
- webApp.setPersistTempDirectory(true);
+ webApp.setTempDirectoryPersistent(true);
}
try
diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java
index 76befee379fa..632b256368a6 100644
--- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java
+++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java
@@ -102,7 +102,7 @@ public static void toProperties(MavenWebAppContext webApp, File propsFile, Strin
//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 static void fromProperties(MavenWebAppContext webApp, Properties webAppPr
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);
diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java
index 6930cfc9ea7c..8ba6a4e4303a 100644
--- a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java
+++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java
@@ -96,7 +96,7 @@ public void testToProperties() throws Exception
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 void testFromProperties() throws Exception
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));
diff --git a/jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartGeneratorConfiguration.java b/jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartGeneratorConfiguration.java
index 0de531b58d6f..561662d82d50 100644
--- a/jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartGeneratorConfiguration.java
+++ b/jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartGeneratorConfiguration.java
@@ -818,7 +818,7 @@ public void configure(WebAppContext context) throws Exception
{
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);
}
}
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java
index 83b24123279b..95409d5302a5 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java
@@ -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.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static jakarta.servlet.ServletContext.TEMPDIR;
+
/**
* Servlet Context.
*
@@ -284,7 +287,14 @@ else if (parent instanceof Collection)
// Link the handlers
relinkHandlers();
}
-
+
+ @Override
+ public void setTempDirectory(File tempDirectory)
+ {
+ super.setTempDirectory(tempDirectory);
+ setAttribute(TEMPDIR, getTempDirectory());
+ }
+
public ServletContextApi newServletContextApi()
{
return new ServletContextApi();
diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml
index 695c37f56708..15c8a6acda43 100644
--- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml
+++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml
@@ -45,7 +45,6 @@
-
diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod
index 829f698b60df..c3ad614884bb 100644
--- a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod
+++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod
@@ -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$
diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java
index f881706aaddc..ff1282bf7ee3 100644
--- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java
+++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java
@@ -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.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 _extraClasspath;
private Throwable _unavailableException;
@@ -244,7 +238,7 @@ public void initializeDefaults(Map properties)
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 void preConfigure() throws Exception
_configurations.preConfigure(this);
}
+ @Override
+ protected void createTempDirectory()
+ {
+ super.createTempDirectory();
+ }
+
public boolean configure() throws Exception
{
return _configurations.configure(this);
@@ -583,13 +583,13 @@ public void destroy()
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 void setContextWhiteList(String... contextWhiteList)
_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
diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java
index 998ef5d43e96..1596ee914b0b 100644
--- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java
+++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java
@@ -20,6 +20,7 @@
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 void preConfigure(final WebAppContext context) throws Exception
// 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 void configure(WebAppContext context) throws Exception
@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 void cloneConfigure(WebAppContext template, WebAppContext context) throws
*
* B. Create a directory based on global settings. The new directory
* will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"
- *
- * 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 ${java.io.tmpdir}
. 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.
+ *
*
* @param context the context to resolve the temp directory from
* @throws Exception if unable to resolve the temp directory
@@ -140,69 +135,25 @@ public void resolveTempDirectory(WebAppContext context)
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);
- 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);
+ context.setTempDirectory(tempDirectory);
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 void makeTempDirectory(File parent, WebAppContext context)
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 void unpack(WebAppContext context) throws IOException
*/
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 static String getCanonicalNameForWebAppTmpDir(WebAppContext context)
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("-");
}
diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TempDirTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TempDirTest.java
index 8692ab869863..3a0f2235844c 100644
--- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TempDirTest.java
+++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TempDirTest.java
@@ -16,7 +16,6 @@
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.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.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 void tearDown()
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
}
- /**
- * ServletContext.TEMPDIR has null
value
- * so webappContent#tempDirectory is created under java.io.tmpdir
- */
- @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 ""
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 void attributeWithValidDirectory(String type) throws Exception
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 void attributeWithValidDirectory(String type) throws Exception
@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 void attributeWithNonExistentDirectory(String type) throws Exception
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTmpDir = webAppContext.getTempDirectory().toPath();
assertThat(webappTmpDir, is(tmpDir));
- assertTrue(Files.exists(webappTmpDir));
}
/**
- * WebAppContext.BASETEMPDIR has null
value
- * so webappContent#tempDirectory is created under java.io.tmpdir
+ * 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 ""
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 void baseTempDirAttributeWithValidDirectory(String type) throws Exception
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));
- }
-
- /**
- * ${jetty.base}
exists but has no work subdirectory called work
- * so webappContent#tempDirectory is created under java.io.tmpdir
- */
- @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")));
- }
-
/**
* ${jetty.base}
directory exists and has a subdirectory called work
* so webappContent#tempDirectory is created under java.io.tmpdir
@@ -304,109 +168,10 @@ public void jettyBaseWorkExists() throws Exception
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 String
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));
- }
}
diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml
index 202e1a5ef572..46beee47a63d 100644
--- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml
+++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml
@@ -37,7 +37,6 @@
-
diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod
index 57d6711fbf86..9ba13d5863ac 100644
--- a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod
+++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod
@@ -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$
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java
index 3e63f9b42f6c..9448cde9b19a 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java
@@ -252,7 +252,7 @@ public Handler get()
return _coreContextHandler;
}
- public org.eclipse.jetty.server.handler.ContextHandler getCoreContextHandler()
+ public CoreContextHandler getCoreContextHandler()
{
return _coreContextHandler;
}
@@ -2390,6 +2390,15 @@ protected void doStart() throws Exception
}
}
+ /*
+ * Expose configureTempDirectory so it can be triggered early by WebInfConfiguration#preConfigure
+ */
+ @Override
+ public void createTempDirectory()
+ {
+ super.createTempDirectory();
+ }
+
@Override
protected void doStop() throws Exception
{
diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml
index ec0bf2d0d03c..e80fc98735dc 100644
--- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml
+++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml
@@ -37,7 +37,6 @@
-
diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod
index 57a255a4d0ef..130c62ada19c 100644
--- a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod
+++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod
@@ -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$
diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java
index 9d7afbded2d7..fce131d28f16 100644
--- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java
+++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java
@@ -56,6 +56,7 @@
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 _extraClasspath;
private Throwable _unavailableException;
@@ -253,7 +249,7 @@ public void initializeDefaults(Map properties)
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 void setContextWhiteList(String... contextWhiteList)
*/
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 File getTempDirectory()
*/
public void setPersistTempDirectory(boolean persist)
{
- _persistTmpDir = persist;
+ getCoreContextHandler().setTempDirectoryPersistent(persist);
}
/**
@@ -1200,7 +1181,7 @@ public void setPersistTempDirectory(boolean persist)
*/
public boolean isPersistTempDirectory()
{
- return _persistTmpDir;
+ return getCoreContextHandler().isTempDirectoryPersistent();
}
/**
diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java
index fe40957a9794..d776b28b1b53 100644
--- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java
+++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java
@@ -19,6 +19,7 @@
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 void preConfigure(final WebAppContext context) throws Exception
// 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 void configure(WebAppContext context) throws Exception
@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 void cloneConfigure(WebAppContext template, WebAppContext context) throws
*
* B. Create a directory based on global settings. The new directory
* will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"
- *
- * 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 ${java.io.tmpdir}
. 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.
+ *
*
* @param context the context to resolve the temp directory from
* @throws Exception if unable to resolve the temp directory
@@ -138,78 +133,25 @@ public void resolveTempDirectory(WebAppContext context)
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 void makeTempDirectory(File parent, WebAppContext context)
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 void unpack(WebAppContext context) throws IOException
// 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 void unpack(WebAppContext context) throws IOException
LOG.debug("Copying WEB-INF/classes from {} to {}", webInfClasses, webInfClassesDir.getAbsolutePath());
webInfClasses.copyTo(webInfClassesDir.toPath());
}
-
webInf = context.getResourceFactory().newResource(extractedWebInfDir.getCanonicalPath());
}
diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/TempDirTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/TempDirTest.java
index 39ec2b914a5b..37e8fc0d06b7 100644
--- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/TempDirTest.java
+++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/TempDirTest.java
@@ -16,7 +16,6 @@
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.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.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 void tearDown()
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
}
- /**
- * ServletContext.TEMPDIR has null
value
- * so webappContent#tempDirectory is created under java.io.tmpdir
- */
- @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 ""
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 void attributeWithValidDirectory(String type) throws Exception
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 void attributeWithValidDirectory(String type) throws Exception
@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 void attributeWithNonExistentDirectory(String type) throws Exception
webInfConfiguration.resolveTempDirectory(webAppContext);
Path webappTmpDir = webAppContext.getTempDirectory().toPath();
assertThat(webappTmpDir, is(tmpDir));
- assertTrue(Files.exists(webappTmpDir));
}
/**
- * WebAppContext.BASETEMPDIR has null
value
- * so webappContent#tempDirectory is created under java.io.tmpdir
+ * 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 ""
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 void baseTempDirAttributeWithValidDirectory(String type) throws Exception
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));
- }
-
- /**
- * ${jetty.base}
exists but has no work subdirectory called work
- * so webappContent#tempDirectory is created under java.io.tmpdir
- */
- @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")));
- }
-
/**
* ${jetty.base}
directory exists and has a subdirectory called work
* so webappContent#tempDirectory is created under java.io.tmpdir
@@ -304,109 +168,10 @@ public void jettyBaseWorkExists() throws Exception
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 String
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));
- }
}