From 3f050ceaba79acd772db380a4f2346265db16e65 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sat, 29 Nov 2025 08:20:33 -0500 Subject: [PATCH 01/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20to=20Spring?= =?UTF-8?q?=20Boot=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/fluentforms-spring-boot-autoconfigure/pom.xml | 10 ++++++++-- .../aem/fluentforms/spring/AemProxyAfSubmission.java | 9 ++++++--- .../fluentforms/spring/AemProxyAutoConfiguration.java | 2 +- .../aem/fluentforms/spring/AemProxyEndpoint.java | 2 +- .../spring/FluentFormsAutoConfiguration.java | 2 +- .../aem/fluentforms/spring/AutoConfigurationTest.java | 2 +- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/spring/fluentforms-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-spring-boot-autoconfigure/pom.xml index 35838067..d7cd1fca 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 @@ -66,7 +66,13 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc + true + provided + + + org.springframework.boot + spring-boot-starter-restclient true provided 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..3f8e3abc 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 @@ -9,8 +9,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.web.client.RestClientSsl; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.restclient.autoconfigure.RestClientSsl; import org.springframework.context.annotation.Bean; import com._4point.aem.fluentforms.spring.AemProxyAfSubmission.AfSubmissionHandler; 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..2b938a2e 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,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.http.HttpHeaders; import org.springframework.http.MediaType; 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..482cd09b 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 @@ -7,8 +7,8 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; 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.Fallback; 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; From a54632b08335090e8271d8da348dd494840b1b4d Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sat, 29 Nov 2025 08:20:38 -0500 Subject: [PATCH 02/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20to=20Spring?= =?UTF-8?q?=20Boot=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/fluentforms-spring-boot-starter/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a1c3922ed0695cf542fe707c5ca01e4184145321 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Wed, 3 Dec 2025 09:23:21 -0500 Subject: [PATCH 03/11] =?UTF-8?q?=E2=9C=A8=20Added=20code=20to=20auto-conf?= =?UTF-8?q?igure=20MaxPartsCount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spring 4 followed Tomcat 11's lead and dropped the max parts count parameter down to 10 from 50. Unfortunately, AEM form submissions contain more than 10 parts, so the default no longer suffices. Rather than require every application to set server.tomcat.max-part-count in their application.properties we will auto-configure the setting if it is too low. --- .../spring/AemProxyAutoConfiguration.java | 34 +++++++- .../spring/AemProxyAfSubmissionTest.java | 17 ++-- .../spring/AemProxyAutoConfigurationTest.java | 86 +++++++++++++++++-- 3 files changed, 121 insertions(+), 16 deletions(-) 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 3f8e3abc..bf471d4f 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,7 +1,10 @@ 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; @@ -11,12 +14,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; 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 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; /** @@ -30,6 +35,9 @@ @EnableConfigurationProperties({AemConfiguration.class, AemProxyConfiguration.class}) @ConditionalOnMissingBean(AemProxyImplemention.class) 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 +51,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/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..f46764a7 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 @@ -160,7 +160,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 { @@ -242,7 +243,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 { @@ -418,11 +420,12 @@ 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 { 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 { From 6b2d9edd210508e19db8d9200e1bca8742cf2137 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Wed, 3 Dec 2025 09:51:02 -0500 Subject: [PATCH 04/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20to=20Spring?= =?UTF-8?q?=20Boot=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/fluentforms-sample-webmvc-app/pom.xml | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/spring/fluentforms-sample-webmvc-app/pom.xml b/spring/fluentforms-sample-webmvc-app/pom.xml index 4dc40985..f23aa050 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,7 +16,7 @@ 17 - 3.13.1 + 3.13.2 3.10.6 4.16.0 0.0.5-SNAPSHOT @@ -44,16 +44,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,7 +75,12 @@ org.springframework.boot - spring-boot-starter-test + spring-boot-starter-restclient-test + test + + + org.springframework.boot + spring-boot-starter-webmvc-test test @@ -108,7 +108,7 @@ org.testcontainers - junit-jupiter + testcontainers-junit-jupiter test From f06b16297da3a3d555cbcf54cf7d47d0aa9b06f6 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sat, 6 Dec 2025 07:42:28 -0500 Subject: [PATCH 05/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20to=20Spring?= =?UTF-8?q?=20Boot=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated main Spring Boot Autoconfigure to not load when RestClientSsl is not available. This will allow the Jersey starter to work without the spring-boot-restclient-starter in the classpath. --- .../pom.xml | 2 +- .../AemProxyJerseyAutoConfiguration.java | 2 +- .../AemProxyJerseyAutoConfigurationTest.java | 4 +- ...luentFormsJerseyAutoConfigurationTest.java | 109 +++++++++--------- .../spring/JerseyAutoConfigurationTest.java | 22 ++-- .../pom.xml | 2 +- .../spring/AemProxyAutoConfiguration.java | 4 + .../fluentforms/spring/AemProxyEndpoint.java | 2 + .../spring/FluentFormsAutoConfiguration.java | 48 ++++---- 9 files changed, 106 insertions(+), 89 deletions(-) diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml index c8f104cc..eb964636 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 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/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/FluentFormsJerseyAutoConfigurationTest.java b/spring/fluentforms-jersey-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/FluentFormsJerseyAutoConfigurationTest.java index 369a2f05..40130b4f 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 @@ -158,40 +158,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 +214,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..c175da6d 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,6 @@ 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.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -37,7 +36,7 @@ class JerseyAutoConfigurationTest { */ 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); } +// @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); @@ -222,15 +221,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-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 bf471d4f..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 @@ -8,6 +8,7 @@ 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; @@ -17,6 +18,7 @@ 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; @@ -29,11 +31,13 @@ * 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"; 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 2b938a2e..ac4eaca4 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,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +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; @@ -43,6 +44,7 @@ * get the AdaptiveForm or HTML5 Form using the FLuentForms libraries. * */ +@ConditionalOnClass(RestClientSsl.class) @CrossOrigin @RestController @RequestMapping("/aem") 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 482cd09b..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.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; + } } } From e4c95be07d06878650b09fa8666fb4ddbbea034c Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sat, 6 Dec 2025 07:53:24 -0500 Subject: [PATCH 06/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20to=20Spring?= =?UTF-8?q?=20Boot=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/fluentforms-sample-web-jersey-app/pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spring/fluentforms-sample-web-jersey-app/pom.xml b/spring/fluentforms-sample-web-jersey-app/pom.xml index 346d861e..e28391eb 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 @@ -18,7 +18,6 @@ 17 3.13.1 3.10.6 - 4.16.0 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT @@ -103,7 +102,7 @@ org.testcontainers - junit-jupiter + testcontainers-junit-jupiter test From a19db8791a17883af37f7fb2fc202f84a058582a Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sat, 6 Dec 2025 07:54:20 -0500 Subject: [PATCH 07/11] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20Remove=20htmlunit=20?= =?UTF-8?q?version=20override?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spring 4 has the latest version and we'll let Spring manage this from here on out. --- spring/fluentforms-sample-webmvc-app/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/spring/fluentforms-sample-webmvc-app/pom.xml b/spring/fluentforms-sample-webmvc-app/pom.xml index f23aa050..297fc6be 100644 --- a/spring/fluentforms-sample-webmvc-app/pom.xml +++ b/spring/fluentforms-sample-webmvc-app/pom.xml @@ -18,7 +18,6 @@ 17 3.13.2 3.10.6 - 4.16.0 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT From 363addeab60891c4139bdbedfc0b23cbce04ca0f Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sun, 7 Dec 2025 09:27:07 -0500 Subject: [PATCH 08/11] =?UTF-8?q?=E2=9E=95=20Switch=20dependency=20to=20mo?= =?UTF-8?q?re=20specific=20spring-boot-starter-jersey-test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pom.xml | 2 +- .../spring/JerseyAutoConfigurationTest.java | 59 ++++++++++--------- .../fluentforms-sample-web-jersey-app/pom.xml | 6 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml index eb964636..94b53efb 100644 --- a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml +++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml @@ -94,7 +94,7 @@ org.springframework.boot - spring-boot-starter-test + spring-boot-starter-jersey-test test 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 c175da6d..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,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.boot.autoconfigure.AutoConfigurations; +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; @@ -33,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); } + @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 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), @@ -63,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 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), @@ -78,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 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), @@ -91,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 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 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 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), diff --git a/spring/fluentforms-sample-web-jersey-app/pom.xml b/spring/fluentforms-sample-web-jersey-app/pom.xml index e28391eb..d8c3dd93 100644 --- a/spring/fluentforms-sample-web-jersey-app/pom.xml +++ b/spring/fluentforms-sample-web-jersey-app/pom.xml @@ -41,10 +41,6 @@ - - org.springframework.boot - spring-boot-starter - org.springframework.boot spring-boot-starter-jersey @@ -74,7 +70,7 @@ org.springframework.boot - spring-boot-starter-test + spring-boot-starter-jersey-test test From 7f3309de9c76a660a627ba7d3aa9810a95fb9387 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Sun, 7 Dec 2025 09:28:09 -0500 Subject: [PATCH 09/11] =?UTF-8?q?=E2=9E=96=20Trivial:=20Remove=20unnecessa?= =?UTF-8?q?ry=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring/FluentFormsJerseyAutoConfigurationTest.java | 1 - 1 file changed, 1 deletion(-) 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 40130b4f..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 = { From cdbbabbc33040de72a1ea2de7b378a3538d0a0fd Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Thu, 11 Dec 2025 07:24:29 -0500 Subject: [PATCH 10/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20WireMock?= =?UTF-8?q?=20to=20latest=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, start using WireMock/Spring Boot integration. This will allow for random ports to be used in that test. Fixed an issue that surfaced during testing. New version of WireMock/Spring Integration uses new version of Apache HTTP client that does not like duplicate headers. The POST proxy code was generating duplicate Transfer-Encoding headers, so had to implement code to remove the duplicate. --- .../pom.xml | 24 ++++++++++++++++++- .../fluentforms/spring/AemProxyEndpoint.java | 16 +++++++++++-- .../spring/AemProxyEndpointTest.java | 9 +++++-- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/spring/fluentforms-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-spring-boot-autoconfigure/pom.xml index d7cd1fca..8230ab79 100644 --- a/spring/fluentforms-spring-boot-autoconfigure/pom.xml +++ b/spring/fluentforms-spring-boot-autoconfigure/pom.xml @@ -19,7 +19,8 @@ 3.0.5 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT - 4.0.0-beta.16 + 4.0.0-beta.21 + 4.0.8 1.20.2 1.2.3 @@ -86,6 +87,7 @@ rest-services.client ${fluentforms.version} + org.springframework.boot @@ -104,6 +106,26 @@ ${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/AemProxyEndpoint.java b/spring/fluentforms-spring-boot-autoconfigure/src/main/java/com/_4point/aem/fluentforms/spring/AemProxyEndpoint.java index ac4eaca4..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 @@ -190,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); @@ -223,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/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..ea597fd7 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", @@ -43,6 +44,10 @@ "jasypt.encryptor.salt-generator-classname=org.jasypt.salt.RandomSaltGenerator", "logging.level.com._4point.aem.fluentforms.spring.AemProxyEndpoint=DEBUG" }) +@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); From 5051949a6cda62947fde0e730ccb259e15500cc9 Mon Sep 17 00:00:00 2001 From: Rob McDougall Date: Fri, 12 Dec 2025 08:07:31 -0500 Subject: [PATCH 11/11] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20to=20lates?= =?UTF-8?q?t=20WireMock=20version,=20Use=20random=20ports=20in=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Altered the tests to use the Spring/WireMock integration which allows for the use of random ports instead of fixed ports. New libraries surfaced an issue with the server code returning duplicate Transfer-Encoding headers in POST responses. This was corrected. --- .../pom.xml | 17 +++++- .../spring/AemProxyJerseyEndpoint.java | 4 +- .../spring/AemProxyJerseyEndpointTest.java | 16 +++-- .../fluentforms-sample-web-jersey-app/pom.xml | 4 +- spring/fluentforms-sample-webmvc-app/pom.xml | 12 +++- .../pom.xml | 24 +++----- .../spring/AemProxyAfSubmissionTest.java | 58 +++---------------- .../spring/AemProxyEndpointTest.java | 5 +- 8 files changed, 59 insertions(+), 81 deletions(-) diff --git a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml index 94b53efb..ae5b642c 100644 --- a/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml +++ b/spring/fluentforms-jersey-spring-boot-autoconfigure/pom.xml @@ -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 @@ -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/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/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-sample-web-jersey-app/pom.xml b/spring/fluentforms-sample-web-jersey-app/pom.xml index d8c3dd93..acd3d642 100644 --- a/spring/fluentforms-sample-web-jersey-app/pom.xml +++ b/spring/fluentforms-sample-web-jersey-app/pom.xml @@ -16,8 +16,8 @@ 17 - 3.13.1 - 3.10.6 + 4.0.0-beta.22 + 4.0.8 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT diff --git a/spring/fluentforms-sample-webmvc-app/pom.xml b/spring/fluentforms-sample-webmvc-app/pom.xml index 297fc6be..104af4c0 100644 --- a/spring/fluentforms-sample-webmvc-app/pom.xml +++ b/spring/fluentforms-sample-webmvc-app/pom.xml @@ -16,8 +16,8 @@ 17 - 3.13.2 - 3.10.6 + 4.0.0-beta.22 + 4.0.8 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT @@ -84,7 +84,13 @@ org.wiremock - wiremock-standalone + wiremock + ${wiremock.version} + test + + + org.wiremock + wiremock-junit5 ${wiremock.version} test diff --git a/spring/fluentforms-spring-boot-autoconfigure/pom.xml b/spring/fluentforms-spring-boot-autoconfigure/pom.xml index 8230ab79..2adf25fd 100644 --- a/spring/fluentforms-spring-boot-autoconfigure/pom.xml +++ b/spring/fluentforms-spring-boot-autoconfigure/pom.xml @@ -19,7 +19,7 @@ 3.0.5 0.0.5-SNAPSHOT 0.0.4-SNAPSHOT - 4.0.0-beta.21 + 4.0.0-beta.22 4.0.8 1.20.2 1.2.3 @@ -102,30 +102,22 @@ org.wiremock - wiremock-standalone + wiremock ${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/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 f46764a7..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(); } @@ -181,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); } @@ -203,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( @@ -255,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); } @@ -278,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())))); } @@ -299,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())); } @@ -314,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())); } @@ -342,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())); } @@ -432,12 +398,10 @@ 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); } @@ -448,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/AemProxyEndpointTest.java b/spring/fluentforms-spring-boot-autoconfigure/src/test/java/com/_4point/aem/fluentforms/spring/AemProxyEndpointTest.java index ea597fd7..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 @@ -42,7 +42,9 @@ "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" @@ -52,7 +54,6 @@ 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.