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

[#391] Cope with Thread::stop being unavailable in JDK 20+ #393

Merged
merged 2 commits into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ jobs:
name: Verify
uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v3
with:
jdk-matrix: '[ "8", "17", "21" ]'
ff-maven: "3.9.5" # Maven version for fail-fast-build
maven-matrix: '[ "3.6.3", "3.8.8", "3.9.5" ]' # Maven versions matrix for verify builds
21 changes: 19 additions & 2 deletions src/main/java/org/codehaus/mojo/exec/ExecJavaMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
public class ExecJavaMojo
extends AbstractExecMojo
{
// Implementation note: Constants can be included in javadocs by {@value #MY_CONST}
private static final String THREAD_STOP_UNAVAILABLE =
"Thread.stop() is unavailable in this JRE version, cannot force-stop any threads";

@Component
private RepositorySystem repositorySystem;

Expand Down Expand Up @@ -171,6 +175,10 @@ public class ExecJavaMojo
* this to <code>true</code> if you are invoking problematic code that you can't fix. An example is
* {@link java.util.Timer} which doesn't respond to interruption. To have <code>Timer</code> fixed, vote for
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6336543">this bug</a>.
* <p>
* <b>Note:</b> In JDK 20+, the long deprecated {@link Thread#stop()} (since JDK 1.2) has been removed and will
* throw an {@link UnsupportedOperationException}. This will be handled gracefully, yielding a log warning
* {@value #THREAD_STOP_UNAVAILABLE} once and not trying to stop any further threads during the same execution.
*
* @since 1.1-beta-1
*/
Expand Down Expand Up @@ -522,6 +530,7 @@ private void terminateThreads( ThreadGroup threadGroup )
thread.interrupt();
}
// Now join with a timeout and call stop() (assuming flags are set right)
boolean threadStopIsAvailable = true;
for ( Thread thread : threads )
{
if ( !thread.isAlive() )
Expand All @@ -543,10 +552,18 @@ private void terminateThreads( ThreadGroup threadGroup )
continue;
}
uncooperativeThreads.add( thread ); // ensure we don't process again
if ( stopUnresponsiveDaemonThreads )
if ( stopUnresponsiveDaemonThreads && threadStopIsAvailable )
{
getLog().warn( "thread " + thread + " will be Thread.stop()'ed" );
thread.stop();
try
{
thread.stop();
}
catch ( UnsupportedOperationException unsupportedOperationException )
{
threadStopIsAvailable = false;
getLog().warn( THREAD_STOP_UNAVAILABLE );
}
}
else
{
Expand Down
26 changes: 23 additions & 3 deletions src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public class ExecJavaMojoTest

private static final File LOCAL_REPO = new File( "src/test/repository" );

private static final int JAVA_VERSION_MAJOR =
Integer.parseInt( System.getProperty( "java.version" ).replaceFirst( "[.].*", "" ) );

/*
* This one won't work yet public void xxtestSimpleRunPropertiesAndArguments() throws MojoExecutionException,
* Exception { File pom = new File( getBasedir(), "src/test/projects/project2/pom.xml" ); String output = execute(
Expand Down Expand Up @@ -198,19 +201,36 @@ public void testWaitNonInterruptibleDaemonThreads()
}

/**
* See <a href="http://jira.codehaus.org/browse/MEXEC-15">MEXEC-15</a>. FIXME: this sometimes fail with
* unit.framework.ComparisonFailure: expected:&lt;...&gt; but was:&lt;...3(f)&gt;
* See <a href="http://jira.codehaus.org/browse/MEXEC-15">MEXEC-15</a>,
* <a href="https://github.com/mojohaus/exec-maven-plugin/issues/391">GitHub-391</a>.
* <p>
* FIXME: This sometimes fails with {@code unit.framework.ComparisonFailure: expected:<...>; but was:<...3(f)>}.
*
* @throws Exception if any exception occurs
*/
public void testUncooperativeThread()
throws Exception
{
// FIXME:
// This will fail the test, because Assume is a JUnit 4 thing, but we are running in JUnit 3 mode, because
// AbstractMojoTestCase extends PlexusTestCase extends TestCase. The latter is a JUnit 3 compatibility class.
// If we would simply use JUnit 4 annotations, conditional ignores via Assume would just work correctly in
// Surefire and IDEs. We could than have two dedicated test cases, one for each JDK 20+ and one for older
// versions. In JUnit 5, we could even conveniently use @EnabledOnJre.
// Assume.assumeTrue( JAVA_VERSION_MAJOR < 20 );

File pom = new File( getBasedir(), "src/test/projects/project10/pom.xml" );
String output = execute( pom, "java" );
// note: execute() will wait a little bit before returning the output,
// thereby allowing the stop()'ed thread to output the final "(f)".
assertEquals( MainUncooperative.SUCCESS, output.trim() );
if ( JAVA_VERSION_MAJOR < 20 )
{
assertEquals( MainUncooperative.SUCCESS, output.trim() );
}
else
{
assertEquals( MainUncooperative.INTERRUPTED_BUT_NOT_STOPPED, output.trim() );
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/org/codehaus/mojo/exec/MainUncooperative.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ public class MainUncooperative
{
public static final String SUCCESS = "1(interrupted)(f)2(f)";

// In JDK 20+, Thread::stop has been removed and just throws an UnsupportedOperationException
public static final String INTERRUPTED_BUT_NOT_STOPPED = "1(interrupted)(f)2";

public static void main( String... args )
throws InterruptedException
{
Expand Down