Examples
Functional style of Soap API call in Java Springboot using Generics and Builder Pattern.
We should be able to call Soap API in the following fashion.
Sample Outcome
return soapBaseClient
.request(getUserService::getUser)
.payload(soapClientRequestMapper.toGetUserRequest(userId))
.then(soapClientResponseMapper::mapToGetUserResponse)
.fetchResponse();
Java Code
Create a generic RequestMethod.java
functional interface which will point the Soap Service method reference or lambda
@FunctionalInterface
public interface RequestMethod<P, R> {
R execute(P payload);
}
Create a generic Request.java
class with payload
and then
methods.
import java.util.function.Function;
public class Request<RequestPayloadType, ResponsePayloadType> {
private final RequestMethod<RequestPayloadType, ResponsePayloadType> requestMethod;
private RequestPayloadType payload;
public Request(RequestMethod<RequestPayloadType, ResponsePayloadType> requestMethod) {
this.requestMethod = requestMethod;
}
public Request<RequestPayloadType, ResponsePayloadType> payload(RequestPayloadType payload) {
this.payload = payload;
return this;
}
public <ProcessedResponseType> RequestCallback<ResponsePayloadType, ProcessedResponseType> then(
Function<ResponsePayloadType, ProcessedResponseType> handler) {
return new RequestCallback<>(
() -> requestMethod.execute(payload),
handler
);
}
}
Create a generic RequestCallback.java
class with fetchResponse
and several other methods to handle success or error scenario.
import com.soap.services.ResponseHeaderType;
import java.util.function.Function;
import java.util.function.Supplier;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class RequestCallback<ResponsePayloadType, ProcessedResponseType> {
private static final String RESPONSE_HEADER_FIELD_NAME = "responseHeader";
private static final String RESPONSE_HEADER_SUCCESS_CODE = "0";
private final Supplier<ResponsePayloadType> requestProcessor;
private final Function<ResponsePayloadType, ProcessedResponseType> successHandler;
public ProcessedResponseType fetchResponse() {
// Call the Soap API
ResponsePayloadType response = processRequest();
// Handle error response
handleError(response);
// Process or Map the success Soap API Response with expected response type
return successHandler.apply(response);
}
private ResponsePayloadType processRequest() {
try {
// Call the Soap API
log.debug("Calling soap api");
return requestProcessor.get();
} catch (Exception e) {
log.error("Soap call failed: ", e);
throw new RuntimeException("Soap Call failed");
}
}
private void handleError(ResponsePayloadType response) {
ResponseHeaderType responseHeaderType = extractResponseHeader(response);
if (!RESPONSE_HEADER_SUCCESS_CODE.equals(responseHeaderType.getResponseCode())) {
throw new RuntimeException("Soap Call failed");
}
}
public ResponseHeaderType extractResponseHeader(ResponsePayloadType response) {
try {
var responseHeaderField = response.getClass().getDeclaredField(RESPONSE_HEADER_FIELD_NAME);
responseHeaderField.setAccessible(true);
var responseHeader = responseHeaderField.get(response);
return (ResponseHeaderType) responseHeader;
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Create a SoapBaseClient.java
class with generic request
method.
import org.springframework.stereotype.Component;
@Component
public class SoapBaseClient {
public <RequestPayloadType, ResponsePayloadType> Request<RequestPayloadType, ResponsePayloadType> request(
RequestMethod<RequestPayloadType, ResponsePayloadType> requestMethod) {
// Passing the input Soap API method reference or lambda expression
return new Request<>(requestMethod);
}
}
Now, we can call Soap service like below.
import com.example.mapper.SoapClientRequestMapper;
import com.example.mapper.SoapClientResponseMapper;
import com.example.mapper.SoapBaseClient;
import com.soap.services.GetUserService;
import java.util.function.Function;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class SoapServiceClient {
private final SoapClientResponseMapper soapClientResponseMapper;
private final SoapClientRequestMapper soapClientRequestMapper;
private final GetUserService getUserService;
private final SoapBaseClient soapBaseClient;
public User getUser(String userId) {
return soapBaseClient
.request(getUserService::getUser)
.payload(soapClientRequestMapper.toGetUserRequest(userId))
.then(soapClientResponseMapper::mapToGetUserResponse)
.fetchResponse();
}
}
Request mapper class.
import com.soap.services.GetUserRequest;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface SoapClientRequestMapper {
@Mapping(target = "userId", source = "userId")
GetUserRequest toGetUserRequest(String userId);
}
Response mapper class.
import com.soap.services.GetUserResponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface SoapClientResponseMapper {
@Mapping(target = "id", source = "userId")
@Mapping(target = "name", source = "userName")
@Mapping(target = "email", source = "userEmail")
User mapToGetUserResponse(GetUserResponse getUserResponse);
}
Last updated
Was this helpful?