Move Actuator Jersey infrastructure to spring-boot-jersey
This commit is contained in:
committed by
Phillip Webb
parent
92547def9d
commit
bf38d1176d
@@ -32,8 +32,6 @@ dependencies {
|
||||
optional("org.apache.tomcat.embed:tomcat-embed-core")
|
||||
optional("org.aspectj:aspectjweaver")
|
||||
optional("org.eclipse.angus:angus-mail")
|
||||
optional("org.glassfish.jersey.core:jersey-server")
|
||||
optional("org.glassfish.jersey.containers:jersey-container-servlet-core")
|
||||
optional("org.hibernate.validator:hibernate-validator")
|
||||
optional("org.influxdb:influxdb-java")
|
||||
optional("org.springframework:spring-context-support")
|
||||
@@ -57,13 +55,11 @@ dependencies {
|
||||
testImplementation("io.projectreactor:reactor-test")
|
||||
testImplementation("net.minidev:json-smart")
|
||||
testImplementation("org.apache.logging.log4j:log4j-to-slf4j")
|
||||
testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson")
|
||||
|
||||
testRuntimeOnly(project(":spring-boot-project:spring-boot-http-codec"))
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("io.projectreactor.netty:reactor-netty-http")
|
||||
testRuntimeOnly("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||
testRuntimeOnly("org.apache.tomcat.embed:tomcat-embed-el")
|
||||
testRuntimeOnly("org.glassfish.jersey.ext:jersey-spring6")
|
||||
testRuntimeOnly("org.hsqldb:hsqldb")
|
||||
}
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
/*
|
||||
* 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.actuate.endpoint.web.jersey;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jakarta.ws.rs.HttpMethod;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import org.glassfish.jersey.process.Inflector;
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.glassfish.jersey.server.model.Resource;
|
||||
import org.glassfish.jersey.server.model.Resource.Builder;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
|
||||
import org.springframework.boot.actuate.endpoint.InvocationContext;
|
||||
import org.springframework.boot.actuate.endpoint.OperationArgumentResolver;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.ProducibleOperationArgumentResolver;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.Link;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A factory for creating Jersey {@link Resource Resources} for {@link WebOperation web
|
||||
* endpoint operations}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class JerseyEndpointResourceFactory {
|
||||
|
||||
/**
|
||||
* Creates {@link Resource Resources} for the operations of the given
|
||||
* {@code webEndpoints}.
|
||||
* @param endpointMapping the base mapping for all endpoints
|
||||
* @param endpoints the web endpoints
|
||||
* @param endpointMediaTypes media types consumed and produced by the endpoints
|
||||
* @param linksResolver resolver for determining links to available endpoints
|
||||
* @param shouldRegisterLinks should register links
|
||||
* @return the resources for the operations
|
||||
*/
|
||||
public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping,
|
||||
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
|
||||
EndpointLinksResolver linksResolver, boolean shouldRegisterLinks) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
endpoints.stream()
|
||||
.flatMap((endpoint) -> endpoint.getOperations().stream())
|
||||
.map((operation) -> createResource(endpointMapping, operation))
|
||||
.forEach(resources::add);
|
||||
if (shouldRegisterLinks) {
|
||||
Resource resource = createEndpointLinksResource(endpointMapping.getPath(), endpointMediaTypes,
|
||||
linksResolver);
|
||||
resources.add(resource);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
protected Resource createResource(EndpointMapping endpointMapping, WebOperation operation) {
|
||||
WebOperationRequestPredicate requestPredicate = operation.getRequestPredicate();
|
||||
String path = requestPredicate.getPath();
|
||||
String matchAllRemainingPathSegmentsVariable = requestPredicate.getMatchAllRemainingPathSegmentsVariable();
|
||||
if (matchAllRemainingPathSegmentsVariable != null) {
|
||||
path = path.replace("{*" + matchAllRemainingPathSegmentsVariable + "}",
|
||||
"{" + matchAllRemainingPathSegmentsVariable + ": .*}");
|
||||
}
|
||||
return getResource(endpointMapping, operation, requestPredicate, path, null, null);
|
||||
}
|
||||
|
||||
protected Resource getResource(EndpointMapping endpointMapping, WebOperation operation,
|
||||
WebOperationRequestPredicate requestPredicate, String path, WebServerNamespace serverNamespace,
|
||||
JerseyRemainingPathSegmentProvider remainingPathSegmentProvider) {
|
||||
Builder resourceBuilder = Resource.builder()
|
||||
.path(endpointMapping.getPath())
|
||||
.path(endpointMapping.createSubPath(path));
|
||||
resourceBuilder.addMethod(requestPredicate.getHttpMethod().name())
|
||||
.consumes(StringUtils.toStringArray(requestPredicate.getConsumes()))
|
||||
.produces(StringUtils.toStringArray(requestPredicate.getProduces()))
|
||||
.handledBy(new OperationInflector(operation, !requestPredicate.getConsumes().isEmpty(), serverNamespace,
|
||||
remainingPathSegmentProvider));
|
||||
return resourceBuilder.build();
|
||||
}
|
||||
|
||||
private Resource createEndpointLinksResource(String endpointPath, EndpointMediaTypes endpointMediaTypes,
|
||||
EndpointLinksResolver linksResolver) {
|
||||
Builder resourceBuilder = Resource.builder().path(endpointPath);
|
||||
resourceBuilder.addMethod("GET")
|
||||
.produces(StringUtils.toStringArray(endpointMediaTypes.getProduced()))
|
||||
.handledBy(new EndpointLinksInflector(linksResolver));
|
||||
return resourceBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Inflector} to invoke the {@link WebOperation}.
|
||||
*/
|
||||
private static final class OperationInflector implements Inflector<ContainerRequestContext, Object> {
|
||||
|
||||
private static final String PATH_SEPARATOR = AntPathMatcher.DEFAULT_PATH_SEPARATOR;
|
||||
|
||||
private static final List<Function<Object, Object>> BODY_CONVERTERS;
|
||||
|
||||
static {
|
||||
List<Function<Object, Object>> converters = new ArrayList<>();
|
||||
converters.add(new ResourceBodyConverter());
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Mono", OperationInflector.class.getClassLoader())) {
|
||||
converters.add(new FluxBodyConverter());
|
||||
converters.add(new MonoBodyConverter());
|
||||
}
|
||||
BODY_CONVERTERS = Collections.unmodifiableList(converters);
|
||||
}
|
||||
|
||||
private final WebOperation operation;
|
||||
|
||||
private final boolean readBody;
|
||||
|
||||
private final WebServerNamespace serverNamespace;
|
||||
|
||||
private final JerseyRemainingPathSegmentProvider remainingPathSegmentProvider;
|
||||
|
||||
private OperationInflector(WebOperation operation, boolean readBody, WebServerNamespace serverNamespace,
|
||||
JerseyRemainingPathSegmentProvider remainingPathSegments) {
|
||||
this.operation = operation;
|
||||
this.readBody = readBody;
|
||||
this.serverNamespace = serverNamespace;
|
||||
this.remainingPathSegmentProvider = remainingPathSegments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response apply(ContainerRequestContext data) {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
if (this.readBody) {
|
||||
arguments.putAll(extractBodyArguments(data));
|
||||
}
|
||||
arguments.putAll(extractPathParameters(data));
|
||||
arguments.putAll(extractQueryParameters(data));
|
||||
try {
|
||||
JerseySecurityContext securityContext = new JerseySecurityContext(data.getSecurityContext());
|
||||
OperationArgumentResolver serverNamespaceArgumentResolver = OperationArgumentResolver
|
||||
.of(WebServerNamespace.class, () -> this.serverNamespace);
|
||||
InvocationContext invocationContext = new InvocationContext(securityContext, arguments,
|
||||
serverNamespaceArgumentResolver,
|
||||
new ProducibleOperationArgumentResolver(() -> data.getHeaders().get("Accept")));
|
||||
Object response = this.operation.invoke(invocationContext);
|
||||
return convertToJaxRsResponse(response, data.getRequest().getMethod());
|
||||
}
|
||||
catch (InvalidEndpointRequestException ex) {
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, String> extractBodyArguments(ContainerRequestContext data) {
|
||||
Map<String, String> entity = ((ContainerRequest) data).readEntity(Map.class,
|
||||
new ParameterizedTypeReference<Map<String, String>>() {
|
||||
}.getType());
|
||||
return (entity != null) ? entity : Collections.emptyMap();
|
||||
}
|
||||
|
||||
private Map<String, Object> extractPathParameters(ContainerRequestContext requestContext) {
|
||||
Map<String, Object> pathParameters = extract(requestContext.getUriInfo().getPathParameters());
|
||||
String matchAllRemainingPathSegmentsVariable = this.operation.getRequestPredicate()
|
||||
.getMatchAllRemainingPathSegmentsVariable();
|
||||
if (matchAllRemainingPathSegmentsVariable != null) {
|
||||
String remainingPathSegments = getRemainingPathSegments(requestContext, pathParameters,
|
||||
matchAllRemainingPathSegmentsVariable);
|
||||
pathParameters.put(matchAllRemainingPathSegmentsVariable, tokenizePathSegments(remainingPathSegments));
|
||||
}
|
||||
return pathParameters;
|
||||
}
|
||||
|
||||
private String getRemainingPathSegments(ContainerRequestContext requestContext,
|
||||
Map<String, Object> pathParameters, String matchAllRemainingPathSegmentsVariable) {
|
||||
if (this.remainingPathSegmentProvider != null) {
|
||||
return this.remainingPathSegmentProvider.get(requestContext, matchAllRemainingPathSegmentsVariable);
|
||||
}
|
||||
return (String) pathParameters.get(matchAllRemainingPathSegmentsVariable);
|
||||
}
|
||||
|
||||
private String[] tokenizePathSegments(String path) {
|
||||
String[] segments = StringUtils.tokenizeToStringArray(path, PATH_SEPARATOR, false, true);
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
if (segments[i].contains("%")) {
|
||||
segments[i] = StringUtils.uriDecode(segments[i], StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
private Map<String, Object> extractQueryParameters(ContainerRequestContext requestContext) {
|
||||
return extract(requestContext.getUriInfo().getQueryParameters());
|
||||
}
|
||||
|
||||
private Map<String, Object> extract(MultivaluedMap<String, String> multivaluedMap) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
multivaluedMap.forEach((name, values) -> {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
result.put(name, (values.size() != 1) ? values : values.get(0));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private Response convertToJaxRsResponse(Object response, String httpMethod) {
|
||||
if (response == null) {
|
||||
boolean isGet = HttpMethod.GET.equals(httpMethod);
|
||||
Status status = isGet ? Status.NOT_FOUND : Status.NO_CONTENT;
|
||||
return Response.status(status).build();
|
||||
}
|
||||
if (!(response instanceof WebEndpointResponse<?> webEndpointResponse)) {
|
||||
return Response.status(Status.OK).entity(convertIfNecessary(response)).build();
|
||||
}
|
||||
return Response.status(webEndpointResponse.getStatus())
|
||||
.header("Content-Type", webEndpointResponse.getContentType())
|
||||
.entity(convertIfNecessary(webEndpointResponse.getBody()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private Object convertIfNecessary(Object body) {
|
||||
for (Function<Object, Object> converter : BODY_CONVERTERS) {
|
||||
body = converter.apply(body);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Body converter from {@link org.springframework.core.io.Resource} to
|
||||
* {@link InputStream}.
|
||||
*/
|
||||
private static final class ResourceBodyConverter implements Function<Object, Object> {
|
||||
|
||||
@Override
|
||||
public Object apply(Object body) {
|
||||
if (body instanceof org.springframework.core.io.Resource) {
|
||||
try {
|
||||
return ((org.springframework.core.io.Resource) body).getInputStream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Body converter from {@link Mono} to {@link Mono#block()}.
|
||||
*/
|
||||
private static final class MonoBodyConverter implements Function<Object, Object> {
|
||||
|
||||
@Override
|
||||
public Object apply(Object body) {
|
||||
if (body instanceof Mono) {
|
||||
return ((Mono<?>) body).block();
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Body converter from {@link Flux} to {@link Flux#collectList Mono<List>}.
|
||||
*/
|
||||
private static final class FluxBodyConverter implements Function<Object, Object> {
|
||||
|
||||
@Override
|
||||
public Object apply(Object body) {
|
||||
if (body instanceof Flux) {
|
||||
return ((Flux<?>) body).collectList();
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Inflector} to for endpoint links.
|
||||
*/
|
||||
private static final class EndpointLinksInflector implements Inflector<ContainerRequestContext, Response> {
|
||||
|
||||
private final EndpointLinksResolver linksResolver;
|
||||
|
||||
private EndpointLinksInflector(EndpointLinksResolver linksResolver) {
|
||||
this.linksResolver = linksResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response apply(ContainerRequestContext request) {
|
||||
Map<String, Link> links = this.linksResolver
|
||||
.resolveLinks(request.getUriInfo().getAbsolutePath().toString());
|
||||
Map<String, Map<String, Link>> entity = OperationResponseBody.of(Collections.singletonMap("_links", links));
|
||||
return Response.ok(entity).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class JerseySecurityContext implements SecurityContext {
|
||||
|
||||
private final jakarta.ws.rs.core.SecurityContext securityContext;
|
||||
|
||||
private JerseySecurityContext(jakarta.ws.rs.core.SecurityContext securityContext) {
|
||||
this.securityContext = securityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPrincipal() {
|
||||
return this.securityContext.getUserPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String role) {
|
||||
return this.securityContext.isUserInRole(role);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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.actuate.endpoint.web.jersey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.glassfish.jersey.server.model.Resource;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroup;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroups;
|
||||
|
||||
/**
|
||||
* A factory for creating Jersey {@link Resource Resources} for health groups with
|
||||
* additional path.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public final class JerseyHealthEndpointAdditionalPathResourceFactory {
|
||||
|
||||
private final JerseyEndpointResourceFactory delegate = new JerseyEndpointResourceFactory();
|
||||
|
||||
private final Set<HealthEndpointGroup> groups;
|
||||
|
||||
private final WebServerNamespace serverNamespace;
|
||||
|
||||
public JerseyHealthEndpointAdditionalPathResourceFactory(WebServerNamespace serverNamespace,
|
||||
HealthEndpointGroups groups) {
|
||||
this.serverNamespace = serverNamespace;
|
||||
this.groups = groups.getAllWithAdditionalPath(serverNamespace);
|
||||
}
|
||||
|
||||
public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping,
|
||||
Collection<ExposableWebEndpoint> endpoints) {
|
||||
return endpoints.stream()
|
||||
.flatMap((endpoint) -> endpoint.getOperations().stream())
|
||||
.flatMap((operation) -> createResources(endpointMapping, operation))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Stream<Resource> createResources(EndpointMapping endpointMapping, WebOperation operation) {
|
||||
WebOperationRequestPredicate requestPredicate = operation.getRequestPredicate();
|
||||
String matchAllRemainingPathSegmentsVariable = requestPredicate.getMatchAllRemainingPathSegmentsVariable();
|
||||
if (matchAllRemainingPathSegmentsVariable != null) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
for (HealthEndpointGroup group : this.groups) {
|
||||
AdditionalHealthEndpointPath additionalPath = group.getAdditionalPath();
|
||||
if (additionalPath != null) {
|
||||
resources.add(this.delegate.getResource(endpointMapping, operation, requestPredicate,
|
||||
additionalPath.getValue(), this.serverNamespace,
|
||||
(data, pathSegmentsVariable) -> data.getUriInfo().getPath()));
|
||||
}
|
||||
}
|
||||
return resources.stream();
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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.actuate.endpoint.web.jersey;
|
||||
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
|
||||
/**
|
||||
* Strategy interface used to provide the remaining path segments for a Jersey actuator
|
||||
* endpoint.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
interface JerseyRemainingPathSegmentProvider {
|
||||
|
||||
String get(ContainerRequestContext requestContext, String matchAllRemainingPathSegmentsVariable);
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jersey support for actuator endpoints.
|
||||
*/
|
||||
package org.springframework.boot.actuate.endpoint.web.jersey;
|
||||
Reference in New Issue
Block a user