You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2021/08/18 00:52:51 UTC
[cxf] 01/02: CXF-8539: Allow client-only Spring Boot
autoconfiguration (#834)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch 3.3.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit ccb295ecc5ad2f0626a7008827bccd9bd3095dc9
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Tue Aug 17 20:51:56 2021 -0400
CXF-8539: Allow client-only Spring Boot autoconfiguration (#834)
(cherry picked from commit 37fbc36cbefc372102a51206ecf4a3d01185b308)
---
.../boot/autoconfigure/CxfAutoConfiguration.java | 2 +
.../spring/boot/autoconfigure/CxfProperties.java | 12 ++
.../autoconfigure/CxfAutoConfigurationTest.java | 10 +
.../spring/boot/SpringClientOnlyJaxrsTest.java | 201 +++++++++++++++++++++
.../src/test/resources/application-client.yml | 10 +
5 files changed, 235 insertions(+)
diff --git a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfiguration.java b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfiguration.java
index 92d862e..2554081 100644
--- a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfiguration.java
+++ b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfiguration.java
@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
@@ -59,6 +60,7 @@ public class CxfAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "cxfServletRegistration")
+ @ConditionalOnProperty(prefix = "cxf", name = "servlet.enabled", matchIfMissing = true)
public ServletRegistrationBean<CXFServlet> cxfServletRegistration() {
String path = this.properties.getPath();
String urlMapping = path.endsWith("/") ? path + "*" : path + "/*";
diff --git a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfProperties.java b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfProperties.java
index 7f18726..709ada5 100644
--- a/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfProperties.java
+++ b/integration/spring-boot/autoconfigure/src/main/java/org/apache/cxf/spring/boot/autoconfigure/CxfProperties.java
@@ -74,6 +74,11 @@ public class CxfProperties {
* Load on startup priority of the Apache CXF servlet.
*/
private int loadOnStartup = -1;
+
+ /**
+ * Enables or disables the servlet registration
+ */
+ private boolean enabled = true;
public Map<String, String> getInit() {
return this.init;
@@ -91,6 +96,13 @@ public class CxfProperties {
this.loadOnStartup = loadOnStartup;
}
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
}
public static class Metrics {
diff --git a/integration/spring-boot/autoconfigure/src/test/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfigurationTest.java b/integration/spring-boot/autoconfigure/src/test/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfigurationTest.java
index 1db2063..cc6b1b3 100644
--- a/integration/spring-boot/autoconfigure/src/test/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfigurationTest.java
+++ b/integration/spring-boot/autoconfigure/src/test/java/org/apache/cxf/spring/boot/autoconfigure/CxfAutoConfigurationTest.java
@@ -22,6 +22,7 @@ import java.util.Map;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.endpoint.ServerImpl;
+import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.spring.boot.jaxrs.CustomJaxRSServer;
import org.hamcrest.Matcher;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
@@ -47,6 +48,7 @@ import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.collection.IsEmptyCollection.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -113,6 +115,14 @@ public class CxfAutoConfigurationTest {
assertThat(ReflectionTestUtils.getField(registrationBean, "loadOnStartup"),
equalTo(1));
}
+
+ @Test
+ public void disableServlet() {
+ load(CxfAutoConfiguration.class, "cxf.servlet.enabled=false");
+ Map<String, ServletRegistrationBean<?>> registrationBeans = CastUtils.cast(this.context
+ .getBeansOfType(ServletRegistrationBean.class));
+ assertThat(registrationBeans.keySet(), empty());
+ }
@Test
public void customInitParameters() {
diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringClientOnlyJaxrsTest.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringClientOnlyJaxrsTest.java
new file mode 100644
index 0000000..ca75f5a
--- /dev/null
+++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringClientOnlyJaxrsTest.java
@@ -0,0 +1,201 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.cxf.systest.jaxrs.spring.boot;
+
+import java.util.Map;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.metrics.MetricsFeature;
+import org.apache.cxf.metrics.MetricsProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.search.RequiredSearch;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static java.util.stream.Collectors.toMap;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.entry;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SpringClientOnlyJaxrsTest.TestConfig.class)
+@ActiveProfiles("client")
+public class SpringClientOnlyJaxrsTest {
+
+ @Autowired
+ private MeterRegistry registry;
+
+ @Autowired
+ private MetricsProvider metricsProvider;
+
+ @LocalServerPort
+ private int port;
+
+ @Configuration
+ @EnableAutoConfiguration
+ static class TestConfig {
+ @Bean
+ public Feature metricsFeature(MetricsProvider metricsProvider) {
+ return new MetricsFeature(metricsProvider);
+ }
+
+ @Bean
+ public JacksonJsonProvider jacksonJsonProvider() {
+ return new JacksonJsonProvider();
+ }
+
+ @Autowired
+ public void setHandlerMapping(RequestMappingHandlerMapping mapping) throws NoSuchMethodException {
+ class LibraryHandler {
+ public ResponseEntity<?> get() {
+ return ResponseEntity.ok().build();
+ }
+
+ public ResponseEntity<?> delete() {
+ return ResponseEntity.badRequest().build();
+ }
+ }
+
+ final LibraryHandler handler = new LibraryHandler();
+
+ mapping.registerMapping(
+ RequestMappingInfo
+ .paths("/api/library")
+ .methods(RequestMethod.GET)
+ .build(),
+ handler,
+ LibraryHandler.class.getMethod("get"));
+
+ mapping.registerMapping(
+ RequestMappingInfo
+ .paths("/api/library")
+ .methods(RequestMethod.DELETE)
+ .build(),
+ handler,
+ LibraryHandler.class.getMethod("delete"));
+ }
+ }
+
+ @Autowired
+ public void setBus(Bus bus) {
+ // By default, the exception are propagated and out fault interceptors are not called
+ bus.setProperty("org.apache.cxf.propagate.exception", Boolean.FALSE);
+ }
+
+ @Before
+ public void setUp() {
+ this.registry.getMeters().forEach(meter -> registry.remove(meter));
+ }
+
+ @Test
+ public void testJaxrsClientSuccessMetric() {
+ final WebTarget target = createWebTarget();
+
+ final Builder builder = target.request().header(HttpHeaders.CONTENT_TYPE, "text/plain");
+ try (Response r = builder.get()) {
+ assertThat(r.getStatus()).isEqualTo(200);
+ }
+
+ // no server meters
+ assertThat(registry.getMeters())
+ .noneMatch(m -> "cxf.server.requests".equals(m.getId().getName()));
+
+ RequiredSearch clientRequestMetrics = registry.get("cxf.client.requests");
+
+ Map<Object, Object> clientTags = clientRequestMetrics.timer().getId().getTags().stream()
+ .collect(toMap(Tag::getKey, Tag::getValue));
+
+ assertThat(clientTags)
+ .containsOnly(
+ entry("exception", "None"),
+ entry("method", "GET"),
+ entry("operation", "UNKNOWN"),
+ entry("uri", "http://localhost:" + port + "/api/library"),
+ entry("outcome", "SUCCESS"),
+ entry("status", "200"));
+ }
+
+ @Test
+ public void testJaxrsClientExceptionMetric() {
+ final WebTarget target = ClientBuilder
+ .newClient()
+ .register(new MetricsFeature(metricsProvider))
+ .target("http://localhost:" + port + "/api/library");
+
+ final Builder builder = target.request().header(HttpHeaders.CONTENT_TYPE, "text/plain");
+ assertThatThrownBy(() -> builder.delete(String.class))
+ .isInstanceOf(BadRequestException.class)
+ .hasMessageContaining("Bad Request");
+
+ // no server meters
+ assertThat(registry.getMeters())
+ .noneMatch(m -> "cxf.server.requests".equals(m.getId().getName()));
+
+ RequiredSearch clientRequestMetrics = registry.get("cxf.client.requests");
+
+ Map<Object, Object> clientTags = clientRequestMetrics.timer().getId().getTags().stream()
+ .collect(toMap(Tag::getKey, Tag::getValue));
+
+ assertThat(clientTags)
+ .containsOnly(
+ entry("exception", "None"),
+ entry("method", "DELETE"),
+ entry("operation", "UNKNOWN"),
+ entry("uri", "http://localhost:" + port + "/api/library"),
+ entry("outcome", "CLIENT_ERROR"),
+ entry("status", "400"));
+ }
+
+ private WebTarget createWebTarget() {
+ return ClientBuilder
+ .newClient()
+ .register(JacksonJsonProvider.class)
+ .register(new MetricsFeature(metricsProvider))
+ .target("http://localhost:" + port + "/api/library");
+ }
+
+}
diff --git a/systests/spring-boot/src/test/resources/application-client.yml b/systests/spring-boot/src/test/resources/application-client.yml
new file mode 100644
index 0000000..71a23f4
--- /dev/null
+++ b/systests/spring-boot/src/test/resources/application-client.yml
@@ -0,0 +1,10 @@
+cxf:
+ path: /api
+ servlet:
+ enabled: false
+ jaxrs:
+ component-scan: false
+ classes-scan: false
+ metrics:
+ jaxws:
+ enabled: false
\ No newline at end of file