Spring Boot - How add soapenv:Header to SOAP Response? - spring-boot

Hello i have exposed a soap ws with spring boot and i need to add soapenv:Header to the response.
the response that i have actually is
<SOAP-ENV:Envelope
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">
<SOAP-ENV:Body xmlns:m = "">
<m:GetQuotationResponse>
<m:Quotation>Here is the quotation</m:Quotation>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
here is the WebserviceConfiguration class
#EnableWs
#Configuration
public class WebServiceConfiguration extends WsConfigurerAdapter {
#Value("${webservices.endpoint.quotationEndpoint}")
private String quotationEndpoint;
#Bean
public ServletRegistrationBean<CXFServlet> wsDispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean<>(cxfServlet, "/services/*");
}
#Bean(name = "cxf")
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public Endpoint quotationEndpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), new quotationEndpoint());
endpoint.publish(quotationEndpoint);
return endpoint;
}
}

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);
}
}

Spring MVC 5 Rest API convert XML to Bean using JAXB

We are migrating Jersey Endpoints to Spring MVC Rest endpoints. These endpoints consumes and produces XML. But Spring MVC is unable to convert the XML input bean because XML tag and Bean's property name is different. I have to take request as String and Convert it using JAXB explicitly. Is there any better way to do that?
Web Configuration
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
public static final String JAXB_PACKAGE = "com.comp.platform.dataholder";
#Autowired
private Jackson2ObjectMapperBuilder builder;
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
converters.add(new FormHttpMessageConverter());
converters.add(createXmlHttpMessageConverter());
converters.add(createMappingJacksonHttpMessageConverter(builder));
// converters.add(createMappingJackson2XmlHttpMessageConverter(xmlMapper));
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
jaxb2RootElementHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
converters.add(jaxb2RootElementHttpMessageConverter);
}
#Bean
public Jackson2ObjectMapperBuilder customJson() {
return new Jackson2ObjectMapperBuilder().indentOutput(true).serializationInclusion(JsonInclude.Include.NON_NULL)
.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE).failOnUnknownProperties(false);
}
#Bean
#Primary
public ObjectMapper createObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper mapper = builder.build();
mapper.setSerializationInclusion(Include.NON_NULL);
return mapper;
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(NonFinancialDataHolder.class, JaxbDataHolder.class, ViestimateDataHolder.class,CTRequest.class,com.app.rest.PolicyDetailRequest.class,com.app.rest.TransactionSubmitRequest.class,com.app.rest.AgentSuppressInfoRequest.class);
return marshaller;
}
/**
* #return
*/
#Bean
public MappingJackson2HttpMessageConverter createMappingJacksonHttpMessageConverter(
Jackson2ObjectMapperBuilder builder) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(builder.build());
return converter;
}
#Autowired
Jaxb2Marshaller jaxb2Marshaller;
private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
Assert.notNull(jaxb2Marshaller, "Jaxb Marshaller dependency required");
MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(jaxb2Marshaller);
xmlConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
return xmlConverter;
}
public JAXBContext getMarshaller() throws JAXBException {
return JAXBContext.newInstance(JAXB_PACKAGE);
}
}
Bean
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"_UserId",
"_Password",
"_RequestDateTime",
"_Policy"
})
#XmlRootElement(name = "PolicyDetailRequest")
public class PolicyDetailRequest
{
#XmlElement(name = "UserId")
protected String _UserId;
#XmlElement(name = "Password")
protected String _Password;
#XmlElement(name = "RequestDateTime")
protected String _RequestDateTime;
#XmlElement(name = "Policy")
protected String _Policy;
}
Rest Endpoint
#Slf4j
#RestController
#RequestMapping("/api")
public class api
{
#Autowired
Jaxb2Marshaller jaxb2Marshaller;
#PostMapping(value="/PolicyResponse",produces=MediaType.APPLICATION_XML_VALUE,consumes = MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public PolicyDetailResponse getPolicyResponse(#RequestBody String data)
{
PolicyDetailRequest request = (PolicyDetailRequest) jaxb2Marshaller.unmarshal(new StreamSource(new StringReader(data)));
PolicyDetailResponse response = new PolicyDetailResponse();
InternetRSHandler handler = new InternetRSHandler();
response= handler.getPolicyRSResponse(request);
return response;
}
}
Input Request
<PolicyDetailRequest>
<UserId>1234567890</UserId>
<Password>password</Password>
<RequestDateTime>20200420-15:07:23</RequestDateTime>
<Policy>A76ABCD12</Policy>
</PolicyDetailRequest>

Spring boot Oauth2 : Token relay from a client using Feign, Ribbon, Zull and Eureka to a ressource

I have an oauth2 client that get a token from an authorization server successfully. (not always has been the case but now it is... :))
The client, the zuul gateway and the resource server are all registered in Eureka.
My client use a Proxy to access to a remote ressource service named microservice-files.
#RestController
#FeignClient(name = "zuul-server")
#RibbonClient(name = "microservice-files")
public interface ProxyMicroserviceFiles {
#GetMapping(value = "microservice-files/root")
FileBean getUserRoot();
}
So I'd like to relay the token to Zull and then to the resource server.
I can relay the token this way to contact Zuul and apparently the load balancing is managed too (I've just test I didn't know and it's great) also zuul can relay the token, but it's not very convenient I'd prefer the previous approach.
#EnableConfigurationProperties
#SpringBootApplication
#EnableFeignClients("com.clientui")
public class ClientUiApplication {
#Bean
public OAuth2RestOperations restOperations(
OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
}
}
here is the test controler
#Controller
public class ClientController {
#Autowired
private RestOperations restOperations;
#RequestMapping("/root")
public ResponseEntity userRootTest() {
String rootUrl = "http://localhost:9004/microservice-files/root";
return restOperations.getForEntity(rootUrl,FileBean.class);
}
}
If I correctly understand your problem then you can use a RequestInterceptor to add a token in each request by the feign. In order to do it you can use the next configuration:
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri("http://127.0.0.1:9000/auth/login");
resource.setUserAuthorizationUri("http://127.0.0.1:9000/auth/authorize");
resource.setClientId("my-client");
resource.setClientSecret("my-secret");
return resource;
}
This is what I did to make it work.
#Bean(name = "oauth2RestTemplate")
#LoadBalanced
public OAuth2RestTemplate restTemplate(SpringClientFactory clientFactory) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails());
RibbonLoadBalancerClient ribbonLoadBalancerClient = new RibbonLoadBalancerClient(clientFactory);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(ribbonLoadBalancerClient);
ClientCredentialsAccessTokenProvider accessTokenProvider = new ClientCredentialsAccessTokenProvider();
accessTokenProvider.setInterceptors(Arrays.asList(loadBalancerInterceptor));
restTemplate.setAccessTokenProvider(accessTokenProvider);
return restTemplate;
}
public ClientCredentialsResourceDetails resourceDetails() {
ClientCredentialsResourceDetails clientCredentialsResourceDetails = new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setId("1");
clientCredentialsResourceDetails.setClientId("my-ms");
clientCredentialsResourceDetails.setClientSecret("123");
clientCredentialsResourceDetails.setAccessTokenUri("http://oauth-server/oauth/token");
clientCredentialsResourceDetails.setScope(Arrays.asList("read"));
clientCredentialsResourceDetails.setGrantType("client_credentials");
return clientCredentialsResourceDetails;
}

SPRING-WS No marshaller registered. Caused by SPRING IOC

I have a SOAP client service which works fine.
The SOAP headers and request are managed in a SOAPConnector class.
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request) {
// CREDENTIALS and REQUEST SETTINGS...
return getWebServiceTemplate().marshalSendAndReceive(url, request, new SetHeader(requestHeader));
}
}
I'm receiving the requested Data once I call my (SoapConnector) service on the main Class.
#SpringBootApplication
public class SpringSoapSecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSoapSecurityDemoApplication.class, args);
}
#Bean
public CommandLineRunner lookup(SOAPConnector soapConnector) {
return args -> {
String hotelCode = "****";
FutureBookingSummaryRequest request = new FutureBookingSummaryRequest();
FetchBookingFilters additionalFilters = new FetchBookingFilters();
// Some additionalFilters settings
request.setAdditionalFilters(additionalFilters);
FutureBookingSummaryResponse response = (FutureBookingSummaryResponse) soapConnector
.callWebService("MY WSDL URL", request);
System.err.println(response.getHotelReservations());
};
}
}
SO FAR IT WORKS FINE.
Then I've tried to create a separate service for the previous request.
BookingService.java
public class BookingService extends WebServiceGatewaySupport {
#Autowired
SOAPConnector soapConnector;
public String getReservations() {
String hotelCode = "****";
FutureBookingSummaryRequest request = new FutureBookingSummaryRequest();
FetchBookingFilters additionalFilters = new FetchBookingFilters();
// Some additionalFilters settings
request.setAdditionalFilters(additionalFilters);
FutureBookingSummaryResponse response = (FutureBookingSummaryResponse) soapConnector
.callWebService("MY WSDL URL", request);
System.err.println(response.getHotelReservations());
};}
In order to inject the SOAPCONNECTOR I've added #Service to SOAPCONNECTOR class , and #Autowired SOAPConnector soapConnector to the service calling it
Now once I call the created BookingService in the main class
#SpringBootApplication
public class SpringSoapSecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSoapSecurityDemoApplication.class, args);
BookingService bookingService = new BookingService();
bookingService.getReservations();
}
}
The SOAPCONNECTOR stops working an I receive the following error :
No marshaller registered. Check configuration of WebServiceTemplate.
I'm pretty sure that's this issue is related to SPRING IOC , dependecy injection .. Since the SOAP service is well configured and working..
Note : I've checked this similiar question
Consuming a SOAP web service error (No marshaller registered. Check configuration of WebServiceTemplate) but the #Autowired didn't solve the issue.
Any help ?
In case someone is facing the same issue, it turned out that I've missed the #Configuration annotation on the beans configuration class. The right one should look like the following:
#Configuration
public class ConsumerConfig {
private String ContextPath = "somePackage";
private String DefaultUri = "someWsdlURI";
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath(ContextPath);
return marshaller;
}
#Bean
public SOAPConnector checkFutureBookingSummary(Jaxb2Marshaller marshaller) {
SOAPConnector client = new SOAPConnector();
client.setDefaultUri(DefaultUri);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}

Spring-ws not preserving CDATA in the soap response

Context
I am using Spring WS for producing web service as mentioned in the link: https://spring.io/guides/gs/producing-web-service/. However, when I am sending xml content wrapped with CDATA I get the response escaped and this is causing problem for non-java clients as they want element content to be within CDATA and everything unescaped.
I have tried
SaajSoapMessageFactory and
AxiomSoapMessageFactory (with payload caching set to false)
Configuration
WebServiceConfig.java
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext
applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
#Bean
public AxiomSoapMessageFactory messageFactory() {
/*SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_11);
messageFactory.afterPropertiesSet();*/
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
//messageFactory.
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_11);
return messageFactory;
}
#Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema
countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new
DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-
producing-web-service");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
#Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}
CountryRepository.java
#Component
public class CountryRepository {
private static final Map<String, Country> countries = new HashMap<>();
#PostConstruct
public void initData() {
Country spain = new Country();
spain.setName("Spain");
spain.setCapital("<![CDATA[<a>Madrid</a>]]>");
spain.setCurrency(Currency.EUR);
spain.setPopulation(46704314);
countries.put(spain.getName(), spain);
Country poland = new Country();
poland.setName("Poland");
poland.setCapital("Warsaw");
poland.setCurrency(Currency.PLN);
poland.setPopulation(38186860);
countries.put(poland.getName(), poland);
Country uk = new Country();
uk.setName("United Kingdom");
uk.setCapital("London");
uk.setCurrency(Currency.GBP);
uk.setPopulation(63705000);
countries.put(uk.getName(), uk);
}
public Country findCountry(String name) {
Assert.notNull(name, "The country's name must not be null");
return countries.get(name);
}
}
Soap UI Request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://spring.io/guides/gs-producing-web-service">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>
Soap UI response
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital><![CDATA[<a>Madrid</a>]]></ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Expected response
<![CDATA[<a>Madrid</a>]]>
Question
Can anyone suggestwhat I am doing wrong and how to correct this behavior?
Thanks in advance.

Resources