Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x

This commit is contained in:
Joakim Erdfelt 2024-06-04 10:48:58 -05:00
commit f653c2f658
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
22 changed files with 212 additions and 98 deletions

View File

@ -1,7 +1,7 @@
--- ---
name: 'Release Process' name: 'Release Process'
about: 'COMMITTER ONLY: Managing the Jetty release process' about: 'COMMITTER ONLY: Managing the Jetty release process'
title: 'Jetty Releases 9.4.x, 10.0.y, 11.0.y' title: 'Jetty Releases 9.4.x, 10.0.y, 11.0.y, 12.0.y'
assignees: '' assignees: ''
labels: Build labels: Build
@ -31,10 +31,12 @@ This release process will produce releases:
- [ ] Verify that branch `jetty-10.0.x` is merged to branch `jetty-11.0.x`. - [ ] Verify that branch `jetty-10.0.x` is merged to branch `jetty-11.0.x`.
- [ ] Assign issue to "build manager", who will stage the releases. - [ ] Assign issue to "build manager", who will stage the releases.
+ [ ] Create and use branches `release/<ver>` to perform version specific release work from. + [ ] Create and use branches `release/<ver>` to perform version specific release work from.
+ [ ] Ensure `git fetch --tags` (as we potentially rewrite tag when re staging local tag can be out of sync and this command will fail and so fail the release script)
+ [ ] Ensure `VERSION.txt` additions for each release will be meaningful, descriptive, correct text. + [ ] Ensure `VERSION.txt` additions for each release will be meaningful, descriptive, correct text.
+ [ ] Stage 9.4 release with Java 11. + [ ] Stage 9.4 release with Java 11.
+ [ ] Stage 10 release with Java 21. + [ ] Stage 10 release with Java 21.
+ [ ] Stage 11 release with Java 21. + [ ] Stage 11 release with Java 21.
+ [ ] Stage 12 release with Java 22.
+ [ ] Push release branches `release/<ver>` to to https://github.com/eclipse/jetty.project + [ ] Push release branches `release/<ver>` to to https://github.com/eclipse/jetty.project
+ [ ] Push release tags `jetty-<ver>` to https://github.com/eclipse/jetty.project + [ ] Push release tags `jetty-<ver>` to https://github.com/eclipse/jetty.project
+ [ ] Edit a draft release (for each Jetty release) in GitHub (https://github.com/eclipse/jetty.project/releases). Content is generated with the "changelog tool". + [ ] Edit a draft release (for each Jetty release) in GitHub (https://github.com/eclipse/jetty.project/releases). Content is generated with the "changelog tool".

View File

@ -291,11 +291,13 @@ jetty-12.0.1 - 29 August 2023
+ 10411 Review deployment of Jetty Context XML files + 10411 Review deployment of Jetty Context XML files
+ 10416 EE9 Copies HttpFields in response + 10416 EE9 Copies HttpFields in response
jetty-9.4.54.v20240208 - 08 February 2024 jetty-11.0.21 - 14 May 2024
+ 1256 DoSFilter leaks USER_AUTH entries + 10805 Jetty response with an invalid HTTP2 packet if the client set the
+ 11259 HTTP/2 connection not closed after idle timeout when TCP congested hpack table size as 0
(CVE-2024-22201) + 11527 Reduce ByteBuffer churning in HttpOutput
+ 11389 Strip default ports on ws/wss scheme uris too + 11634 Socks5Proxy does not support IP addresses with IP segments above 127
+ 11656 Upgrade jetty-quiche-native to version 0.21.0
+ 11782 HttpExchange retained by HttpSenderOverHTTP which caused memory leak
jetty-11.0.20 - 29 January 2024 jetty-11.0.20 - 29 January 2024
+ 11081 Dropped WebSocket messages due to race condition in WebSocket frame + 11081 Dropped WebSocket messages due to race condition in WebSocket frame
@ -307,6 +309,12 @@ jetty-11.0.20 - 29 January 2024
+ 11273 Support BSD expr in startup script + 11273 Support BSD expr in startup script
+ 11349 Update quiche to 0.20.0 + 11349 Update quiche to 0.20.0
jetty-9.4.54.v20240208 - 08 February 2024
+ 1256 DoSFilter leaks USER_AUTH entries
+ 11259 HTTP/2 connection not closed after idle timeout when TCP congested
(CVE-2024-22201)
+ 11389 Strip default ports on ws/wss scheme uris too
jetty-11.0.19 - 15 December 2023 jetty-11.0.19 - 15 December 2023
+ 9900 Improve `Request.getBeginNanoTime()` accuracy + 9900 Improve `Request.getBeginNanoTime()` accuracy
+ 10812 jetty-deploy has unnecessary dependency on awaitility/hamcrest pulled + 10812 jetty-deploy has unnecessary dependency on awaitility/hamcrest pulled

View File

@ -92,7 +92,7 @@ DEPLOY_OPTS="-DskipTests -Dasciidoctor.skip=false -Dmaven.build.cache.enabled=fa
# DEPLOY_OPTS="$DEPLOY_OPTS -DaltDeploymentRepository=intarget::default::file://$ALT_DEPLOY_DIR/" # DEPLOY_OPTS="$DEPLOY_OPTS -DaltDeploymentRepository=intarget::default::file://$ALT_DEPLOY_DIR/"
# Uncomment for Java 1.7 # Uncomment for Java 1.7
export MAVEN_OPTS="-Xmx2g" export MAVEN_OPTS="-Xmx4g"
echo "" echo ""
echo "-----------------------------------------------" echo "-----------------------------------------------"

View File

@ -31,7 +31,7 @@ This uniquely identifies the jetty server instance and is applied to the `Sessio
You can either provide a value for this property, or you can allow Jetty to try and synthesize a `workerName` - the latter option is _only_ advisable in the case of a single, non-clustered deployment. You can either provide a value for this property, or you can allow Jetty to try and synthesize a `workerName` - the latter option is _only_ advisable in the case of a single, non-clustered deployment.
There are two ways a default `workerName` can be synthesized: There are two ways a default `workerName` can be synthesized:
* if running on Google AppEngine, the `workerName` will be formed by concatenating the values of the environment variables `JETTY_WORKER_INSTANCE` and `GAE_MODULE_INSTANCE` * if running on Google AppEngine, the `workerName` will be formed by concatenating the values of the environment variables `JETTY_WORKER_INSTANCE` and `GAE_INSTANCE`
* otherwise, the `workerName` will be formed by concatenating the environment variable `JETTY_WORKER_INSTANCE` and the literal `0`. * otherwise, the `workerName` will be formed by concatenating the environment variable `JETTY_WORKER_INSTANCE` and the literal `0`.
So, if you're not running on Google AppEngine, and you haven't configured one, the workerName will always be: `node0`. So, if you're not running on Google AppEngine, and you haven't configured one, the workerName will always be: `node0`.

View File

@ -11,7 +11,7 @@
// ======================================================================== // ========================================================================
// //
= Eclipse Jetty = Eclipse Jetty {page-version}
This section of the site contains the documentation for {page-component-title} {page-version}. This section of the site contains the documentation for {page-component-title} {page-version}.

View File

@ -38,7 +38,6 @@ import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.Retainable; import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.io.WriteFlusher;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
@ -48,7 +47,7 @@ import org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class HTTP2Connection extends AbstractConnection implements Parser.Listener, WriteFlusher.Listener, Connection.UpgradeTo public class HTTP2Connection extends AbstractConnection implements Parser.Listener, Connection.UpgradeTo
{ {
private static final Logger LOG = LoggerFactory.getLogger(HTTP2Connection.class); private static final Logger LOG = LoggerFactory.getLogger(HTTP2Connection.class);
@ -302,12 +301,6 @@ public class HTTP2Connection extends AbstractConnection implements Parser.Listen
session.onConnectionFailure(error, reason); session.onConnectionFailure(error, reason);
} }
@Override
public void onFlushed(long bytes) throws IOException
{
session.onFlushed(bytes);
}
protected class HTTP2Producer implements ExecutionStrategy.Producer protected class HTTP2Producer implements ExecutionStrategy.Producer
{ {
private final Callback fillableCallback = new FillableCallback(); private final Callback fillableCallback = new FillableCallback();

View File

@ -60,7 +60,6 @@ import org.eclipse.jetty.http2.parser.Parser;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.CyclicTimeouts; import org.eclipse.jetty.io.CyclicTimeouts;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.WriteFlusher;
import org.eclipse.jetty.util.AtomicBiInteger; import org.eclipse.jetty.util.AtomicBiInteger;
import org.eclipse.jetty.util.Atomics; import org.eclipse.jetty.util.Atomics;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
@ -1084,11 +1083,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
streamsState.onStreamDestroyed(); streamsState.onStreamDestroyed();
} }
public void onFlushed(long bytes) throws IOException
{
flusher.onFlushed(bytes);
}
private void terminate(Throwable cause) private void terminate(Throwable cause)
{ {
flusher.terminate(cause); flusher.terminate(cause);
@ -1263,8 +1257,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
public abstract boolean generate(ByteBufferPool.Accumulator accumulator) throws HpackException; public abstract boolean generate(ByteBufferPool.Accumulator accumulator) throws HpackException;
public abstract long onFlushed(long bytes) throws IOException;
boolean hasHighPriority() boolean hasHighPriority()
{ {
return false; return false;
@ -1355,16 +1347,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
return true; return true;
} }
@Override
public long onFlushed(long bytes)
{
long flushed = Math.min(frameBytes, bytes);
if (LOG.isDebugEnabled())
LOG.debug("Flushed {}/{} frame bytes for {}", flushed, bytes, this);
frameBytes -= flushed;
return bytes - flushed;
}
/** /**
* <p>Performs actions just before writing the frame to the network.</p> * <p>Performs actions just before writing the frame to the network.</p>
* <p>Some frame, when sent over the network, causes the receiver * <p>Some frame, when sent over the network, causes the receiver
@ -1433,7 +1415,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
private class DataEntry extends Entry private class DataEntry extends Entry
{ {
private int frameBytes; private int frameBytes;
private int frameRemaining;
private int dataBytes; private int dataBytes;
private int dataRemaining; private int dataRemaining;
@ -1477,7 +1458,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
DataFrame dataFrame = (DataFrame)frame; DataFrame dataFrame = (DataFrame)frame;
int frameBytes = generator.data(accumulator, dataFrame, length); int frameBytes = generator.data(accumulator, dataFrame, length);
this.frameBytes += frameBytes; this.frameBytes += frameBytes;
this.frameRemaining += frameBytes;
int dataBytes = frameBytes - Frame.HEADER_LENGTH; int dataBytes = frameBytes - Frame.HEADER_LENGTH;
this.dataBytes += dataBytes; this.dataBytes += dataBytes;
@ -1492,27 +1472,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
return true; return true;
} }
@Override
public long onFlushed(long bytes) throws IOException
{
long flushed = Math.min(frameRemaining, bytes);
if (LOG.isDebugEnabled())
LOG.debug("Flushed {}/{} frame bytes for {}", flushed, bytes, this);
frameRemaining -= flushed;
// We should only forward data (not frame) bytes,
// but we trade precision for simplicity.
Object channel = stream.getAttachment();
if (channel instanceof WriteFlusher.Listener)
((WriteFlusher.Listener)channel).onFlushed(flushed);
return bytes - flushed;
}
@Override @Override
public void succeeded() public void succeeded()
{ {
bytesWritten.addAndGet(frameBytes); bytesWritten.addAndGet(frameBytes);
frameBytes = 0; frameBytes = 0;
frameRemaining = 0;
flowControl.onDataSent(stream, dataBytes); flowControl.onDataSent(stream, dataBytes);
dataBytes = 0; dataBytes = 0;

View File

@ -293,15 +293,6 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
return Action.SCHEDULED; return Action.SCHEDULED;
} }
public void onFlushed(long bytes) throws IOException
{
// A single EndPoint write may be flushed multiple times (for example with SSL).
for (HTTP2Session.Entry entry : processedEntries)
{
bytes = entry.onFlushed(bytes);
}
}
@Override @Override
public void succeeded() public void succeeded()
{ {

View File

@ -429,8 +429,8 @@ public abstract class WriteFlusher
if (written > 0) if (written > 0)
{ {
Connection connection = _endPoint.getConnection(); Connection connection = _endPoint.getConnection();
if (connection instanceof Listener) if (connection instanceof Listener listener)
((Listener)connection).onFlushed(written); listener.onFlushed(written);
} }
if (flushed) if (flushed)
@ -581,7 +581,10 @@ public abstract class WriteFlusher
/** /**
* <p>A listener of {@link WriteFlusher} events. * <p>A listener of {@link WriteFlusher} events.
* If implemented by a Connection class, the {@link #onFlushed(long)} event will be delivered to it.</p> * If implemented by a Connection class, the {@link #onFlushed(long)} event will be delivered to it.</p>
*
* @deprecated functionality removed, no replacement
*/ */
@Deprecated(since = "12.0.10", forRemoval = true)
public interface Listener public interface Listener
{ {
/** /**

View File

@ -15,7 +15,7 @@
<Property name="jetty.sessionIdManager.workerName"> <Property name="jetty.sessionIdManager.workerName">
<Default>node<Env name="JETTY_WORKER_INSTANCE"> <Default>node<Env name="JETTY_WORKER_INSTANCE">
<Default> <Default>
<Env name="GAE_MODULE_INSTANCE"> <Env name="GAE_INSTANCE" deprecated="GAE_MODULE_INSTANCE">
<Default>0</Default> <Default>0</Default>
</Env> </Env>
</Default> </Default>

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.util.component.AttributeContainerMap;
import org.eclipse.jetty.util.component.ClassLoaderDump; import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.DumpableAttributes; import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.DumpableCollection; import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.component.DumpableMap;
import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.component.Graceful; import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
@ -137,6 +138,7 @@ public class Server extends Handler.Wrapper implements Attributes
public Server(@Name("threadPool") ThreadPool pool) public Server(@Name("threadPool") ThreadPool pool)
{ {
this(pool, null, null); this(pool, null, null);
installBean(new DumpableMap("System Properties", System.getProperties()));
} }
public Server(@Name("threadPool") ThreadPool threadPool, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool bufferPool) public Server(@Name("threadPool") ThreadPool threadPool, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool bufferPool)
@ -791,7 +793,7 @@ public class Server extends Handler.Wrapper implements Attributes
*/ */
public Resource getDefaultStyleSheet() public Resource getDefaultStyleSheet()
{ {
return newResource("jetty-dir.css"); return newResource("/org/eclipse/jetty/server/jetty-dir.css");
} }
/** /**
@ -801,7 +803,7 @@ public class Server extends Handler.Wrapper implements Attributes
*/ */
public Resource getDefaultFavicon() public Resource getDefaultFavicon()
{ {
return newResource("favicon.ico"); return newResource("/org/eclipse/jetty/server/favicon.ico");
} }
/** /**

View File

@ -52,7 +52,6 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.io.WriteFlusher;
import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.AbstractMetaDataConnection; import org.eclipse.jetty.server.AbstractMetaDataConnection;
import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.ConnectionFactory;
@ -81,7 +80,7 @@ import static org.eclipse.jetty.http.HttpStatus.INTERNAL_SERVER_ERROR_500;
/** /**
* <p>A {@link Connection} that handles the HTTP protocol.</p> * <p>A {@link Connection} that handles the HTTP protocol.</p>
*/ */
public class HttpConnection extends AbstractMetaDataConnection implements Runnable, WriteFlusher.Listener, Connection.UpgradeFrom, Connection.UpgradeTo, ConnectionMetaData public class HttpConnection extends AbstractMetaDataConnection implements Runnable, Connection.UpgradeFrom, Connection.UpgradeTo, ConnectionMetaData
{ {
private static final Logger LOG = LoggerFactory.getLogger(HttpConnection.class); private static final Logger LOG = LoggerFactory.getLogger(HttpConnection.class);
private static final HttpField PREAMBLE_UPGRADE_H2C = new HttpField(HttpHeader.UPGRADE, "h2c"); private static final HttpField PREAMBLE_UPGRADE_H2C = new HttpField(HttpHeader.UPGRADE, "h2c");
@ -336,13 +335,6 @@ public class HttpConnection extends AbstractMetaDataConnection implements Runnab
BufferUtil.append(getRequestBuffer(), buffer); BufferUtil.append(getRequestBuffer(), buffer);
} }
@Override
public void onFlushed(long bytes) throws IOException
{
// TODO is this callback still needed? Couldn't we wrap send callback instead?
// Either way, the dat rate calculations from HttpOutput.onFlushed should be moved to Channel.
}
void releaseRequestBuffer() void releaseRequestBuffer()
{ {
if (_retainableByteBuffer != null && !_retainableByteBuffer.hasRemaining()) if (_retainableByteBuffer != null && !_retainableByteBuffer.hasRemaining())

View File

@ -32,8 +32,10 @@ import org.eclipse.jetty.server.internal.HttpChannelState;
import org.eclipse.jetty.server.internal.HttpConnection; import org.eclipse.jetty.server.internal.HttpConnection;
import org.eclipse.jetty.util.Blocker; import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.Invocable; import org.eclipse.jetty.util.thread.Invocable;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -143,6 +145,26 @@ public class ServerTest
assertThat(response.getContent(), is("Hello")); assertThat(response.getContent(), is("Hello"));
} }
@Test
public void testDump() throws Exception
{
testSimpleGET();
((QueuedThreadPool)(_server.getThreadPool())).tryExecute(() -> {});
String dump = _server.dump();
assertThat(dump, containsString("oejs.Server@"));
assertThat(dump, containsString("QueuedThreadPool"));
assertThat(dump, containsString("+= ReservedThreadExecutor@"));
assertThat(dump, containsString(".ArrayByteBufferPool@"));
assertThat(dump, containsString("+- System Properties size="));
assertThat(dump, containsString("+> java.home: "));
assertThat(dump, containsString("+> java.runtime.version: "));
assertThat(dump, containsString("+= oejsh.ContextHandler@"));
assertThat(dump, containsString("+= LocalConnector@"));
assertThat(dump, containsString("key: +-"));
assertThat(dump, containsString("JVM: "));
assertThat(dump, containsString(Jetty.VERSION));
}
public static Stream<Arguments> completionScenarios() public static Stream<Arguments> completionScenarios()
{ {
List<Arguments> arguments = new ArrayList<>(); List<Arguments> arguments = new ArrayList<>();

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995 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.server.subpackage;
import java.util.stream.Stream;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class ServerDefaultResourcesTest
{
public static Stream<Arguments> arguments()
{
return Stream.of(
new Server(),
new Server(){}
).map(Arguments::of);
}
@ParameterizedTest
@MethodSource("arguments")
public void testDefaultStyleSheet(Server server) throws Exception
{
try
{
server.start();
assertNotNull(server.getDefaultStyleSheet());
}
finally
{
server.stop();
}
}
@ParameterizedTest
@MethodSource("arguments")
public void testDefaultFavicon(Server server) throws Exception
{
try
{
server.start();
assertNotNull(server.getDefaultFavicon());
}
finally
{
server.stop();
}
}
}

View File

@ -745,10 +745,10 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{ {
try try
{ {
dump(System.err, ""); Dumpable.dump(this, System.err);
System.err.println(Dumpable.KEY); System.err.println();
} }
catch (IOException e) catch (Throwable e)
{ {
LOG.warn("Unable to dump", e); LOG.warn("Unable to dump", e);
} }

View File

@ -14,8 +14,13 @@
package org.eclipse.jetty.util.component; package org.eclipse.jetty.util.component;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -23,6 +28,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedObject;
@ -31,7 +37,7 @@ import org.eclipse.jetty.util.annotation.ManagedOperation;
@ManagedObject("Dumpable Object") @ManagedObject("Dumpable Object")
public interface Dumpable public interface Dumpable
{ {
String KEY = "key: +- bean, += managed, +~ unmanaged, +? auto, +: iterable, +] array, +@ map, +> undefined"; String KEY = "key: +- bean, += managed, +~ unmanaged, +? auto, +: iterable, +] array, +@ map, +> undefined\n";
@ManagedOperation(value = "Dump the nested Object state as a String", impact = "INFO") @ManagedOperation(value = "Dump the nested Object state as a String", impact = "INFO")
default String dump() default String dump()
@ -50,24 +56,55 @@ public interface Dumpable
void dump(Appendable out, String indent) throws IOException; void dump(Appendable out, String indent) throws IOException;
/** /**
* Utility method to implement {@link #dump()} by calling {@link #dump(Appendable, String)} * Utility method to dump to a {@link String}
* *
* @param dumpable The dumpable to dump * @param dumpable The dumpable to dump
* @return The dumped string * @return The dumped string
* @see #dump(Appendable, String)
*/ */
static String dump(Dumpable dumpable) static String dump(Dumpable dumpable)
{ {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
dump(dumpable, b);
return b.toString();
}
/**
* Utility method to dump to an {@link Appendable}
*
* @param dumpable The dumpable to dump
* @param out The destination of the dump
*/
static void dump(Dumpable dumpable, Appendable out)
{
try try
{ {
dumpable.dump(b, ""); dumpable.dump(out, "");
out.append(KEY);
Runtime runtime = Runtime.getRuntime();
Instant now = Instant.now();
String zone = System.getProperty("user.timezone");
out.append("JVM: %s %s %s; OS: %s %s %s; Jetty: %s; CPUs: %d; mem(free/total/max): %,d/%,d/%,d MiB\nUTC: %s; %s: %s".formatted(
System.getProperty("java.vm.vendor"),
System.getProperty("java.vm.name"),
System.getProperty("java.vm.version"),
System.getProperty("os.name"),
System.getProperty("os.arch"),
System.getProperty("os.version"),
Jetty.VERSION,
runtime.availableProcessors(),
runtime.freeMemory() / (1024 * 1024),
runtime.totalMemory() / (1024 * 1024),
runtime.maxMemory() / (1024 * 1024),
DateTimeFormatter.ISO_DATE_TIME.format(now.atOffset(ZoneOffset.UTC)),
zone,
DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(now.atZone(ZoneId.of(zone)))));
} }
catch (IOException e) catch (IOException e)
{ {
b.append(e.toString()); throw new UncheckedIOException(e);
} }
b.append(KEY);
return b.toString();
} }
/** /**
@ -301,7 +338,7 @@ public interface Dumpable
* interface to allow it to refine which of its beans can be * interface to allow it to refine which of its beans can be
* dumped. * dumped.
*/ */
public interface DumpableContainer extends Dumpable interface DumpableContainer extends Dumpable
{ {
default boolean isDumpable(Object o) default boolean isDumpable(Object o)
{ {

View File

@ -0,0 +1,39 @@
//
// ========================================================================
// Copyright (c) 1995 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.component;
import java.io.IOException;
import java.util.Comparator;
import java.util.Map;
public class DumpableMap implements Dumpable
{
private final String _name;
private final Map<?, ?> _map;
public DumpableMap(String name, Map<?, ?> map)
{
_name = name;
_map = map;
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Object[] array = _map.entrySet().stream()
.sorted(Map.Entry.comparingByKey(Comparator.comparing(String::valueOf)))
.map(e -> Dumpable.named(String.valueOf(e.getKey()), e.getValue())).toArray(Object[]::new);
Dumpable.dumpObjects(out, indent, _name + " size=" + array.length, array);
}
}

View File

@ -25,7 +25,6 @@ import org.eclipse.jetty.util.MemoryUtils;
import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -236,8 +235,12 @@ public class ThreadIdPool<E> implements Dumpable
int capacity = capacity(); int capacity = capacity();
List<Object> slots = new ArrayList<>(capacity); List<Object> slots = new ArrayList<>(capacity);
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
slots.add(_items.get(toSlot(i))); {
Dumpable.dumpObjects(out, indent, this, new DumpableCollection("items", slots)); E slot = _items.get(toSlot(i));
if (slot != null)
slots.add(Dumpable.named(Integer.toString(i), slot));
}
Dumpable.dumpObjects(out, indent, this, slots.toArray());
} }
@Override @Override

View File

@ -833,8 +833,6 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertThat(count(dump, " - STARTED"), is(3)); assertThat(count(dump, " - STARTED"), is(3));
assertThat(dump, containsString(",3<=3<=4,i=1,r=2,")); assertThat(dump, containsString(",3<=3<=4,i=1,r=2,"));
assertThat(dump, containsString("[ReservedThreadExecutor@")); assertThat(dump, containsString("[ReservedThreadExecutor@"));
assertThat(count(dump, "> ReservedThread@"), is(1));
assertThat(count(dump, "> null"), is(1));
assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(0)); assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(0));
pool.setDetailedDump(true); pool.setDetailedDump(true);

View File

@ -125,14 +125,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi
if (_out == null) if (_out == null)
{ {
handler.dumpStdErr(); handler.dumpStdErr();
System.err.println(Dumpable.KEY);
} }
else else
{ {
try try
{ {
handler.dump(_out); Dumpable.dump(handler, _out);
_out.println(Dumpable.KEY);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -52,7 +52,7 @@
<configuration> <configuration>
<instructions> <instructions>
<Bundle-Description>javax.websocket.server Implementation</Bundle-Description> <Bundle-Description>javax.websocket.server Implementation</Bundle-Description>
<Export-Package>org.eclipse.jetty.websocket.javax.server.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package> <Export-Package>org.eclipse.jetty.ee8.websocket.javax.server.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability> <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
<Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.ee8.webapp.Configuration,osgi.serviceloader;osgi.serviceloader=javax.servlet.ServletContainerInitializer,osgi.serviceloader;osgi.serviceloader=javax.websocket.server.ServerEndpointConfig$Configurator</Provide-Capability> <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.ee8.webapp.Configuration,osgi.serviceloader;osgi.serviceloader=javax.servlet.ServletContainerInitializer,osgi.serviceloader;osgi.serviceloader=javax.websocket.server.ServerEndpointConfig$Configurator</Provide-Capability>
</instructions> </instructions>

View File

@ -121,14 +121,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi
if (_out == null) if (_out == null)
{ {
handler.dumpStdErr(); handler.dumpStdErr();
System.err.println(Dumpable.KEY);
} }
else else
{ {
try try
{ {
handler.dump(_out); Dumpable.dump(handler, _out);
_out.println(Dumpable.KEY);
} }
catch (Exception e) catch (Exception e)
{ {