Consistently return non-zero exit codes for jarmode failures
Update jar mode launchers to catch all exceptions and return a non-zero exit code. This refinement also allows us to consolidate the existing error reporting logic to a central locations. Modes that wish to report a simple error rather than a full stacktrace can throw the newly introduced `JarModeErrorException`. Fixes gh-43435
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,8 @@ public interface JarMode {
|
||||
* Run the jar in the given mode.
|
||||
* @param mode the mode to use
|
||||
* @param args any program arguments
|
||||
* @throws JarModeErrorException on an error that should print a simple error message
|
||||
*/
|
||||
void run(String mode, String[] args);
|
||||
void run(String mode, String[] args) throws JarModeErrorException;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.jarmode;
|
||||
|
||||
/**
|
||||
* Simple {@link RuntimeException} used to fail the jar mode with a simple printed error
|
||||
* message.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.3.7
|
||||
*/
|
||||
public class JarModeErrorException extends RuntimeException {
|
||||
|
||||
public JarModeErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JarModeErrorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.loader.launch;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.jarmode.JarMode;
|
||||
import org.springframework.boot.loader.jarmode.JarModeErrorException;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -31,11 +32,31 @@ final class JarModeRunner {
|
||||
|
||||
static final String DISABLE_SYSTEM_EXIT = JarModeRunner.class.getName() + ".DISABLE_SYSTEM_EXIT";
|
||||
|
||||
static final String SUPPRESSED_SYSTEM_EXIT_CODE = JarModeRunner.class.getName() + ".SUPPRESSED_SYSTEM_EXIT_CODE";
|
||||
|
||||
private JarModeRunner() {
|
||||
}
|
||||
|
||||
static void main(String[] args) {
|
||||
String mode = System.getProperty("jarmode");
|
||||
boolean disableSystemExit = Boolean.getBoolean(DISABLE_SYSTEM_EXIT);
|
||||
try {
|
||||
runJarMode(mode, args);
|
||||
if (disableSystemExit) {
|
||||
System.setProperty(SUPPRESSED_SYSTEM_EXIT_CODE, "0");
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
printError(ex);
|
||||
if (disableSystemExit) {
|
||||
System.setProperty(SUPPRESSED_SYSTEM_EXIT_CODE, "1");
|
||||
return;
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runJarMode(String mode, String[] args) {
|
||||
List<JarMode> candidates = SpringFactoriesLoader.loadFactories(JarMode.class,
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
for (JarMode candidate : candidates) {
|
||||
@@ -44,10 +65,17 @@ final class JarModeRunner {
|
||||
return;
|
||||
}
|
||||
}
|
||||
System.err.println("Unsupported jarmode '" + mode + "'");
|
||||
if (!Boolean.getBoolean(DISABLE_SYSTEM_EXIT)) {
|
||||
System.exit(1);
|
||||
throw new JarModeErrorException("Unsupported jarmode '" + mode + "'");
|
||||
}
|
||||
|
||||
private static void printError(Throwable ex) {
|
||||
if (ex instanceof JarModeErrorException) {
|
||||
String message = ex.getMessage();
|
||||
System.err.println("Error: " + message);
|
||||
System.err.println();
|
||||
return;
|
||||
}
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,12 @@ class TestJarMode implements JarMode {
|
||||
@Override
|
||||
public void run(String mode, String[] args) {
|
||||
System.out.println("running in " + mode + " jar mode " + Arrays.asList(args));
|
||||
if (args.length > 0 && "error".equals(args[0])) {
|
||||
throw new JarModeErrorException("error message");
|
||||
}
|
||||
if (args.length > 0 && "fail".equals(args[0])) {
|
||||
throw new IllegalStateException("bad");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ class LauncherTests {
|
||||
System.setProperty("jarmode", "test");
|
||||
new TestLauncher().launch(new String[] { "boot" });
|
||||
assertThat(out).contains("running in test jar mode [boot]");
|
||||
assertThat(System.getProperty(JarModeRunner.SUPPRESSED_SYSTEM_EXIT_CODE)).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,6 +71,25 @@ class LauncherTests {
|
||||
System.setProperty("jarmode", "idontexist");
|
||||
new TestLauncher().launch(new String[] { "boot" });
|
||||
assertThat(out).contains("Unsupported jarmode 'idontexist'");
|
||||
assertThat(System.getProperty(JarModeRunner.SUPPRESSED_SYSTEM_EXIT_CODE)).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void launchWhenJarModeRunFailsWithErrorExceptionPrintsSimpleMessage(CapturedOutput out) throws Exception {
|
||||
System.setProperty("jarmode", "test");
|
||||
new TestLauncher().launch(new String[] { "error" });
|
||||
assertThat(out).contains("running in test jar mode [error]");
|
||||
assertThat(out).contains("Error: error message");
|
||||
assertThat(System.getProperty(JarModeRunner.SUPPRESSED_SYSTEM_EXIT_CODE)).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void launchWhenJarModeRunFailsWithErrorExceptionPrintsStackTrace(CapturedOutput out) throws Exception {
|
||||
System.setProperty("jarmode", "test");
|
||||
new TestLauncher().launch(new String[] { "fail" });
|
||||
assertThat(out).contains("running in test jar mode [fail]");
|
||||
assertThat(out).contains("java.lang.IllegalStateException: bad");
|
||||
assertThat(System.getProperty(JarModeRunner.SUPPRESSED_SYSTEM_EXIT_CODE)).isEqualTo("1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user