sortedEnvXmlProperties = envXmlFilenameMap.keySet().stream().sorted().toList();
+
+ //apply each environment context xml file
+ for (String property : sortedEnvXmlProperties)
+ {
+ Path envXmlPath = envXmlFilenameMap.get(property);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Applying environment specific context file {}", envXmlPath);
context = applyXml(context, envXmlPath, env, properties);
}
}
@@ -427,9 +487,10 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path))
throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app);
context = contextHandlerClass.getDeclaredConstructor().newInstance();
- properties.put(Deployable.WAR, path.toString());
}
+ //set a backup value for the path to the war in case it hasn't already been set
+ properties.put(Deployable.WAR, path.toString());
return initializeContextHandler(context, path, properties);
}
finally
diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java
index aa8acefca239..a68db97d0e66 100644
--- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java
+++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.deploy.providers;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@@ -34,7 +35,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -88,7 +91,6 @@ public void testStartupContext() throws Exception
// Check Server for Handlers
jetty.assertContextHandlerExists("/bar");
-
}
@Test
@@ -96,17 +98,24 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception
{
Path jettyBase = jetty.getJettyBasePath();
Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context.xml", StandardOpenOption.CREATE_NEW);
+ Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), Deployable.ENVIRONMENT_XML + ".other = etc/core-context-other.xml", StandardOpenOption.CREATE_NEW);
assertTrue(Files.exists(propsFile));
+ assertTrue(Files.exists(props2File));
Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING);
+
jetty.copyWebapp("bar-core-context.properties", "bar.properties");
startJetty();
- //check environment context xml was applied to the produced context
+ //check core-context.xml was applied to the produced context
ContextHandler context = jetty.getContextHandler("/bar");
assertNotNull(context);
- assertThat(context.getAttribute("somename"), equalTo("somevalue"));
+ assertThat(context.getAttribute("core-context-0"), equalTo("core-context-0"));
assertTrue(context instanceof BarContextHandler);
-
+ //check core-context-other.xml was applied to the produced context
+ assertThat(context.getAttribute("other"), equalTo("othervalue"));
+ //check core-context-other.xml was applied AFTER core-context.xml
+ assertThat(context.getAttribute("somename"), equalTo("othervalue"));
}
@Test
@@ -116,13 +125,108 @@ public void testStartupWithAbsoluteEnvironmentContext() throws Exception
Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = " +
MavenPaths.findTestResourceFile("etc/core-context.xml"), StandardOpenOption.CREATE_NEW);
assertTrue(Files.exists(propsFile));
+ Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), Deployable.ENVIRONMENT_XML + ".other = " + MavenPaths.findTestResourceFile("etc/core-context-other.xml"), StandardOpenOption.CREATE_NEW);
+ assertTrue(Files.exists(props2File));
Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING);
- jetty.copyWebapp("bar-core-context.properties", "bar-core-context.properties");
+ Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING);
+ jetty.copyWebapp("bar-core-context.properties", "bar.properties");
startJetty();
- //check environment context xml was applied to the produced context
+ //check core environment context xml was applied to the produced context
ContextHandler context = jetty.getContextHandler("/bar");
assertNotNull(context);
- assertThat(context.getAttribute("somename"), equalTo("somevalue"));
+ assertThat(context.getAttribute("core-context-0"), equalTo("core-context-0"));
+ assertTrue(context instanceof BarContextHandler);
+ //check core-context-other.xml was applied to the produced context
+ assertThat(context.getAttribute("other"), equalTo("othervalue"));
+ //check core-context-other.xml was applied AFTER core-context.xml
+ assertThat(context.getAttribute("somename"), equalTo("othervalue"));
+ }
+
+ @Test
+ public void testNonEnvironmentPropertyFileNotApplied() throws Exception
+ {
+ Path jettyBase = jetty.getJettyBasePath();
+ Path propsFile = Files.writeString(jettyBase.resolve("webapps/non-env.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW);
+ Path propsEE8File = Files.writeString(jettyBase.resolve("webapps/ee8.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW);
+ Path propsEE9File = Files.writeString(jettyBase.resolve("webapps/ee9.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW);
+ Path propsEE10File = Files.writeString(jettyBase.resolve("webapps/ee10.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW);
+ Path propsNonCoreFile = Files.writeString(jettyBase.resolve("webapps/not-core.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW);
+ assertTrue(Files.exists(propsFile));
+ assertTrue(Files.exists(propsEE8File));
+ assertTrue(Files.exists(propsEE9File));
+ assertTrue(Files.exists(propsEE10File));
+ jetty.copyWebapp("bar-core-context.properties", "bar.properties");
+ startJetty();
+
+ //test that the context was deployed as expected and that the non-applicable properties files were ignored
+ ContextHandler context = jetty.getContextHandler("/bar");
+ assertNotNull(context);
+ assertTrue(context instanceof BarContextHandler);
+ }
+
+ /**
+ * Test that properties of the same name will be overridden, in the order of the name of the .properties file
+ * @throws Exception
+ */
+ @Test
+ public void testPropertyOverriding() throws Exception
+ {
+ Path jettyBase = jetty.getJettyBasePath();
+ Path propsCoreAFile = Files.writeString(jettyBase.resolve("webapps/core-a.properties"), Deployable.ENVIRONMENT_XML + " = etc/a.xml", StandardOpenOption.CREATE_NEW);
+ Path propsCoreBFile = Files.writeString(jettyBase.resolve("webapps/core-b.properties"), Deployable.ENVIRONMENT_XML + " = etc/b.xml", StandardOpenOption.CREATE_NEW);
+ Path propsCoreCFile = Files.writeString(jettyBase.resolve("webapps/core-c.properties"), Deployable.ENVIRONMENT_XML + " = etc/c.xml", StandardOpenOption.CREATE_NEW);
+ Path propsCoreDFile = Files.writeString(jettyBase.resolve("webapps/core-d.properties"), Deployable.ENVIRONMENT_XML + " = etc/d.xml", StandardOpenOption.CREATE_NEW);
+ assertTrue(Files.exists(propsCoreAFile));
+ assertTrue(Files.exists(propsCoreBFile));
+ assertTrue(Files.exists(propsCoreCFile));
+ assertTrue(Files.exists(propsCoreDFile));
+ Path aPath = jettyBase.resolve("etc/a.xml");
+ writeXmlDisplayName(aPath, "A WebApp");
+ Path bPath = jettyBase.resolve("etc/b.xml");
+ writeXmlDisplayName(bPath, "B WebApp");
+ Path cPath = jettyBase.resolve("etc/c.xml");
+ writeXmlDisplayName(cPath, "C WebApp");
+ Path dPath = jettyBase.resolve("etc/d.xml");
+ writeXmlDisplayName(dPath, "D WebApp");
+ assertTrue(Files.exists(propsCoreAFile));
+ assertTrue(Files.exists(propsCoreBFile));
+ assertTrue(Files.exists(propsCoreCFile));
+ assertTrue(Files.exists(propsCoreDFile));
+
+ jetty.copyWebapp("bar-core-context.properties", "bar.properties");
+ startJetty();
+
+ ContextHandler context = jetty.getContextHandler("/bar");
+ assertNotNull(context);
+ assertEquals("D WebApp", context.getDisplayName());
+ }
+
+ /**
+ * Test that properties defined in an environment-specific properties file
+ * are used for substitution.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testPropertySubstitution() throws Exception
+ {
+ Path jettyBase = jetty.getJettyBasePath();
+ Path propsCoreAFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context-sub.xml\ntest.displayName=DisplayName Set By Property", StandardOpenOption.CREATE_NEW);
+ Files.copy(MavenPaths.findTestResourceFile("etc/core-context-sub.xml"), jettyBase.resolve("etc/core-context-sub.xml"), StandardCopyOption.REPLACE_EXISTING);
+ jetty.copyWebapp("bar-core-context.properties", "bar.properties");
+ startJetty();
+ ContextHandler context = jetty.getContextHandler("/bar");
+ assertNotNull(context);
+ assertEquals("DisplayName Set By Property", context.getDisplayName());
+ }
+
+ private static void writeXmlDisplayName(Path filePath, String displayName) throws IOException
+ {
+ String content = "\n" +
+ "\n" +
+ " ";
+
+ Files.writeString(filePath, content + displayName + "\n", StandardOpenOption.CREATE_NEW);
}
}
diff --git a/jetty-core/jetty-deploy/src/test/resources/etc/core-context-other.xml b/jetty-core/jetty-deploy/src/test/resources/etc/core-context-other.xml
new file mode 100644
index 000000000000..3879d3d117bc
--- /dev/null
+++ b/jetty-core/jetty-deploy/src/test/resources/etc/core-context-other.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ other
+ othervalue
+
+
+ somename
+ othervalue
+
+
diff --git a/jetty-core/jetty-deploy/src/test/resources/etc/core-context-sub.xml b/jetty-core/jetty-deploy/src/test/resources/etc/core-context-sub.xml
new file mode 100644
index 000000000000..ff3c5b7caa16
--- /dev/null
+++ b/jetty-core/jetty-deploy/src/test/resources/etc/core-context-sub.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jetty-core/jetty-deploy/src/test/resources/etc/core-context.xml b/jetty-core/jetty-deploy/src/test/resources/etc/core-context.xml
index cccca5c2950c..ef4e4e2f236a 100644
--- a/jetty-core/jetty-deploy/src/test/resources/etc/core-context.xml
+++ b/jetty-core/jetty-deploy/src/test/resources/etc/core-context.xml
@@ -19,4 +19,8 @@
somename
somevalue
+
+ core-context-0
+ core-context-0
+
diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-debug.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-debug.xml
deleted file mode 100644
index 0e091f017e4c..000000000000
--- a/jetty-core/jetty-server/src/main/config/etc/jetty-debug.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- /yyyy_mm_dd.debug.log
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [
-
-
-
- ]
-
-
-
-
-
diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-debuglog.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-debuglog.xml
index 31500c94753f..af4ec8f6507f 100644
--- a/jetty-core/jetty-server/src/main/config/etc/jetty-debuglog.xml
+++ b/jetty-core/jetty-server/src/main/config/etc/jetty-debuglog.xml
@@ -9,17 +9,18 @@
-
-
- /yyyy_mm_dd.debug.log
-
-
-
-
-
-
-
-
+
+
+ /yyyy_mm_dd.debug.log
+
+
+
+
+
+
+
+
+
diff --git a/jetty-core/jetty-server/src/main/config/modules/debug.mod b/jetty-core/jetty-server/src/main/config/modules/debug.mod
deleted file mode 100644
index b30cc8dde4c3..000000000000
--- a/jetty-core/jetty-server/src/main/config/modules/debug.mod
+++ /dev/null
@@ -1,42 +0,0 @@
-# DO NOT EDIT THIS FILE - See: https://jetty.org/docs/
-
-[description]
-Enables the DebugListener.
-Generates additional logging regarding detailed request handling events.
-Renames threads to include request URI.
-
-[tags]
-server
-debug
-
-[depend]
-deploy
-
-[files]
-logs/
-
-[xml]
-etc/jetty-debug.xml
-
-[ini-template]
-
-## How many days to retain old log files
-# jetty.debug.retainDays=14
-
-## Should existing log be appended to
-# jetty.debug.append=true
-
-## Log directory for jetty debug logs
-# jetty.debug.logs=./logs
-
-## Timezone of the log entries
-# jetty.debug.timezone=GMT
-
-## Show Request/Response headers
-# jetty.debug.showHeaders=true
-
-## Rename threads while in context scope
-# jetty.debug.renameThread=false
-
-## Dump context as deployed
-# jetty.debug.dumpContext=true
diff --git a/jetty-core/jetty-server/src/main/config/modules/debuglog.mod b/jetty-core/jetty-server/src/main/config/modules/debuglog.mod
index de8ec60b7851..4aaeaf0dfe08 100644
--- a/jetty-core/jetty-server/src/main/config/modules/debuglog.mod
+++ b/jetty-core/jetty-server/src/main/config/modules/debuglog.mod
@@ -1,8 +1,7 @@
# DO NOT EDIT THIS FILE - See: https://jetty.org/docs/
[description]
-Deprecated Debug Log using DebugHandle.
-Replaced with the debug module.
+Debug Log using DebugHandle.
[tags]
server
@@ -19,6 +18,7 @@ logs/
etc/jetty-debuglog.xml
[ini-template]
+#tag::documentation[]
## Logging directory (relative to $jetty.base)
# jetty.debuglog.dir=logs
@@ -30,3 +30,7 @@ etc/jetty-debuglog.xml
## Timezone of the log entries
# jetty.debuglog.timezone=GMT
+
+## Show Request/Response headers
+# jetty.debug.showHeaders=true
+#end::documentation[]
\ No newline at end of file
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
index d8454e4eb54e..43b2c6a3d9a0 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
@@ -16,8 +16,8 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
-import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
@@ -30,16 +30,98 @@
/**
* Debug Handler.
+ *
* A lightweight debug handler that can be used in production code.
* Details of the request and response are written to an output stream
* and the current thread name is updated with information that will link
* to the details in that output.
+ *
+ *
+ * Note that due to async processing, the logging of request processing may
+ * appear out of order.
+ *
*/
public class DebugHandler extends Handler.Wrapper implements Connection.Listener
{
- private final DateCache _date = new DateCache("HH:mm:ss", Locale.US);
+ private static final DateCache __date = new DateCache("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
private OutputStream _out;
private PrintStream _print;
+ private boolean _showHeaders;
+ private final String _attr = String.format("__R%s@%x", this.getClass().getSimpleName(), System.identityHashCode(this));
+
+ /**
+ * Callback class used to manage possibly async handling by downstream handlers.
+ * The
+ */
+ private class HandlingCallback extends Callback.Nested
+ {
+ private final Request _request;
+ private final Response _response;
+ private final AtomicBoolean _completing = new AtomicBoolean(false);
+
+ private HandlingCallback(Callback callback, Request request, Response response)
+ {
+ super(callback);
+ _request = request;
+ _response = response;
+ }
+
+ /**
+ * Called when the handler chain has completed. We log either the completion of the request
+ * or an in-progress message if the request is still being processed asynchronously (ie this
+ * callback's {@link #succeeded()} or {@link #failed(Throwable)} methods have not yet been called).
+ */
+ private void onHandlingCompleted(Throwable throwable)
+ {
+ if (_completing.compareAndSet(false, true))
+ {
+ logContinuation();
+ }
+ else
+ {
+ logCompletion(throwable);
+ }
+ }
+
+ /**
+ * Called when this callback completes, either with success or failure. We output a
+ * request completion log message only in the case where we have been called back
+ * asynchronously after the handler chain has finished.
+ * @param throwable null if the handling succeeded or otherwise the failure that occurred
+ */
+ private void onCallbackCompleted(Throwable throwable)
+ {
+ //If we can set the value to true, we know that handling has not yet finalized and onHandlingCompleted(Throwable)
+ //will be called and will log the completion message. On the other hand, if we are not able to set it, then this means that
+ //handling has already finished, so we must log the completion message.
+ if (!_completing.compareAndSet(false, true))
+ logCompletion(throwable);
+ }
+
+ private void logCompletion(Throwable throwable)
+ {
+ log("<< r=%s async=false %d %s%n%s", findRequestName(_request), _response.getStatus(), (throwable == null ? "" : throwable.toString()), _response.getHeaders());
+ }
+
+ private void logContinuation()
+ {
+ log("|| r=%s async=true", findRequestName(_request));
+ }
+
+ @Override
+ public void succeeded()
+ {
+ onCallbackCompleted(null);
+ super.succeeded();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ onCallbackCompleted(x);
+ super.failed(x);
+ }
+ }
public DebugHandler()
{
@@ -51,42 +133,95 @@ public DebugHandler(Handler handler)
super(handler);
}
+ public boolean isShowHeaders()
+ {
+ return _showHeaders;
+ }
+
+ public void setShowHeaders(boolean showHeaders)
+ {
+ _showHeaders = showHeaders;
+ }
+
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
Thread thread = Thread.currentThread();
String name = thread.getName() + ":" + request.getHttpURI();
+ boolean willHandle = false;
+ Throwable ex = null;
+ String rname = findRequestName(request);
+ HandlingCallback handlingCallback = new HandlingCallback(callback, request, response);
- String ex = null;
try
{
- print(name, "REQUEST " + Request.getRemoteAddr(request) +
- " " + request.getMethod() +
- " " + request.getHeaders().get("Cookie") +
- "; " + request.getHeaders().get("User-Agent"));
+ String headers = _showHeaders ? ("\n" + request.getHeaders().toString()) : "";
+
+ log(">> r=%s %s %s %s %s %s",
+ rname,
+ request.getMethod(),
+ request.getHttpURI(),
+ request.getConnectionMetaData().getProtocol(),
+ request.getConnectionMetaData(),
+ headers);
thread.setName(name);
- return getHandler().handle(request, response, callback);
+ willHandle = getHandler().handle(request, response, handlingCallback);
+ return willHandle;
}
catch (Throwable x)
{
- ex = x + ":" + x.getCause();
+ ex = x;
throw x;
}
finally
{
- // TODO this should be done in a completion event
- print(name, "RESPONSE " + response.getStatus() + (ex == null ? "" : ("/" + ex)) + " " + response.getHeaders().get(HttpHeader.CONTENT_TYPE));
+ if (!willHandle)
+ {
+ //Log that the request was not going to be handled
+ log("!! r=%s not handled", rname);
+ }
+ else
+ {
+ handlingCallback.onHandlingCompleted(ex);
+ }
}
}
- private void print(String name, String message)
+ protected void log(String format, Object... arg)
{
+ if (!isRunning())
+ return;
+
+ String s = String.format(format, arg);
+
long now = System.currentTimeMillis();
- final String d = _date.format(now);
- final int ms = (int)(now % 1000);
+ long ms = now % 1000;
+ if (_print != null)
+ _print.printf("%s.%03d:%s%n", __date.format(now), ms, s);
+ }
+
+ protected String findRequestName(Request request)
+ {
+ if (request == null)
+ return null;
- _print.println(d + (ms > 99 ? "." : (ms > 9 ? ".0" : ".00")) + ms + ":" + name + " " + message);
+ try
+ {
+ String n = (String)request.getAttribute(_attr);
+ if (n == null)
+ {
+ n = String.format("%s@%x", request.getHttpURI(), request.hashCode());
+ request.setAttribute(_attr, n);
+ }
+ return n;
+ }
+ catch (IllegalStateException e)
+ {
+ // TODO can we avoid creating and catching this exception? see #8024
+ // Handle the case when the request has already been completed
+ return String.format("%s@%x", request.getHttpURI(), request.hashCode());
+ }
}
@Override
@@ -119,6 +254,7 @@ protected void doStop() throws Exception
/**
* Get the out.
+ *
* @return the out
*/
public OutputStream getOutputStream()
@@ -128,6 +264,7 @@ public OutputStream getOutputStream()
/**
* Set the out to set.
+ *
* @param out the out to set
*/
public void setOutputStream(OutputStream out)
@@ -138,12 +275,12 @@ public void setOutputStream(OutputStream out)
@Override
public void onOpened(Connection connection)
{
- print(Thread.currentThread().getName(), "OPENED " + connection.toString());
+ log("%s OPENED %s", Thread.currentThread().getName(), connection.toString());
}
@Override
public void onClosed(Connection connection)
{
- print(Thread.currentThread().getName(), "CLOSED " + connection.toString());
+ log("%s CLOSED %s", Thread.currentThread().getName(), connection.toString());
}
}
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 63c32f0d45cf..353a410bcc77 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
@@ -214,7 +214,11 @@ public void initializeDefaults(Map properties)
switch (property)
{
- case Deployable.WAR -> setWar(value);
+ case Deployable.WAR ->
+ {
+ if (getWar() == null)
+ setWar(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);
diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java
index 6f6bfc2366ff..077f10c6a6b8 100644
--- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java
+++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java
@@ -14,6 +14,7 @@
package org.eclipse.jetty.tests.distribution;
import java.net.URI;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.NoSuchElementException;
@@ -699,4 +700,50 @@ public void testJettyDemo(String env) throws Exception
}
}
+ @ParameterizedTest
+ @MethodSource("provideEnvironmentsToTest")
+ public void testDebugLogModule(String env) throws Exception
+ {
+ Path jettyBase = newTestJettyBaseDirectory();
+ String jettyVersion = System.getProperty("jettyVersion");
+ JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .jettyBase(jettyBase)
+ .build();
+
+ int httpPort = Tester.freePort();
+ int sslPort = Tester.freePort();
+
+ String[] argsConfig = {
+ "--add-modules=http," + toEnvironment("demos", env) + ",debuglog"
+ };
+
+ try (JettyHomeTester.Run runConfig = distribution.start(argsConfig))
+ {
+ assertTrue(runConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
+ assertEquals(0, runConfig.getExitValue());
+
+ String[] argsStart = {
+ "jetty.http.port=" + httpPort,
+ "jetty.ssl.port=" + sslPort,
+ "jetty.server.dumpAfterStart=true"
+ };
+
+ try (JettyHomeTester.Run runStart = distribution.start(argsStart))
+ {
+ assertTrue(runStart.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
+ startHttpClient();
+ String baseURI = "http://localhost:%d/%s-test".formatted(httpPort, env);
+
+ ContentResponse response = client.POST(baseURI + "/dump/info").send();
+ assertEquals(HttpStatus.OK_200, response.getStatus(), new ResponseDetails(response));
+ Path jettyLogs = jettyBase.resolve("logs");
+ assertTrue(Files.isDirectory(jettyLogs));
+ try (Stream files = Files.list(jettyLogs))
+ {
+ assertTrue(files.anyMatch(p -> p.toFile().getName().endsWith(".debug.log")));
+ }
+ }
+ }
+ }
}