From 0717de723f8767b407c58056f681788450a2728c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 23 Mar 2020 20:03:44 -0700 Subject: [PATCH] Polish --- .../cache/CachingOperationInvoker.java | 21 +---- .../src/docs/asciidoc/deployment.adoc | 5 ++ .../asciidoc/production-ready-features.adoc | 7 +- .../docs/asciidoc/spring-boot-features.adoc | 5 ++ .../platform/docker/HttpClientHttp.java | 2 - .../src/docs/asciidoc/packaging.adoc | 2 + .../boot/gradle/dsl/SpringBootExtension.java | 20 +++-- .../boot/gradle/tasks/bundling/BootJar.java | 2 +- .../tasks/bundling/LayerConfiguration.java | 12 +-- .../boot/loader/tools/Library.java | 35 +++++---- .../boot/loader/tools/LibraryCoordinates.java | 33 ++++++-- .../tools/layer/library/CoordinateFilter.java | 78 ++++++++----------- .../loader/tools/LibraryCoordinatesTests.java | 6 ++ .../boot/maven/ArtifactsLibraries.java | 2 +- .../boot/maven/CustomLayersProvider.java | 78 ++++++++++--------- .../boot/maven/RepackageMojo.java | 10 ++- .../boot/maven/CustomLayersProviderTests.java | 6 +- .../ApplicationAvailabilityProvider.java | 20 +++++ .../boot/availability/LivenessState.java | 10 +-- .../boot/availability/ReadinessState.java | 10 +-- .../embedded/jetty/JettyGracefulShutdown.java | 22 +++--- .../web/embedded/jetty/JettyWebServer.java | 23 +++--- .../embedded/netty/NettyGracefulShutdown.java | 16 ++-- .../web/embedded/netty/NettyWebServer.java | 3 +- .../web/embedded/tomcat/TomcatWebServer.java | 3 +- .../UndertowReactiveWebServerFactory.java | 5 +- .../undertow/UndertowServletWebServer.java | 3 +- .../embedded/undertow/UndertowWebServer.java | 3 +- .../boot/web/server/GracefulShutdown.java | 5 ++ .../web/server/ImmediateGracefulShutdown.java | 3 +- 30 files changed, 252 insertions(+), 198 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java index c63a7149762..0b3d4a52e86 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java @@ -182,25 +182,12 @@ public class CachingOperationInvoker implements OperationInvoker { if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (obj == null || getClass() != obj.getClass()) { return false; } CacheKey other = (CacheKey) obj; - if (this.apiVersion != other.apiVersion) { - return false; - } - if (this.principal == null) { - if (other.principal != null) { - return false; - } - } - else if (!this.principal.equals(other.principal)) { - return false; - } - return true; + return this.apiVersion.equals(other.apiVersion) + && ObjectUtils.nullSafeEquals(this.principal, other.principal); } @Override @@ -208,7 +195,7 @@ public class CachingOperationInvoker implements OperationInvoker { final int prime = 31; int result = 1; result = prime * result + this.apiVersion.hashCode(); - result = prime * result + ((this.principal == null) ? 0 : this.principal.hashCode()); + result = prime * result + ObjectUtils.nullSafeHashCode(this.principal); return result; } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/deployment.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/deployment.adoc index 5aaf91e1520..ddb207c3da8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/deployment.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/deployment.adoc @@ -181,6 +181,8 @@ See the {spring-boot-module-api}/cloud/CloudFoundryVcapEnvironmentPostProcessor. TIP: The https://github.com/pivotal-cf/java-cfenv/[Java CFEnv] project is a better fit for tasks such as configuring a DataSource. + + [[cloud-deployment-kubernetes]] === Kubernetes Spring Boot auto-detects Kubernetes deployment environments by checking the environment for `"*_SERVICE_HOST"` and `"*_SERVICE_PORT"` variables. @@ -188,6 +190,8 @@ You can override this detection with the configprop:management.health.probes.ena Spring Boot helps you to <> and export it with <>. + + [[cloud-deployment-kubernetes-container-lifecycle]] ==== Kubernetes Container Lifecycle When Kubernetes deletes an application instance, the shutdown process involves several subsystems concurrently: shutdown hooks, unregistering the service, removing the instance from the load-balancer... @@ -207,6 +211,7 @@ lifecycle: Once the pre-stop hook has completed, SIGTERM will be sent to the container and <> will begin, allowing any remaining in-flight requests to complete. + [[cloud-deployment-heroku]] === Heroku Heroku is another popular PaaS platform. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc index 145bfa87ef2..64cf3aa1d4a 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc @@ -870,6 +870,7 @@ It's also possible to override the `show-details` and `roles` properties if requ TIP: You can use `@Qualifier("groupname")` if you need to register custom `StatusAggregator` or `HttpCodeStatusMapper` beans for use with the group. + [[production-ready-kubernetes-probes]] === Kubernetes Probes Applications deployed on Kubernetes can provide information about their internal state with https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes[Container Probes]. @@ -905,6 +906,8 @@ The `"startupProbe"` is not necessarily needed here as the `"readinessProbe"` fa WARNING: If your Actuator endpoints are deployed on a separate management context, be aware that endpoints are then not using the same web infrastructure (port, connection pools, framework components) as the main application. In this case, a Probe check could be successful even if the main application does not work properly (for example, it cannot accept new connections). + + [[production-ready-kubernetes-probes-external-state]] ==== Checking external state with Kubernetes Probes Actuator configures the "liveness" and "readiness" Probes as Health Groups; this means that all the <> are available for them. @@ -927,6 +930,7 @@ Some external systems might not be shared by application instances or not essent Also, Kubernetes will react differently to applications being taken out of the load-balancer, depending on its https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/[autoscaling configuration]. + [[production-ready-kubernetes-probes-lifecycle]] ==== Application lifecycle and Probes states An important aspect of the Kubernetes Probes support is its consistency with the application lifecycle. @@ -952,7 +956,6 @@ When a Spring Boot application starts: |live |ready |Startup tasks are finished. The application is receiving traffic. - |=== When a Spring Boot application shuts down: @@ -975,12 +978,12 @@ When a Spring Boot application shuts down: |broken |unready |The application context is closed and the application cannot serve traffic. - |=== TIP: Check out the <> for more information about Kubernetes deployment. + [[production-ready-application-info]] === Application Information Application information exposes various information collected from all {spring-boot-actuator-module-code}/info/InfoContributor.java[`InfoContributor`] beans defined in your `ApplicationContext`. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index fce5de63654..3fdb3f0db18 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -191,12 +191,15 @@ NOTE: There are some restrictions when creating an `ApplicationContext` hierarch For example, Web components *must* be contained within the child context, and the same `Environment` is used for both parent and child contexts. See the {spring-boot-module-api}/builder/SpringApplicationBuilder.html[`SpringApplicationBuilder` Javadoc] for full details. + + [[boot-features-application-availability-state]] === Application Availability State When deployed on plaftorms, applications can provide information about their availability to the platform using infrastructure like https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/[Kubernetes Probes]. Spring Boot manages this application state with the `ApplicationAvailabilityProvider` and makes it available to application components and the platform itself. + [[boot-features-application-availability-liveness]] ==== Liveness State The "Liveness" state of an application tells whether its internal state allows it to work correctly, or recover by itself if it's currently failing. @@ -209,6 +212,8 @@ The internal state of Spring Boot applications is mostly represented by the Spri If the application context has started successfully, Spring Boot assumes that the application is in a valid state. An application is considered live as soon as the context has been refreshed, see <>. + + [[boot-features-application-availability-readiness]] ==== Readiness State The "Readiness" state of an application tells whether the application is ready to handle traffic. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/HttpClientHttp.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/HttpClientHttp.java index 6572c9322aa..3942f608058 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/HttpClientHttp.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/HttpClientHttp.java @@ -150,8 +150,6 @@ class HttpClientHttp implements Http { /** * {@link HttpEntity} to send {@link Content} content. - * - * @author Phillip Webb */ private static class WritableHttpEntity extends AbstractHttpEntity { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc index 7d0b59c0867..52ec3c63363 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc @@ -306,6 +306,8 @@ include::../gradle/packaging/boot-jar-layered-exclude-tools.gradle[tags=layered] include::../gradle/packaging/boot-jar-layered-exclude-tools.gradle.kts[tags=layered] ---- + + [[packaging-layers-configuration]] ===== Custom Layers configuration Depending on your application, you may want to tune how layers are created and add new ones. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java index b1ce0d4fea4..4fd036bf2c1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java @@ -24,6 +24,7 @@ import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; import org.gradle.jvm.tasks.Jar; @@ -91,15 +92,11 @@ public class SpringBootExtension { * @param configurer the task configurer */ public void buildInfo(Action configurer) { - TaskProvider bootBuildInfo = this.project.getTasks().register("bootBuildInfo", BuildInfo.class, - (task) -> { - task.setGroup(BasePlugin.BUILD_GROUP); - task.setDescription("Generates a META-INF/build-info.properties file."); - task.getConventionMapping().map("destinationDir", - () -> new File(determineMainSourceSetResourcesOutputDir(), "META-INF")); - }); + TaskContainer tasks = this.project.getTasks(); + TaskProvider bootBuildInfo = tasks.register("bootBuildInfo", BuildInfo.class, + this::configureBuildInfoTask); this.project.getPlugins().withType(JavaPlugin.class, (plugin) -> { - this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME).dependsOn(bootBuildInfo.get()); + tasks.getByName(JavaPlugin.CLASSES_TASK_NAME).dependsOn(bootBuildInfo.get()); this.project.afterEvaluate((evaluated) -> { BuildInfoProperties properties = bootBuildInfo.get().getProperties(); if (properties.getArtifact() == null) { @@ -112,6 +109,13 @@ public class SpringBootExtension { } } + private void configureBuildInfoTask(BuildInfo task) { + task.setGroup(BasePlugin.BUILD_GROUP); + task.setDescription("Generates a META-INF/build-info.properties file."); + task.getConventionMapping().map("destinationDir", + () -> new File(determineMainSourceSetResourcesOutputDir(), "META-INF")); + } + private File determineMainSourceSetResourcesOutputDir() { return this.project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets() .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput().getResourcesDir(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index 54281aa84cd..87e9489cc89 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -245,7 +245,7 @@ public class BootJar extends Jar implements BootArchive { String coordinates = this.coordinatesByFileName.get(details.getName()); LibraryCoordinates libraryCoordinates = (coordinates != null) ? new LibraryCoordinates(coordinates) : new LibraryCoordinates("?:?:?"); - return this.layers.getLayer(new Library(null, details.getFile(), null, false, libraryCoordinates)); + return this.layers.getLayer(new Library(null, details.getFile(), null, libraryCoordinates, false)); } if (path.startsWith("BOOT-INF/classes/")) { return this.layers.getLayer(details.getSourcePath()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayerConfiguration.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayerConfiguration.java index 8afcfa8fbd3..3c49c3c85a0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayerConfiguration.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayerConfiguration.java @@ -153,12 +153,6 @@ public class LayerConfiguration { private static final class StrategySpec { - private enum TYPE { - - LIBRARIES, RESOURCES; - - } - private final TYPE type; private List libraryFilters; @@ -232,6 +226,12 @@ public class LayerConfiguration { return new StrategySpec(TYPE.RESOURCES); } + private enum TYPE { + + LIBRARIES, RESOURCES; + + } + } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Library.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Library.java index 3c31f601bfe..e8398fd9cf3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Library.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Library.java @@ -37,10 +37,10 @@ public class Library { private final LibraryScope scope; - private final boolean unpackRequired; - private final LibraryCoordinates coordinates; + private final boolean unpackRequired; + /** * Create a new {@link Library}. * @param file the source file @@ -69,15 +69,24 @@ public class Library { * @param unpackRequired if the library needs to be unpacked before it can be used */ public Library(String name, File file, LibraryScope scope, boolean unpackRequired) { - this(name, file, scope, unpackRequired, null); + this(name, file, scope, null, unpackRequired); } - public Library(String name, File file, LibraryScope scope, boolean unpackRequired, LibraryCoordinates coordinates) { + /** + * Create a new {@link Library}. + * @param name the name of the library as it should be written or {@code null} to use + * the file name + * @param file the source file + * @param scope the scope of the library + * @param coordinates the library coordinates or {@code null} + * @param unpackRequired if the library needs to be unpacked before it can be used + */ + public Library(String name, File file, LibraryScope scope, LibraryCoordinates coordinates, boolean unpackRequired) { this.name = (name != null) ? name : file.getName(); this.file = file; this.scope = scope; - this.unpackRequired = unpackRequired; this.coordinates = coordinates; + this.unpackRequired = unpackRequired; } /** @@ -113,6 +122,14 @@ public class Library { return this.scope; } + /** + * Return the {@linkplain LibraryCoordinates coordinates} of the library. + * @return the coordinates + */ + public LibraryCoordinates getCoordinates() { + return this.coordinates; + } + /** * Return if the file cannot be used directly as a nested jar and needs to be * unpacked. @@ -122,14 +139,6 @@ public class Library { return this.unpackRequired; } - /** - * Return the {@linkplain LibraryCoordinates coordinates} of the library. - * @return the coordinates - */ - public LibraryCoordinates getCoordinates() { - return this.coordinates; - } - long getLastModified() { return this.file.lastModified(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LibraryCoordinates.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LibraryCoordinates.java index 8167d1f2b92..761703e706a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LibraryCoordinates.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LibraryCoordinates.java @@ -54,24 +54,45 @@ public final class LibraryCoordinates { Assert.isTrue(elements.length >= 2, "Coordinates must contain at least 'groupId:artifactId'"); this.groupId = elements[0]; this.artifactId = elements[1]; - if (elements.length > 2) { - this.version = elements[2]; - } - else { - this.version = null; - } + this.version = (elements.length > 2) ? elements[2] : null; } + /** + * Return the group ID of the coordinates. + * @return the group ID + */ public String getGroupId() { return this.groupId; } + /** + * Return the artifact ID of the coordinates. + * @return the artifact ID + */ public String getArtifactId() { return this.artifactId; } + /** + * Return the version of the coordinates. + * @return the version + */ public String getVersion() { return this.version; } + /** + * Return the coordinates in the form {@code groupId:artifactId:version}. + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append((this.groupId != null) ? this.groupId : ""); + builder.append(":"); + builder.append((this.artifactId != null) ? this.artifactId : ""); + builder.append(":"); + builder.append((this.version != null) ? this.version : ""); + return builder.toString(); + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layer/library/CoordinateFilter.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layer/library/CoordinateFilter.java index 0e1f7337fd5..06137e815e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layer/library/CoordinateFilter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/layer/library/CoordinateFilter.java @@ -16,9 +16,9 @@ package org.springframework.boot.loader.tools.layer.library; -import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.springframework.boot.loader.tools.Library; import org.springframework.boot.loader.tools.LibraryCoordinates; @@ -32,58 +32,21 @@ import org.springframework.boot.loader.tools.LibraryCoordinates; */ public class CoordinateFilter implements LibraryFilter { - private final List includes = new ArrayList<>(); + private static final String EMPTY_COORDINATES = "::"; - private final List excludes = new ArrayList<>(); + private final List includes; + + private final List excludes; public CoordinateFilter(List includes, List excludes) { - this.includes.addAll(includes); - this.excludes.addAll(excludes); + this.includes = includes.stream().map(this::asPattern).collect(Collectors.toList()); + this.excludes = excludes.stream().map(this::asPattern).collect(Collectors.toList()); } - @Override - public boolean isLibraryIncluded(Library library) { - return isMatch(library, this.includes); - } - - @Override - public boolean isLibraryExcluded(Library library) { - return isMatch(library, this.excludes); - } - - private boolean isMatch(Library library, List toMatch) { + private Pattern asPattern(String string) { StringBuilder builder = new StringBuilder(); - LibraryCoordinates coordinates = library.getCoordinates(); - if (coordinates != null) { - if (coordinates.getGroupId() != null) { - builder.append(coordinates.getGroupId()); - } - builder.append(":"); - if (coordinates.getArtifactId() != null) { - builder.append(coordinates.getArtifactId()); - } - builder.append(":"); - if (coordinates.getVersion() != null) { - builder.append(coordinates.getVersion()); - } - } - else { - builder.append("::"); - } - String input = builder.toString(); - for (String patternString : toMatch) { - Pattern pattern = buildPatternForString(patternString); - if (pattern.matcher(input).matches()) { - return true; - } - } - return false; - } - - private Pattern buildPatternForString(String pattern) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < pattern.length(); i++) { - char c = pattern.charAt(i); + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); if (c == '.') { builder.append("\\."); } @@ -97,4 +60,25 @@ public class CoordinateFilter implements LibraryFilter { return Pattern.compile(builder.toString()); } + @Override + public boolean isLibraryIncluded(Library library) { + return isMatch(library, this.includes); + } + + @Override + public boolean isLibraryExcluded(Library library) { + return isMatch(library, this.excludes); + } + + private boolean isMatch(Library library, List patterns) { + LibraryCoordinates coordinates = library.getCoordinates(); + String input = (coordinates != null) ? coordinates.toString() : EMPTY_COORDINATES; + for (Pattern pattern : patterns) { + if (pattern.matcher(input).matches()) { + return true; + } + } + return false; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LibraryCoordinatesTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LibraryCoordinatesTests.java index b110b5c3f4a..1971662ed25 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LibraryCoordinatesTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LibraryCoordinatesTests.java @@ -65,4 +65,10 @@ class LibraryCoordinatesTests { assertThatIllegalArgumentException().isThrownBy(() -> new LibraryCoordinates("com.acme")); } + @Test + void toStringReturnsString() { + assertThat(new LibraryCoordinates("com.acme:my-library:1.0.0")).hasToString("com.acme:my-library:1.0.0"); + assertThat(new LibraryCoordinates("com.acme:my-library")).hasToString("com.acme:my-library:"); + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ArtifactsLibraries.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ArtifactsLibraries.java index 8dced3b17e7..444aedb350a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ArtifactsLibraries.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ArtifactsLibraries.java @@ -82,7 +82,7 @@ public class ArtifactsLibraries implements Libraries { } LibraryCoordinates coordinates = new LibraryCoordinates(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); - callback.library(new Library(name, artifact.getFile(), scope, isUnpackRequired(artifact), coordinates)); + callback.library(new Library(name, artifact.getFile(), scope, coordinates, isUnpackRequired(artifact))); } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CustomLayersProvider.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CustomLayersProvider.java index 40e4ba55f15..4832e9eb6b9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CustomLayersProvider.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/CustomLayersProvider.java @@ -35,6 +35,7 @@ import org.springframework.boot.loader.tools.layer.library.CoordinateFilter; import org.springframework.boot.loader.tools.layer.library.FilteredLibraryStrategy; import org.springframework.boot.loader.tools.layer.library.LibraryFilter; import org.springframework.boot.loader.tools.layer.library.LibraryStrategy; +import org.springframework.util.Assert; /** * Produces a {@link CustomLayers} based on the given {@link Document}. @@ -46,12 +47,12 @@ public class CustomLayersProvider { public CustomLayers getLayers(Document document) { Element root = document.getDocumentElement(); - NodeList nl = root.getChildNodes(); + NodeList nodes = root.getChildNodes(); List layers = new ArrayList<>(); List libraryStrategies = new ArrayList<>(); List resourceStrategies = new ArrayList<>(); - for (int i = 0; i < nl.getLength(); i++) { - Node node = nl.item(i); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); if (node instanceof Element) { processNode(layers, libraryStrategies, resourceStrategies, (Element) node); } @@ -80,14 +81,13 @@ public class CustomLayersProvider { private List getLayers(Element element) { List layers = new ArrayList<>(); - NodeList nodes = element.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - if (node instanceof Element) { - Element ele = (Element) node; - String nodeName = ele.getNodeName(); - if ("layer".equals(nodeName)) { - layers.add(new Layer(ele.getTextContent())); + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node childNode = childNodes.item(i); + if (childNode instanceof Element) { + Element childElement = (Element) childNode; + if ("layer".equals(childElement.getNodeName())) { + layers.add(new Layer(childElement.getTextContent())); } } } @@ -98,26 +98,11 @@ public class CustomLayersProvider { FilterFactory filterFactory, Predicate filterPredicate) { List contents = new ArrayList<>(); for (int i = 0; i < nodes.getLength(); i++) { - Node item = nodes.item(i); - if (item instanceof Element) { - Element element = (Element) item; + Node node = nodes.item(i); + if (node instanceof Element) { + Element element = (Element) node; if ("layer-content".equals(element.getTagName())) { - NodeList filterList = item.getChildNodes(); - if (filterList.getLength() == 0) { - throw new IllegalArgumentException("Filters for layer-content must not be empty."); - } - List filters = new ArrayList<>(); - for (int j = 0; j < filterList.getLength(); j++) { - Node filterNode = filterList.item(j); - if (filterNode instanceof Element) { - List includeList = getPatterns((Element) filterNode, "include"); - List excludeList = getPatterns((Element) filterNode, "exclude"); - if (filterPredicate.test(filterNode.getNodeName())) { - E filter = filterFactory.getFilter(includeList, excludeList); - filters.add(filter); - } - } - } + List filters = getFilters(node, filterFactory, filterPredicate); String layer = element.getAttribute("layer"); contents.add(strategyFactory.getStrategy(layer, filters)); } @@ -126,16 +111,33 @@ public class CustomLayersProvider { return contents; } - private List getPatterns(Element element, String key) { - NodeList patterns = element.getElementsByTagName(key); - List values = new ArrayList<>(); - for (int j = 0; j < patterns.getLength(); j++) { - Node item = patterns.item(j); - if (item instanceof Element) { - values.add(item.getTextContent()); + private List getFilters(Node node, FilterFactory factory, Predicate predicate) { + NodeList childNodes = node.getChildNodes(); + Assert.state(childNodes.getLength() > 0, "Filters for layer-content must not be empty."); + List filters = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node childNode = childNodes.item(i); + if (childNode instanceof Element) { + List include = getPatterns((Element) childNode, "include"); + List exclude = getPatterns((Element) childNode, "exclude"); + if (predicate.test(childNode.getNodeName())) { + filters.add(factory.getFilter(include, exclude)); + } } } - return values; + return filters; + } + + private List getPatterns(Element element, String key) { + List patterns = new ArrayList<>(); + NodeList nodes = element.getElementsByTagName(key); + for (int j = 0; j < nodes.getLength(); j++) { + Node node = nodes.item(j); + if (node instanceof Element) { + patterns.add(node.getTextContent()); + } + } + return patterns; } interface StrategyFactory { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java index 622ebc51c62..2223c381d63 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java @@ -186,14 +186,16 @@ public class RepackageMojo extends AbstractPackagerMojo { if (this.outputTimestamp == null || this.outputTimestamp.length() < 2) { return null; } - long epochSeconds; + return FileTime.from(getOutputTimestampEpochSeconds(), TimeUnit.SECONDS); + } + + private long getOutputTimestampEpochSeconds() { try { - epochSeconds = Long.parseLong(this.outputTimestamp); + return Long.parseLong(this.outputTimestamp); } catch (NumberFormatException ex) { - epochSeconds = OffsetDateTime.parse(this.outputTimestamp).toInstant().getEpochSecond(); + return OffsetDateTime.parse(this.outputTimestamp).toInstant().getEpochSecond(); } - return FileTime.from(epochSeconds, TimeUnit.SECONDS); } /** diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/CustomLayersProviderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/CustomLayersProviderTests.java index a10f81ed89a..43f4dfb9e25 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/CustomLayersProviderTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/CustomLayersProviderTests.java @@ -30,7 +30,7 @@ import org.springframework.boot.loader.tools.layer.CustomLayers; import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -74,14 +74,14 @@ public class CustomLayersProviderTests { @Test void getLayerResolverWhenDocumentContainsLibraryLayerWithNoFilters() { - assertThatIllegalArgumentException() + assertThatIllegalStateException() .isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("library-layer-no-filter.xml"))) .withMessage("Filters for layer-content must not be empty."); } @Test void getLayerResolverWhenDocumentContainsResourceLayerWithNoFilters() { - assertThatIllegalArgumentException() + assertThatIllegalStateException() .isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("resource-layer-no-filter.xml"))) .withMessage("Filters for layer-content must not be empty."); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java index 0819d548a03..5c36c2043cf 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/availability/ApplicationAvailabilityProvider.java @@ -18,6 +18,7 @@ package org.springframework.boot.availability; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.util.Assert; /** * Holds the availability state of the application. @@ -36,19 +37,38 @@ public class ApplicationAvailabilityProvider implements ApplicationListener= 0, closeable, gracefulShutdown); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java index 2b893e231b3..fe0ca418feb 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java @@ -38,7 +38,6 @@ import org.xnio.channels.BoundChannel; import org.springframework.boot.web.server.Compression; import org.springframework.boot.web.server.GracefulShutdown; -import org.springframework.boot.web.server.ImmediateGracefulShutdown; import org.springframework.boot.web.server.PortInUseException; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServerException; @@ -234,7 +233,7 @@ public class UndertowServletWebServer implements WebServer { httpHandler = gracefulShutdownHandler; } else { - this.gracefulShutdown = new ImmediateGracefulShutdown(); + this.gracefulShutdown = GracefulShutdown.IMMEDIATE; } this.builder.setHandler(httpHandler); return this.builder.build(); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java index ef68ba10b25..8ecf76ab72a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java @@ -30,7 +30,6 @@ import org.apache.commons.logging.LogFactory; import org.xnio.channels.BoundChannel; import org.springframework.boot.web.server.GracefulShutdown; -import org.springframework.boot.web.server.ImmediateGracefulShutdown; import org.springframework.boot.web.server.PortInUseException; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServerException; @@ -84,7 +83,7 @@ public class UndertowWebServer implements WebServer { * @since 2.0.4 */ public UndertowWebServer(Undertow.Builder builder, boolean autoStart, Closeable closeable) { - this(builder, autoStart, closeable, new ImmediateGracefulShutdown()); + this(builder, autoStart, closeable, GracefulShutdown.IMMEDIATE); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/GracefulShutdown.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/GracefulShutdown.java index f7aeed22081..8809212d7e7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/GracefulShutdown.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/GracefulShutdown.java @@ -24,6 +24,11 @@ package org.springframework.boot.web.server; */ public interface GracefulShutdown { + /** + * A {@link GracefulShutdown} that returns immediately with no grace period. + */ + GracefulShutdown IMMEDIATE = new ImmediateGracefulShutdown(); + /** * Shuts down the {@link WebServer}, returning {@code true} if activity ceased during * the grace period, otherwise {@code false}. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/ImmediateGracefulShutdown.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/ImmediateGracefulShutdown.java index c5a31f7974b..d599a2790ec 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/ImmediateGracefulShutdown.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/ImmediateGracefulShutdown.java @@ -20,9 +20,8 @@ package org.springframework.boot.web.server; * A {@link GracefulShutdown} that returns immediately with no grace period. * * @author Andy Wilkinson - * @since 2.3.0 */ -public class ImmediateGracefulShutdown implements GracefulShutdown { +class ImmediateGracefulShutdown implements GracefulShutdown { @Override public boolean shutDownGracefully() {