Handling SOAP Fault in Spring Boot WebServiceTemplate - spring-boot

I am new to SOAP, trying to run a sample SOAP client using Spring Boot
How the SOAP fault, Exceptions or Errors are handled while using WebServiceTemplate
public class CountryClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(CountryClient.class);
public GetCountryResponse getCountry(String country) {
GetCountryRequest request = new GetCountryRequest();
request.setName(country);
log.info("Requesting location for " + country);
GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
.marshalSendAndReceive("http://localhost:8080/ws/countries", request,
new SoapActionCallback(
"http://spring.io/guides/gs-producing-web-service/GetCountryRequest"));
return response;
}
}

One way is writing your custom interceptor which implements Spring WS's ClientInterceptor interface. You should override handleFault method to handle SOAP faults with your custom logic.
public class MySoapClientInterceptor implements ClientInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MySoapClientInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
LOGGER.info("intercepted a fault...");
SoapBody soapBody = getSoapBody(messageContext);
SoapFault soapFault = soapBody.getFault();
LOGGER.error(soapFault.getFaultStringOrReason());
throw new RuntimeException(String.format("Error occured while invoking SOAP service - %s ", soapFault.getFaultStringOrReason()));
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
private SoapBody getSoapBody(MessageContext messageContext) {
SoapMessage soapMessage = (SoapMessage) messageContext.getResponse();
SoapEnvelope soapEnvelope = soapMessage.getEnvelope();
return soapEnvelope.getBody();
}
}
Then you need to register your custom Interceptor class as an interceptor at your SOAP client config class. At the bean definition of CountryClient at your Configuration class in your case.
#Configuration
public class SoapClientConfig {
#Value("${soap.server.url}")
public String soap_server_url;
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.example");
return marshaller;
}
#Bean
public SoapConnector soapConnector(Jaxb2Marshaller marshaller) {
SoapConnector client = new SoapConnector();
client.setDefaultUri(soap_server_url);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
ClientInterceptor[] clientInterceptors = {new MySoapClientInterceptor()};
client.setInterceptors(clientInterceptors);
return client;
}
}

Related

Spring WS - return non-MTOM on MTOM request

I have a SOAP service that receives a binary attachment (MTOM). The issue is that I would like to return a non-MTOM response (a "clean" SOAP return). The current code return a multipart response and uses "xop+xml" content-type.
The code is:
#Configuration
public class MyWebServiceConfig {
#Value("${config1}")
private String contextPath;
#Value("${config2}")
private String namespace;
#Value("${config3}")
private String wsdl;
#Bean
ServletRegistrationBean<?> webServicesRegistration(ApplicationContext ctx) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(ctx);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(messageDispatcherServlet, contextPath, "*.wsdl");
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(namespace);
marshaller.setMtomEnabled(true);
return marshaller;
}
#Bean
public MarshallingPayloadMethodProcessor methodProcessor(Jaxb2Marshaller marshaller) {
return new MarshallingPayloadMethodProcessor(marshaller);
}
#Bean
DefaultMethodEndpointAdapter endpointAdapter(MarshallingPayloadMethodProcessor methodProcessor) {
DefaultMethodEndpointAdapter adapter = new DefaultMethodEndpointAdapter();
adapter.setMethodArgumentResolvers(Collections.singletonList(methodProcessor));
adapter.setMethodReturnValueHandlers(Collections.singletonList(methodProcessor));
return adapter;
}
#Bean
public SimpleWsdl11Definition contentStore() {
SimpleWsdl11Definition definition = new SimpleWsdl11Definition();
definition.setWsdl(new ClassPathResource(wsdl));
return definition;
}
}
I have tried changing to setMtomEnabled(false) - but then the request is not handled correctly.
Is the anyway of receiving MTOM - but returning a non-MTOM response?
Edit:
Endpoint looks like this:
#Endpoint
public class MyEndpoint {
private ObjectFactory objectFactory;
public MyEndpoint() {
this.objectFactory = new ObjectFactory();
}
#PayloadRoot(localPart = "SendMyRequest", namespace = "somenamespace")
#ResponsePayload
public JAXBElement<MyReceipt> store(#RequestPayload JAXBElement<MyRequest> storeContentRequest) throws IOException {
MyReceipt receipt = new MyReceipt();
receipt.setf1(123);
receipt.setf2(456);
return this.objectFactory.createMyResponse(receipt);
}
}

PayloadRootSmartSoapEndpointInterceptor Intercepts multiple EndPoints

I'm trying to add a Custom Interceptors to the interceptors List in my EndPoint Config, but i have a problem where PayloadRootSmartSoapEndpointInterceptor intercepts 2 of my Endpoints instead of one, I have Defined 2 SOAP EndPoints using spring-ws.
#EnableWs
#Configuration
#Order(1)
public class Config extends WsConfigurerAdapter {
private String namespaceBti = "http://tarim.bull.ro/BullTarimWS/BTIService";
private String namespaceBtiLst = "http://tarim.bull.ro/BullTarimWS/BTILSTService";
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/public/btiWS/*");
}
//Service 1
#Bean(name = "BTIService")
public DefaultWsdl11Definition defaultWsdl11DefinitionBti(#Qualifier("BTISchema") XsdSchema certificateSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("BtiPort");
wsdl11Definition.setLocationUri("/public/btiWS"); //<context-path>
wsdl11Definition.setTargetNamespace(namespaceBti);
wsdl11Definition.setRequestSuffix("Input");
wsdl11Definition.setResponseSuffix("Output");
wsdl11Definition.setSchema(certificateSchema);
return wsdl11Definition;
}
#Bean(name="BTISchema")
public XsdSchema certificateSchemaBti() {
return new SimpleXsdSchema(new ClassPathResource("xml-resources/GETBTI.xsd"));
}
// Service 2
#Bean(name = "BTILSTService") //name of the wsdl in the URL
public DefaultWsdl11Definition defaultWsdl11DefinitionBtiLst(#Qualifier("BTILSTSchema") XsdSchema certificateSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("BtiLstPort");
wsdl11Definition.setLocationUri("/public/btiWS"); //<context-path>
wsdl11Definition.setTargetNamespace(namespaceBtiLst);
wsdl11Definition.setRequestSuffix("Input");
wsdl11Definition.setResponseSuffix("Output");
wsdl11Definition.setSchema(certificateSchema);
return wsdl11Definition;
}
#Bean(name="BTILSTSchema")
public XsdSchema certificateSchemaBtiLst() {
return new SimpleXsdSchema(new ClassPathResource("xml-resources/GETBTILST.xsd"));
}
#Autowired
private WriteBtiDto writeBtiDto;
Adding a Custom Interceptor to the list>
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new PayloadRootSmartSoapEndpointInterceptor(
new BtiEndpointInterceptor(), //let Spring Build and Manage The Bean, not me
BtiEndpoint.getNamespaceUri(),
BtiEndpoint.getLocalPart()
));
}
BTI EndPoint
#Endpoint()
public class BtiEndpoint {
private static final String NAMESPACE_URI="http://tarim.bull.ro/BullTarimWS/BTIService";
private static final String LOCAL_PART = "CXMLTYPE-GETBTIInput";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = LOCAL_PART)
#ResponsePayload
public CXMLTYPEGETBTIOutput getBTI(#RequestPayload CXMLTYPEGETBTIInput request){
CXMLTYPEGETBTIOutput response = new CXMLTYPEGETBTIOutput();
return response;
}
// GETTERS AND SETTER FOR NAMESPACE AND LOCAL PART
BTILST EndPoint
#Endpoint()
public class BtiLstEndpoint {
private static final String NAMESPACE_URI="http://tarim.bull.ro/BullTarimWS/BTILSTService";
private static final String LOCAL_PART = "CXMLTYPE-GETBTILSTInput";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = LOCAL_PART)
#ResponsePayload
public CXMLTYPEGETBTILSTOutput getBTI(#RequestPayload CXMLTYPEGETBTILSTInput request){
CXMLTYPEGETBTILSTOutput response = new CXMLTYPEGETBTILSTOutput();
return response;
}
// GETTERS AND SETTER FOR NAMESPACE AND LOCAL PART
EndpointInterceptor
#Component
public class BtiEndpointInterceptor implements EndpointInterceptor {
private static final Log LOG = LogFactory.getLog(BtiEndpointInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext, Object o) throws Exception {
LOG.info("1. Global Request Handling");
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext, Object o) throws Exception {
LOG.info("2. Global Response Handling");
return true;
}
#Override
public boolean handleFault(MessageContext messageContext, Object o) throws Exception {
LOG.info("Global Exception Handling");
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) {
}

Read custom header value from the response

When I send request from the Soap UI under raw response tab I see the following result(find attachment). Now in AOP controller I want to read this header value which is marked as red. How it is possible? Thanks in advance.
In my application to send soap requests I have WebServiceTemplate. I applied custom interceptor WebServiceInterceptor (which implements ClientInterceptor interface) on this web service template. In overridden afterCompletion method, which injects MessageContext, I was able to take this property from the SaajMessageHeader.
Here is what code looks like:
#Configuration
public class MyWebServiceConfig {
#Bean(name = "myWSClient")
public WebServiceTemplate myWSClient() throws Exception {
WebServiceTemplate template = new WebServiceTemplate();
...
WebServiceInterceptor[] interceptors = { new WebServiceInterceptor() };
template.setInterceptors(interceptors);
return template;
}
private static class WebServiceInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
...
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
try {
SaajSoapMessage message = (SaajSoapMessage) messageContext.getResponse();
String []traceId = message.getSaajMessage().getMimeHeaders().getHeader("ITRACING_TRACE_ID");
if(traceId != null && traceId.length > 0){
process.setTraceId(traceId[0]);
}
} catch (Exception e) {
}
}
}

Adding custom header to response in spring rest / spring boot

i am trying to send session id in response header in rest controller but excludepathpattern() seems not working
** the configuration class is not triggering **
i have tried changing the sevlet version but it didnt work
ContextListener
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
Map<String, HttpSession> map = new HashMap<>();
context.setAttribute("activeUsers", map);
HttpSessionListener
ServletContext context = session.getServletContext();
Map<String, HttpSession> activeUsers = (Map<String, HttpSession>) context.getAttribute("activeUsers");
activeUsers.put(session.getId(), session);
HandlerInterceptor
ServletContext context = request.getServletContext();
Map<String, HttpSession> activeUsers = (Map<String, HttpSession>) context.getAttribute("activeUsers");
String sessionId = request.getHeader("sessionId");
String requestUrl = request.getRequestURL().toString();
if (requestUrl.contains("/getOtp") || requestUrl.contains("/validateOtp")) {
return true;
} else {
if (activeUsers.containsKey(sessionId)) {
return true;
} else {
response.setStatus(401);
return false;
}
}
interceptorconfigurartion by extendig websecurityconfigure
#Configuration
#EnableAutoConfiguration
public class SessionInterceptorConfig implements WebMvcConfigurer {
#Autowired
private SessionHanlderInterceptor sessionHandlerIntercepto;
#Override
public void addInterceptors(InterceptorRegistry registry) {
// List<String> paths = new ArrayList<String>();
// paths.add("/auth/*");
registry.addInterceptor(sessionHandlerIntercepto).excludePathPatterns("/auth/**");
}
#Bean
public ServletListenerRegistrationBean<CustomSessionListener> filterRegistrationBean() {
ServletListenerRegistrationBean<CustomSessionListener> registrationBean = new ServletListenerRegistrationBean<CustomSessionListener>();
CustomSessionListener customURLFilter = new CustomSessionListener();
registrationBean.setListener(customURLFilter);
registrationBean.setOrder(1); // set precedence
return registrationBean;
}
#Bean
public ServletListenerRegistrationBean<CustomServletContextListener> filterContextRregistration() {
ServletListenerRegistrationBean<CustomServletContextListener> registrationBean = new ServletListenerRegistrationBean<CustomServletContextListener>();
CustomServletContextListener customURLFilter = new CustomServletContextListener();
registrationBean.setListener(customURLFilter);
registrationBean.setOrder(1); // set precedence
return registrationBean;
}
Sprinboot main class
#SpringBootApplication
public class CustomerApplication extends SpringBootServletInitializer {
i expect to add the session id to header in response and to check for the sessionid in request
You can use spring web component "OncePerRequestFilter". You need to inject a bean which extends OncePerRequestFilter. Example:
public class CustomHeaderFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) throws IOException, ServletException {
response.setHeader(customHeaderName, customHeaderValue);
chain.doFilter(request, response);
}
}

Autowired RestTemplate returns incorrect ResponseErrorHandler

everyone. I am now working in a Spring Boot project that one of the function is to send some HTTP requests to third party API and get the responses.
I decided to use RestTemplate as the http client and I created a bean for RestTemplate in a java file with #Configuration
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RequestLoggingInterceptor());
restTemplate.setInterceptors(interceptors);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
return restTemplate;
}
Custom error handler and logging interceptor are set in restTemplate. Then, I injected the RestTemplate into my service class by
#Autowired
RestTemplate restTemplate;
However, I found that my custom error handler is not working properly and I tried to check the error handler by
ResponseErrorHandler customErrorHandler = restTemplate.getErrorHandler();
I found that theErrorHandler is still DefaultResponseErrorHandler but not my custom errorHandler. Why? Is there anything wrong?
Updated
Below are my custom error handler and logging interceptor
public class RestResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
return true;
}
return false;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
throw new RestResponseErrorException(response.getStatusCode() + "" + response.getStatusText());
}
}
}
and
public class RequestLoggingInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Request begin -----------------------------");
log.debug("URI:{}", request.getURI());
log.debug("Method:{}", request.getMethod());
log.debug("Headers:{}", request.getHeaders().toString());
log.debug("Request body:{}", new String(body, "UTF-8"));
log.debug("Request end -----------------------------");
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Response begin -----------------------------");
log.debug("Status code:{}", response.getStatusCode());
log.debug("Status text:{}", response.getStatusText());
log.debug("Headers:{}", response.getHeaders().toString());
log.debug("Response body:{}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("Response end -----------------------------");
}
}
}

Resources