Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #10271 - Introduce jetty-pid.xml #10272

Merged
merged 23 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
adca81d
Issue #10271 - new start.jar --pid-file=<name> arg
joakime Aug 8, 2023
6133dd0
Fixing license header
joakime Aug 8, 2023
a0dbc2a
Changes from review
joakime Aug 8, 2023
8e7e10f
Changes from review
joakime Aug 8, 2023
22b527c
Merge remote-tracking branch 'origin/jetty-10.0.x' into fix/10.0.x/st…
joakime Aug 8, 2023
778bbb9
Changes from review
joakime Aug 8, 2023
4c91aed
More cleanup
joakime Aug 9, 2023
3b8f159
Ensure pidfile is truncated on write (so only 1 pid is present)
joakime Aug 9, 2023
394db41
Javadoc
joakime Aug 9, 2023
3712349
Changes from review
joakime Aug 14, 2023
029a537
Merge remote-tracking branch 'origin/jetty-10.0.x' into fix/10.0.x/st…
joakime Aug 14, 2023
443c9c9
Use ShutdownThread, not ShutdownMonitor.
joakime Aug 15, 2023
0323557
Commenting out the property in the ini-template
joakime Aug 21, 2023
b3368ae
Merge remote-tracking branch 'origin/jetty-10.0.x' into fix/10.0.x/st…
joakime Aug 21, 2023
565f55a
Fixing license header
joakime Aug 21, 2023
724dbb9
Changes from review
joakime Aug 22, 2023
f4258ec
Add DEBUG logging to show actual filename used in PidFile and StateLi…
joakime Aug 22, 2023
a8f9f6d
Deregister PidFile on stop
joakime Aug 22, 2023
453fa9a
Simplify PidFile and add distro tests for pid module
joakime Aug 23, 2023
e851ca7
Better PidFile management
joakime Aug 23, 2023
d88d617
state.mod depends on server as it configures the server.
joakime Aug 24, 2023
33f6418
Updates from review
joakime Aug 24, 2023
804c8e0
Merge remote-tracking branch 'origin/jetty-10.0.x' into fix/10.0.x/st…
joakime Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions jetty-home/src/main/resources/bin/jetty.sh
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";;
esac


JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE")
JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE" "jetty.pid=$JETTY_PID")

##################################################
# Get the list of config.xml files from jetty.conf
Expand Down Expand Up @@ -457,6 +457,7 @@ case "$ACTION" in
exit
fi

# Startup from a service file
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
then
unset CH_USER
Expand All @@ -482,6 +483,7 @@ case "$ACTION" in
exit 1
fi

# Startup if switching users (not as a service, or from root)
if [ -n "$JETTY_USER" ] && [ `whoami` != "$JETTY_USER" ]
then
unset SU_SHELL
Expand All @@ -492,16 +494,14 @@ case "$ACTION" in

touch "$JETTY_PID"
chown "$JETTY_USER" "$JETTY_PID"
# FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc.
su - "$JETTY_USER" $SU_SHELL -c "
cd \"$JETTY_BASE\"
echo ${RUN_ARGS[*]} start-log-file=\"$JETTY_START_LOG\" | xargs ${JAVA} > /dev/null &
disown \$!
echo \$! > \"$JETTY_PID\""
disown $(pgrep -P $!)"
else
# Startup if not switching users
echo ${RUN_ARGS[*]} | xargs ${JAVA} > /dev/null &
disown $!
echo $! > "$JETTY_PID"
disown $(pgrep -P $!)
fi

fi
Expand All @@ -523,6 +523,7 @@ case "$ACTION" in

stop)
echo -n "Stopping Jetty: "
# Stop from a service file
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then
start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP

Expand All @@ -535,6 +536,7 @@ case "$ACTION" in
sleep 1
done
else
# Stop from a non-service path
if [ ! -f "$JETTY_PID" ] ; then
echo "ERROR: no pid found at $JETTY_PID"
exit 1
Expand Down
15 changes: 0 additions & 15 deletions jetty-home/src/main/resources/etc/jetty-started.xml

This file was deleted.

2 changes: 1 addition & 1 deletion jetty-home/src/main/resources/etc/jetty.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
# Each line in this file becomes an argument to start.jar
# in addition to those found in the start.ini file
# =======================================================
jetty-started.xml
--module=pid,state
12 changes: 12 additions & 0 deletions jetty-server/src/main/config/etc/jetty-state.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">

<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.server.StateLifeCycleListener">
<Arg><Property name="jetty.state" default="jetty.state"/></Arg>
</New>
</Arg>
</Call>
</Configure>
17 changes: 17 additions & 0 deletions jetty-server/src/main/config/modules/state.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/

[description]
Creates and updates state file used by jetty.sh

[tags]
start

[depends]
server

[xml]
etc/jetty-state.xml

[ini-template]
## State file path
# jetty.state=${jetty.base}/jetty.state
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// ========================================================================
// 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;

import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.eclipse.jetty.util.component.LifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

/**
* A LifeCycle Listener that writes state changes to a file.
* <p>This can be used with the jetty.sh script to wait for successful startup.
*/
public class StateLifeCycleListener implements LifeCycle.Listener
{
private static final Logger LOG = LoggerFactory.getLogger(StateLifeCycleListener.class);

private final Path _filename;

public StateLifeCycleListener(String filename)
{
_filename = Paths.get(filename).toAbsolutePath();

if (LOG.isDebugEnabled())
LOG.debug("State File: {}", _filename);
}

private void writeState(String action, LifeCycle lifecycle)
{
try (Writer out = Files.newBufferedWriter(_filename, UTF_8, WRITE, CREATE, APPEND))
{
out.append(action).append(" ").append(lifecycle.toString()).append("\n");
}
catch (Exception e)
{
LOG.warn("Unable to write state", e);
}
}

@Override
public void lifeCycleStarting(LifeCycle event)
{
writeState("STARTING", event);
}

@Override
public void lifeCycleStarted(LifeCycle event)
{
writeState("STARTED", event);
}

@Override
public void lifeCycleFailure(LifeCycle event, Throwable cause)
{
writeState("FAILED", event);
}

@Override
public void lifeCycleStopping(LifeCycle event)
{
writeState("STOPPING", event);
}

@Override
public void lifeCycleStopped(LifeCycle event)
{
writeState("STOPPED", event);
}
}
8 changes: 8 additions & 0 deletions jetty-util/src/main/config/etc/jetty-pid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">

<Configure>
<Call class="org.eclipse.jetty.util.PidFile" name="create">
<Arg name="file"><Property name="jetty.pid" default="jetty.pid"/></Arg>
</Call>
</Configure>
19 changes: 19 additions & 0 deletions jetty-util/src/main/config/modules/pid.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/

[description]
Creates the PID file for the Jetty process

[tags]
start

[before]
server
threadpool
jvm

[xml]
etc/jetty-pid.xml

[ini-template]
## PID file path
# jetty.pid=${jetty.base}/jetty.pid
83 changes: 83 additions & 0 deletions jetty-util/src/main/java/org/eclipse/jetty/util/PidFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// ========================================================================
// 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 java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jetty.util.annotation.Name;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;

/**
* Create a PID file for the running process.
*
* <p>
* Will register itself in a {@link Runtime#addShutdownHook(Thread)}
* to cleanup the PID file it created during normal JVM shutdown.
* </p>
*/
public class PidFile extends Thread
{
private static final Logger LOG = LoggerFactory.getLogger(PidFile.class);
private static final Set<Path> activeFiles = ConcurrentHashMap.newKeySet();

public static void create(@Name("file") String filename) throws IOException
{
Path pidFile = Paths.get(filename).toAbsolutePath();

if (activeFiles.add(pidFile))
{
Runtime.getRuntime().addShutdownHook(new PidFile(pidFile));

if (Files.exists(pidFile))
LOG.info("Overwriting existing PID file: {}", pidFile);

// Create the PID file as soon as possible.
long pid = ProcessHandle.current().pid();
Files.writeString(pidFile, Long.toString(pid), UTF_8, CREATE, WRITE, TRUNCATE_EXISTING);
if (LOG.isDebugEnabled())
LOG.debug("PID file: {}", pidFile);
}
}

private final Path pidFile;

private PidFile(Path pidFile)
{
this.pidFile = pidFile;
}

@Override
public void run()
{
try
{
Files.deleteIfExists(pidFile);
}
catch (Throwable t)
{
LOG.info("Unable to remove PID file: {}", pidFile, t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
/**
* A LifeCycle Listener that writes state changes to a file.
* <p>This can be used with the jetty.sh script to wait for successful startup.
* @deprecated use {@code org.eclipse.jetty.server.StateLifeCycleListener} instead
*/
@Deprecated
public class FileNoticeLifeCycleListener implements LifeCycle.Listener
{
private static final Logger LOG = LoggerFactory.getLogger(FileNoticeLifeCycleListener.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -107,6 +109,45 @@ public void testStartStop() throws Exception
}
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testPidFile(boolean includeJettyPidConfig) throws Exception
{
String jettyVersion = System.getProperty("jettyVersion");
JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();

try (JettyHomeTester.Run run1 = distribution.start("--add-modules=http,pid"))
{
assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());

Path pidfile = run1.getConfig().getJettyBase().resolve("jetty.pid");

int port = distribution.freePort();

String[] args = new String[includeJettyPidConfig ? 2 : 1];
args[0] = "jetty.http.port=" + port;
if (includeJettyPidConfig)
args[1] = "jetty.pid=" + pidfile;

try (JettyHomeTester.Run run2 = distribution.start(args))
{
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));

assertTrue(Files.isRegularFile(pidfile), "PID file should exist");

startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port);
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
}

await().atMost(Duration.ofSeconds(10)).until(() -> !Files.exists(pidfile));
}
}

@Test
public void testQuickStartGenerationAndRun() throws Exception
{
Expand Down