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}: * */ @@ -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()); - } -}