From 02bb0b99de1968a320fa8562980b52fab4c91acd Mon Sep 17 00:00:00 2001 From: achraftt Date: Fri, 31 Oct 2025 23:11:24 +0100 Subject: [PATCH 1/4] BAEL-9458: Sending XML POST Requests with Spring RestTemplate --- .../spring-resttemplate-3/pom.xml | 10 +- .../xmlpost/config/RestTemplateConfig.java | 13 +++ .../xmlpost/model/PaymentRequest.java | 62 ++++++++++ .../xmlpost/model/PaymentResponse.java | 50 ++++++++ .../xmlpost/service/PaymentService.java | 43 +++++++ .../service/PaymentServiceUnitTest.java | 107 ++++++++++++++++++ 6 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java create mode 100644 spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentRequest.java create mode 100644 spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentResponse.java create mode 100644 spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java create mode 100644 spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java diff --git a/spring-web-modules/spring-resttemplate-3/pom.xml b/spring-web-modules/spring-resttemplate-3/pom.xml index 86b0dccb2e4f..1174107a6219 100644 --- a/spring-web-modules/spring-resttemplate-3/pom.xml +++ b/spring-web-modules/spring-resttemplate-3/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-resttemplate-3 0.1-SNAPSHOT @@ -63,6 +63,12 @@ commons-io ${commons-io.version} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java new file mode 100644 index 000000000000..7d439bf0c2be --- /dev/null +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.baeldung.xmlpost.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentRequest.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentRequest.java new file mode 100644 index 000000000000..5283fa71f2eb --- /dev/null +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentRequest.java @@ -0,0 +1,62 @@ +package com.baeldung.xmlpost.model; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JacksonXmlRootElement(localName = "PaymentRequest") +public class PaymentRequest { + + @JacksonXmlProperty(localName = "transactionId") + private String transactionId; + + @JacksonXmlProperty(localName = "amount") + private Double amount; + + @JacksonXmlProperty(localName = "currency") + private String currency; + + @JacksonXmlProperty(localName = "recipient") + private String recipient; + + public PaymentRequest() { + } + + public PaymentRequest(String transactionId, Double amount, String currency, String recipient) { + this.transactionId = transactionId; + this.amount = amount; + this.currency = currency; + this.recipient = recipient; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public Double getAmount() { + return amount; + } + + public void setAmount(Double amount) { + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public String getRecipient() { + return recipient; + } + + public void setRecipient(String recipient) { + this.recipient = recipient; + } +} diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentResponse.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentResponse.java new file mode 100644 index 000000000000..317875672c45 --- /dev/null +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/model/PaymentResponse.java @@ -0,0 +1,50 @@ +package com.baeldung.xmlpost.model; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JacksonXmlRootElement(localName = "PaymentResponse") +public class PaymentResponse { + + @JacksonXmlProperty(localName = "status") + private String status; + + @JacksonXmlProperty(localName = "message") + private String message; + + @JacksonXmlProperty(localName = "referenceNumber") + private String referenceNumber; + + public PaymentResponse() { + } + + public PaymentResponse(String status, String message, String referenceNumber) { + this.status = status; + this.message = message; + this.referenceNumber = referenceNumber; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getReferenceNumber() { + return referenceNumber; + } + + public void setReferenceNumber(String referenceNumber) { + this.referenceNumber = referenceNumber; + } +} diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java new file mode 100644 index 000000000000..935252a0f7f2 --- /dev/null +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java @@ -0,0 +1,43 @@ +package com.baeldung.xmlpost.service; + +import com.baeldung.xmlpost.model.PaymentRequest; +import com.baeldung.xmlpost.model.PaymentResponse; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; + +@Service +public class PaymentService { + + private final RestTemplate restTemplate; + + public PaymentService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public ResponseEntity sendPaymentRequest(PaymentRequest request, String paymentUrl) { + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_XML); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); + + HttpEntity entity = new HttpEntity<>(request, headers); + + return restTemplate.postForEntity(paymentUrl, entity, PaymentResponse.class); + } + + public PaymentResponse processPayment(PaymentRequest request, String paymentUrl) { + try { + ResponseEntity response = sendPaymentRequest(request, paymentUrl); + return response.getBody(); + } catch (Exception ex) { + throw new RuntimeException("Payment processing failed: " + ex.getMessage(), ex); + } + } +} + diff --git a/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java b/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java new file mode 100644 index 000000000000..884a0a1877ce --- /dev/null +++ b/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java @@ -0,0 +1,107 @@ +package com.baeldung.xmlpost.service; + + +import com.baeldung.xmlpost.model.PaymentRequest; +import com.baeldung.xmlpost.model.PaymentResponse; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PaymentServiceUnitTest { + + @Mock + private RestTemplate restTemplate; + + @InjectMocks + private PaymentService paymentService; + + private final String testUrl = "http://mock-payment-service"; + + @Test + void givenValidPaymentRequest_whenProcessPayment_thenReturnSuccessfulResponse() { + PaymentRequest request = new PaymentRequest("TXN001", 100.50, "USD", "Jane Doe"); + PaymentResponse expectedResponse = new PaymentResponse( + "SUCCESS", "Payment processed successfully", "REF12345" + ); + + ResponseEntity mockResponse = + new ResponseEntity<>(expectedResponse, HttpStatus.OK); + + when(restTemplate.postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class))) + .thenReturn(mockResponse); + + PaymentResponse actualResponse = paymentService.processPayment(request, testUrl); + + assertNotNull(actualResponse); + assertEquals("SUCCESS", actualResponse.getStatus()); + assertEquals("REF12345", actualResponse.getReferenceNumber()); + assertEquals("Payment processed successfully", actualResponse.getMessage()); + + verify(restTemplate).postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class)); + } + + @Test + void givenRemoteServiceReturnsBadRequest_whenProcessPayment_thenThrowMeaningfulException() { + PaymentRequest request = new PaymentRequest("TXN002", 200.0, "EUR", "John Smith"); + + when(restTemplate.postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Invalid amount")); + + RuntimeException exception = assertThrows(RuntimeException.class, + () -> paymentService.processPayment(request, testUrl)); + + assertTrue(exception.getMessage().contains("Payment processing failed")); + assertTrue(exception.getMessage().contains("Invalid amount")); + } + + @Test + void givenNetworkFailure_whenProcessPayment_thenThrowConnectionErrorException() { + PaymentRequest request = new PaymentRequest("TXN003", 150.75, "GBP", "Alice Brown"); + + when(restTemplate.postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class))) + .thenThrow(new RuntimeException("Connection timeout")); + + Exception exception = assertThrows(RuntimeException.class, + () -> paymentService.processPayment(request, testUrl)); + + assertTrue(exception.getMessage().contains("Payment processing failed")); + assertTrue(exception.getMessage().contains("Connection timeout")); + } + + @Test + void givenXmlRequest_whenProcessPayment_thenSetCorrectXmlHttpHeaders() { + PaymentRequest request = new PaymentRequest("TXN004", 300.0, "CAD", "Bob Wilson"); + PaymentResponse expectedResponse = new PaymentResponse("SUCCESS", "OK", "REF67890"); + + when(restTemplate.postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class))) + .thenReturn(new ResponseEntity<>(expectedResponse, HttpStatus.OK)); + + paymentService.processPayment(request, testUrl); + + verify(restTemplate).postForEntity( + eq(testUrl), + argThat((HttpEntity entity) -> { + boolean hasXmlContentType = entity.getHeaders().getContentType() + .includes(MediaType.APPLICATION_XML); + boolean acceptsXml = entity.getHeaders().getAccept() + .contains(MediaType.APPLICATION_XML); + return hasXmlContentType && acceptsXml; + }), + eq(PaymentResponse.class) + ); + } +} \ No newline at end of file From ecc2f5e02b83b6a8e46728cdba787975343d4dda Mon Sep 17 00:00:00 2001 From: achraftt Date: Sat, 8 Nov 2025 15:28:14 +0100 Subject: [PATCH 2/4] BAEL-9458: Sending XML POST Requests with Spring RestTemplate --- .../xmlpost/service/PaymentService.java | 20 ++++++++----------- .../service/PaymentServiceUnitTest.java | 15 -------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java index 935252a0f7f2..8ba92d242cc0 100644 --- a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java @@ -13,27 +13,23 @@ @Service public class PaymentService { - private final RestTemplate restTemplate; public PaymentService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } - public ResponseEntity sendPaymentRequest(PaymentRequest request, String paymentUrl) { + public PaymentResponse processPayment(PaymentRequest request, String paymentUrl) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_XML); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_XML); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); + HttpEntity entity = new HttpEntity<>(request, headers); - HttpEntity entity = new HttpEntity<>(request, headers); + ResponseEntity response = + restTemplate.postForEntity(paymentUrl, entity, PaymentResponse.class); - return restTemplate.postForEntity(paymentUrl, entity, PaymentResponse.class); - } - - public PaymentResponse processPayment(PaymentRequest request, String paymentUrl) { - try { - ResponseEntity response = sendPaymentRequest(request, paymentUrl); return response.getBody(); } catch (Exception ex) { throw new RuntimeException("Payment processing failed: " + ex.getMessage(), ex); diff --git a/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java b/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java index 884a0a1877ce..18ade69ff92e 100644 --- a/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java +++ b/spring-web-modules/spring-resttemplate-3/src/test/java/com/baeldung/xmlpost/service/PaymentServiceUnitTest.java @@ -1,6 +1,5 @@ package com.baeldung.xmlpost.service; - import com.baeldung.xmlpost.model.PaymentRequest; import com.baeldung.xmlpost.model.PaymentResponse; import org.junit.jupiter.api.Test; @@ -68,20 +67,6 @@ void givenRemoteServiceReturnsBadRequest_whenProcessPayment_thenThrowMeaningfulE assertTrue(exception.getMessage().contains("Invalid amount")); } - @Test - void givenNetworkFailure_whenProcessPayment_thenThrowConnectionErrorException() { - PaymentRequest request = new PaymentRequest("TXN003", 150.75, "GBP", "Alice Brown"); - - when(restTemplate.postForEntity(eq(testUrl), any(HttpEntity.class), eq(PaymentResponse.class))) - .thenThrow(new RuntimeException("Connection timeout")); - - Exception exception = assertThrows(RuntimeException.class, - () -> paymentService.processPayment(request, testUrl)); - - assertTrue(exception.getMessage().contains("Payment processing failed")); - assertTrue(exception.getMessage().contains("Connection timeout")); - } - @Test void givenXmlRequest_whenProcessPayment_thenSetCorrectXmlHttpHeaders() { PaymentRequest request = new PaymentRequest("TXN004", 300.0, "CAD", "Bob Wilson"); From b6bb56d25efd35742f260ea55784cecc782f591e Mon Sep 17 00:00:00 2001 From: achraftt Date: Fri, 14 Nov 2025 00:40:56 +0100 Subject: [PATCH 3/4] BAEL-9458: Sending XML POST Requests with Spring RestTemplate --- .../compress/RestTemplateConfiguration.java | 25 ++++++++++++++----- .../xmlpost/service/PaymentService.java | 3 ++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java index 12b1e4249ed2..693b01f5cda3 100644 --- a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java @@ -2,20 +2,33 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.web.client.RestTemplate; +import java.util.stream.Collectors; + @Configuration public class RestTemplateConfiguration { - /** - * A RestTemplate that compresses requests. - * - * @return RestTemplate - */ @Bean - public RestTemplate getRestTemplate() { + @Primary + public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); + + // Remove XML converters to ensure JSON is used + restTemplate.setMessageConverters( + restTemplate.getMessageConverters().stream() + .filter(converter -> !(converter instanceof MappingJackson2XmlHttpMessageConverter)) + .collect(Collectors.toList()) + ); + restTemplate.getInterceptors().add(new CompressingClientHttpRequestInterceptor()); return restTemplate; } + + @Bean("xmlRestTemplate") + public RestTemplate xmlRestTemplate() { + return new RestTemplate(); + } } diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java index 8ba92d242cc0..6c65c9d07a48 100644 --- a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/service/PaymentService.java @@ -2,6 +2,7 @@ import com.baeldung.xmlpost.model.PaymentRequest; import com.baeldung.xmlpost.model.PaymentResponse; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -15,7 +16,7 @@ public class PaymentService { private final RestTemplate restTemplate; - public PaymentService(RestTemplate restTemplate) { + public PaymentService(@Qualifier("xmlRestTemplate") RestTemplate restTemplate) { this.restTemplate = restTemplate; } From 0edd7b37c5b664b15a75595d32dc935ec691444d Mon Sep 17 00:00:00 2001 From: achraftt Date: Fri, 14 Nov 2025 00:47:43 +0100 Subject: [PATCH 4/4] BAEL-9458: Sending XML POST Requests with Spring RestTemplate --- .../com/baeldung/compress/RestTemplateConfiguration.java | 5 ----- .../java/com/baeldung/xmlpost/config/RestTemplateConfig.java | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java index 693b01f5cda3..2bbf03b801e0 100644 --- a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/compress/RestTemplateConfiguration.java @@ -26,9 +26,4 @@ public RestTemplate restTemplate() { restTemplate.getInterceptors().add(new CompressingClientHttpRequestInterceptor()); return restTemplate; } - - @Bean("xmlRestTemplate") - public RestTemplate xmlRestTemplate() { - return new RestTemplate(); - } } diff --git a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java index 7d439bf0c2be..671e32f751d6 100644 --- a/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java +++ b/spring-web-modules/spring-resttemplate-3/src/main/java/com/baeldung/xmlpost/config/RestTemplateConfig.java @@ -6,8 +6,8 @@ @Configuration public class RestTemplateConfig { - @Bean - public RestTemplate restTemplate() { + @Bean("xmlRestTemplate") + public RestTemplate xmlRestTemplate() { return new RestTemplate(); } } \ No newline at end of file