I am developing a SOAP webservice application written in Spring soap webservice
The application simply returns the same object it receives from the client as response.
In CountryEndpoint.java
#PayloadRoot(namespace = "http://xml.dexter.com/Country", localPart = "CountryLocalPart")
public Country getCountry(SoapHeader soapHeader, #RequestPayload Country request, #SoapHeader(value = "MessageHeader") SoapHeaderElement messageHeader, MessageContext messageContext) {
System.out.println("THE REQUEST: "+request);
return request;
}
The request SOAP XML
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:Country xmlns="http://example.org" xmlns:ns2="http://xml.dexter.org/Country" xmlns:h="http://pqr.com/pqr.xsd xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<ns2:countryName>Spain</ns2:countryName>
<ns2:population>1213123</ns2:population>
</ns2:Country>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The response SOAP XML
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:Country xmlns:ns2="http://xml.dexter.com/Country">
<ns2:countryName>Spain</ns2:countryName>
<ns2:population>1213123</ns2:population>
</ns2:Country>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The Country XML Root element seems to have the xmlns attributes missing.
This fails the JAXB unmarshalling step in the client.
The error is
ERROR: org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].
[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in
context with path [] threw exception [Request processing failed; nested
exception is org.springframework.oxm.UnmarshallingFailureException: JAXB
unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/",
local:"Header"). Expected elements are <{http://www.....}Country>,<{http:
//www.....}Category>,<{http://www.....}SomeOther>] with root cause
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Header"). Expected
elements are ...
I tried adding #PayloadRoots and also #Namespaces with array of #Namespace but nothing worked.
Is there a way I could have the response SOAP xml same as request SOAP xml?
Thanks!
The namespace URIs in the Country element on the request with the prefixes h, s, and default are superfluous as they don't correspond to any XML elements or attributes. There would be no reason to send them on the response and they would impact JAXB's ability to unmarshal the result assuming everything is properly mapped.
Based on your exception. Your JAXB client is trying to unmarshal the Header element and you need to unmarshal the Country element.
Related
Hi I am trying to implement a pass through SOAP proxy via #RestController in spring. For this purpose I have mapped a rest controller in following way:
#RestController
class MyProxy {
#PostMapping(value = "/**")
public ResponseEntity<String> proxyPost(#RequestBody(required = false) String body, HttpServletRequest request) {}
}
The regular SOAP requests are going OK. The problem comes when a MTOM type of SOAP request is send via the proxy. Then spring failes with unrecognized content type. Here is the exception:
Caused by: org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: Unsupported Content-Type [multipart/related; type="application/xop+xml"; boundary="uuid:dacf4733-80b4-41bc-b2e1-db69b6beadf6"; start="<root.message#cxf.apache.org>"; start-info="text/xml"], expected [multipart/form-data]
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:124)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:115)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:88)
at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:122)
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1205)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
... 60 common frames omitted
Caused by: javax.servlet.ServletException: Unsupported Content-Type [multipart/related; type="application/xop+xml"; boundary="uuid:dacf4733-80b4-41bc-b2e1-db69b6beadf6"; start="<root.message#cxf.apache.org>"; start-info="text/xml"], expected [multipart/form-data]
at org.eclipse.jetty.server.Request.getParts(Request.java:2407)
at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:317)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95)
... 66 common frames omitted
When receiving a multipart/* request Spring delegates this to the configured Multipart handler. This is enabled by default and for this case should be disabled.
spring.servlet.multipart.enabled=false
Adding the above to your properties should disable it and prevent the parsing, so you can handle it in your controller.
I am creating a soap web service using jaxb2 plugin. Hence I have generated jaxb classes from wsdl file. I have entered the sample of generated codes here.
//ObjectFactory.java
#XmlElementDecl(namespace = "http://xxxxx/", name = "InquiryResponse")
public JAXBElement<InquiryResponse> createInquiryResponse(InquiryResponse value) {
return new JAXBElement<InquiryResponse>(_InquiryResponse_QNAME, InquiryResponse.class, null, value);
}
//package-info.java
#javax.xml.bind.annotation.XmlSchema(namespace = "http://XXXX/",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED,
xmlns = {
#XmlNs(namespaceURI = "http://XXXX/", prefix = "ws")
})
package X.X.X;
import javax.xml.bind.annotation.XmlNs;
And the response is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<InquiryResponse xmlns:ws="http://XXXX/">
<InquiryResponse>
<refId>0004440</refId>
<status>UP</status>
</InquiryResponse>
</InquiryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But what I wanted is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ws:InquiryResponse xmlns:ws="http://XXXX/">
<InquiryResponse>
<refId>0004440</refId>
<status>UP</status>
</InquiryResponse>
</InquiryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I am using a spring boot. Please help me. Thanks.
I believe that happens because you specified elementFormDefault as UNQUALIFIED in your package-info.java. Try switching this to QUALIFIED to see if it helps.
This question comes from
relevant details. When using websocket and stomp in springboot project, No 'javax.websocket.server.ServerContainer' ServletContext attribute error appears.
// org.springframework.web.socket.server.standard.AbstractStandardUpgradeStrategy.java
protected ServerContainer getContainer(HttpServletRequest request) {
ServletContext servletContext = request.getServletContext();
String attrName = "javax.websocket.server.ServerContainer";
ServerContainer container = (ServerContainer) servletContext.getAttribute(attrName);
// container is null
Assert.notNull(container, "No 'javax.websocket.server.ServerContainer' ServletContext attribute. " +
"Are you running in a Servlet container that supports JSR-356?");
return container;
}
I tried the following solution, but it doesn't help.
// layout of project
projectname
--src/main/webapp/WEB-INF/web.xml // created manually
--module1/src/main/java/Application.java // Class with annotiation #SpringBootApplication
--module2/src/main/java/...
web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<absolute-ordering>
<name>spring_web</name>
</absolute-ordering>
</web-app>
I understand from this discussion that a fix for this issue was included in Tomcat 9.0.31, 8.5.51, and 7.0.100 releases.
For each Outbound Message, Salesforce provides a full self-contained WSDL.
Implementing a Spring service for a single one is easy, using jaxws-maven-plugin to generate the classes and #Endpoint, #PayloadRoot, etc to bind the endpoint.
However, multiple Outbound Messages all share the same QNs (for example http://soap.sforce.com/2005/09/outbound:notifications or urn:sobject.enterprise.soap.sforce.com:sObject) for different structures and type hierarchies.
I know how to map the same XML names to different handlers based on URL path.
I know how to use a separate package for the generated classes with a bindings file:
<?xml version="1.0" encoding="UTF-8"?>
<jaxws:bindings
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
wsdlLocation="../wsdl/SFDC_Contact_Outbound_Msg.wsdl"
version="2.0">
<jaxb:bindings node="//xs:schema[#targetNamespace='http://soap.sforce.com/2005/09/outbound']">
<jaxb:schemaBindings>
<jaxb:package name="com.sforce.soap.outbound.contact"/>
</jaxb:schemaBindings>
</jaxb:bindings>
<jaxb:bindings node="//xs:schema[#targetNamespace='urn:sobject.enterprise.soap.sforce.com']">
<jaxb:schemaBindings>
<jaxb:package name="com.sforce.soap.enterprise.sobject.contact"/>
</jaxb:schemaBindings>
</jaxb:bindings>
</jaxws:bindings>
However, when trying to initialise the Jaxb2Marshaller from the generated code, it still cannot handle the XML conflicts:
[WARN] [main] 09:40:45.687 AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'marshaller' defined in class path resource [WebServiceConfig.class]:
Invocation of init method failed; nested exception is org.springframework.oxm.UncategorizedMappingException:
Unknown JAXB exception; nested exception is com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 6 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{urn:sobject.enterprise.soap.sforce.com}sObject". Use #XmlType.name and #XmlType.namespace to assign different names to them.
...
I do not want to add any more manual steps when the SDFC-generated WSDL changes, other than dropping in the new files.
Is there a way to change the namespaces in package-info.java without changing the source WSDL?
Is there a way to easily (i.e. not with a separate #Bean method for each) create a separate marshaller for each package that could all then be added to the DefaultMethodEndpointAdapter?
Is there another way to implement all these Outbound Message receivers?
Is there a way to change the namespaces?
If you did they wouldn't match the namespaces in the actuall message, so it wouldn't be unmarshallable.
Is there a way to easily create a separate marshaller for each package?
Well, here's a way to do it.
#PostConstruct
public void marshallers() throws IOException {
List<String> types = Arrays.stream(applicationContext.getResources("classpath:com/sforce/soap/outbound/*"))
.map(Resource::getFilename)
.collect(Collectors.toList());
for (String type : types) {
String beanName = "marshallingPayloadMethodProcessor_" + type;
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(
"com.sforce.soap.outbound." + type,
"com.sforce.soap.enterprise.sobject." + type
);
try {
marshaller.afterPropertiesSet();
} catch (Exception ex) {
throw new BeanInitializationException("Could not initialize bean " + beanName, ex);
}
MarshallingPayloadMethodProcessor processor = new MarshallingPayloadMethodProcessor(marshaller);
beanFactory.registerSingleton(beanName, processor);
}
Couple of caveats on this:
If deploying via a jar, then classpath directories don't exist, so will need an alternate way to get the package names.
registerSingleton appears to break some application contexts - causing unrelated beans to be no longer be found. I have no idea why this is.
I cannot set FaultCode.RECEIVER for my fault message;
The code in response is always SERVER
Here is my response:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Something wrong!</faultstring>
<faultactor>http://fault.actor.com/actor/</faultactor>
<detail>
<ServiceException>
<code>ABCD</code>
<message>Invalid input</message>
</ServiceException>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The fault code is SOAP-ENV:Server instead of SOAP-ENV:Receiver
Heres my exception class
#SoapFault(faultCode = FaultCode.RECEIVER, faultStringOrReason = "Something wrong!")
public class CustomFault extends RuntimeException
When i'm putting faultCode = FaultCode.CLIENT, it changes in response to Client, but with RECEIVER doesn't work.
For adding fault details in response i'm using SoapFaultAnnotationExceptionResolver
Any ideas?