Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions spring-web-modules/spring-resttemplate-3/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
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">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-resttemplate-3</artifactId>
<version>0.1-SNAPSHOT</version>
Expand Down Expand Up @@ -63,6 +63,12 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>

<!-- XML support using Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
</dependencies>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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 PaymentResponse processPayment(PaymentRequest request, String paymentUrl) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML));

HttpEntity<PaymentRequest> entity = new HttpEntity<>(request, headers);

ResponseEntity<PaymentResponse> response =
restTemplate.postForEntity(paymentUrl, entity, PaymentResponse.class);

return response.getBody();
} catch (Exception ex) {
throw new RuntimeException("Payment processing failed: " + ex.getMessage(), ex);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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<PaymentResponse> 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 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<PaymentRequest> 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)
);
}
}