parent
fb2f7c1e38
commit
ba93e6c0ed
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.endpoint.web.jersey;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.model.Resource;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.jersey.ManagementContextResourceConfigCustomizer;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||||
|
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||||
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||||
|
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.ExposableServletEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyHealthEndpointAdditionalPathResourceFactory;
|
||||||
|
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||||
|
import org.springframework.boot.actuate.health.HealthEndpointGroups;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Jersey
|
||||||
|
* {@link Endpoint @Endpoint} concerns.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Michael Simons
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @author HaiTao Zhang
|
||||||
|
*/
|
||||||
|
@ManagementContextConfiguration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||||
|
@ConditionalOnClass(ResourceConfig.class)
|
||||||
|
@ConditionalOnBean(WebEndpointsSupplier.class)
|
||||||
|
@ConditionalOnMissingBean(type = "org.springframework.web.servlet.DispatcherServlet")
|
||||||
|
class JerseyWebEndpointManagementContextConfiguration {
|
||||||
|
|
||||||
|
private static final EndpointId HEALTH_ENDPOINT_ID = EndpointId.of("health");
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JerseyWebEndpointsResourcesRegistrar jerseyWebEndpointsResourcesRegistrar(Environment environment,
|
||||||
|
WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier,
|
||||||
|
EndpointMediaTypes endpointMediaTypes, WebEndpointProperties webEndpointProperties) {
|
||||||
|
String basePath = webEndpointProperties.getBasePath();
|
||||||
|
boolean shouldRegisterLinks = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
|
||||||
|
return new JerseyWebEndpointsResourcesRegistrar(webEndpointsSupplier, servletEndpointsSupplier,
|
||||||
|
endpointMediaTypes, basePath, shouldRegisterLinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
|
||||||
|
@ConditionalOnBean(HealthEndpoint.class)
|
||||||
|
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
|
||||||
|
JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar jerseyDifferentPortAdditionalHealthEndpointPathsResourcesRegistrar(
|
||||||
|
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups healthEndpointGroups) {
|
||||||
|
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
|
||||||
|
ExposableWebEndpoint health = webEndpoints.stream()
|
||||||
|
.filter((endpoint) -> endpoint.getEndpointId().equals(HEALTH_ENDPOINT_ID)).findFirst().get();
|
||||||
|
return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(health, healthEndpointGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRegisterLinksMapping(WebEndpointProperties properties, Environment environment,
|
||||||
|
String basePath) {
|
||||||
|
return properties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
|
||||||
|
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register endpoints with the {@link ResourceConfig} for the management context.
|
||||||
|
*/
|
||||||
|
static class JerseyWebEndpointsResourcesRegistrar implements ManagementContextResourceConfigCustomizer {
|
||||||
|
|
||||||
|
private final WebEndpointsSupplier webEndpointsSupplier;
|
||||||
|
|
||||||
|
private final ServletEndpointsSupplier servletEndpointsSupplier;
|
||||||
|
|
||||||
|
private final EndpointMediaTypes mediaTypes;
|
||||||
|
|
||||||
|
private final String basePath;
|
||||||
|
|
||||||
|
private final boolean shouldRegisterLinks;
|
||||||
|
|
||||||
|
JerseyWebEndpointsResourcesRegistrar(WebEndpointsSupplier webEndpointsSupplier,
|
||||||
|
ServletEndpointsSupplier servletEndpointsSupplier, EndpointMediaTypes endpointMediaTypes,
|
||||||
|
String basePath, boolean shouldRegisterLinks) {
|
||||||
|
this.webEndpointsSupplier = webEndpointsSupplier;
|
||||||
|
this.servletEndpointsSupplier = servletEndpointsSupplier;
|
||||||
|
this.mediaTypes = endpointMediaTypes;
|
||||||
|
this.basePath = basePath;
|
||||||
|
this.shouldRegisterLinks = shouldRegisterLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ResourceConfig config) {
|
||||||
|
register(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(ResourceConfig config) {
|
||||||
|
Collection<ExposableWebEndpoint> webEndpoints = this.webEndpointsSupplier.getEndpoints();
|
||||||
|
Collection<ExposableServletEndpoint> servletEndpoints = this.servletEndpointsSupplier.getEndpoints();
|
||||||
|
EndpointLinksResolver linksResolver = getLinksResolver(webEndpoints, servletEndpoints);
|
||||||
|
EndpointMapping mapping = new EndpointMapping(this.basePath);
|
||||||
|
Collection<Resource> endpointResources = new JerseyEndpointResourceFactory().createEndpointResources(
|
||||||
|
mapping, webEndpoints, this.mediaTypes, linksResolver, this.shouldRegisterLinks);
|
||||||
|
register(endpointResources, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EndpointLinksResolver getLinksResolver(Collection<ExposableWebEndpoint> webEndpoints,
|
||||||
|
Collection<ExposableServletEndpoint> servletEndpoints) {
|
||||||
|
List<ExposableEndpoint<?>> endpoints = new ArrayList<>(webEndpoints.size() + servletEndpoints.size());
|
||||||
|
endpoints.addAll(webEndpoints);
|
||||||
|
endpoints.addAll(servletEndpoints);
|
||||||
|
return new EndpointLinksResolver(endpoints, this.basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(Collection<Resource> resources, ResourceConfig config) {
|
||||||
|
config.registerResources(new HashSet<>(resources));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar
|
||||||
|
implements ManagementContextResourceConfigCustomizer {
|
||||||
|
|
||||||
|
private final ExposableWebEndpoint endpoint;
|
||||||
|
|
||||||
|
private final HealthEndpointGroups groups;
|
||||||
|
|
||||||
|
JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(ExposableWebEndpoint endpoint,
|
||||||
|
HealthEndpointGroups groups) {
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ResourceConfig config) {
|
||||||
|
register(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(ResourceConfig config) {
|
||||||
|
EndpointMapping mapping = new EndpointMapping("");
|
||||||
|
JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory(
|
||||||
|
WebServerNamespace.MANAGEMENT, this.groups);
|
||||||
|
Collection<Resource> endpointResources = resourceFactory
|
||||||
|
.createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false)
|
||||||
|
.stream().filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
register(endpointResources, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(Collection<Resource> resources, ResourceConfig config) {
|
||||||
|
config.registerResources(new HashSet<>(resources));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-configuration for exposing actuator web endpoints using Jersey.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey;
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.metrics.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.AnnotationFinder;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.DefaultJerseyTagsProvider;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.MetricsApplicationEventListener;
|
||||||
|
import io.micrometer.core.instrument.config.MeterFilter;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Web.Server;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link EnableAutoConfiguration Auto-configuration} for Jersey server instrumentation.
|
||||||
|
*
|
||||||
|
* @author Michael Weirauch
|
||||||
|
* @author Michael Simons
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 2.1.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration(after = { MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
|
||||||
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||||
|
@ConditionalOnClass({ ResourceConfig.class, MetricsApplicationEventListener.class })
|
||||||
|
@ConditionalOnBean({ MeterRegistry.class, ResourceConfig.class })
|
||||||
|
@EnableConfigurationProperties(MetricsProperties.class)
|
||||||
|
public class JerseyServerMetricsAutoConfiguration {
|
||||||
|
|
||||||
|
private final MetricsProperties properties;
|
||||||
|
|
||||||
|
public JerseyServerMetricsAutoConfiguration(MetricsProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(JerseyTagsProvider.class)
|
||||||
|
public DefaultJerseyTagsProvider jerseyTagsProvider() {
|
||||||
|
return new DefaultJerseyTagsProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(MeterRegistry meterRegistry,
|
||||||
|
JerseyTagsProvider tagsProvider) {
|
||||||
|
Server server = this.properties.getWeb().getServer();
|
||||||
|
return (config) -> config.register(
|
||||||
|
new MetricsApplicationEventListener(meterRegistry, tagsProvider, server.getRequest().getMetricName(),
|
||||||
|
server.getRequest().getAutotime().isEnabled(), new AnnotationUtilsAnnotationFinder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(0)
|
||||||
|
public MeterFilter jerseyMetricsUriTagFilter() {
|
||||||
|
String metricName = this.properties.getWeb().getServer().getRequest().getMetricName();
|
||||||
|
MeterFilter filter = new OnlyOnceLoggingDenyMeterFilter(
|
||||||
|
() -> String.format("Reached the maximum number of URI tags for '%s'.", metricName));
|
||||||
|
return MeterFilter.maximumAllowableTags(metricName, "uri", this.properties.getWeb().getServer().getMaxUriTags(),
|
||||||
|
filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AnnotationFinder} that uses {@link AnnotationUtils}.
|
||||||
|
*/
|
||||||
|
private static class AnnotationUtilsAnnotationFinder implements AnnotationFinder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||||
|
return AnnotationUtils.findAnnotation(annotatedElement, annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-configuration for Jersey actuator metrics.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.actuate.autoconfigure.metrics.jersey;
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Jersey
|
||||||
|
* infrastructure when a separate management context with a web server running on a
|
||||||
|
* different port is required.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @since 2.1.0
|
||||||
|
*/
|
||||||
|
@ManagementContextConfiguration(value = ManagementContextType.CHILD, proxyBeanMethods = false)
|
||||||
|
@Import(JerseyManagementContextConfiguration.class)
|
||||||
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||||
|
@ConditionalOnClass(ResourceConfig.class)
|
||||||
|
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
|
||||||
|
public class JerseyChildManagementContextConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JerseyApplicationPath jerseyApplicationPath() {
|
||||||
|
return () -> "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig(ObjectProvider<ManagementContextResourceConfigCustomizer> customizers) {
|
||||||
|
ResourceConfig resourceConfig = new ResourceConfig();
|
||||||
|
customizers.orderedStream().forEach((customizer) -> customizer.customize(resourceConfig));
|
||||||
|
return resourceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared configuration for Jersey-based actuators regardless of management context type.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
class JerseyManagementContextConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ServletRegistrationBean<ServletContainer> jerseyServletRegistration(JerseyApplicationPath jerseyApplicationPath,
|
||||||
|
ResourceConfig resourceConfig) {
|
||||||
|
return new ServletRegistrationBean<>(new ServletContainer(resourceConfig),
|
||||||
|
jerseyApplicationPath.getUrlMapping());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Jersey
|
||||||
|
* infrastructure when the management context is the same as the main application context.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @since 2.1.0
|
||||||
|
*/
|
||||||
|
@ManagementContextConfiguration(value = ManagementContextType.SAME, proxyBeanMethods = false)
|
||||||
|
@EnableConfigurationProperties(JerseyProperties.class)
|
||||||
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||||
|
@ConditionalOnClass(ResourceConfig.class)
|
||||||
|
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
|
||||||
|
public class JerseySameManagementContextConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfigCustomizer managementResourceConfigCustomizerAdapter(
|
||||||
|
ObjectProvider<ManagementContextResourceConfigCustomizer> customizers) {
|
||||||
|
return (config) -> customizers.orderedStream().forEach((customizer) -> customizer.customize(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(JerseyManagementContextConfiguration.class)
|
||||||
|
@ConditionalOnMissingBean(ResourceConfig.class)
|
||||||
|
static class JerseyInfrastructureConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(JerseyApplicationPath.class)
|
||||||
|
JerseyApplicationPath jerseyApplicationPath(JerseyProperties properties, ResourceConfig config) {
|
||||||
|
return new DefaultJerseyApplicationPath(properties.getApplicationPath(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig(ObjectProvider<ResourceConfigCustomizer> resourceConfigCustomizers) {
|
||||||
|
ResourceConfig resourceConfig = new ResourceConfig();
|
||||||
|
resourceConfigCustomizers.orderedStream().forEach((customizer) -> customizer.customize(resourceConfig));
|
||||||
|
return resourceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface that can be implemented by beans wishing to customize Jersey's
|
||||||
|
* {@link ResourceConfig} in the management context before it is used.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 2.3.10
|
||||||
|
*/
|
||||||
|
public interface ManagementContextResourceConfigCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the resource config.
|
||||||
|
* @param config the {@link ResourceConfig} to customize
|
||||||
|
*/
|
||||||
|
void customize(ResourceConfig config);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for a Jersey-based management context.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.actuate.autoconfigure.web.jersey;
|
@ -1,7 +1,10 @@
|
|||||||
org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration
|
||||||
|
org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration
|
||||||
|
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration
|
||||||
|
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseyChildManagementContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementChildContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementChildContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementChildContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementChildContextConfiguration
|
||||||
org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration
|
org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.endpoint.web.jersey;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.model.Resource;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for web endpoints running on Jersey.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class JerseyWebEndpointIntegrationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenJerseyIsConfiguredToUseAFilterThenResourceRegistrationSucceeds() {
|
||||||
|
new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseySameManagementContextConfiguration.class,
|
||||||
|
JerseyAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class,
|
||||||
|
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||||
|
JerseyWebEndpointManagementContextConfiguration.class))
|
||||||
|
.withUserConfiguration(ResourceConfigConfiguration.class)
|
||||||
|
.withClassLoader(new FilteredClassLoader(DispatcherServlet.class))
|
||||||
|
.withPropertyValues("spring.jersey.type=filter", "server.port=0").run((context) -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
Set<Resource> resources = context.getBean(ResourceConfig.class).getResources();
|
||||||
|
assertThat(resources).hasSize(1);
|
||||||
|
Resource resource = resources.iterator().next();
|
||||||
|
assertThat(resource.getPath()).isEqualTo("/actuator");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ResourceConfigConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig() {
|
||||||
|
return new ResourceConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.endpoint.web.jersey;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration.JerseyWebEndpointsResourcesRegistrar;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyWebEndpointManagementContextConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Michael Simons
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
class JerseyWebEndpointManagementContextConfigurationTests {
|
||||||
|
|
||||||
|
private final WebApplicationContextRunner runner = new WebApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(WebEndpointAutoConfiguration.class,
|
||||||
|
JerseyWebEndpointManagementContextConfiguration.class))
|
||||||
|
.withBean(WebEndpointsSupplier.class, () -> Collections::emptyList);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jerseyWebEndpointsResourcesRegistrarForEndpointsIsAutoConfigured() {
|
||||||
|
this.runner.run((context) -> assertThat(context).hasSingleBean(JerseyWebEndpointsResourcesRegistrar.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnServletWebApplication() {
|
||||||
|
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseySameManagementContextConfiguration.class));
|
||||||
|
contextRunner
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnClassResourceConfig() {
|
||||||
|
this.runner.withClassLoader(new FilteredClassLoader(ResourceConfig.class))
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.integrationtest;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for the Jersey actuator endpoints.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
class JerseyEndpointIntegrationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void linksAreProvidedToAllEndpointTypes() {
|
||||||
|
testJerseyEndpoints(new Class<?>[] { EndpointsConfiguration.class, ResourceConfigConfiguration.class });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void linksPageIsNotAvailableWhenDisabled() {
|
||||||
|
getContextRunner(new Class<?>[] { EndpointsConfiguration.class, ResourceConfigConfiguration.class })
|
||||||
|
.withPropertyValues("management.endpoints.web.discovery.enabled:false").run((context) -> {
|
||||||
|
int port = context
|
||||||
|
.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
|
||||||
|
.getWebServer().getPort();
|
||||||
|
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port)
|
||||||
|
.responseTimeout(Duration.ofMinutes(5)).build();
|
||||||
|
client.get().uri("/actuator").exchange().expectStatus().isNotFound();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void actuatorEndpointsWhenUserProvidedResourceConfigBeanNotAvailable() {
|
||||||
|
testJerseyEndpoints(new Class<?>[] { EndpointsConfiguration.class });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void actuatorEndpointsWhenSecurityAvailable() {
|
||||||
|
WebApplicationContextRunner contextRunner = getContextRunner(
|
||||||
|
new Class<?>[] { EndpointsConfiguration.class, ResourceConfigConfiguration.class },
|
||||||
|
getAutoconfigurations(SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class));
|
||||||
|
contextRunner.run((context) -> {
|
||||||
|
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
|
||||||
|
.getWebServer().getPort();
|
||||||
|
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port)
|
||||||
|
.responseTimeout(Duration.ofMinutes(5)).build();
|
||||||
|
client.get().uri("/actuator").exchange().expectStatus().isUnauthorized();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void testJerseyEndpoints(Class<?>[] userConfigurations) {
|
||||||
|
getContextRunner(userConfigurations).run((context) -> {
|
||||||
|
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
|
||||||
|
.getWebServer().getPort();
|
||||||
|
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port)
|
||||||
|
.responseTimeout(Duration.ofMinutes(5)).build();
|
||||||
|
client.get().uri("/actuator").exchange().expectStatus().isOk().expectBody().jsonPath("_links.beans")
|
||||||
|
.isNotEmpty().jsonPath("_links.restcontroller").doesNotExist().jsonPath("_links.controller")
|
||||||
|
.doesNotExist();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
WebApplicationContextRunner getContextRunner(Class<?>[] userConfigurations,
|
||||||
|
Class<?>... additionalAutoConfigurations) {
|
||||||
|
FilteredClassLoader classLoader = new FilteredClassLoader(DispatcherServlet.class);
|
||||||
|
return new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withClassLoader(classLoader)
|
||||||
|
.withConfiguration(AutoConfigurations.of(getAutoconfigurations(additionalAutoConfigurations)))
|
||||||
|
.withUserConfiguration(userConfigurations)
|
||||||
|
.withPropertyValues("management.endpoints.web.exposure.include:*", "server.port:0");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?>[] getAutoconfigurations(Class<?>... additional) {
|
||||||
|
List<Class<?>> autoconfigurations = new ArrayList<>(Arrays.asList(JacksonAutoConfiguration.class,
|
||||||
|
JerseyAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||||
|
ServletWebServerFactoryAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||||
|
ManagementContextAutoConfiguration.class, BeansEndpointAutoConfiguration.class));
|
||||||
|
autoconfigurations.addAll(Arrays.asList(additional));
|
||||||
|
return autoconfigurations.toArray(new Class<?>[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerEndpoint(id = "controller")
|
||||||
|
static class TestControllerEndpoint {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestControllerEndpoint(id = "restcontroller")
|
||||||
|
static class TestRestControllerEndpoint {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class EndpointsConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TestControllerEndpoint testControllerEndpoint() {
|
||||||
|
return new TestControllerEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TestRestControllerEndpoint testRestControllerEndpoint() {
|
||||||
|
return new TestRestControllerEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ResourceConfigConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig testResourceConfig() {
|
||||||
|
return new ResourceConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.integrationtest;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for health groups on an additional path on Jersey.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
class JerseyHealthEndpointAdditionalPathIntegrationTests extends
|
||||||
|
AbstractHealthEndpointAdditionalPathIntegrationTests<WebApplicationContextRunner, ConfigurableWebApplicationContext, AssertableWebApplicationContext> {
|
||||||
|
|
||||||
|
JerseyHealthEndpointAdditionalPathIntegrationTests() {
|
||||||
|
super(new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
EndpointAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class,
|
||||||
|
WebEndpointAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
|
||||||
|
HealthEndpointAutoConfiguration.class, DiskSpaceHealthContributorAutoConfiguration.class))
|
||||||
|
.withInitializer(new ServerPortInfoApplicationContextInitializer())
|
||||||
|
.withClassLoader(new FilteredClassLoader(DispatcherServlet.class)).withPropertyValues("server.port=0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.metrics.jersey;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
import io.micrometer.core.instrument.Tag;
|
||||||
|
import io.micrometer.core.instrument.Timer;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.DefaultJerseyTagsProvider;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider;
|
||||||
|
import io.micrometer.core.instrument.binder.jersey.server.MetricsApplicationEventListener;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.monitoring.RequestEvent;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyServerMetricsAutoConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Michael Weirauch
|
||||||
|
* @author Michael Simons
|
||||||
|
*/
|
||||||
|
class JerseyServerMetricsAutoConfigurationTests {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple())
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseyServerMetricsAutoConfiguration.class));
|
||||||
|
|
||||||
|
private final WebApplicationContextRunner webContextRunner = new WebApplicationContextRunner(
|
||||||
|
AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseyAutoConfiguration.class,
|
||||||
|
JerseyServerMetricsAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class,
|
||||||
|
SimpleMetricsExportAutoConfiguration.class, MetricsAutoConfiguration.class))
|
||||||
|
.withUserConfiguration(ResourceConfiguration.class).withPropertyValues("server.port:0");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldOnlyBeActiveInWebApplicationContext() {
|
||||||
|
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ResourceConfigCustomizer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldProvideAllNecessaryBeans() {
|
||||||
|
this.webContextRunner.run((context) -> assertThat(context).hasSingleBean(DefaultJerseyTagsProvider.class)
|
||||||
|
.hasSingleBean(ResourceConfigCustomizer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHonorExistingTagProvider() {
|
||||||
|
this.webContextRunner.withUserConfiguration(CustomJerseyTagsProviderConfiguration.class)
|
||||||
|
.run((context) -> assertThat(context).hasSingleBean(CustomJerseyTagsProvider.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void httpRequestsAreTimed() {
|
||||||
|
this.webContextRunner.run((context) -> {
|
||||||
|
doRequest(context);
|
||||||
|
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||||
|
Timer timer = registry.get("http.server.requests").tag("uri", "/users/{id}").timer();
|
||||||
|
assertThat(timer.count()).isEqualTo(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void noHttpRequestsTimedWhenJerseyInstrumentationMissingFromClasspath() {
|
||||||
|
this.webContextRunner.withClassLoader(new FilteredClassLoader(MetricsApplicationEventListener.class))
|
||||||
|
.run((context) -> {
|
||||||
|
doRequest(context);
|
||||||
|
|
||||||
|
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||||
|
assertThat(registry.find("http.server.requests").timer()).isNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doRequest(AssertableWebApplicationContext context) {
|
||||||
|
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
|
||||||
|
.getWebServer().getPort();
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
restTemplate.getForEntity(URI.create("http://localhost:" + port + "/users/3"), String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ResourceConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig() {
|
||||||
|
return new ResourceConfig().register(new TestResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/users")
|
||||||
|
public class TestResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}")
|
||||||
|
public String getUser(@PathParam("id") String id) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class CustomJerseyTagsProviderConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JerseyTagsProvider customJerseyTagsProvider() {
|
||||||
|
return new CustomJerseyTagsProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CustomJerseyTagsProvider implements JerseyTagsProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Tag> httpRequestTags(RequestEvent event) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Tag> httpLongRequestTags(RequestEvent event) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.security.servlet;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link EndpointRequest} with Jersey.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
class JerseyEndpointRequestIntegrationTests extends AbstractEndpointRequestIntegrationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toLinksWhenApplicationPathSetShouldMatch() {
|
||||||
|
getContextRunner().withPropertyValues("spring.jersey.application-path=/admin").run((context) -> {
|
||||||
|
WebTestClient webTestClient = getWebTestClient(context);
|
||||||
|
webTestClient.get().uri("/admin/actuator/").exchange().expectStatus()
|
||||||
|
.isEqualTo(expectedStatusWithTrailingSlash());
|
||||||
|
webTestClient.get().uri("/admin/actuator").exchange().expectStatus().isOk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toEndpointWhenApplicationPathSetShouldMatch() {
|
||||||
|
getContextRunner().withPropertyValues("spring.jersey.application-path=/admin").run((context) -> {
|
||||||
|
WebTestClient webTestClient = getWebTestClient(context);
|
||||||
|
webTestClient.get().uri("/admin/actuator/e1").exchange().expectStatus().isOk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toAnyEndpointWhenApplicationPathSetShouldMatch() {
|
||||||
|
getContextRunner()
|
||||||
|
.withPropertyValues("spring.jersey.application-path=/admin", "spring.security.user.password=password")
|
||||||
|
.run((context) -> {
|
||||||
|
WebTestClient webTestClient = getWebTestClient(context);
|
||||||
|
webTestClient.get().uri("/admin/actuator/e2").exchange().expectStatus().isUnauthorized();
|
||||||
|
webTestClient.get().uri("/admin/actuator/e2").header("Authorization", getBasicAuth()).exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toAnyEndpointShouldMatchServletEndpoint() {
|
||||||
|
getContextRunner().withPropertyValues("spring.security.user.password=password",
|
||||||
|
"management.endpoints.web.exposure.include=se1").run((context) -> {
|
||||||
|
WebTestClient webTestClient = getWebTestClient(context);
|
||||||
|
webTestClient.get().uri("/actuator/se1").exchange().expectStatus().isUnauthorized();
|
||||||
|
webTestClient.get().uri("/actuator/se1").header("Authorization", getBasicAuth()).exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
webTestClient.get().uri("/actuator/se1/list").exchange().expectStatus().isUnauthorized();
|
||||||
|
webTestClient.get().uri("/actuator/se1/list").header("Authorization", getBasicAuth()).exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toAnyEndpointWhenApplicationPathSetShouldMatchServletEndpoint() {
|
||||||
|
getContextRunner().withPropertyValues("spring.jersey.application-path=/admin",
|
||||||
|
"spring.security.user.password=password", "management.endpoints.web.exposure.include=se1")
|
||||||
|
.run((context) -> {
|
||||||
|
WebTestClient webTestClient = getWebTestClient(context);
|
||||||
|
webTestClient.get().uri("/admin/actuator/se1").exchange().expectStatus().isUnauthorized();
|
||||||
|
webTestClient.get().uri("/admin/actuator/se1").header("Authorization", getBasicAuth()).exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
webTestClient.get().uri("/admin/actuator/se1/list").exchange().expectStatus().isUnauthorized();
|
||||||
|
webTestClient.get().uri("/admin/actuator/se1/list").header("Authorization", getBasicAuth())
|
||||||
|
.exchange().expectStatus().isOk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpStatus expectedStatusWithTrailingSlash() {
|
||||||
|
return HttpStatus.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected WebApplicationContextRunner createContextRunner() {
|
||||||
|
return new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
|
||||||
|
.withUserConfiguration(JerseyEndpointConfiguration.class)
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseyAutoConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(WebEndpointProperties.class)
|
||||||
|
static class JerseyEndpointConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TomcatServletWebServerFactory tomcat() {
|
||||||
|
return new TomcatServletWebServerFactory(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig() {
|
||||||
|
return new ResourceConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyChildManagementContextConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
@ClassPathExclusions("spring-webmvc-*")
|
||||||
|
class JerseyChildManagementContextConfigurationTests {
|
||||||
|
|
||||||
|
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||||
|
.withUserConfiguration(JerseyChildManagementContextConfiguration.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnServletWebApplication() {
|
||||||
|
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseySameManagementContextConfiguration.class));
|
||||||
|
contextRunner
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnClassResourceConfig() {
|
||||||
|
this.contextRunner.withClassLoader(new FilteredClassLoader(ResourceConfig.class))
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jerseyApplicationPathIsAutoConfigured() {
|
||||||
|
this.contextRunner.run((context) -> {
|
||||||
|
JerseyApplicationPath bean = context.getBean(JerseyApplicationPath.class);
|
||||||
|
assertThat(bean.getPath()).isEqualTo("/");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void servletRegistrationBeanIsAutoConfigured() {
|
||||||
|
this.contextRunner.run((context) -> {
|
||||||
|
ServletRegistrationBean<ServletContainer> bean = context.getBean(ServletRegistrationBean.class);
|
||||||
|
assertThat(bean.getUrlMappings()).containsExactly("/*");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceConfigCustomizerBeanIsNotRequired() {
|
||||||
|
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ResourceConfig.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceConfigIsCustomizedWithResourceConfigCustomizerBean() {
|
||||||
|
this.contextRunner.withUserConfiguration(CustomizerConfiguration.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ResourceConfig.class);
|
||||||
|
ResourceConfig config = context.getBean(ResourceConfig.class);
|
||||||
|
ManagementContextResourceConfigCustomizer customizer = context
|
||||||
|
.getBean(ManagementContextResourceConfigCustomizer.class);
|
||||||
|
then(customizer).should().customize(config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class CustomizerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ManagementContextResourceConfigCustomizer resourceConfigCustomizer() {
|
||||||
|
return mock(ManagementContextResourceConfigCustomizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseySameManagementContextConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
@ClassPathExclusions("spring-webmvc-*")
|
||||||
|
class JerseySameManagementContextConfigurationTests {
|
||||||
|
|
||||||
|
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseySameManagementContextConfiguration.class));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnServletWebApplication() {
|
||||||
|
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseySameManagementContextConfiguration.class));
|
||||||
|
contextRunner
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void autoConfigurationIsConditionalOnClassResourceConfig() {
|
||||||
|
this.contextRunner.withClassLoader(new FilteredClassLoader(ResourceConfig.class))
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jerseyApplicationPathIsAutoConfiguredWhenNeeded() {
|
||||||
|
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DefaultJerseyApplicationPath.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jerseyApplicationPathIsConditionalOnMissingBean() {
|
||||||
|
this.contextRunner.withUserConfiguration(ConfigWithJerseyApplicationPath.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(JerseyApplicationPath.class);
|
||||||
|
assertThat(context).hasBean("testJerseyApplicationPath");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void existingResourceConfigBeanShouldNotAutoConfigureRelatedBeans() {
|
||||||
|
this.contextRunner.withUserConfiguration(ConfigWithResourceConfig.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ResourceConfig.class);
|
||||||
|
assertThat(context).doesNotHaveBean(JerseyApplicationPath.class);
|
||||||
|
assertThat(context).doesNotHaveBean(ServletRegistrationBean.class);
|
||||||
|
assertThat(context).hasBean("customResourceConfig");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void servletRegistrationBeanIsAutoConfiguredWhenNeeded() {
|
||||||
|
this.contextRunner.withPropertyValues("spring.jersey.application-path=/jersey").run((context) -> {
|
||||||
|
ServletRegistrationBean<ServletContainer> bean = context.getBean(ServletRegistrationBean.class);
|
||||||
|
assertThat(bean.getUrlMappings()).containsExactly("/jersey/*");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceConfigIsCustomizedWithResourceConfigCustomizerBean() {
|
||||||
|
this.contextRunner.withUserConfiguration(CustomizerConfiguration.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ResourceConfig.class);
|
||||||
|
ResourceConfig config = context.getBean(ResourceConfig.class);
|
||||||
|
ManagementContextResourceConfigCustomizer customizer = context
|
||||||
|
.getBean(ManagementContextResourceConfigCustomizer.class);
|
||||||
|
then(customizer).should().customize(config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ConfigWithJerseyApplicationPath {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
JerseyApplicationPath testJerseyApplicationPath() {
|
||||||
|
return mock(JerseyApplicationPath.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ConfigWithResourceConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig customResourceConfig() {
|
||||||
|
return new ResourceConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class CustomizerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ManagementContextResourceConfigCustomizer resourceConfigCustomizer() {
|
||||||
|
return mock(ManagementContextResourceConfigCustomizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,356 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.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.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, Object> extractBodyArguments(ContainerRequestContext data) {
|
||||||
|
Map<String, Object> entity = ((ContainerRequest) data).readEntity(Map.class);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!(response instanceof WebEndpointResponse)) {
|
||||||
|
return Response.status(Status.OK).entity(convertIfNecessary(response)).build();
|
||||||
|
}
|
||||||
|
WebEndpointResponse<?> webEndpointResponse = (WebEndpointResponse<?>) response;
|
||||||
|
return Response.status(webEndpointResponse.getStatus())
|
||||||
|
.header("Content-Type", webEndpointResponse.getContentType())
|
||||||
|
.entity(convertIfNecessary(webEndpointResponse.getBody())).build();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object convertIfNecessary(Object body) throws IOException {
|
||||||
|
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());
|
||||||
|
return Response.ok(Collections.singletonMap("_links", links)).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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.Set;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.model.Resource;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
|
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 class JerseyHealthEndpointAdditionalPathResourceFactory extends JerseyEndpointResourceFactory {
|
||||||
|
|
||||||
|
private final Set<HealthEndpointGroup> groups;
|
||||||
|
|
||||||
|
private final WebServerNamespace serverNamespace;
|
||||||
|
|
||||||
|
public JerseyHealthEndpointAdditionalPathResourceFactory(WebServerNamespace serverNamespace,
|
||||||
|
HealthEndpointGroups groups) {
|
||||||
|
this.serverNamespace = serverNamespace;
|
||||||
|
this.groups = groups.getAllWithAdditionalPath(serverNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Resource createResource(EndpointMapping endpointMapping, WebOperation operation) {
|
||||||
|
WebOperationRequestPredicate requestPredicate = operation.getRequestPredicate();
|
||||||
|
String matchAllRemainingPathSegmentsVariable = requestPredicate.getMatchAllRemainingPathSegmentsVariable();
|
||||||
|
if (matchAllRemainingPathSegmentsVariable != null) {
|
||||||
|
for (HealthEndpointGroup group : this.groups) {
|
||||||
|
AdditionalHealthEndpointPath additionalPath = group.getAdditionalPath();
|
||||||
|
if (additionalPath != null) {
|
||||||
|
return getResource(endpointMapping, operation, requestPredicate, additionalPath.getValue(),
|
||||||
|
this.serverNamespace, (data, pathSegmentsVariable) -> data.getUriInfo().getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.ws.rs.ext.ContextResolver;
|
||||||
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.model.Resource;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
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.annotation.AbstractWebEndpointIntegrationTests;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||||
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for web endpoints exposed using Jersey.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @see JerseyEndpointResourceFactory
|
||||||
|
*/
|
||||||
|
public class JerseyWebEndpointIntegrationTests
|
||||||
|
extends AbstractWebEndpointIntegrationTests<AnnotationConfigServletWebServerApplicationContext> {
|
||||||
|
|
||||||
|
public JerseyWebEndpointIntegrationTests() {
|
||||||
|
super(JerseyWebEndpointIntegrationTests::createApplicationContext,
|
||||||
|
JerseyWebEndpointIntegrationTests::applyAuthenticatedConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnnotationConfigServletWebServerApplicationContext createApplicationContext() {
|
||||||
|
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
|
||||||
|
context.register(JerseyConfiguration.class);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyAuthenticatedConfiguration(AnnotationConfigServletWebServerApplicationContext context) {
|
||||||
|
context.register(AuthenticatedConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPort(AnnotationConfigServletWebServerApplicationContext context) {
|
||||||
|
return context.getWebServer().getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validateErrorBody(WebTestClient.BodyContentSpec body, HttpStatus status, String path,
|
||||||
|
String message) {
|
||||||
|
// Jersey doesn't support the general error page handling
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test
|
||||||
|
@Disabled("Jersey does not distinguish between /example and /example/")
|
||||||
|
protected void operationWithTrailingSlashShouldNotMatch() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class JerseyConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TomcatServletWebServerFactory tomcat() {
|
||||||
|
return new TomcatServletWebServerFactory(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ServletRegistrationBean<ServletContainer> servletContainer(ResourceConfig resourceConfig) {
|
||||||
|
return new ServletRegistrationBean<>(new ServletContainer(resourceConfig), "/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig(Environment environment, WebEndpointDiscoverer endpointDiscoverer,
|
||||||
|
EndpointMediaTypes endpointMediaTypes) {
|
||||||
|
ResourceConfig resourceConfig = new ResourceConfig();
|
||||||
|
String endpointPath = environment.getProperty("endpointPath");
|
||||||
|
Collection<Resource> resources = new JerseyEndpointResourceFactory().createEndpointResources(
|
||||||
|
new EndpointMapping(endpointPath), endpointDiscoverer.getEndpoints(), endpointMediaTypes,
|
||||||
|
new EndpointLinksResolver(endpointDiscoverer.getEndpoints()), StringUtils.hasText(endpointPath));
|
||||||
|
resourceConfig.registerResources(new HashSet<>(resources));
|
||||||
|
resourceConfig.register(JacksonFeature.class);
|
||||||
|
resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()), ContextResolver.class);
|
||||||
|
return resourceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class AuthenticatedConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Filter securityFilter() {
|
||||||
|
return new OncePerRequestFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||||
|
context.setAuthentication(new UsernamePasswordAuthenticationToken("Alice", "secret",
|
||||||
|
Arrays.asList(new SimpleGrantedAuthority("ROLE_ACTUATOR"))));
|
||||||
|
SecurityContextHolder.setContext(context);
|
||||||
|
try {
|
||||||
|
filterChain.doFilter(new SecurityContextHolderAwareRequestWrapper(request, "ROLE_"), response);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private ObjectMapperContextResolver(ObjectMapper objectMapper) {
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectMapper getContext(Class<?> type) {
|
||||||
|
return this.objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
||||||
|
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.ServletRegistration;
|
||||||
|
import jakarta.ws.rs.ext.ContextResolver;
|
||||||
|
import jakarta.xml.bind.annotation.XmlElement;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.spring.SpringComponentProvider;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.glassfish.jersey.servlet.ServletProperties;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ConditionalOnMissingFilterBean;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.web.servlet.DynamicRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.web.WebApplicationInitializer;
|
||||||
|
import org.springframework.web.context.ServletContextAware;
|
||||||
|
import org.springframework.web.filter.RequestContextFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link EnableAutoConfiguration Auto-configuration} for Jersey.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration(before = DispatcherServletAutoConfiguration.class, after = JacksonAutoConfiguration.class)
|
||||||
|
@ConditionalOnClass({ SpringComponentProvider.class, ServletRegistration.class })
|
||||||
|
@ConditionalOnBean(type = "org.glassfish.jersey.server.ResourceConfig")
|
||||||
|
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||||
|
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
@EnableConfigurationProperties(JerseyProperties.class)
|
||||||
|
public class JerseyAutoConfiguration implements ServletContextAware {
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(JerseyAutoConfiguration.class);
|
||||||
|
|
||||||
|
private final JerseyProperties jersey;
|
||||||
|
|
||||||
|
private final ResourceConfig config;
|
||||||
|
|
||||||
|
public JerseyAutoConfiguration(JerseyProperties jersey, ResourceConfig config,
|
||||||
|
ObjectProvider<ResourceConfigCustomizer> customizers) {
|
||||||
|
this.jersey = jersey;
|
||||||
|
this.config = config;
|
||||||
|
customizers.orderedStream().forEach((customizer) -> customizer.customize(this.config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
|
||||||
|
public FilterRegistrationBean<RequestContextFilter> requestContextFilter() {
|
||||||
|
FilterRegistrationBean<RequestContextFilter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter(new RequestContextFilter());
|
||||||
|
registration.setOrder(this.jersey.getFilter().getOrder() - 1);
|
||||||
|
registration.setName("requestContextFilter");
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public JerseyApplicationPath jerseyApplicationPath() {
|
||||||
|
return new DefaultJerseyApplicationPath(this.jersey.getApplicationPath(), this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(name = "jerseyFilterRegistration")
|
||||||
|
@ConditionalOnProperty(prefix = "spring.jersey", name = "type", havingValue = "filter")
|
||||||
|
public FilterRegistrationBean<ServletContainer> jerseyFilterRegistration(JerseyApplicationPath applicationPath) {
|
||||||
|
FilterRegistrationBean<ServletContainer> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter(new ServletContainer(this.config));
|
||||||
|
registration.setUrlPatterns(Collections.singletonList(applicationPath.getUrlMapping()));
|
||||||
|
registration.setOrder(this.jersey.getFilter().getOrder());
|
||||||
|
registration.addInitParameter(ServletProperties.FILTER_CONTEXT_PATH, stripPattern(applicationPath.getPath()));
|
||||||
|
addInitParameters(registration);
|
||||||
|
registration.setName("jerseyFilter");
|
||||||
|
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stripPattern(String path) {
|
||||||
|
if (path.endsWith("/*")) {
|
||||||
|
path = path.substring(0, path.lastIndexOf("/*"));
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(name = "jerseyServletRegistration")
|
||||||
|
@ConditionalOnProperty(prefix = "spring.jersey", name = "type", havingValue = "servlet", matchIfMissing = true)
|
||||||
|
public ServletRegistrationBean<ServletContainer> jerseyServletRegistration(JerseyApplicationPath applicationPath) {
|
||||||
|
ServletRegistrationBean<ServletContainer> registration = new ServletRegistrationBean<>(
|
||||||
|
new ServletContainer(this.config), applicationPath.getUrlMapping());
|
||||||
|
addInitParameters(registration);
|
||||||
|
registration.setName(getServletRegistrationName());
|
||||||
|
registration.setLoadOnStartup(this.jersey.getServlet().getLoadOnStartup());
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServletRegistrationName() {
|
||||||
|
return ClassUtils.getUserClass(this.config.getClass()).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInitParameters(DynamicRegistrationBean<?> registration) {
|
||||||
|
this.jersey.getInit().forEach(registration::addInitParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServletContext(ServletContext servletContext) {
|
||||||
|
String servletRegistrationName = getServletRegistrationName();
|
||||||
|
ServletRegistration registration = servletContext.getServletRegistration(servletRegistrationName);
|
||||||
|
if (registration != null) {
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
logger.info("Configuring existing registration for Jersey servlet '" + servletRegistrationName + "'");
|
||||||
|
}
|
||||||
|
registration.setInitParameters(this.jersey.getInit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public static final class JerseyWebApplicationInitializer implements WebApplicationInitializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||||
|
// We need to switch *off* the Jersey WebApplicationInitializer because it
|
||||||
|
// will try and register a ContextLoaderListener which we don't need
|
||||||
|
servletContext.setInitParameter("contextConfigLocation", "<NONE>");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnClass(JacksonFeature.class)
|
||||||
|
@ConditionalOnSingleCandidate(ObjectMapper.class)
|
||||||
|
static class JacksonResourceConfigCustomizer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfigCustomizer resourceConfigCustomizer(final ObjectMapper objectMapper) {
|
||||||
|
return (ResourceConfig config) -> {
|
||||||
|
config.register(JacksonFeature.class);
|
||||||
|
config.register(new ObjectMapperContextResolver(objectMapper), ContextResolver.class);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnClass({ JakartaXmlBindAnnotationIntrospector.class, XmlElement.class })
|
||||||
|
static class JaxbObjectMapperCustomizer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
void addJaxbAnnotationIntrospector(ObjectMapper objectMapper) {
|
||||||
|
JakartaXmlBindAnnotationIntrospector jaxbAnnotationIntrospector = new JakartaXmlBindAnnotationIntrospector(
|
||||||
|
objectMapper.getTypeFactory());
|
||||||
|
objectMapper.setAnnotationIntrospectors(
|
||||||
|
createPair(objectMapper.getSerializationConfig(), jaxbAnnotationIntrospector),
|
||||||
|
createPair(objectMapper.getDeserializationConfig(), jaxbAnnotationIntrospector));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationIntrospector createPair(MapperConfig<?> config,
|
||||||
|
JakartaXmlBindAnnotationIntrospector jaxbAnnotationIntrospector) {
|
||||||
|
return AnnotationIntrospector.pair(config.getAnnotationIntrospector(), jaxbAnnotationIntrospector);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private ObjectMapperContextResolver(ObjectMapper objectMapper) {
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectMapper getContext(Class<?> type) {
|
||||||
|
return this.objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ConfigurationProperties @ConfigurationProperties} for Jersey.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "spring.jersey")
|
||||||
|
public class JerseyProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jersey integration type.
|
||||||
|
*/
|
||||||
|
private Type type = Type.SERVLET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init parameters to pass to Jersey through the servlet or filter.
|
||||||
|
*/
|
||||||
|
private Map<String, String> init = new HashMap<>();
|
||||||
|
|
||||||
|
private final Filter filter = new Filter();
|
||||||
|
|
||||||
|
private final Servlet servlet = new Servlet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path that serves as the base URI for the application. If specified, overrides the
|
||||||
|
* value of "@ApplicationPath".
|
||||||
|
*/
|
||||||
|
private String applicationPath;
|
||||||
|
|
||||||
|
public Filter getFilter() {
|
||||||
|
return this.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Servlet getServlet() {
|
||||||
|
return this.servlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getInit() {
|
||||||
|
return this.init;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInit(Map<String, String> init) {
|
||||||
|
this.init = init;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApplicationPath() {
|
||||||
|
return this.applicationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplicationPath(String applicationPath) {
|
||||||
|
this.applicationPath = applicationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
|
||||||
|
SERVLET, FILTER
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Filter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jersey filter chain order.
|
||||||
|
*/
|
||||||
|
private int order;
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Servlet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load on startup priority of the Jersey servlet.
|
||||||
|
*/
|
||||||
|
private int loadOnStartup = -1;
|
||||||
|
|
||||||
|
public int getLoadOnStartup() {
|
||||||
|
return this.loadOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoadOnStartup(int loadOnStartup) {
|
||||||
|
this.loadOnStartup = loadOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface that can be implemented by beans wishing to customize Jersey's
|
||||||
|
* {@link ResourceConfig} before it is used.
|
||||||
|
*
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ResourceConfigCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the resource config.
|
||||||
|
* @param config the {@link ResourceConfig} to customize
|
||||||
|
*/
|
||||||
|
void customize(ResourceConfig config);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-configuration for Jersey.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.autoconfigure.jersey;
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.servlet;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyProperties;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link JerseyApplicationPath} that derives the path from
|
||||||
|
* {@link JerseyProperties} or the {@code @ApplicationPath} annotation.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @since 2.1.0
|
||||||
|
*/
|
||||||
|
public class DefaultJerseyApplicationPath implements JerseyApplicationPath {
|
||||||
|
|
||||||
|
private final String applicationPath;
|
||||||
|
|
||||||
|
private final ResourceConfig config;
|
||||||
|
|
||||||
|
public DefaultJerseyApplicationPath(String applicationPath, ResourceConfig config) {
|
||||||
|
this.applicationPath = applicationPath;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return resolveApplicationPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveApplicationPath() {
|
||||||
|
if (StringUtils.hasLength(this.applicationPath)) {
|
||||||
|
return this.applicationPath;
|
||||||
|
}
|
||||||
|
// Jersey doesn't like to be the default servlet, so map to /* as a fallback
|
||||||
|
return MergedAnnotations.from(this.config.getApplication().getClass(), SearchStrategy.TYPE_HIERARCHY)
|
||||||
|
.get(ApplicationPath.class).getValue(MergedAnnotation.VALUE, String.class).orElse("/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.servlet;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that can be used by auto-configurations that need path details Jersey's
|
||||||
|
* application path that serves as the base URI for the application.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @since 2.0.7
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface JerseyApplicationPath {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured path of the application.
|
||||||
|
* @return the configured path
|
||||||
|
*/
|
||||||
|
String getPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a form of the given path that's relative to the Jersey application path.
|
||||||
|
* @param path the path to make relative
|
||||||
|
* @return the relative path
|
||||||
|
*/
|
||||||
|
default String getRelativePath(String path) {
|
||||||
|
String prefix = getPrefix();
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
return prefix + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a cleaned up version of the path that can be used as a prefix for URLs. The
|
||||||
|
* resulting path will have path will not have a trailing slash.
|
||||||
|
* @return the prefix
|
||||||
|
* @see #getRelativePath(String)
|
||||||
|
*/
|
||||||
|
default String getPrefix() {
|
||||||
|
String result = getPath();
|
||||||
|
int index = result.indexOf('*');
|
||||||
|
if (index != -1) {
|
||||||
|
result = result.substring(0, index);
|
||||||
|
}
|
||||||
|
if (result.endsWith("/")) {
|
||||||
|
result = result.substring(0, result.length() - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a URL mapping pattern that can be used with a
|
||||||
|
* {@link ServletRegistrationBean} to map Jersey's servlet.
|
||||||
|
* @return the path as a servlet URL mapping
|
||||||
|
*/
|
||||||
|
default String getUrlMapping() {
|
||||||
|
String path = getPath();
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
if (path.equals("/")) {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
if (path.contains("*")) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
return path + "*";
|
||||||
|
}
|
||||||
|
return path + "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.core.Application;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using a custom {@link Application}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomApplicationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/test/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApplicationPath("/test")
|
||||||
|
static class TestApplication extends Application {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/hello")
|
||||||
|
public static class TestController {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello World";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
static class TestConfiguration {
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public class JerseyConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TestApplication testApplication() {
|
||||||
|
return new TestApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ResourceConfig conf(TestApplication app) {
|
||||||
|
ResourceConfig config = ResourceConfig.forApplication(app);
|
||||||
|
config.register(TestController.class);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "spring.jersey.type=filter", "server.servlet.context-path=/app",
|
||||||
|
"server.servlet.register-default-servlet=true" })
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomFilterContextPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/rest/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("/rest")
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "spring.jersey.type=filter", "server.servlet.register-default-servlet=true" })
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomFilterPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/rest/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("rest")
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom load on startup.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.jersey.servlet.load-on-startup=5")
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomLoadOnStartupTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext context;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
assertThat(this.context.getBean("jerseyServletRegistration")).hasFieldOrPropertyWithValue("loadOnStartup", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom ObjectMapper.
|
||||||
|
*
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = "spring.jackson.default-property-inclusion=non_null")
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomObjectMapperProviderTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> response = this.restTemplate.getForEntity("/rest/message", String.class);
|
||||||
|
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
|
||||||
|
assertThat("{\"subject\":\"Jersey\"}").isEqualTo(response.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("/rest")
|
||||||
|
@Path("/message")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Message message() {
|
||||||
|
return new Message("Jersey", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Message {
|
||||||
|
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
Message(String subject, String body) {
|
||||||
|
this.subject = subject;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return this.subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(String body) {
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JacksonAutoConfiguration.class,
|
||||||
|
JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "server.servlet.contextPath=/app")
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomServletContextPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/rest/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("/rest")
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationCustomServletPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/rest/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("/rest")
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "spring.jersey.type=filter", "server.servlet.register-default-servlet=true" })
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationDefaultFilterPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using default servlet paths.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationDefaultServletPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.ApplicationPath;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.xml.bind.annotation.XmlTransient;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} with an ObjectMapper.
|
||||||
|
*
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = "spring.jackson.default-property-inclusion:non-null")
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationObjectMapperProviderTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void responseIsSerializedUsingAutoConfiguredObjectMapper() {
|
||||||
|
ResponseEntity<String> response = this.restTemplate.getForEntity("/rest/message", String.class);
|
||||||
|
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
|
||||||
|
assertThat(response.getBody()).isEqualTo("{\"subject\":\"Jersey\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@ApplicationPath("/rest")
|
||||||
|
@Path("/message")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Message message() {
|
||||||
|
return new Message("Jersey", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Message {
|
||||||
|
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
Message() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Message(String subject, String body) {
|
||||||
|
this.subject = subject;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return this.subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(String body) {
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlTransient
|
||||||
|
public String getFoo() {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JacksonAutoConfiguration.class,
|
||||||
|
JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.apache.catalina.Context;
|
||||||
|
import org.apache.catalina.Wrapper;
|
||||||
|
import org.apache.tomcat.util.buf.UDecoder;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfigurationServletContainerTests.Application;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.system.CapturedOutput;
|
||||||
|
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||||
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that verify the behavior when deployed to a Servlet container where Jersey may
|
||||||
|
* have already initialized itself.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@DirtiesContext
|
||||||
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
|
class JerseyAutoConfigurationServletContainerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void existingJerseyServletIsAmended(CapturedOutput output) {
|
||||||
|
assertThat(output).contains("Configuring existing registration for Jersey servlet");
|
||||||
|
assertThat(output).contains("Servlet " + Application.class.getName() + " was not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ImportAutoConfiguration({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
@Import(ContainerConfiguration.class)
|
||||||
|
@Path("/hello")
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ContainerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TomcatServletWebServerFactory tomcat() {
|
||||||
|
return new TomcatServletWebServerFactory() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postProcessContext(Context context) {
|
||||||
|
Wrapper jerseyServlet = context.createWrapper();
|
||||||
|
String servletName = Application.class.getName();
|
||||||
|
jerseyServlet.setName(servletName);
|
||||||
|
jerseyServlet.setServletClass(ServletContainer.class.getName());
|
||||||
|
jerseyServlet.setServlet(new ServletContainer());
|
||||||
|
jerseyServlet.setOverridable(false);
|
||||||
|
context.addChild(jerseyServlet);
|
||||||
|
String pattern = UDecoder.URLDecode("/*", StandardCharsets.UTF_8);
|
||||||
|
context.addServletMappingDecoded(pattern, servletName);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.filter.RequestContextFilter;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class JerseyAutoConfigurationTests {
|
||||||
|
|
||||||
|
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(JerseyAutoConfiguration.class))
|
||||||
|
.withUserConfiguration(ResourceConfigConfiguration.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requestContextFilterRegistrationIsAutoConfigured() {
|
||||||
|
this.contextRunner.run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(FilterRegistrationBean.class);
|
||||||
|
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
|
||||||
|
assertThat(registration.getFilter()).isInstanceOf(RequestContextFilter.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUserDefinesARequestContextFilterTheAutoConfiguredRegistrationBacksOff() {
|
||||||
|
this.contextRunner.withUserConfiguration(RequestContextFilterConfiguration.class).run((context) -> {
|
||||||
|
assertThat(context).doesNotHaveBean(FilterRegistrationBean.class);
|
||||||
|
assertThat(context).hasSingleBean(RequestContextFilter.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUserDefinesARequestContextFilterRegistrationTheAutoConfiguredRegistrationBacksOff() {
|
||||||
|
this.contextRunner.withUserConfiguration(RequestContextFilterRegistrationConfiguration.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(FilterRegistrationBean.class);
|
||||||
|
assertThat(context).hasBean("customRequestContextFilterRegistration");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenJaxbIsAvailableTheObjectMapperIsCustomizedWithAnAnnotationIntrospector() {
|
||||||
|
this.contextRunner.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)).run((context) -> {
|
||||||
|
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||||
|
assertThat(objectMapper.getSerializationConfig().getAnnotationIntrospector().allIntrospectors().stream()
|
||||||
|
.filter(JakartaXmlBindAnnotationIntrospector.class::isInstance)).hasSize(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenJaxbIsNotAvailableTheObjectMapperCustomizationBacksOff() {
|
||||||
|
this.contextRunner.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
|
||||||
|
.withClassLoader(new FilteredClassLoader("jakarta.xml.bind.annotation")).run((context) -> {
|
||||||
|
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||||
|
assertThat(objectMapper.getSerializationConfig().getAnnotationIntrospector().allIntrospectors()
|
||||||
|
.stream().filter(JakartaXmlBindAnnotationIntrospector.class::isInstance)).isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenJacksonJaxbModuleIsNotAvailableTheObjectMapperCustomizationBacksOff() {
|
||||||
|
this.contextRunner.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
|
||||||
|
.withClassLoader(new FilteredClassLoader(JakartaXmlBindAnnotationIntrospector.class)).run((context) -> {
|
||||||
|
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||||
|
assertThat(objectMapper.getSerializationConfig().getAnnotationIntrospector().allIntrospectors()
|
||||||
|
.stream().filter(JakartaXmlBindAnnotationIntrospector.class::isInstance)).isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ResourceConfigConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfig resourceConfig() {
|
||||||
|
return new ResourceConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class RequestContextFilterConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RequestContextFilter requestContextFilter() {
|
||||||
|
return new RequestContextFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class RequestContextFilterRegistrationConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
FilterRegistrationBean<RequestContextFilter> customRequestContextFilterRegistration() {
|
||||||
|
return new FilterRegistrationBean<>(new RequestContextFilter());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.jersey;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyAutoConfiguration} when using custom application path.
|
||||||
|
*
|
||||||
|
* @author Eddú Meléndez
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.jersey.application-path=/api")
|
||||||
|
@DirtiesContext
|
||||||
|
class JerseyAutoConfigurationWithoutApplicationPathTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/api/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MinimalWebConfiguration
|
||||||
|
@Path("/hello")
|
||||||
|
public static class Application extends ResourceConfig {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
Application() {
|
||||||
|
register(Application.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Configuration
|
||||||
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, JerseyAutoConfiguration.class,
|
||||||
|
PropertyPlaceholderAutoConfiguration.class })
|
||||||
|
protected @interface MinimalWebConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.autoconfigure.web.servlet;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JerseyApplicationPath}.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
class JerseyApplicationPathTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRelativePathReturnsRelativePath() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring").getRelativePath("boot")).isEqualTo("spring/boot");
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring/").getRelativePath("boot")).isEqualTo("spring/boot");
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring").getRelativePath("/boot")).isEqualTo("spring/boot");
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring/*").getRelativePath("/boot")).isEqualTo("spring/boot");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPrefixWhenHasSimplePathReturnPath() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring").getPrefix()).isEqualTo("spring");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPrefixWhenHasPatternRemovesPattern() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring/*.do").getPrefix()).isEqualTo("spring");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPrefixWhenPathEndsWithSlashRemovesSlash() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring/").getPrefix()).isEqualTo("spring");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenPathIsEmptyReturnsSlash() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "").getUrlMapping()).isEqualTo("/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenPathIsSlashReturnsSlash() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "/").getUrlMapping()).isEqualTo("/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenPathContainsStarReturnsPath() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "/spring/*.do").getUrlMapping()).isEqualTo("/spring/*.do");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenHasPathNotEndingSlashReturnsSlashStarPattern() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "/spring/boot").getUrlMapping()).isEqualTo("/spring/boot/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenHasPathDoesNotStartWithSlashPrependsSlash() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "spring/boot").getUrlMapping()).isEqualTo("/spring/boot/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlMappingWhenHasPathEndingWithSlashReturnsSlashStarPattern() {
|
||||||
|
assertThat(((JerseyApplicationPath) () -> "/spring/boot/").getUrlMapping()).isEqualTo("/spring/boot/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
[[howto.jersey]]
|
||||||
|
== Jersey
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[howto.jersey.spring-security]]
|
||||||
|
=== Secure Jersey endpoints with Spring Security
|
||||||
|
Spring Security can be used to secure a Jersey-based web application in much the same way as it can be used to secure a Spring MVC-based web application.
|
||||||
|
However, if you want to use Spring Security's method-level security with Jersey, you must configure Jersey to use `setStatus(int)` rather `sendError(int)`.
|
||||||
|
This prevents Jersey from committing the response before Spring Security has had an opportunity to report an authentication or authorization failure to the client.
|
||||||
|
|
||||||
|
The `jersey.config.server.response.setStatusOverSendError` property must be set to `true` on the application's `ResourceConfig` bean, as shown in the following example:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim"]
|
||||||
|
----
|
||||||
|
include::{docs-java}/howto/jersey/springsecurity/JerseySetStatusOverSendErrorConfig.java[]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[howto.jersey.alongside-another-web-framework]]
|
||||||
|
=== Use Jersey Alongside Another Web Framework
|
||||||
|
To use Jersey alongside another web framework, such as Spring MVC, it should be configured so that it will allow the other framework to handle requests that it cannot handle.
|
||||||
|
First, configure Jersey to use a filter rather than a servlet by configuring the configprop:spring.jersey.type[] application property with a value of `filter`.
|
||||||
|
Second, configure your `ResourceConfig` to forward requests that would have resulted in a 404, as shown in the following example.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim"]
|
||||||
|
----
|
||||||
|
include::{docs-java}/howto/jersey/alongsideanotherwebframework/JerseyConfig.java[]
|
||||||
|
----
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.howto.jersey.alongsideanotherwebframework;
|
||||||
|
|
||||||
|
class Endpoint {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.howto.jersey.alongsideanotherwebframework;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.servlet.ServletProperties;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JerseyConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
public JerseyConfig() {
|
||||||
|
register(Endpoint.class);
|
||||||
|
property(ServletProperties.FILTER_FORWARD_ON_404, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.howto.jersey.springsecurity;
|
||||||
|
|
||||||
|
class Endpoint {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.howto.jersey.springsecurity;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
public JerseySetStatusOverSendErrorConfig() {
|
||||||
|
register(Endpoint.class);
|
||||||
|
setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.web.servlet.jersey;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Path("/hello")
|
||||||
|
public class MyEndpoint {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.docs.web.servlet.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyJerseyConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
public MyJerseyConfig() {
|
||||||
|
register(MyEndpoint.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.springframework.boot.starter"
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "Starter for building RESTful web applications using JAX-RS and Jersey. An alternative to spring-boot-starter-web"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-json"))
|
||||||
|
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat"))
|
||||||
|
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-validation"))
|
||||||
|
api("org.springframework:spring-web")
|
||||||
|
api("org.glassfish.jersey.containers:jersey-container-servlet-core")
|
||||||
|
api("org.glassfish.jersey.containers:jersey-container-servlet")
|
||||||
|
api("org.glassfish.jersey.core:jersey-server")
|
||||||
|
api("org.glassfish.jersey.ext:jersey-bean-validation") {
|
||||||
|
exclude group: "jakarta.el", module: "jakarta.el-api"
|
||||||
|
exclude group: "org.glassfish", module: "jakarta.el"
|
||||||
|
}
|
||||||
|
api("org.glassfish.jersey.ext:jersey-spring6")
|
||||||
|
api("org.glassfish.jersey.media:jersey-media-json-jackson")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRuntimeClasspathForConflicts {
|
||||||
|
ignore { name -> name.startsWith("org/aopalliance/intercept/") }
|
||||||
|
ignore { name -> name.startsWith("org/aopalliance/aop/") }
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "org.springframework.boot.conventions"
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "Spring Boot Jersey smoke test"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jersey"))
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat"))
|
||||||
|
|
||||||
|
if (JavaVersion.current().java9Compatible) {
|
||||||
|
runtimeOnly("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||||
|
}
|
||||||
|
|
||||||
|
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Path("/hello")
|
||||||
|
public class Endpoint {
|
||||||
|
|
||||||
|
private final Service service;
|
||||||
|
|
||||||
|
public Endpoint(Service service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.service.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JerseyConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
public JerseyConfig() {
|
||||||
|
register(Endpoint.class);
|
||||||
|
register(ReverseEndpoint.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Path("/reverse")
|
||||||
|
public class ReverseEndpoint {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String reverse(@QueryParam("input") @NotNull String input) {
|
||||||
|
return new StringBuilder(input).reverse().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SampleJerseyApplication extends SpringBootServletInitializer {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new SampleJerseyApplication().configure(new SpringApplicationBuilder(SampleJerseyApplication.class)).run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Service {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
public String message() {
|
||||||
|
return this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "logging.level.root=debug")
|
||||||
|
abstract class AbstractJerseyApplicationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/hello", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void reverse() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/reverse?input=olleh", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(entity.getBody()).isEqualTo("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validation() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/reverse", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void actuatorStatus() {
|
||||||
|
ResponseEntity<String> entity = this.restTemplate.getForEntity("/actuator/health", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(entity.getBody()).isEqualTo("{\"status\":\"UP\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import smoketest.jersey.AbstractJerseyManagementPortTests.ResourceConfigConfiguration;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||||
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for integration tests for Jersey using separate management and main service
|
||||||
|
* ports.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.server.port=0")
|
||||||
|
@Import(ResourceConfigConfiguration.class)
|
||||||
|
class AbstractJerseyManagementPortTests {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@LocalManagementPort
|
||||||
|
private int managementPort;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate testRestTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceShouldBeAvailableOnMainPort() {
|
||||||
|
ResponseEntity<String> entity = this.testRestTemplate.getForEntity("http://localhost:" + this.port + "/test",
|
||||||
|
String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(entity.getBody()).contains("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceShouldNotBeAvailableOnManagementPort() {
|
||||||
|
ResponseEntity<String> entity = this.testRestTemplate
|
||||||
|
.getForEntity("http://localhost:" + this.managementPort + "/test", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void actuatorShouldBeAvailableOnManagementPort() {
|
||||||
|
ResponseEntity<String> entity = this.testRestTemplate
|
||||||
|
.getForEntity("http://localhost:" + this.managementPort + "/actuator/health", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void actuatorShouldNotBeAvailableOnMainPort() {
|
||||||
|
ResponseEntity<String> entity = this.testRestTemplate
|
||||||
|
.getForEntity("http://localhost:" + this.port + "/actuator/health", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestConfiguration
|
||||||
|
static class ResourceConfigConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceConfigCustomizer customizer() {
|
||||||
|
return new ResourceConfigCustomizer() {
|
||||||
|
@Override
|
||||||
|
public void customize(ResourceConfig config) {
|
||||||
|
config.register(TestEndpoint.class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/test")
|
||||||
|
public static class TestEndpoint {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String test() {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||||
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for separate management and main service ports with custom
|
||||||
|
* application path.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "management.server.port=0", "spring.jersey.application-path=/app" })
|
||||||
|
class JerseyApplicationPathAndManagementPortTests {
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@LocalManagementPort
|
||||||
|
private int managementPort;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate testRestTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void applicationPathShouldNotAffectActuators() {
|
||||||
|
ResponseEntity<String> entity = this.testRestTemplate
|
||||||
|
.getForEntity("http://localhost:" + this.managementPort + "/actuator/health", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for separate management and main service ports with empty base path
|
||||||
|
* for endpoints.
|
||||||
|
*
|
||||||
|
* @author HaiTao Zhang
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "management.server.port=0", "management.endpoints.web.base-path=/" })
|
||||||
|
class JerseyDifferentPortSampleActuatorApplicationTests {
|
||||||
|
|
||||||
|
@LocalManagementPort
|
||||||
|
private int managementPort;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void linksEndpointShouldBeAvailable() {
|
||||||
|
ResponseEntity<String> entity = new TestRestTemplate("user", getPassword())
|
||||||
|
.getForEntity("http://localhost:" + this.managementPort + "/", String.class);
|
||||||
|
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(entity.getBody()).contains("\"_links\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPassword() {
|
||||||
|
return "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smoke tests for Jersey configured as a Filter.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
@TestPropertySource(properties = { "spring.jersey.type=filter", "server.servlet.register-default-servlet=true" })
|
||||||
|
class JerseyFilterApplicationTests extends AbstractJerseyApplicationTests {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for Jersey configured as a Servlet using separate management and main
|
||||||
|
* service ports.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
@TestPropertySource(properties = { "spring.jersey.type=filter", "server.servlet.register-default-servlet=true" })
|
||||||
|
class JerseyFilterManagementPortTests extends AbstractJerseyManagementPortTests {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smoke tests for Jersey configured as a Servlet.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class JerseyServletApplicationTests extends AbstractJerseyApplicationTests {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.jersey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for Jersey configured as a Servlet using separate management and main
|
||||||
|
* service ports.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class JerseyServletManagementPortTests extends AbstractJerseyManagementPortTests {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
id "java"
|
||||||
|
id "org.springframework.boot.conventions"
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "Spring Boot secure Jersey smoke test"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jersey"))
|
||||||
|
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
|
||||||
|
|
||||||
|
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Path("/hello")
|
||||||
|
public class Endpoint {
|
||||||
|
|
||||||
|
private final Service service;
|
||||||
|
|
||||||
|
public Endpoint(Service service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String message() {
|
||||||
|
return "Hello " + this.service.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JerseyConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
public JerseyConfig() {
|
||||||
|
register(Endpoint.class);
|
||||||
|
register(ReverseEndpoint.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Path("/reverse")
|
||||||
|
public class ReverseEndpoint {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String reverse(@QueryParam("input") @NotNull String input) {
|
||||||
|
return new StringBuilder(input).reverse().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServlet;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.EndpointServlet;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SampleSecureJerseyApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(SampleSecureJerseyApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
TestServletEndpoint servletEndpoint() {
|
||||||
|
return new TestServletEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServletEndpoint(id = "se1")
|
||||||
|
static class TestServletEndpoint implements Supplier<EndpointServlet> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EndpointServlet get() {
|
||||||
|
return new EndpointServlet(ExampleServlet.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExampleServlet extends HttpServlet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
|
||||||
|
import org.springframework.boot.actuate.web.mappings.MappingsEndpoint;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Bean
|
||||||
|
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
|
||||||
|
return new InMemoryUserDetailsManager(
|
||||||
|
User.withDefaultPasswordEncoder().username("user").password("password").authorities("ROLE_USER")
|
||||||
|
.build(),
|
||||||
|
User.withDefaultPasswordEncoder().username("admin").password("admin")
|
||||||
|
.authorities("ROLE_ACTUATOR", "ROLE_USER").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http.authorizeRequests()
|
||||||
|
.requestMatchers(EndpointRequest.to("health")).permitAll()
|
||||||
|
.requestMatchers(EndpointRequest.toAnyEndpoint().excluding(MappingsEndpoint.class)).hasRole("ACTUATOR")
|
||||||
|
.antMatchers("/**").hasRole("USER")
|
||||||
|
.and()
|
||||||
|
.httpBasic();
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 smoketest.secure.jersey;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Service {
|
||||||
|
|
||||||
|
@Value("${message:World}")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
public String message() {
|
||||||
|
return this.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
management.endpoints.web.exposure.include=*
|
||||||
|
management.endpoint.health.show-details=always
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue