SOAP Custom Response Header - JAX-WS - java

In my JAX-WS web service, I need to customize my SOAP Response Envelope.
At the moment, it is like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<methodResponse xmlns="http://interfaces.webservice.ucmdb.com">
<methodReturn>202</methodReturn>
</methodResponse >
</soapenv:Body>
</soapenv:Envelope>
And i need it to look like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<code>123</code>
</soapenv:Header>
<soapenv:Body>
<getCIResponse xmlns="http://interfaces.webservice.ucmdb.com">
<getCIReturn>202</getCIReturn>
</getCIResponse>
</soapenv:Body>
</soapenv:Envelope>
So, how does one write the <soapenv:Header> part in a JAX-WS soap envelope?
Here is my WebMethod implementation (quite simple at the moment):
#WebMethod
public int operation(#WebParam(name="username", header=false)String id) {
return 202;
}

1.) In order to add a header to your SOAP message in JAX-WS you can implement the SOAPHandler interface. This will give you access to the SOAPMessage and you can use the SAAJ api to add/update the SOAP Header. Keep in mind this handler is bi-directional.
public class Handler1 implements SOAPHandler<SOAPMessageContext>{
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean isOutbound = (boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(isOutbound){
SOAPMessage msg = context.getMessage();
try {
//Grab the header
SOAPHeader header = msg.getSOAPHeader();
//Add whatever QName you need
header.addHeaderElement(new QName("code"));
//Save changes.
msg.saveChanges();
} catch (SOAPException e) {
e.printStackTrace();
}
}
//True continue, false halt.
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
//Can also add a QName here.
return null;
}
}
2.) In order to register the Handler you need to add the handler via an xml handler configuration file that should be kept in the classpath.
<?xml version="1.0" encoding="UTF-8"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee">
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-class>com.calvinmmiller.service.Handler1</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>
3.) This file can be added to the SEI(Service Endpoint Interface) with the annotation below:
import javax.jws.HandlerChain;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
#WebService
#HandlerChain( file = "handlerFile.xml")
public class Service {
public int operation(String id){
return 202;
}
public static void main(String[] args){
Endpoint.publish("http://localhost:8080/soap", new Service());
}
}

Related

Java WS get SOAP request CDATA body

I need to get the following SOAP request into my controller.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<req:ResultMsg
xmlns:req="http://cps.huawei.com/cpsinterface/result"><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<Result>
<ResultType>0</ResultType>
<ResultCode>0</ResultCode>
<ResultDesc>The service request is processed successfully.</ResultDesc>
<OriginatorConversationID>S_X2013012921001</OriginatorConversationID>
<ConversationID>AG_20130129T102103</ConversationID>
<TranactionID>XD2013012923789234</TranactionID>
<ResultParameters></ResultParameters>
</Result>]]></req:ResultMsg>
</soapenv:Body>
</soapenv:Envelope>
I tried to set a POJO and a string for the CDATA part but not working.
Current implementation as below,
Interface:
#WebService(name = "VCashCallbackService",
targetNamespace = "http://cps.huawei.com/cpsinterface/result")
public interface VCashCallbackService {
#WebMethod(operationName = "ResultMsg")
#WebResult(name = "GetDataResponse")
GetDataResponse getData(#WebParam(name = "ResultMsg",
targetNamespace = "http://cps.huawei.com/cpsinterface/resul",
mode = WebParam.Mode.IN) Holder<String> searchResultDataXML);
}
Impl
#WebService(serviceName = "VCashCallbackService",
portName = "VCashCallbackServicePort",
endpointInterface = "com.argon.eightd.web.restful.mock.VCashCallbackService",
targetNamespace = "http://cps.huawei.com/cpsinterface/result")
#BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
#Service
public class VCashCallbackServiceImpl implements VCashCallbackService {
private static final Logger LOGGER = LoggerFactory.getLogger(VCashCallbackServiceImpl.class);
#Override
public GetDataResponse getData(Holder<String> searchResultDataXML) {
LOGGER.info("SOAP result: {}", searchResultDataXML.searchResultDataXML.value);
return new GetDataResponse();
}
}
Log
SOAP result: null
But this approach is getting the request as above in the WebMethod.
Thanks in advance.

Apache CXF removes namespace qualifiers in a body section when I add SOAP:HEADER dynamically. How do I return the qualifiers?

I developed an app which sends SOAP requests to different services through Apache CXF 2.7.10.
For some requests I have to pass a 'SOAP:HEADR' with additional data for processing, for others I haven't to do. By this reason I add headers dynamically in my code.
A pure request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getList xmlns:ns2="http://service.com/front/list">
<code>1</code>
<requestId>636978</requestId>
<version>0.1</version>
<buildId>401163</buildId>
<clientId>500</clientId>
<active>true</active>
</ns2:getList>
</soap:Body>
</soap:Envelope>
A request with header:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"/>
<soap:Body>
<getList xmlns="http://service.com/front/list" xmlns:ns2="http://service.com/front/list">
<code>1</code>
<requestId>636992</requestId>
<version>0.1</version>
<buildId>401163</buildId>
<clientId>500</clientId>
<active>true</active>
</getList>
</soap:Body>
</soap:Envelope>
The code for clearing headers:
bindingProvider.binding.handlerChain = null
The code for adding headers:
bindingProvider.binding.handlerChain = listOf(CacheSoapHeaderHandler())
CacheSoapHeaderHandler:
public class CacheSoapHeaderHandler implements SOAPHandler<SOAPMessageContext> {
private static final ObjectFactory OBJECT_FACTORY = new ObjectFactory();
private static final JAXBContext context = createContext();
public CacheSoapHeaderHandler() {
}
#Override
public boolean handleMessage(SOAPMessageContext smc) {
return true;
}
#Override
public Set<QName> getHeaders() {
return null;
}
#Override
public void close(MessageContext arg0) {
}
#Override
public boolean handleFault(SOAPMessageContext arg0) {
return true;
}
private static JAXBContext createContext() {
try {
return JAXBContext.newInstance(CacheInfo.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
How can I return a 'ns2' qualifier in a body section when I add headers?
A solution: you can change a message header and a body in a SOAPHandler including namespaces and them prefixes (qualifiers):
#Override
public boolean handleMessage(SOAPMessageContext smc) {
try {
smc.getMessage().getSOAPBody().getFirstChild().setPrefix("ns2");
smc.getMessage().getSOAPBody().getFirstChild().getAttributes().removeNamedItem("xmlns");
} catch (Exception e) {
throw new IllegalArgumentException("Can't generate a WS request", e);
}
return true;
}

How to intercept SOAP message and transforming it before processing?

I have task where I need to transform custom SOAP message. At the beginning I have to get this custom SOAP message, then I need to transform this message using XSLT, then process this message in my WebService. The reverse process is repeated.
I have XSLT file and method for transforming SOAP message, but I don't know where I need to call this method for transforming. How do I intercept SOAP message and where I have to do it? Because I have only class with one method (example below) and i don't understand how I can transform this message before processing in webservice.
#WebService
public class Calculator {
public String showCard(final CreditCard creditCard) {
return creditCard.toString();
}
}
Here is the technical mapping what I need to do.
How do I intercept SOAP message and where I have to do it?
You need a SoapHandler to capture the soap request before the execution of the bussiness logic of the endpoint, and other SoapHandler to transform the outbound response.
Define a SoapHandler class (example extracted from mkyong)
public class CalculatorSoapHandler implements SOAPHandler<SOAPMessageContext>{
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
//for response message only, true for outbound messages, false for inbound
if(!isRequest){
try{
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
//process....
}catch(SOAPException e){
System.err.println(e);
}catch(IOException e){
System.err.println(e);
}
}
//continue other handler chain
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
}
}
Create a soap handler XML file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-class>com.CalculatorSoapHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>
Attach SOAP Handler to Web Service using #HandlerChain
#WebService
#HandlerChain(file="handler-chain.xml")
public class Calculator {
If you use a jax-ws framework like CXF or spring-WS, check the documentation to see specific configuration

Add Security Header info to Java Code generated from WSDL

I used the Netbeans Web Service wizard to generate Java code given a WSDL. If I drag the web service method into a class, then it creates some Java code to call that web service (ex: SubmitApplication). I can see how to populate objects to send info to that web service, but the service also requires a security header with username/password.
There is a generated class called SecurityHeader that contains the username/password attributes. I can create this object with a valid username/password, but I cannot see how to pass that object or add it to the SubmitApplication call. How can the SecurityHeader be added to the SubmitApplication call?
Here is an example of what the SOAP request should look like:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<SecurityHeader xmlns="http://schemas.turss.com/BDS/1.0/">
<CreateTime>6/8/2012 8:32:59 PM</CreateTime>
<Owner>Sample_Owner</Owner>
<HashKey>Sample_Hash_Key</HashKey>
</SecurityHeader>
</soap:Header>
<soap:Body>
<SubmitApplication xmlns="http://schemas.turss.com/BDS/1.0/">
<newSearch>
<CurrentApplicant xmlns="http://schemas.turss.com/BDS/1.0/proxy">
<FirstName>Bob</FirstName>
<MiddleName />
<LastName>Smith</LastName>
<Suffix />
<BirthDate>1970-10-20T00:00:00</BirthDate>
<SSN />
<Address />
<City />
<State />
<PostalCode />
</CurrentApplicant>
<PermissiblePurpose xmlns="http://schemas.turss.com/BDS/1.0/proxy">TenantScreening</PermissiblePurpose>
</newSearch>
</SubmitApplication>
</soap:Body>
</soap:Envelope>
I found a sample code that add a token string to a soap security header. Here is the header form of the code below :
<TicketHeader>
<Ticket>OD01096347CCA</Ticket>
</TicketHeader>
The method to add the header to the message :
// Security token
String token;
// MyService and MySoapService are stubs generated from WSDL
MyService service = new MyService();
MyServiceSoap ep = service.getMyServiceSoap();
Binding binding = ((BindingProvider) ep).getBinding();
List handlers = binding.getHandlerChain();
handlers.add(new MySOAPHandler(token));
binding.setHandlerChain(handlers);
code of MySoapHandler :
public class MySOAPHandler implements SOAPHandler {
private String token;
public DHSOAPHandler(String token) {
this.token = token;
}
public boolean handleMessage(SOAPMessageContext messageContext) {
SOAPMessage
msg = messageContext.getMessage();
if ((Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)){
try {
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement el = header.addHeaderElement(envelope.createName("TicketHeader",
"", "http://ws.service.com/"));
el = el.addChildElement(envelope.createName("Ticket", "", "http://ws.service.com/"));
el.setValue(token);
msg.saveChanges();
}
catch (SOAPException e) {
return false;
}
}
return true;
}
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}
public void close(MessageContext messageContext){
}
// I'm not quite sure about what should this function do, but I guess something like this...
public Set getHeaders(){
Set headers = new HashSet();
headers.add(new QName("https://ws.service.com/", "TicketHeader"));
return headers;
}
}

Can I create a generic web service/dispatch method that responds to ALL requests with JAX-WS?

I'm trying to create a generic web service that will always respond with "OK", regardless of the request's header or body contents. I can do this in Axis2 with a RawXMLInOutMessageReceiver, but I'd prefer to use JAX-WS (which I am completely new to) if at all possible. So far I've got a simple interface:
#WebService
public interface DummyService {
#WebMethod String processMessage(Object obj);
}
and a simple implementaion:
#WebService(endpointInterface = "com.dummyservice.DummyService")
public class DummyServiceImpl implements DummyService {
#Override
public String processMessage(Object obj) {
return "OK";
}
}
I can successfully publish the service with javax.xml.ws.Endpoint#publish(...), but when I hit it with a simple SOAP request, e.g.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<derp/>
</soapenv:Body>
</soapenv:Envelope>
I'm greeted with a SOAPFault stating Cannot find dispatch method for {}derp.
Is it even possible to create a generic/dumb web service that will ACK everything with JAX-WS? If so, could someone point me in the right direction?
EDIT
Thanks to the tip from McDowell, I was able to do this with a SOAPHandler:
public class DummySOAPHandler implements SOAPHandler {
#Override
public boolean handleMessage(MessageContext context) {
return process((SOAPMessageContext) context);
}
#Override
public boolean handleFault(MessageContext context) {
return process((SOAPMessageContext) context);
}
#Override
public void close(MessageContext context) { }
#Override
public Set<QName> getHeaders() {
return null;
}
private boolean process(SOAPMessageContext ctx) {
try {
SOAPMessage message = ctx.getMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPBody body = message.getSOAPBody();
if ((Boolean) ctx.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {
Iterator<SOAPElement> bodyChildren = body.getChildElements();
while (bodyChildren.hasNext()) {
SOAPElement child = bodyChildren.next();
child.detachNode();
}
body.addBodyElement(envelope.createName("OK"));
message.saveChanges();
}
} catch (SOAPException e) {
e.printStackTrace();
}
return true;
}
}
I expect your service is expecting something of the form:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dum="http://yournamespace/">
<soapenv:Header/>
<soapenv:Body>
<dum:processMessage>
<!-- xsd:anyType -->
</dum:processMessage>
</soapenv:Body>
</soapenv:Envelope>
Add ?WSDL to your endpoint and inspect the operation input XML type and the namespaces.
You might be able to do something with a logical handler (javadoc) to transform the incoming request to this form - I haven't tried.

Categories