diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml
index c8f104cc..ae5b642c 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml
@@ -3,7 +3,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
com._4point.aem.fluentforms
@@ -20,7 +20,8 @@
0.0.4-SNAPSHOT
- 4.0.0-beta.16
+ 4.0.0-beta.22
+ 4.0.8
1.20.2
1.2.3
@@ -94,7 +95,7 @@
org.springframework.boot
- spring-boot-starter-test
+ spring-boot-starter-jersey-test
test
@@ -105,10 +106,22 @@
org.wiremock
- wiremock-standalone
+ wiremock
${wiremock.version}
test
+
+ org.wiremock
+ wiremock-junit5
+ ${wiremock.version}
+ test
+
+
+ org.wiremock.integrations
+ wiremock-spring-boot
+ ${wiremock-spring-boot.version}
+ test
+
org.pitest
pitest-junit5-plugin
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfiguration.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfiguration.java
index c6070e67..3847199c 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfiguration.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfiguration.java
@@ -12,8 +12,8 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
-import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.system.JavaVersion;
import org.springframework.context.annotation.Bean;
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpoint.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpoint.java
index 84ef323e..001ceabf 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpoint.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpoint.java
@@ -204,7 +204,9 @@ public Response proxyPost(@PathParam("remainder") String remainder, @HeaderParam
.log("Returning POST response from target '{}'.");
}
- return Response.fromResponse(result).build();
+ return Response.fromResponse(result)
+ .header("Transfer-Encoding", null) // Remove the Transfer-Encoding header
+ .build();
}
private InputStream debugInput(InputStream in, String target) {
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfigurationTest.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfigurationTest.java
index cfb13982..a12bec70 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfigurationTest.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyAutoConfigurationTest.java
@@ -6,11 +6,11 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer;
import org.springframework.boot.test.context.SpringBootTest;
-@SpringBootTest(classes = {com._4point.aem.fluentforms.spring.FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class},
+@SpringBootTest(classes = {com._4point.aem.fluentforms.spring.FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
properties = {
"fluentforms.aem.servername=localhost",
"fluentforms.aem.port=4502",
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpointTest.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpointTest.java
index ae37cd30..6e98b99f 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpointTest.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyJerseyEndpointTest.java
@@ -23,16 +23,17 @@
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.web.client.RestClient;
+import org.wiremock.spring.ConfigureWireMock;
+import org.wiremock.spring.EnableWireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
-@WireMockTest(httpPort = AemProxyJerseyEndpointTest.WIREMOCK_PORT)
+@WireMockTest()
@SpringBootTest(classes = {com._4point.aem.fluentforms.spring.AemProxyJerseyEndpointTest.TestApplication.class},
webEnvironment = WebEnvironment.RANDOM_PORT,
properties = {
"fluentforms.aem.servername=localhost",
-"fluentforms.aem.port=" + AemProxyJerseyEndpointTest.WIREMOCK_PORT,
"fluentforms.aem.user=ENC(7FgD3ZsSExfUGRYlXNc++6C1upPBURNKq6HouzagnNZW4FsBwFs5+crawv+djhw6)",
"fluentforms.aem.password=ENC(QmQ6iTm/+TOO8U3dDuBzJWH129vReWgYNdgqQwWhjWaQy6j8sMnk2/Auhehmlh3v)",
//"fluentforms.aem.useSsl=true",
@@ -41,13 +42,18 @@
"jasypt.encryptor.password=4Point",
"jasypt.encryptor.iv-generator-classname=org.jasypt.iv.RandomIvGenerator",
"jasypt.encryptor.salt-generator-classname=org.jasypt.salt.RandomSaltGenerator",
-"logging.level.com._4point.aem.fluentforms.spring.AemProxyEndpoint=DEBUG"
+"logging.level.com._4point.aem.fluentforms.spring.AemProxyEndpoint=DEBUG",
+// Wiremock produces a lot of output, the following entries reduce that output. They can be removed for debugging.
+"logging.level.org.wiremock.spring=WARN", "logging.level.WireMock.wiremock=WARN",
})
-@Timeout(value = 5, unit = TimeUnit.MINUTES) // Fail tests that take longer than this to prevent hanging.
+@EnableWireMock(@ConfigureWireMock(
+ portProperties = "fluentforms.aem.port"
+ )
+ )
+@Timeout(value = 30, unit = TimeUnit.SECONDS) // Fail tests that take longer than this to prevent hanging.
class AemProxyJerseyEndpointTest {
private final static Logger logger = LoggerFactory.getLogger(AemProxyJerseyEndpointTest.class);
- static final int WIREMOCK_PORT = 5504;
static final String AF_BASE_LOCATION = "/aem";
// The following is a string that contains all possible values that may be modified by the AemProxyEndpoint.
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/FluentFormsJerseyAutoConfigurationTest.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/FluentFormsJerseyAutoConfigurationTest.java
index 369a2f05..21b929e0 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/FluentFormsJerseyAutoConfigurationTest.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/FluentFormsJerseyAutoConfigurationTest.java
@@ -32,7 +32,6 @@
import com._4point.aem.fluentforms.api.generatePDF.GeneratePDFService;
import com._4point.aem.fluentforms.api.output.OutputService;
import com._4point.aem.fluentforms.api.pdfUtility.PdfUtilityService;
-import com._4point.aem.fluentforms.spring.rest_services.client.SpringRestClientRestClient;
@SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class},
properties = {
@@ -158,40 +157,42 @@ private static String inputStreamToString(InputStream inputStream) throws IOExce
return result;
}
- @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
- properties = {
- "fluentforms.aem.servername=localhost",
- "fluentforms.aem.port=4502",
- "fluentforms.aem.user=admin",
- "fluentforms.aem.password=admin",
- "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
- })
- public static class SpringRestClientTest {
-
- @Test
- void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
- RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
- assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
- }
- }
+// TODO: For now we are going to ignore mixed scenarios (where we are using one starter with a different rest client
+// @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
+// properties = {
+// "fluentforms.aem.servername=localhost",
+// "fluentforms.aem.port=4502",
+// "fluentforms.aem.user=admin",
+// "fluentforms.aem.password=admin",
+// "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
+// })
+// public static class SpringRestClientTest {
+//
+// @Test
+// void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
+// RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
+// assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
+// }
+// }
- @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
- properties = {
- "fluentforms.aem.servername=localhost",
- "fluentforms.aem.port=4502",
- "fluentforms.aem.user=admin",
- "fluentforms.aem.password=admin",
- "fluentforms.aem.usessl=true",
- "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
- })
- public static class SpringRestClient_SslNoBundleNameTest {
-
- @Test
- void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
- RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
- assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
- }
- }
+// TODO: For now we are going to ignore mixed scenarios (where we are using one starter with a different rest client
+// @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
+// properties = {
+// "fluentforms.aem.servername=localhost",
+// "fluentforms.aem.port=4502",
+// "fluentforms.aem.user=admin",
+// "fluentforms.aem.password=admin",
+// "fluentforms.aem.usessl=true",
+// "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
+// })
+// public static class SpringRestClient_SslNoBundleNameTest {
+//
+// @Test
+// void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
+// RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
+// assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
+// }
+// }
@SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class},
properties = {
@@ -212,26 +213,27 @@ void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemC
}
}
- @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
- properties = {
- "fluentforms.aem.servername=localhost",
- "fluentforms.aem.port=4502",
- "fluentforms.aem.user=admin",
- "fluentforms.aem.password=admin",
- "fluentforms.aem.usessl=true",
- "spring.ssl.bundle.jks.aem.truststore.location=file:src/test/resources/aemforms.p12",
- "spring.ssl.bundle.jks.aem.truststore.password=Pa$$123",
- "spring.ssl.bundle.jks.aem.truststore.type=PKCS12",
- "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
- })
- public static class SpringRestClient_SslBundleTest {
-
- @Test
- void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
- RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
- assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
- }
- }
+// TODO: For now we are going to ignore mixed scenarios (where we are using one starter with a different rest client
+// @SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
+// properties = {
+// "fluentforms.aem.servername=localhost",
+// "fluentforms.aem.port=4502",
+// "fluentforms.aem.user=admin",
+// "fluentforms.aem.password=admin",
+// "fluentforms.aem.usessl=true",
+// "spring.ssl.bundle.jks.aem.truststore.location=file:src/test/resources/aemforms.p12",
+// "spring.ssl.bundle.jks.aem.truststore.password=Pa$$123",
+// "spring.ssl.bundle.jks.aem.truststore.type=PKCS12",
+// "fluentforms.restclient=springrestclient" // Configure for Spring RestClient
+// })
+// public static class SpringRestClient_SslBundleTest {
+//
+// @Test
+// void testRestClientFactory(@Autowired RestClientFactory factory, @Autowired AemConfiguration config) {
+// RestClient client = factory.apply(toAemConfig(config) , "testRestClientFactory", ()->"correlationId");
+// assertTrue(client instanceof SpringRestClientRestClient, "RestClientFactory should return a SpringRestClientRestClient when configured to do so");
+// }
+// }
@SpringBootTest(classes = {FluentFormsJerseyAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class},
properties = {
diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/JerseyAutoConfigurationTest.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/JerseyAutoConfigurationTest.java
index 5cf571aa..02c31155 100644
--- a/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/JerseyAutoConfigurationTest.java
+++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/JerseyAutoConfigurationTest.java
@@ -6,7 +6,7 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -34,23 +34,27 @@ class JerseyAutoConfigurationTest {
* This class provides mock versions of beans that would normally be provided by Spring Boot in a real application. We
* only need to mock out the RestClient.Builder and RestClientSsl beans because those are the only Spring Boot provided
* beans that our AutoConfigurations depend on.
+ *
+ * In theory, we should not need to provide these however spring-boot-starter-jersey brings in spring-boot-restclient
+ * on to the test classpath which, in turn, triggers the inclusion of the fluent-forms springRestClientFactory which
+ * requires the mocks.
*/
private static class SpringBootMocks {
@Bean RestClient.Builder mockRestClientBuilder() { return Mockito.mock(RestClient.Builder.class, Mockito.RETURNS_DEEP_STUBS); }
@Bean private RestClientSsl mockRestClientSsl() { return Mockito.mock(RestClientSsl.class); }
}
- private static final AutoConfigurations AUTO_CONFIG = AutoConfigurations.of(FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class, SpringBootMocks.class);
+ private static final AutoConfigurations AUTO_CONFIG = AutoConfigurations.of(FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, SpringBootMocks.class);
- private static final AutoConfigurations LOCAL_SUBMIT_CONFIG = AutoConfigurations.of(FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class, DummyLocalSubmitHandler.class, SpringBootMocks.class);
+ private static final AutoConfigurations LOCAL_SUBMIT_CONFIG = AutoConfigurations.of(FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, DummyLocalSubmitHandler.class, SpringBootMocks.class);
- private static final AutoConfigurations ALTERNATE_PROXY_CONFIG = AutoConfigurations.of(DummyProxyImplementation.class, FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class, SpringBootMocks.class);
+ private static final AutoConfigurations ALTERNATE_PROXY_CONFIG = AutoConfigurations.of(DummyProxyImplementation.class, FluentFormsJerseyAutoConfiguration.class, AemProxyJerseyAutoConfiguration.class, FluentFormsAutoConfiguration.class, SpringBootMocks.class);
// Tests to make sure that only the FluentFormsLibraries are loaded in a non-web application.
private static final ContextConsumer super AssertableApplicationContext> FF_LIBRARIES_ONLY = (context) -> {
assertAll(
- ()->assertThat(context).hasSingleBean(FluentFormsAutoConfiguration.class),
- ()->assertThat(context).getBean(FluentFormsAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsAutoConfiguration.class)),
+ ()->assertThat(context).hasSingleBean(FluentFormsJerseyAutoConfiguration.class),
+ ()->assertThat(context).getBean(FluentFormsJerseyAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsJerseyAutoConfiguration.class)),
()->assertThat(context).hasSingleBean(OutputService.class),
()->assertThat(context).getBean("outputService").isNotNull(),
()->assertThat(context).doesNotHaveBean(AemProxyAutoConfiguration.class),
@@ -64,8 +68,8 @@ private static class SpringBootMocks {
// Tests to make sure that only the FluentFormsLibraries are loaded in a web application.
private static final ContextConsumer super AssertableWebApplicationContext> WEB_FF_LIBRARIES_ONLY = (context) -> {
assertAll(
- ()->assertThat(context).hasSingleBean(FluentFormsAutoConfiguration.class),
- ()->assertThat(context).getBean(FluentFormsAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsAutoConfiguration.class)),
+ ()->assertThat(context).hasSingleBean(FluentFormsJerseyAutoConfiguration.class),
+ ()->assertThat(context).getBean(FluentFormsJerseyAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsJerseyAutoConfiguration.class)),
()->assertThat(context).hasSingleBean(OutputService.class),
()->assertThat(context).getBean("outputService").isNotNull(),
()->assertThat(context).doesNotHaveBean(AemProxyAutoConfiguration.class),
@@ -79,8 +83,8 @@ private static class SpringBootMocks {
// Tests to make sure that all beans are loaded by default in a web application.
private static final ContextConsumer super AssertableWebApplicationContext> WEB_ALL_DEFAULT_SERVICES = (context) -> {
assertAll(
- ()->assertThat(context).hasSingleBean(FluentFormsAutoConfiguration.class),
- ()->assertThat(context).getBean(FluentFormsAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsAutoConfiguration.class)),
+ ()->assertThat(context).hasSingleBean(FluentFormsJerseyAutoConfiguration.class),
+ ()->assertThat(context).getBean(FluentFormsJerseyAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsJerseyAutoConfiguration.class)),
()->assertThat(context).hasSingleBean(OutputService.class),
()->assertThat(context).getBean("outputService").isNotNull(),
()->assertThat(context).hasSingleBean(AemProxyJerseyAutoConfiguration.class),
@@ -92,27 +96,27 @@ private static class SpringBootMocks {
};
// Tests to make sure that all beans are loaded by default in a web application.
- private static final ContextConsumer super AssertableWebApplicationContext> WEB_ALL_SPRINGMVC_SERVICES = (context) -> {
- assertAll(
- ()->assertThat(context).hasSingleBean(FluentFormsAutoConfiguration.class),
- ()->assertThat(context).getBean(FluentFormsAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsAutoConfiguration.class)),
- ()->assertThat(context).hasSingleBean(OutputService.class),
- ()->assertThat(context).getBean("outputService").isNotNull(),
- ()->assertThat(context).hasSingleBean(AemProxyAutoConfiguration.class),
- ()->assertThat(context).getBean(AemProxyAutoConfiguration.class.getName()).isSameAs(context.getBean(AemProxyAutoConfiguration.class)),
- ()->assertThat(context).hasSingleBean(SpringAfSubmitProcessor.class),
- ()->assertThat(context).getBean(SpringAfSubmitProcessor.class).isSameAs(context.getBean(AemProxyAfSubmission.AfSubmitAemProxyProcessor.class)),
- ()->assertThat(context).doesNotHaveBean(AfSubmissionHandler.class),
- ()->assertThat(context).doesNotHaveBean(AemProxyJerseyAutoConfiguration.class),
- ()->assertThat(context).doesNotHaveBean(JerseyAfSubmitProcessor.class)
- );
- };
+// private static final ContextConsumer super AssertableWebApplicationContext> WEB_ALL_SPRINGMVC_SERVICES = (context) -> {
+// assertAll(
+// ()->assertThat(context).hasSingleBean(FluentFormsJerseyAutoConfiguration.class),
+// ()->assertThat(context).getBean(FluentFormsJerseyAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsJerseyAutoConfiguration.class)),
+// ()->assertThat(context).hasSingleBean(OutputService.class),
+// ()->assertThat(context).getBean("outputService").isNotNull(),
+// ()->assertThat(context).hasSingleBean(AemProxyAutoConfiguration.class),
+// ()->assertThat(context).getBean(AemProxyAutoConfiguration.class.getName()).isSameAs(context.getBean(AemProxyAutoConfiguration.class)),
+// ()->assertThat(context).hasSingleBean(SpringAfSubmitProcessor.class),
+// ()->assertThat(context).getBean(SpringAfSubmitProcessor.class).isSameAs(context.getBean(AemProxyAfSubmission.AfSubmitAemProxyProcessor.class)),
+// ()->assertThat(context).doesNotHaveBean(AfSubmissionHandler.class),
+// ()->assertThat(context).doesNotHaveBean(AemProxyJerseyAutoConfiguration.class),
+// ()->assertThat(context).doesNotHaveBean(JerseyAfSubmitProcessor.class)
+// );
+// };
// Tests to make sure that all beans are loaded in a web application that contains a local submit handler.
private static final ContextConsumer super AssertableWebApplicationContext> WEB_LOCAL_SUBMIT_SERVICES = (context) -> {
assertAll(
- ()->assertThat(context).hasSingleBean(FluentFormsAutoConfiguration.class),
- ()->assertThat(context).getBean(FluentFormsAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsAutoConfiguration.class)),
+ ()->assertThat(context).hasSingleBean(FluentFormsJerseyAutoConfiguration.class),
+ ()->assertThat(context).getBean(FluentFormsJerseyAutoConfiguration.class.getName()).isSameAs(context.getBean(FluentFormsJerseyAutoConfiguration.class)),
()->assertThat(context).hasSingleBean(OutputService.class),
()->assertThat(context).getBean("outputService").isNotNull(),
()->assertThat(context).hasSingleBean(AemProxyJerseyAutoConfiguration.class),
@@ -222,15 +226,16 @@ void webContext_ProxyEnabled_DefaultProxyConfigured() {
.run(WEB_ALL_DEFAULT_SERVICES);
}
- // All services should start when the jersey proxy type is configured.
- @Test
- void webContext_ProxyEnabled_SpringMvcProxyConfigured() {
- this.webContextRunner
- .withPropertyValues("fluentforms.aem.servername=localhost", "fluentforms.aem.port=4502",
- "fluentforms.aem.user=user", "fluentforms.aem.password=password",
- "fluentforms.rproxy.type=springmvc")
- .run(WEB_ALL_SPRINGMVC_SERVICES);
- }
+ // TODO: For now we are going to ignore mixed scenarios (where we are using one starter with a different rest client
+// // All services should start when the jersey proxy type is configured.
+// @Test
+// void webContext_ProxyEnabled_SpringMvcProxyConfigured() {
+// this.webContextRunner
+// .withPropertyValues("fluentforms.aem.servername=localhost", "fluentforms.aem.port=4502",
+// "fluentforms.aem.user=user", "fluentforms.aem.password=password",
+// "fluentforms.rproxy.type=springmvc")
+// .run(WEB_ALL_SPRINGMVC_SERVICES);
+// }
public static class DummyLocalSubmitHandler implements AfSubmissionHandler {
diff --git a/spring/fluentforms-jersey-spring-boot-starter/pom.xml b/spring/fluentforms-jersey-spring-boot-starter/pom.xml
index 02273b72..e7612e08 100644
--- a/spring/fluentforms-jersey-spring-boot-starter/pom.xml
+++ b/spring/fluentforms-jersey-spring-boot-starter/pom.xml
@@ -6,7 +6,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
fluentforms-jersey-spring-boot-starter
diff --git a/spring/fluentforms-sample-web-jersey-app/pom.xml b/spring/fluentforms-sample-web-jersey-app/pom.xml
index 346d861e..acd3d642 100644
--- a/spring/fluentforms-sample-web-jersey-app/pom.xml
+++ b/spring/fluentforms-sample-web-jersey-app/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
com._4point.aem.fluentforms
@@ -16,9 +16,8 @@
17
- 3.13.1
- 3.10.6
- 4.16.0
+ 4.0.0-beta.22
+ 4.0.8
0.0.5-SNAPSHOT
0.0.4-SNAPSHOT
@@ -42,10 +41,6 @@
-
- org.springframework.boot
- spring-boot-starter
-
org.springframework.boot
spring-boot-starter-jersey
@@ -75,7 +70,7 @@
org.springframework.boot
- spring-boot-starter-test
+ spring-boot-starter-jersey-test
test
@@ -103,7 +98,7 @@
org.testcontainers
- junit-jupiter
+ testcontainers-junit-jupiter
test
diff --git a/spring/fluentforms-sample-webmvc-app/pom.xml b/spring/fluentforms-sample-webmvc-app/pom.xml
index 4dc40985..104af4c0 100644
--- a/spring/fluentforms-sample-webmvc-app/pom.xml
+++ b/spring/fluentforms-sample-webmvc-app/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
com._4point.aem.fluentforms
@@ -16,9 +16,8 @@
17
- 3.13.1
- 3.10.6
- 4.16.0
+ 4.0.0-beta.22
+ 4.0.8
0.0.5-SNAPSHOT
0.0.4-SNAPSHOT
@@ -44,16 +43,11 @@
org.springframework.boot
- spring-boot-starter
+ spring-boot-starter-webmvc
org.springframework.boot
- spring-boot-starter-web
-
-
-
- org.springframework.boot
- spring-boot-starter-jersey
+ spring-boot-starter-restclient
com.fasterxml.jackson.core
@@ -80,12 +74,23 @@
org.springframework.boot
- spring-boot-starter-test
+ spring-boot-starter-restclient-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
+
+ org.wiremock
+ wiremock
+ ${wiremock.version}
test
org.wiremock
- wiremock-standalone
+ wiremock-junit5
${wiremock.version}
test
@@ -108,7 +113,7 @@
org.testcontainers
- junit-jupiter
+ testcontainers-junit-jupiter
test
diff --git a/spring/fluentforms-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-spring-boot-autoconfigure/pom.xml
index 35838067..2adf25fd 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/pom.xml
+++ b/spring/fluentforms-spring-boot-autoconfigure/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
com._4point.aem.fluentforms
@@ -19,7 +19,8 @@
3.0.5
0.0.5-SNAPSHOT
0.0.4-SNAPSHOT
- 4.0.0-beta.16
+ 4.0.0-beta.22
+ 4.0.8
1.20.2
1.2.3
@@ -66,7 +67,13 @@
org.springframework.boot
- spring-boot-starter-web
+ spring-boot-starter-webmvc
+ true
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-restclient
true
provided
@@ -80,6 +87,7 @@
rest-services.client
${fluentforms.version}
+
org.springframework.boot
@@ -94,10 +102,22 @@
org.wiremock
- wiremock-standalone
+ wiremock
+ ${wiremock.version}
+ test
+
+
+ org.wiremock
+ wiremock-junit5
${wiremock.version}
test
+
+ org.wiremock.integrations
+ wiremock-spring-boot
+ ${wiremock-spring-boot.version}
+ test
+
org.pitest
pitest-junit5-plugin
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmission.java b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmission.java
index 3e0a4e65..24948455 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmission.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmission.java
@@ -14,7 +14,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.boot.ssl.NoSuchSslBundleException;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
@@ -622,12 +622,15 @@ class ExtractedData {
);
}
- // Transfer headers from JAX-RS construct to Spring construct (in order to keep JAX-RS encapsulated in this class)
+ // Transfer headers from WebMVC construct to Spring construct
private MultiValueMapAdapter transferHeaders(HttpHeaders headers) {
if (logger.isDebugEnabled()) {
headers.forEach((k,v)->logger.atDebug().addArgument(k).addArgument(v.size()).log("Found Http header {} with {} values."));
}
- return new MultiValueMapAdapter(headers);
+ // Starting with Spring Framework 7, they do not recommend using MultiValueMap to store HttpHeaders but
+ // that will require changing the AfSubmissionHandler.Submission interface which has larger implications so,
+ // for now, we're leaving in the deprecated call.
+ return new MultiValueMapAdapter(headers.asMultiValueMap());
}
// Convert the SubmitResponse object into a JAX-RS Response object.
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfiguration.java b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfiguration.java
index 1dd997ac..12998a26 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfiguration.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfiguration.java
@@ -1,22 +1,29 @@
package com._4point.aem.fluentforms.spring;
import java.util.List;
+import java.util.function.Consumer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
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.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
-import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
+import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Fallback;
import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmissionHandler;
+import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmitAemProxyProcessor;
import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmitLocalProcessor;
import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmitLocalProcessor.InternalAfSubmitAemProxyProcessor;
-import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmitAemProxyProcessor;
import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.SpringAfSubmitProcessor;
/**
@@ -24,12 +31,17 @@
* resources (.css, .js, etc.) that the browser will request. These requests are forwarded to AEM.
*/
@AutoConfiguration
+@ConditionalOnClass(RestClientSsl.class)
@ConditionalOnWebApplication(type=Type.SERVLET)
@ConditionalOnProperty(prefix="fluentforms.rproxy", name="enabled", havingValue="true", matchIfMissing=true )
@ConditionalOnProperty(prefix="fluentforms.rproxy", name="type", havingValue="springmvc", matchIfMissing=true )
@EnableConfigurationProperties({AemConfiguration.class, AemProxyConfiguration.class})
@ConditionalOnMissingBean(AemProxyImplemention.class)
+@Fallback
public class AemProxyAutoConfiguration {
+ private static final int MINIMUM_PART_COUNT = 20;
+ private static final String SERVER_TOMCAT_MAX_PART_COUNT = "server.tomcat.max-part-count";
+ private final static Logger logger = LoggerFactory.getLogger(AemProxyAutoConfiguration.class);
/**
* Marker bean to indicate that the Spring MVC-based AEM Proxy implementation is being used.
@@ -43,6 +55,30 @@ AemProxyImplemention aemProxyImplemention() {
};
}
+ // Spring Boot 4 lowered the max part count to 10 which is too low for AEM submissions, so we raise it.
+ @Bean
+ WebServerFactoryCustomizer webserverFactoryCustomizer() {
+ return factory->factory.addConnectorCustomizers(c->
+ correctMaxPartCount(SERVER_TOMCAT_MAX_PART_COUNT, c.getMaxPartCount(), c::setMaxPartCount)
+ );
+ }
+
+ private static void correctMaxPartCount(String settingName, Integer currentSetting, Consumer settingSetter) {
+ if (currentSetting >= 0 && currentSetting < MINIMUM_PART_COUNT) {
+ settingSetter.accept(MINIMUM_PART_COUNT);
+ logger.atInfo()
+ .addArgument(settingName)
+ .addArgument(currentSetting)
+ .addArgument(MINIMUM_PART_COUNT)
+ .log("{} changed from {} to {}.");
+ } else {
+ logger.atInfo().addArgument(settingName)
+ .addArgument(currentSetting)
+ .log("{} remains at {}.");
+ }
+
+ }
+
@Bean
AemProxyEndpoint aemProxyEndpoint(AemConfiguration aemConfig, AemProxyConfiguration aemProxyConfig, @Autowired(required = false) RestClientSsl restClientSsl) {
return new AemProxyEndpoint(aemConfig, aemProxyConfig, restClientSsl);
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyEndpoint.java b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyEndpoint.java
index 2804d8f0..668ad132 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyEndpoint.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyEndpoint.java
@@ -13,7 +13,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.boot.ssl.NoSuchSslBundleException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -43,6 +44,7 @@
* get the AdaptiveForm or HTML5 Form using the FLuentForms libraries.
*
*/
+@ConditionalOnClass(RestClientSsl.class)
@CrossOrigin
@RestController
@RequestMapping("/aem")
@@ -188,6 +190,16 @@ private static InputStream fixTogglesDotJsonLocation(InputStream is) {
return new ReplacingInputStream(is, target, replacement);
}
+ /**
+ * This function acts as a reverse proxy for anything POSTed to the server. It just forwards
+ * anything it receives on AEM and then returns the response. It logs slightly differently
+ * if the request is a form submission.
+ *
+ * @param remainder
+ * @param contentType
+ * @param in
+ * @return
+ */
@PostMapping("/{*remainder}")
public ResponseEntity proxyPost(@PathVariable("remainder") String remainder, @RequestHeader(value = "Content-Type", required = false) String contentType, byte[] in) {
logger.atDebug().log("Proxying POST request. remainder={}", remainder);
@@ -221,8 +233,10 @@ public ResponseEntity proxyPost(@PathVariable("remainder") String remain
.log("Returning POST response from target '{}'.");
}
- return response;
- }
+ return ResponseEntity.status(response.getStatusCode())
+ .headers(removeChunkedTransferEncoding(response.getHeaders()))
+ .body(response.getBody());
+}
private static byte[] debugInput(byte[] inputBytes, String target) {
logger.atDebug()
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/FluentFormsAutoConfiguration.java b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/FluentFormsAutoConfiguration.java
index 00a99df8..9a86dfbc 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/FluentFormsAutoConfiguration.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/FluentFormsAutoConfiguration.java
@@ -5,12 +5,14 @@
import java.util.function.Function;
import org.springframework.boot.autoconfigure.AutoConfiguration;
+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.web.client.RestClientSsl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.boot.ssl.NoSuchSslBundleException;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Fallback;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.client.RestClient;
@@ -69,27 +71,33 @@ private T setAemFields(T builder, AemConfiguration aemConfig
);
}
-
- // matchIfMissing is set to true so that, by default (if nothing is specified in the properties file), then the SpringRestClient is used.
- @ConditionalOnProperty(prefix="fluentforms", name="restclient", havingValue="springrestclient", matchIfMissing=true )
- @ConditionalOnMissingBean
- @Fallback
- @Bean
- public RestClientFactory springRestClientFactory(AemConfiguration aemConfig, RestClient.Builder restClientBuilder, RestClientSsl restClientSsl) {
- return SpringRestClientRestClient.factory(aemConfig.useSsl() ? restClientBuilder.apply(getSslBundle(aemConfig.sslBundle(), restClientSsl))
- : restClientBuilder
- ); // Create a RestClientFactory using Spring RestClient implementation
- }
- private static Consumer getSslBundle(String sslBundleName, RestClientSsl restClientSsl) {
- try {
- return restClientSsl.fromBundle(sslBundleName);
- } catch (NoSuchSslBundleException e) {
- // Default to normal SSL context (which includes the default trust store)
- // This is not ideal since misspelling the bundle name silently fails, but is required to avoid breaking existing code.
- // At dome point it should probably be changed to let the exception pass and only use the default SSL context
- // if the SSL bundle name is empty.
- return b->{}; // No-op;
+ // To prevent class path exceptions when loading just the Jersey libraries (because RestSsl is unavailable)
+ // we need to wrap the springRestClientFactory in it's own class.
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(RestClientSsl.class)
+ public static class SpringRestClientFactoryProvider {
+ // matchIfMissing is set to true so that, by default (if nothing is specified in the properties file), then the SpringRestClient is used.
+ @ConditionalOnProperty(prefix="fluentforms", name="restclient", havingValue="springrestclient", matchIfMissing=true )
+ @ConditionalOnMissingBean
+ @Fallback
+ @Bean
+ public RestClientFactory springRestClientFactory(AemConfiguration aemConfig, RestClient.Builder restClientBuilder, RestClientSsl restClientSsl) {
+ return SpringRestClientRestClient.factory(aemConfig.useSsl() ? restClientBuilder.apply(getSslBundle(aemConfig.sslBundle(), restClientSsl))
+ : restClientBuilder
+ ); // Create a RestClientFactory using Spring RestClient implementation
+ }
+
+ private static Consumer getSslBundle(String sslBundleName, RestClientSsl restClientSsl) {
+ try {
+ return restClientSsl.fromBundle(sslBundleName);
+ } catch (NoSuchSslBundleException e) {
+ // Default to normal SSL context (which includes the default trust store)
+ // This is not ideal since misspelling the bundle name silently fails, but is required to avoid breaking existing code.
+ // At dome point it should probably be changed to let the exception pass and only use the default SSL context
+ // if the SSL bundle name is empty.
+ return b->{}; // No-op;
+ }
}
}
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmissionTest.java b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmissionTest.java
index 960760fa..8dfabaeb 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmissionTest.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAfSubmissionTest.java
@@ -7,6 +7,7 @@
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.Function;
@@ -39,6 +40,7 @@
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -69,19 +71,14 @@ class AemProxyAfSubmissionTest {
private static final String AEM_SUBMIT_ADAPTIVE_FORM_SERVICE_PATH = SUBMIT_ADAPTIVE_FORM_SERVICE_PATH.substring(4); // Same as above minus "/aem"
private static final String SAMPLE_RESPONSE_BODY = "body";
-// record JakartaRestClient(WebTarget target, URI uri) {};
-//
-// public static JakartaRestClient setUpRestClient(int port) {
-// var uri = getBaseUri(port);
-// var target = ClientBuilder.newClient() //newClient(clientConfig)
-// .property(ClientProperties.FOLLOW_REDIRECTS, Boolean.FALSE) // Disable re-directs so that we can test for "thank you page" redirection.
-// .register(MultiPartFeature.class)
-// .target(uri);
-// return new JakartaRestClient(target, uri);
-// }
-
private static RestClient createRestClient(int port) {
+ // Configure the underlying HttpClient to not follow redirects
+ HttpClient httpClient = HttpClient.newBuilder()
+ .followRedirects(HttpClient.Redirect.NEVER) // Use NEVER to prevent all redirects
+ .build();
+
return RestClient.builder()
+ .requestFactory(new JdkClientHttpRequestFactory(httpClient))
.baseUrl(getBaseUri(port))
.build();
}
@@ -160,7 +157,8 @@ public static void main(String[] args) {
"fluentforms.aem.useSsl=true",
"spring.ssl.bundle.jks.aem.truststore.location=file:src/test/resources/aemforms.p12",
"spring.ssl.bundle.jks.aem.truststore.password=Pa$$123",
- "spring.ssl.bundle.jks.aem.truststore.type=PKCS12"
+ "spring.ssl.bundle.jks.aem.truststore.type=PKCS12",
+ "server.tomcat.max-part-count=" + AemProxyAutoConfigurationTest.MINIMUM_PARTS_COUNT, // Normally supplied by AutoConfiguration
}
)
public static class AemProxyAfSubmissionTestWithAemAfSubmitProcessorTest {
@@ -180,12 +178,10 @@ public static class AemProxyAfSubmissionTestWithAemAfSubmitProcessorTest {
@LocalServerPort
private int port;
-// private JakartaRestClient jrc;
private RestClient restClient;
@BeforeEach
public void setUp() throws Exception {
-// jrc = setUpRestClient(port);
restClient = createRestClient(port);
}
@@ -202,16 +198,12 @@ void test() {
MultiValueMap> parts = getPdfForm.parts();
ResponseEntity response = restClient.post()
.uri(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.accept(MediaType.APPLICATION_PDF)
.retrieve()
.toEntity(byte[].class)
;
-// .target.path().request().accept(APPLICATION_PDF)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
-
// then
assertThat(response, allOf(hasStatus(HttpStatus.OK), hasEntityMatching(equalTo(expectedResponseString.getBytes()))));
WireMock.verify(
@@ -242,7 +234,8 @@ void test() {
AemProxyAfSubmissionTestWithLocalAfSubmitProcessorTest.MockSubmissionProcessor2.class}
,properties={
// "debug",
- "logging.level.com._4point.aem.fluentforms.spring=DEBUG"
+ "logging.level.com._4point.aem.fluentforms.spring=DEBUG",
+ "server.tomcat.max-part-count=" + AemProxyAutoConfigurationTest.MINIMUM_PARTS_COUNT, // Normally supplied by AutoConfiguration
}
)
public static class AemProxyAfSubmissionTestWithLocalAfSubmitProcessorTest {
@@ -253,12 +246,10 @@ public static class AemProxyAfSubmissionTestWithLocalAfSubmitProcessorTest {
@LocalServerPort
private int port;
-// private JakartaRestClient jrc;
private RestClient restClient;
@BeforeEach
public void setUp() throws Exception {
-// jrc = setUpRestClient(port);
restClient = createRestClient(port);
}
@@ -276,12 +267,6 @@ void testResponse() {
.toEntity(byte[].class)
;
-// Response response = jrc.target
-// .path(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .request()
-// .accept(MediaType.TEXT_PLAIN_TYPE)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
-
assertThat(response, allOf(hasStatus(HttpStatus.OK),hasEntityMatching(equalTo(AF_SUBMIT_LOCAL_PROCESSOR_RESPONSE.getBytes()))));
}
@@ -297,11 +282,6 @@ void testRedirect() {
.retrieve()
.toBodilessEntity()
;
-// Response response = jrc.target
-// .path(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .request()
-// .accept(MediaType.TEXT_PLAIN_TYPE)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
assertThat(response, allOf(hasStatus(HttpStatus.TEMPORARY_REDIRECT), doesNotHaveEntity()));
}
@@ -312,17 +292,11 @@ void testSeeOther() {
ResponseEntity response = restClient.post()
.uri(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .contentType(MediaType.MULTIPART_FORM_DATA)
.body(getPdfForm.parts())
.accept(MediaType.TEXT_PLAIN)
.retrieve()
.toBodilessEntity()
;
-// Response response = jrc.target
-// .path(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .request()
-// .accept(MediaType.TEXT_PLAIN_TYPE)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
assertThat(response, allOf(hasStatus(HttpStatus.SEE_OTHER), doesNotHaveEntity()));
}
@@ -340,12 +314,6 @@ void testProxy() {
.toBodilessEntity()
;
-// Response response = jrc.target
-// .path(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH+"anythingElse")
-// .request()
-// .accept(MediaType.TEXT_PLAIN_TYPE)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
-
assertThat(response, allOf(hasStatus(HttpStatus.OK), doesNotHaveEntity()));
}
@@ -418,23 +386,22 @@ public InternalAfSubmitAemProxyProcessor aemProxyProcessor() {
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = {TestApplication.class, AemProxyAfSubmissionTestWithCustomAfSubmitProcessorTest.MockSubmitProcessor.class}
-// ,properties= {
-// "debug"
-// ,"logging.level.com._4point.aem.fluentforms.spring=DEBUG"
-// ,"logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=TRACE"
-// }
+ ,properties= {
+ "server.tomcat.max-part-count=" + AemProxyAutoConfigurationTest.MINIMUM_PARTS_COUNT, // Normally supplied by AutoConfiguration
+// "debug",
+// "logging.level.com._4point.aem.fluentforms.spring=DEBUG",
+// "logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=TRACE",
+ }
)
public static class AemProxyAfSubmissionTestWithCustomAfSubmitProcessorTest {
@LocalServerPort
private int port;
-// private JakartaRestClient jrc;
private RestClient restClient;
@BeforeEach
public void setUp() throws Exception {
-// jrc = setUpRestClient(port);
restClient = createRestClient(port);
}
@@ -445,17 +412,11 @@ void test() {
MultiValueMap> parts = getPdfForm.parts();
ResponseEntity response = restClient.post()
.uri(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.accept(MediaType.APPLICATION_PDF)
.retrieve()
.toEntity(byte[].class)
;
-// Response response = jrc.target
-// .path(SUBMIT_ADAPTIVE_FORM_SERVICE_PATH)
-// .request()
-// .accept(APPLICATION_PDF)
-// .post(Entity.entity(getPdfForm, getPdfForm.getMediaType()));
assertThat(response, allOf(hasStatus(HttpStatus.OK), hasEntityMatching(equalTo(SAMPLE_RESPONSE_BODY.getBytes()))));
}
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfigurationTest.java b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfigurationTest.java
index 7cafea43..37075518 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfigurationTest.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyAutoConfigurationTest.java
@@ -1,22 +1,27 @@
package com._4point.aem.fluentforms.spring;
import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
-import org.junit.jupiter.api.Test;
+import org.apache.catalina.connector.Connector;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.tomcat.TomcatConnectorCustomizer;
+import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+
-@SpringBootTest(classes = {com._4point.aem.fluentforms.spring.FluentFormsAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class},
-properties = {
- "fluentforms.aem.servername=localhost",
- "fluentforms.aem.port=4502",
- "fluentforms.aem.user=admin",
- "fluentforms.aem.password=admin)",
- })
class AemProxyAutoConfigurationTest {
+ static final int MINIMUM_PARTS_COUNT = 20;
// TODO: Maybe add more tests here later.
// @Test
@@ -24,6 +29,71 @@ class AemProxyAutoConfigurationTest {
// assertNotNull(afProxyConfigurer);
// }
+
+ @SpringBootTest(
+ classes = {com._4point.aem.fluentforms.spring.FluentFormsAutoConfigurationTest.TestApplication.class, FluentFormsAutoConfiguration.class, AemProxyAutoConfiguration.class},
+ properties = {
+ "fluentforms.aem.servername=localhost",
+ "fluentforms.aem.port=4502",
+ "fluentforms.aem.user=admin",
+ "fluentforms.aem.password=admin)",
+ })
+
+ static class AemProxyAutoConfiguration_TomcatWenServerFactory_MaxPartsCount_Test {
+
+ private static TomcatConnectorCustomizer customizer;
+ private Connector mockConnector = Mockito.mock(Connector.class);
+
+ @BeforeAll
+ static void setup(@Autowired WebServerFactoryCustomizer webserverFactoryCustomizer) {
+ assertNotNull(webserverFactoryCustomizer);
+ customizer = retrieveTomcatCustomizer(webserverFactoryCustomizer);
+ }
+
+ // This routine emulates the way Spring retrieves the customizer. It ensures that the customizer is
+ // configured so that Spring can find it.
+ private static TomcatConnectorCustomizer retrieveTomcatCustomizer(WebServerFactoryCustomizer webserverFactoryCustomizer) {
+ TomcatServletWebServerFactory tomcatFactory = mock(TomcatServletWebServerFactory.class);
+ ArgumentCaptor customizerCaptor = ArgumentCaptor.forClass(TomcatConnectorCustomizer.class);
+ doNothing().when(tomcatFactory).addConnectorCustomizers(customizerCaptor.capture());
+ webserverFactoryCustomizer.customize(tomcatFactory);
+ return customizerCaptor.getValue();
+ }
+
+ @ParameterizedTest
+ @DisplayName("If greater than or equal to the minimum, value should be left unaltered.")
+ @ValueSource(ints = {30, -1, MINIMUM_PARTS_COUNT}) // All greater than or equal to the minimum
+ void testTomcatMaxPartCountSetting_NoChange(int getValue) {
+ // Given: Mock just the get()
+ when(mockConnector.getMaxPartCount()).thenReturn(getValue);
+
+ // When
+ customizer.customize(mockConnector);
+
+ //Then
+ verify(mockConnector).getMaxPartCount();
+ verify(mockConnector, Mockito.times(0)).setMaxPartCount(Mockito.anyInt());
+ }
+
+ @ParameterizedTest
+ @DisplayName("If lower than the minimum, value should be set to minimum.")
+ @ValueSource(ints = {0, 10}) // All lower than minimum
+ void testTomcatMaxPartCountSetting(int getValue) {
+ // Given: Mock the get() and the set()
+ when(mockConnector.getMaxPartCount()).thenReturn(getValue);
+ ArgumentCaptor updatedMaxPartCountCaptor = ArgumentCaptor.forClass(Integer.class);
+ doNothing().when(mockConnector).setMaxPartCount(updatedMaxPartCountCaptor.capture());
+
+ // When
+ customizer.customize(mockConnector );
+
+ // Then
+ verify(mockConnector).getMaxPartCount();
+ verify(mockConnector).setMaxPartCount(MINIMUM_PARTS_COUNT);
+ assertEquals(updatedMaxPartCountCaptor.getValue(), MINIMUM_PARTS_COUNT);
+ }
+ }
+
@SpringBootApplication
@EnableConfigurationProperties({AemConfiguration.class,AemProxyConfiguration.class})
public static class TestApplication {
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyEndpointTest.java b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyEndpointTest.java
index 5b19f7da..d2a4bbbd 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyEndpointTest.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyEndpointTest.java
@@ -23,16 +23,17 @@
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.web.client.RestClient;
+import org.wiremock.spring.ConfigureWireMock;
+import org.wiremock.spring.EnableWireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
-@WireMockTest(httpPort = AemProxyEndpointTest.WIREMOCK_PORT)
+@WireMockTest()
@SpringBootTest(classes = {com._4point.aem.fluentforms.spring.AemProxyEndpointTest.TestApplication.class},
webEnvironment = WebEnvironment.RANDOM_PORT,
properties = {
"fluentforms.aem.servername=localhost",
-"fluentforms.aem.port=" + AemProxyEndpointTest.WIREMOCK_PORT,
"fluentforms.aem.user=ENC(7FgD3ZsSExfUGRYlXNc++6C1upPBURNKq6HouzagnNZW4FsBwFs5+crawv+djhw6)",
"fluentforms.aem.password=ENC(QmQ6iTm/+TOO8U3dDuBzJWH129vReWgYNdgqQwWhjWaQy6j8sMnk2/Auhehmlh3v)",
//"fluentforms.aem.useSsl=true",
@@ -41,13 +42,18 @@
"jasypt.encryptor.password=4Point",
"jasypt.encryptor.iv-generator-classname=org.jasypt.iv.RandomIvGenerator",
"jasypt.encryptor.salt-generator-classname=org.jasypt.salt.RandomSaltGenerator",
-"logging.level.com._4point.aem.fluentforms.spring.AemProxyEndpoint=DEBUG"
+"logging.level.com._4point.aem.fluentforms.spring.AemProxyEndpoint=DEBUG",
+//Wiremock produces a lot of output, the following entries reduce that output. They can be removed for debugging.
+"logging.level.org.wiremock.spring=WARN", "logging.level.WireMock.wiremock=WARN",
})
+@EnableWireMock(@ConfigureWireMock(
+ portProperties = "fluentforms.aem.port"
+ )
+ )
@Timeout(value = 5, unit = TimeUnit.MINUTES) // Fail tests that take longer than this to prevent hanging.
class AemProxyEndpointTest {
private final static Logger logger = LoggerFactory.getLogger(AemProxyEndpointTest.class);
- static final int WIREMOCK_PORT = 5504;
static final String AF_BASE_LOCATION = "/aem";
// The following is a string that contains all possible values that may be modified by the AemProxyEndpoint.
diff --git a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AutoConfigurationTest.java b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AutoConfigurationTest.java
index 1d1bf50e..dc78e3a9 100644
--- a/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AutoConfigurationTest.java
+++ b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AutoConfigurationTest.java
@@ -6,7 +6,7 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
+import org.springframework.boot.restclient.autoconfigure.RestClientSsl;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
diff --git a/spring/fluentforms-spring-boot-starter/pom.xml b/spring/fluentforms-spring-boot-starter/pom.xml
index f91e248f..b42746fa 100644
--- a/spring/fluentforms-spring-boot-starter/pom.xml
+++ b/spring/fluentforms-spring-boot-starter/pom.xml
@@ -6,7 +6,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.7
+ 4.0.0
fluentforms-spring-boot-starter