mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Polish
This commit is contained in:
parent
9a33a723fe
commit
0717de723f
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 <<spring-boot-features.adoc#boot-features-application-availability-state,manage the state of your application>> and export it with <<production-ready-features.adoc#production-ready-kubernetes-probes, HTTP Kubernetes Probes using Actuator>>.
|
||||
|
||||
|
||||
|
||||
[[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 <<spring-boot-features#boot-features-graceful-shutdown,graceful shutdown>> will begin, allowing any remaining in-flight requests to complete.
|
||||
|
||||
|
||||
|
||||
[[cloud-deployment-heroku]]
|
||||
=== Heroku
|
||||
Heroku is another popular PaaS platform.
|
||||
|
@ -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 <<production-ready-health-groups, Health Groups features>> 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 <<deployment.adoc#cloud-deployment-kubernetes-container-lifecycle,Kubernetes container lifecycle section>> 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`.
|
||||
|
@ -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-events-and-listeners, Spring Boot application lifecycle and related Application Events>>.
|
||||
|
||||
|
||||
|
||||
[[boot-features-application-availability-readiness]]
|
||||
==== Readiness State
|
||||
The "Readiness" state of an application tells whether the application is ready to handle traffic.
|
||||
|
@ -150,8 +150,6 @@ class HttpClientHttp implements Http {
|
||||
|
||||
/**
|
||||
* {@link HttpEntity} to send {@link Content} content.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
private static class WritableHttpEntity extends AbstractHttpEntity {
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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<BuildInfo> configurer) {
|
||||
TaskProvider<BuildInfo> 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<BuildInfo> 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();
|
||||
|
@ -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());
|
||||
|
@ -153,12 +153,6 @@ public class LayerConfiguration {
|
||||
|
||||
private static final class StrategySpec {
|
||||
|
||||
private enum TYPE {
|
||||
|
||||
LIBRARIES, RESOURCES;
|
||||
|
||||
}
|
||||
|
||||
private final TYPE type;
|
||||
|
||||
private List<LibraryFilter> libraryFilters;
|
||||
@ -232,6 +226,12 @@ public class LayerConfiguration {
|
||||
return new StrategySpec(TYPE.RESOURCES);
|
||||
}
|
||||
|
||||
private enum TYPE {
|
||||
|
||||
LIBRARIES, RESOURCES;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<String> includes = new ArrayList<>();
|
||||
private static final String EMPTY_COORDINATES = "::";
|
||||
|
||||
private final List<String> excludes = new ArrayList<>();
|
||||
private final List<Pattern> includes;
|
||||
|
||||
private final List<Pattern> excludes;
|
||||
|
||||
public CoordinateFilter(List<String> includes, List<String> 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<String> 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<Pattern> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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:");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Layer> layers = new ArrayList<>();
|
||||
List<LibraryStrategy> libraryStrategies = new ArrayList<>();
|
||||
List<ResourceStrategy> 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<Layer> getLayers(Element element) {
|
||||
List<Layer> 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<E> filterFactory, Predicate<String> filterPredicate) {
|
||||
List<T> 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<E> filters = new ArrayList<>();
|
||||
for (int j = 0; j < filterList.getLength(); j++) {
|
||||
Node filterNode = filterList.item(j);
|
||||
if (filterNode instanceof Element) {
|
||||
List<String> includeList = getPatterns((Element) filterNode, "include");
|
||||
List<String> excludeList = getPatterns((Element) filterNode, "exclude");
|
||||
if (filterPredicate.test(filterNode.getNodeName())) {
|
||||
E filter = filterFactory.getFilter(includeList, excludeList);
|
||||
filters.add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<E> 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<String> getPatterns(Element element, String key) {
|
||||
NodeList patterns = element.getElementsByTagName(key);
|
||||
List<String> 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 <E> List<E> getFilters(Node node, FilterFactory<E> factory, Predicate<String> predicate) {
|
||||
NodeList childNodes = node.getChildNodes();
|
||||
Assert.state(childNodes.getLength() > 0, "Filters for layer-content must not be empty.");
|
||||
List<E> filters = new ArrayList<>();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node childNode = childNodes.item(i);
|
||||
if (childNode instanceof Element) {
|
||||
List<String> include = getPatterns((Element) childNode, "include");
|
||||
List<String> exclude = getPatterns((Element) childNode, "exclude");
|
||||
if (predicate.test(childNode.getNodeName())) {
|
||||
filters.add(factory.getFilter(include, exclude));
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
return filters;
|
||||
}
|
||||
|
||||
private List<String> getPatterns(Element element, String key) {
|
||||
List<String> 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<E, T> {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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<Appl
|
||||
|
||||
private ReadinessState readinessState;
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationAvailabilityProvider} instance with
|
||||
* {@link LivenessState#broken()} and {@link ReadinessState#unready()}.
|
||||
*/
|
||||
public ApplicationAvailabilityProvider() {
|
||||
this(LivenessState.broken(), ReadinessState.unready());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationAvailabilityProvider} with the given states.
|
||||
* @param livenessState the liveness state
|
||||
* @param readinessState the readiness state
|
||||
*/
|
||||
public ApplicationAvailabilityProvider(LivenessState livenessState, ReadinessState readinessState) {
|
||||
Assert.notNull(livenessState, "LivenessState must not be null");
|
||||
Assert.notNull(readinessState, "ReadinessState must not be null");
|
||||
this.livenessState = livenessState;
|
||||
this.readinessState = readinessState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link LivenessState} of the application.
|
||||
* @return the liveness state
|
||||
*/
|
||||
public LivenessState getLivenessState() {
|
||||
return this.livenessState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ReadinessState} of the application.
|
||||
* @return the readiness state
|
||||
*/
|
||||
public ReadinessState getReadinessState() {
|
||||
return this.readinessState;
|
||||
}
|
||||
|
@ -41,15 +41,14 @@ public final class LivenessState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
LivenessState that = (LivenessState) o;
|
||||
return this.status == that.status;
|
||||
return this.status == ((LivenessState) obj).status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,6 +75,7 @@ public final class LivenessState {
|
||||
* The application is running and its internal state is correct.
|
||||
*/
|
||||
LIVE,
|
||||
|
||||
/**
|
||||
* The internal state of the application is broken.
|
||||
*/
|
||||
|
@ -41,15 +41,14 @@ public final class ReadinessState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ReadinessState that = (ReadinessState) o;
|
||||
return this.availability == that.availability;
|
||||
return this.availability == ((ReadinessState) obj).availability;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,6 +75,7 @@ public final class ReadinessState {
|
||||
* The application is not willing to receive traffic.
|
||||
*/
|
||||
UNREADY,
|
||||
|
||||
/**
|
||||
* The application is ready to receive traffic.
|
||||
*/
|
||||
|
@ -55,15 +55,7 @@ class JettyGracefulShutdown implements GracefulShutdown {
|
||||
logger.info("Commencing graceful shutdown, allowing up to " + this.period.getSeconds()
|
||||
+ "s for active requests to complete");
|
||||
for (Connector connector : this.server.getConnectors()) {
|
||||
try {
|
||||
connector.shutdown().get();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
// Continue
|
||||
}
|
||||
shutdown(connector);
|
||||
}
|
||||
this.shuttingDown = true;
|
||||
long end = System.currentTimeMillis() + this.period.toMillis();
|
||||
@ -87,6 +79,18 @@ class JettyGracefulShutdown implements GracefulShutdown {
|
||||
return activeRequests == 0;
|
||||
}
|
||||
|
||||
private void shutdown(Connector connector) {
|
||||
try {
|
||||
connector.shutdown().get();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShuttingDown() {
|
||||
return this.shuttingDown;
|
||||
|
@ -37,7 +37,6 @@ import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
||||
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;
|
||||
@ -101,20 +100,20 @@ public class JettyWebServer implements WebServer {
|
||||
this.autoStart = autoStart;
|
||||
Assert.notNull(server, "Jetty Server must not be null");
|
||||
this.server = server;
|
||||
GracefulShutdown gracefulShutdown = null;
|
||||
if (shutdownGracePeriod != null) {
|
||||
StatisticsHandler handler = new StatisticsHandler();
|
||||
handler.setHandler(server.getHandler());
|
||||
server.setHandler(handler);
|
||||
gracefulShutdown = new JettyGracefulShutdown(server, handler::getRequestsActive, shutdownGracePeriod);
|
||||
}
|
||||
else {
|
||||
gracefulShutdown = new ImmediateGracefulShutdown();
|
||||
}
|
||||
this.gracefulShutdown = gracefulShutdown;
|
||||
this.gracefulShutdown = createGracefulShutdown(server, shutdownGracePeriod);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private GracefulShutdown createGracefulShutdown(Server server, Duration shutdownGracePeriod) {
|
||||
if (shutdownGracePeriod == null) {
|
||||
return GracefulShutdown.IMMEDIATE;
|
||||
}
|
||||
StatisticsHandler handler = new StatisticsHandler();
|
||||
handler.setHandler(server.getHandler());
|
||||
server.setHandler(handler);
|
||||
return new JettyGracefulShutdown(server, handler::getRequestsActive, shutdownGracePeriod);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
synchronized (this.monitor) {
|
||||
try {
|
||||
|
@ -55,12 +55,7 @@ final class NettyGracefulShutdown implements GracefulShutdown {
|
||||
}
|
||||
this.shuttingDown = true;
|
||||
try {
|
||||
if (this.period != null) {
|
||||
server.disposeNow(this.period);
|
||||
}
|
||||
else {
|
||||
server.disposeNow();
|
||||
}
|
||||
disposeNow(server);
|
||||
logger.info("Graceful shutdown complete");
|
||||
return true;
|
||||
}
|
||||
@ -73,6 +68,15 @@ final class NettyGracefulShutdown implements GracefulShutdown {
|
||||
}
|
||||
}
|
||||
|
||||
private void disposeNow(DisposableServer server) {
|
||||
if (this.period != null) {
|
||||
server.disposeNow(this.period);
|
||||
}
|
||||
else {
|
||||
server.disposeNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShuttingDown() {
|
||||
return this.shuttingDown;
|
||||
|
@ -35,7 +35,6 @@ import reactor.netty.http.server.HttpServerResponse;
|
||||
import reactor.netty.http.server.HttpServerRoutes;
|
||||
|
||||
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;
|
||||
@ -96,7 +95,7 @@ public class NettyWebServer implements WebServer {
|
||||
}
|
||||
else {
|
||||
this.httpServer = httpServer;
|
||||
this.shutdown = new ImmediateGracefulShutdown();
|
||||
this.shutdown = GracefulShutdown.IMMEDIATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.naming.ContextBindings;
|
||||
|
||||
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;
|
||||
@ -102,7 +101,7 @@ public class TomcatWebServer implements WebServer {
|
||||
this.tomcat = tomcat;
|
||||
this.autoStart = autoStart;
|
||||
this.gracefulShutdown = (shutdownGracePeriod != null) ? new TomcatGracefulShutdown(tomcat, shutdownGracePeriod)
|
||||
: new ImmediateGracefulShutdown();
|
||||
: GracefulShutdown.IMMEDIATE;
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ import org.xnio.XnioWorker;
|
||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.GracefulShutdown;
|
||||
import org.springframework.boot.web.server.ImmediateGracefulShutdown;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
@ -103,12 +102,12 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||
}
|
||||
handler = UndertowCompressionConfigurer.configureCompression(getCompression(), handler);
|
||||
Closeable closeable = null;
|
||||
GracefulShutdown gracefulShutdown = null;
|
||||
if (isAccessLogEnabled()) {
|
||||
AccessLogHandlerConfiguration accessLogHandlerConfiguration = configureAccessLogHandler(builder, handler);
|
||||
closeable = accessLogHandlerConfiguration.closeable;
|
||||
handler = accessLogHandlerConfiguration.accessLogHandler;
|
||||
}
|
||||
GracefulShutdown gracefulShutdown = null;
|
||||
GracefulShutdownHandler gracefulShutdownHandler = Handlers.gracefulShutdown(handler);
|
||||
Duration gracePeriod = getShutdown().getGracePeriod();
|
||||
if (gracePeriod != null) {
|
||||
@ -116,7 +115,7 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||
handler = gracefulShutdownHandler;
|
||||
}
|
||||
else {
|
||||
gracefulShutdown = new ImmediateGracefulShutdown();
|
||||
gracefulShutdown = GracefulShutdown.IMMEDIATE;
|
||||
}
|
||||
builder.setHandler(handler);
|
||||
return new UndertowWebServer(builder, getPort() >= 0, closeable, gracefulShutdown);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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}.
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user