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 cc21b4d8f1c8..7830226a3b00 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
@@ -19,6 +19,8 @@
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
+import java.time.Duration;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -46,7 +48,6 @@
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.NanoTime;
-import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.VirtualThreads;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@@ -87,6 +88,7 @@ public class Server extends Handler.Wrapper implements Attributes
private final AutoLock _dateLock = new AutoLock();
private final MimeTypes.Mutable _mimeTypes = new MimeTypes.Mutable();
private String _serverInfo = __serverInfo;
+ private ZonedDateTime _startupDateTime;
private boolean _openEarly = true;
private boolean _stopAtShutdown;
private boolean _dumpAfterStart;
@@ -253,10 +255,10 @@ public File getTempDirectory()
* {@link ContextHandler}. A {@code Server}'s {@link Context}:
*
* - has a {@code null} {@link Context#getContextPath() context path}
- * - returns the {@link ClassLoader} that loaded the {@link Server} from {@link Context#getClassLoader()}.
+ * - returns the {@link ClassLoader} that loaded the Server from {@link Context#getClassLoader()}.
* - is an {@link java.util.concurrent.Executor} that delegates to the {@link Server#getThreadPool() Server ThreadPool}
* - is a {@link org.eclipse.jetty.util.Decorator} using the {@link DecoratedObjectFactory} found
- * as a {@link #getBean(Class) bean} of the {@link Server}
+ * as a {@link #getBean(Class) bean} of the Server
* - has the same {@link #getTempDirectory() temporary director} of the {@link Server#getTempDirectory() server}
*
*/
@@ -530,12 +532,12 @@ public HttpField getDateField()
long seconds = now / 1000;
DateField df = _dateField;
- if (df == null || df._seconds != seconds)
+ if (df == null || df.seconds != seconds)
{
try (AutoLock ignore = _dateLock.lock())
{
df = _dateField;
- if (df == null || df._seconds != seconds)
+ if (df == null || df.seconds != seconds)
{
HttpField field = new ResponseHttpFields.PersistentPreEncodedHttpField(HttpHeader.DATE, DateGenerator.formatDate(now));
_dateField = new DateField(seconds, field);
@@ -543,7 +545,23 @@ public HttpField getDateField()
}
}
}
- return df._dateField;
+ return df.dateField;
+ }
+
+ /**
+ * @return the startup date and time in the system timezone, or {@code null} if not started
+ */
+ public ZonedDateTime getStartupDateTime()
+ {
+ return _startupDateTime;
+ }
+
+ /**
+ * @return the time, in milliseconds, since this Server was started
+ */
+ public long getUptimeMillis()
+ {
+ return _startupDateTime == null ? 0 : Duration.between(_startupDateTime, ZonedDateTime.now()).toMillis();
}
@Override
@@ -551,8 +569,10 @@ protected void doStart() throws Exception
{
try
{
- //If the Server should be stopped when the jvm exits, register
- //with the shutdown handler thread.
+ _startupDateTime = ZonedDateTime.now();
+
+ // If the Server should be stopped when the jvm exits,
+ // register with the shutdown handler thread.
if (getStopAtShutdown())
ShutdownThread.register(this);
@@ -615,7 +635,7 @@ protected void doStart() throws Exception
if (_dryRun)
{
- LOG.info(String.format("Started(dry run) %s @%dms", this, Uptime.getUptime()));
+ LOG.info("Started(dry run) {} @{}ms", this, getUptimeMillis());
throw new StopException();
}
@@ -635,7 +655,7 @@ protected void doStart() throws Exception
}
multiException.ifExceptionThrow();
- LOG.info(String.format("Started %s @%dms", this, Uptime.getUptime()));
+ LOG.info("Started {} @{}ms", this, getUptimeMillis());
}
catch (Throwable th)
{
@@ -675,10 +695,12 @@ protected void doStop() throws Exception
if (isDumpBeforeStop())
dumpStdErr();
- LOG.info(String.format("Stopped %s", this));
+ LOG.info("Stopped {}", this);
if (LOG.isDebugEnabled())
LOG.debug("doStop {}", this);
+ _startupDateTime = null;
+
Throwable multiException = null;
if (getStopTimeout() > 0)
@@ -887,18 +909,7 @@ public static void main(String... args)
System.err.println(getVersion());
}
- private static class DateField
- {
- final long _seconds;
- final HttpField _dateField;
-
- public DateField(long seconds, HttpField dateField)
- {
- super();
- _seconds = seconds;
- _dateField = dateField;
- }
- }
+ private record DateField(long seconds, HttpField dateField) {}
private static class DynamicErrorHandler extends ErrorHandler {}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/ServerMBean.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/ServerMBean.java
index 66cada120267..c005916d9581 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/ServerMBean.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/jmx/ServerMBean.java
@@ -14,7 +14,8 @@
package org.eclipse.jetty.server.jmx;
import java.time.Duration;
-import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
import java.util.List;
import org.eclipse.jetty.server.Server;
@@ -26,12 +27,9 @@
@ManagedObject
public class ServerMBean extends Handler.AbstractMBean
{
- private final Instant startup;
-
public ServerMBean(Object managedObject)
{
super(managedObject);
- startup = Instant.now();
}
@Override
@@ -69,13 +67,20 @@ public List getContexts()
@ManagedAttribute("The UTC startup instant")
public String getStartupTime()
{
- return startup.toString();
+ ZonedDateTime zoned = getManagedObject().getStartupDateTime();
+ return String.valueOf(zoned.withZoneSameInstant(ZoneOffset.UTC));
+ }
+
+ @ManagedAttribute("The startup date time in the system timezone")
+ public String getStartupDateTime()
+ {
+ return String.valueOf(getManagedObject().getStartupDateTime());
}
@ManagedAttribute("The uptime duration in d:HH:mm:ss.SSS")
public String getUpTime()
{
- Duration upTime = Duration.between(startup, Instant.now());
+ Duration upTime = Duration.ofMillis(getManagedObject().getUptimeMillis());
return "%d:%02d:%02d:%02d.%03d".formatted(
upTime.toDaysPart(),
upTime.toHoursPart(),
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Uptime.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Uptime.java
index 9ae07a199b96..1cbdb2a00c1b 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Uptime.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Uptime.java
@@ -18,7 +18,10 @@
/**
* Provide for a Uptime class that is compatible with Android, GAE, and the new Java 8 compact profiles
+ *
+ * @deprecated no replacement, will be removed, functionality moved to the {@code Server} class.
*/
+@Deprecated(forRemoval = true, since = "12.1.4")
public class Uptime
{
public static final int NOIMPL = -1;
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
index 2da77dfdb13a..615e32dae7b2 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java
@@ -19,7 +19,6 @@
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.TypeUtil;
-import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.thread.AutoLock;
@@ -244,7 +243,7 @@ private void setStarted()
{
_state = State.STARTED;
if (LOG.isDebugEnabled())
- LOG.debug("STARTED @{}ms {}", Uptime.getUptime(), this);
+ LOG.debug("STARTED {}", this);
for (EventListener listener : _eventListeners)
{
if (listener instanceof Listener)
diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/UptimeTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/UptimeTest.java
deleted file mode 100644
index 608dff4df662..000000000000
--- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/UptimeTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// ========================================================================
-// 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;
-
-import org.junit.jupiter.api.Test;
-
-public class UptimeTest
-{
- @Test
- public void testUptime()
- {
- // should not throw an exception (if it does, the exception flows out and fails the testcase)
- System.err.printf("Uptime = %,d%n", Uptime.getUptime());
- }
-}