Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 26, 2025

The exec:java goal exits prematurely when RMI, JMX, or similar frameworks create non-daemon threads outside the plugin's isolated thread group. The documentation stated daemon threads are "joined and interrupted once all known non daemon threads have quit" without clarifying that "known" means threads within the plugin's own thread group.

Changes

  • Added "Important limitation: Thread Group Isolation" subsection to usage documentation
  • Explains that the java goal only tracks threads in its isolated thread group, not system or framework-created thread groups
  • Documents specific affected scenarios: RMI registry creation, JMX remote connectors, framework-managed thread pools
  • Provides workaround: use exec:exec goal for programs requiring framework threads to keep the JVM alive

Example

When running an RMI server with exec:java:

Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
RemoteTest stub = (RemoteTest) UnicastRemoteObject.exportObject(engine, 0);
registry.rebind("RemoteTestJ2", stub);
// Plugin exits here despite RMI's non-daemon threads still running

The RMI registry creates non-daemon threads in the system thread group, which are not tracked by exec:java. The plugin exits when its own thread group completes, terminating the server prematurely. Users should use exec:exec instead for such scenarios.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • www.apache.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/exec-maven-plugin/exec-maven-plugin org.codehaus.plexus.classworlds.launcher.Launcher site -DskipTests (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Maven exec plugin java goal with RMI exits early</issue_title>
<issue_description>The org.codehaus.mojo:exec-maven-plugin:3.3.0 plugin “java” goal documentation states that “daemon threads are joined and interrupted once all known non daemon threads have quit”. That is not what I observe with some code involving an RMI thread (except that I do not know what is an “unknown” thread). It looks like either a bug or a problem in the documentation.

Details

My “Server” program starts a non-daemon thread (through RMI) and when I run it using the “java” goal, the JVM exits when reaching the end of the main method. I’d expect that it keeps running due to the non daemon thread still running. When I run it using direct invocation of java, as expected, the program keeps running even after main finishes.

Code

Here is the Server.

public class Server {
  public static void main(String[] args) throws Exception {
    Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
    RemoteTest engine = new RemoteTestImpl();
    RemoteTest stub = (RemoteTest) UnicastRemoteObject.exportObject(engine, 0);
    registry.rebind("RemoteTestJ2", stub);
    Thread.sleep(3000);
    Thread.getAllStackTraces().keySet().forEach(thread -> {
      System.out.println("Thread: " + thread.getName() + " is daemon: " + thread.isDaemon());
    });
  }
}

Here is the Client.

public class User {
  public static void main(String[] args) throws Exception {
    Registry registry = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
    RemoteTest rem = (RemoteTest) registry.lookup("RemoteTestJ2");
    while(true) {
      System.out.println("Tested: " + rem.test(0, 1));
    }
  }
}

The full code is here.

Reproduce the problem

  • git clone https://github.com/oliviercailloux/JGradLib-Additioner.git ; cd JGradLib-Additioner ; git checkout rmistop
  • Start Server: mvn -Dexec.mainClass=io.github.oliviercailloux.javagrade.graders.Server compile org.codehaus.mojo:exec-maven-plugin:3.3.0:java.
  • Start Client (in parallel, just after having started Server): java -cp "target/classes/" io.github.oliviercailloux.javagrade.graders.User.

Expected: The RMI mechanism starts a non-daemon thread in the background and prevents exit of the JVM, so that the Server stays alive indefinitely and the Client prints forever the result of the test call.

Actual: the Server starts, RMI starts a non-daemon thread as expected (as well as daemon threads), the Server sleeps for three seconds, prints its threads, and exits. The Client, meanwhile, starts flooding the terminal with results of the test calls, but after three seconds, crashes with a stack trace complaining that it cannot reach the remote end any more.

Variants with no problem

Both the exec plugin and the RMI aspect seem required to trigger this odd behavior.

If running the server with java -cp "target/classes/" io.github.oliviercailloux.javagrade.graders.Server instead of the exec plugin, the server keeps running as expected. Same if I use exec:java (with appropriate options).

Alternatively, if I change the server code to simply start a new Thread that is kept busy (sleeps in a while loop), the server keeps running as expected.

Also posted on SO.</issue_description>

Comments on the Issue (you are @copilot in this section)

@slawekjaranowski There is a response on SO ... should we fix some of documentation ...?

PR are welcome</comment_new>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Add detailed documentation explaining that the java goal only tracks
threads in its isolated thread group, which causes it to exit early
when frameworks like RMI create non-daemon threads in other thread groups.

This clarifies the behavior reported in the issue where RMI servers
exit prematurely when using exec:java, and provides clear guidance
to use exec:exec as a workaround.

Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix early exit issue with Maven exec plugin java goal Document thread group isolation limitation in java goal Oct 26, 2025
Copilot AI requested a review from slachiewicz October 26, 2025 09:29
@slachiewicz slachiewicz marked this pull request as ready for review October 26, 2025 11:40
@slachiewicz slachiewicz merged commit 03b87b5 into master Nov 1, 2025
32 checks passed
@slachiewicz slachiewicz deleted the copilot/fix-maven-exec-plugin-early-exit branch November 1, 2025 08:11
@slawekjaranowski slawekjaranowski added this to the 3.6.3 milestone Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maven exec plugin java goal with RMI exits early

2 participants