Skip to content

Commit 6d5edc4

Browse files
committed
Merge pull request #30457 from dugenkui03
* gh-30457: Polish "Order ExitCodeGenerators and return first non-zero exit code" Order ExitCodeGenerators and return first non-zero exit code Closes gh-30457
2 parents 245e602 + 8727361 commit 6d5edc4

File tree

4 files changed

+46
-21
lines changed

4 files changed

+46
-21
lines changed

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/spring-application.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ include::code:MyApplication[]
354354
Also, the `ExitCodeGenerator` interface may be implemented by exceptions.
355355
When such an exception is encountered, Spring Boot returns the exit code provided by the implemented `getExitCode()` method.
356356

357+
If there is more than `ExitCodeGenerator`, the first non-zero exit code that is generated is used.
358+
To control the order in which the generators are called, additionally implement the `org.springframework.core.Ordered` interface or use the `org.springframework.core.annotation.Order` annotation.
359+
357360

358361

359362
[[features.spring-application.admin]]

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ExitCodeGenerators.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,14 +21,19 @@
2121
import java.util.Iterator;
2222
import java.util.List;
2323

24+
import org.springframework.core.Ordered;
25+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
26+
import org.springframework.core.annotation.Order;
2427
import org.springframework.util.Assert;
2528

2629
/**
27-
* Maintains a collection of {@link ExitCodeGenerator} instances and allows the final exit
28-
* code to be calculated.
30+
* Maintains an ordered collection of {@link ExitCodeGenerator} instances and allows the
31+
* final exit code to be calculated. Generators are ordered by {@link Order @Order} and
32+
* {@link Ordered}.
2933
*
3034
* @author Dave Syer
3135
* @author Phillip Webb
36+
* @author GenKui Du
3237
* @see #getExitCode()
3338
* @see ExitCodeGenerator
3439
*/
@@ -71,6 +76,7 @@ void addAll(Iterable<? extends ExitCodeGenerator> generators) {
7176
void add(ExitCodeGenerator generator) {
7277
Assert.notNull(generator, "Generator must not be null");
7378
this.generators.add(generator);
79+
AnnotationAwareOrderComparator.sort(this.generators);
7480
}
7581

7682
@Override
@@ -79,20 +85,22 @@ public Iterator<ExitCodeGenerator> iterator() {
7985
}
8086

8187
/**
82-
* Get the final exit code that should be returned based on all contained generators.
88+
* Get the final exit code that should be returned. The final exit code is the first
89+
* non-zero exit code that is {@link ExitCodeGenerator#getExitCode generated}.
8390
* @return the final exit code.
8491
*/
8592
int getExitCode() {
8693
int exitCode = 0;
8794
for (ExitCodeGenerator generator : this.generators) {
8895
try {
8996
int value = generator.getExitCode();
90-
if (value > 0 && value > exitCode || value < 0 && value < exitCode) {
97+
if (value != 0) {
9198
exitCode = value;
99+
break;
92100
}
93101
}
94102
catch (Exception ex) {
95-
exitCode = (exitCode != 0) ? exitCode : 1;
103+
exitCode = 1;
96104
ex.printStackTrace();
97105
}
98106
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@
6161
import org.springframework.context.support.AbstractApplicationContext;
6262
import org.springframework.context.support.GenericApplicationContext;
6363
import org.springframework.core.GenericTypeResolver;
64+
import org.springframework.core.Ordered;
6465
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
66+
import org.springframework.core.annotation.Order;
6567
import org.springframework.core.env.CommandLinePropertySource;
6668
import org.springframework.core.env.CompositePropertySource;
6769
import org.springframework.core.env.ConfigurableEnvironment;
@@ -1320,9 +1322,10 @@ public static void main(String[] args) throws Exception {
13201322
* Static helper that can be used to exit a {@link SpringApplication} and obtain a
13211323
* code indicating success (0) or otherwise. Does not throw exceptions but should
13221324
* print stack traces of any encountered. Applies the specified
1323-
* {@link ExitCodeGenerator} in addition to any Spring beans that implement
1324-
* {@link ExitCodeGenerator}. In the case of multiple exit codes the highest value
1325-
* will be used (or if all values are negative, the lowest value will be used)
1325+
* {@link ExitCodeGenerator ExitCodeGenerators} in addition to any Spring beans that
1326+
* implement {@link ExitCodeGenerator}. When multiple generators are available, the
1327+
* first non-zero exit code is used. Generators ordered based on their {@link Ordered}
1328+
* implementation and {@link Order @Order} annotation.
13261329
* @param context the context to close if possible
13271330
* @param exitCodeGenerators exit code generators
13281331
* @return the outcome (0 if successful)

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ExitCodeGeneratorsTests.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,10 +21,13 @@
2121

2222
import org.junit.jupiter.api.Test;
2323

24+
import org.springframework.core.Ordered;
25+
2426
import static org.assertj.core.api.Assertions.assertThat;
2527
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
2628
import static org.mockito.BDDMockito.given;
2729
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.withSettings;
2831

2932
/**
3033
* Tests for {@link ExitCodeGenerators}.
@@ -62,18 +65,9 @@ void getExitCodeWhenGeneratorThrowsShouldReturnOne() {
6265
}
6366

6467
@Test
65-
void getExitCodeWhenAllNegativeShouldReturnLowestValue() {
66-
ExitCodeGenerators generators = new ExitCodeGenerators();
67-
generators.add(mockGenerator(-1));
68-
generators.add(mockGenerator(-3));
69-
generators.add(mockGenerator(-2));
70-
assertThat(generators.getExitCode()).isEqualTo(-3);
71-
}
72-
73-
@Test
74-
void getExitCodeWhenAllPositiveShouldReturnHighestValue() {
68+
void getExitCodeWithUnorderedGeneratorsReturnsFirstNonZeroExitCode() {
7569
ExitCodeGenerators generators = new ExitCodeGenerators();
76-
generators.add(mockGenerator(1));
70+
generators.add(mockGenerator(0));
7771
generators.add(mockGenerator(3));
7872
generators.add(mockGenerator(2));
7973
assertThat(generators.getExitCode()).isEqualTo(3);
@@ -89,12 +83,29 @@ void getExitCodeWhenUsingExitCodeExceptionMapperShouldCallMapper() {
8983
assertThat(generators.getExitCode()).isEqualTo(2);
9084
}
9185

86+
@Test
87+
void getExitCodeWithOrderedGeneratorsReturnsFirstNonZeroExitCode() {
88+
ExitCodeGenerators generators = new ExitCodeGenerators();
89+
generators.add(orderedMockGenerator(0, 1));
90+
generators.add(orderedMockGenerator(1, 3));
91+
generators.add(orderedMockGenerator(2, 2));
92+
generators.add(mockGenerator(3));
93+
assertThat(generators.getExitCode()).isEqualTo(2);
94+
}
95+
9296
private ExitCodeGenerator mockGenerator(int exitCode) {
9397
ExitCodeGenerator generator = mock(ExitCodeGenerator.class);
9498
given(generator.getExitCode()).willReturn(exitCode);
9599
return generator;
96100
}
97101

102+
private ExitCodeGenerator orderedMockGenerator(int exitCode, int order) {
103+
ExitCodeGenerator generator = mock(ExitCodeGenerator.class, withSettings().extraInterfaces(Ordered.class));
104+
given(generator.getExitCode()).willReturn(exitCode);
105+
given(((Ordered) generator).getOrder()).willReturn(order);
106+
return generator;
107+
}
108+
98109
private ExitCodeExceptionMapper mockMapper(Class<?> exceptionType, int exitCode) {
99110
return (exception) -> {
100111
if (exceptionType.isInstance(exception)) {

0 commit comments

Comments
 (0)