Axis 1.4 How to modify soap envelope attributes? - java

This is envelope which i wanna to send to service:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ger="http://www.more.com.br/MC/Geral" xmlns:num="http://www.more.com.br/SN/Numero">
How make this using Axis 1.4
I need modify the namespace!
I'm using JDK 1.5

Since I couldn't find this answer anywhere, here is how I did it using Axis 1.4.
First of all, you need to create a Handler class which will modify the SOAP Envelope. This Handler must extend BasicHandler.
public class AxisClientEnvelopeHandler extends BasicHandler {
#Override
public void invoke(MessageContext msgContext) throws AxisFault {
try {
// get the soap header
SOAPMessageContext smc = (SOAPMessageContext) msgContext;
SOAPMessage message = smc.getMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
// fiddle with the namespaces
envelope.addNamespaceDeclaration("YOUR NAMESPACE");
} catch (SOAPException e) {
e.printStackTrace();
}
}
}
Then you have to add this Handler to your SOAP calls. This is done by setting some properties on your service locator.
// Add Handler to Axis SOAP calls
SimpleProvider clientConfig = new SimpleProvider();
AxisClientEnvelopeHandler envelopeHandler = new AxisClientEnvelopeHandler();
SimpleChain reqHandler = new SimpleChain();
SimpleChain respHandler = new SimpleChain();
reqHandler.addHandler(envelopeHandler);
Handler pivot = new HTTPSender();
Handler transport = new SimpleTargetedChain(reqHandler, pivot, respHandler);
clientConfig.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, transport);
locator.setEngineConfiguration(clientConfig);
locator.setEngine(new AxisClient(clientConfig));
After that, you can call make your calls and the SOAP Envelope will be modfied according to your Handler.

Related

Set Header of A Soap Request in Spring

I have been working on integrating a SOAP API with my Spring API. I have to add headers to my request,but i'm getting this is,
Could not complete request
org.springframework.ws.soap.client.SoapFaultClientException: No WS-Security header found
Please find my code below
public String createSaleOrder(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder = setSaleOrderObject(suborder);
CreateSaleOrderRequest request = new CreateSaleOrderRequest();
request.setSaleOrder(saleorder);
this.getWebServiceTemplate().marshalSendAndReceive(uri, request,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
SoapMessage soapmessage = (SoapMessage)message;
SoapHeader header = soapmessage.getSoapHeader();
StringBuilder soapheader = new StringBuilder();
soapheader.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://wewewqdad.com/services/\">");
soapheader.append("<soapenv:Header>");
soapheader.append("<wsse:Security soapenv:mustUnderstand=\"1\" xmlns:wsse=\"http://docs.oasis-open.asdasda-1.0.xsd\"> ");
soapheader.append("<wsse:UsernameToken wsu:Id=\"UsernameToken-2\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oaasdasd-dasdasd-1.0.xsd\">");
soapheader.append("<wsse:Username>username</wsse:Username>");
soapheader.append("<wsse:Password Type=\"http://docs.aseasdasd-wss-username-token-profile-1.0#PasswordText\">password</wsse:Password>");
soapheader.append("<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">VMEZT//J0bZw7HfZZyXAZQ==</wsse:Nonce>");
soapheader.append("<wsu:Created>2014-09-04 T1015.41.649Z</wsu:Created>");
soapheader.append("</wsse:UsernameToken>");
soapheader.append("</wsse:Security>");
soapheader.append("</soapenv:Header>");
soapheader.append("<soapenv:Body>");
soapheader.append("</soapenv:Body>");
soapheader.append("</soapenv:Envelope>");
StringSource HeaderSource = new StringSource(soapheader.toString());
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(HeaderSource,header.getResult());
}
});
return "Pushed";
}
UPDATE
The header is set. But the request object now contains extra tags which is not required and hence throwing errors. And also the tags like how can we change these to
Raw soaprequest is given below :
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><soapenv:Envelope xmlns:ser="http://abc.efg.com/services/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/asdasdasd-1.0.xsd" soapenv:mustUnderstand="1"> <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-2"><wsse:Username>asdasdasd</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oaasdasda-1.0#PasswordText">adsasdasdasda</wsse:Password><wsse:Nonce EncodingType="http://dasdasda004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">VMESASDASZT//asdasdasd</wsse:Nonce><wsu:Created>2014-09-04 T1015.41.649Z</wsu:Created></wsse:UsernameToken></wsse:Security></soapenv:Header></soapenv:Envelope></SOAP-ENV:Header><SOAP-ENV:Body><ns2:CreateSaleOrderRequest xmlns:ns2="http://asdasd.com/services/"><ns2:SaleOrder><ns2:DisplayOrderCode>156</ns2:DisplayOrderCode></ns2:SaleOrder></ns2:CreateSaleOrderRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>
In your soapHeader you wrote more than the actual header but the whole Soap Envelope. Not sure this is expected.
Anyway, you could use a library to handle WS Security like WSS4J (often included in other frameworks).

Deserialize header in jaxws handler

I am using a jax-ws Handler to intercept a SOAP message so that I can grab certain elements from the Header. Is there a way to deserialize a Header and parse it into either wsdl2java or xmlbeans generated object? I am currently migrating from AXIS to CXF. Under AXIS, I could get the Header xml and parse it using a Factory class. I have not found a good way to do this with the objects generated from CXF wsdl2java. I realize that I can use the getElementsByTagName to retrieve each Node, but it is just easier to work with the actual object.
You can use SAAJ API to manipulate the SOAP message directly. Extend AbstractSoapInterceptor by implementing the handleMessage() method and add it to the chain of InInterceptors.
public class MyCustomInInterceptor extends AbstractSoapInterceptor {
public void handleMessage(SoapMessage soapMessage) throws Fault {
try {
SOAPMessage message = soapMessage.getContent(SOAPMessage.class);
SOAPPart sp = message.getSOAPPart();
SOAPEnvelope se = sp.getEnvelope();
SOAPBody sb = se.getBody();
SOAPHeader sh = se.getHeader();
}
catch(Exception e)
{
log.error(e);
throw new Fault(e);
}
}
}
NOTE: You will also need to attach SAAJInInterceptor to the chain of interceptors or else soapMessage.getContent(SOAPMessage.class) will return null

SOAP: Migrate method from AXIS 1.4 to CXF for adding a new header

Hi! I'm trying to add a header to a SOAP call.
I'm new at working with web services.
Every time I need to use the web service for download a file, I need to add a header like:
<UserIdHeader xmlns="http://www.stats.com/">
<TicketId>defe3a08-4c8a-47c4-9303-98e09c475532</TicketId>
</UserIdHeader>
I'm using CXF. I have one example made with Axis 1.4.
The only thing that is remaining (I hope) is to add this header.
This is what I have to update:
private static void addHeader(String ticketId) {
SOAPHeaderElement header = new SOAPHeaderElement("http://www.stats.com/", "UserIdHeader");
SOAPElement node;
org.apache.axis.client.Stub s = (Stub) service;
s.clearHeaders();
try {
node = header.addChildElement("TicketId");
node.addTextNode(ticketId);
s.setHeader((org.apache.axis.message.SOAPHeaderElement) header);
} catch (SOAPException e) {
e.printStackTrace();
}
}
And this is how I tried to update that:
private static void addHeader(String ticketId) throws JAXBException, ParserConfigurationException {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element eTicketId = doc.createElement("TicketId");
eTicketId.setTextContent(ticketId);
List<Header> headers = new ArrayList<Header>();
Header header = new Header(new QName("http://www.stats.com/", "UserIdHeader"), eTicketId,
new JAXBDataBinding(String.class));
headers.add(header);
BindingProvider.class.cast(service).getRequestContext().put(Header.HEADER_LIST, headers);
}
But I'm getting the following exception:
[PhaseInterceptorChain] Interceptor for {http://www.stats.com/}Service#{http://www.stats.com/}GetFileList has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: Marshalling Error: org.apache.xerces.dom.ElementImpl is not known to this context
at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:261)
at org.apache.cxf.jaxb.io.DataWriterImpl.write(DataWriterImpl.java:168)
at org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.writeSoapEnvelopeStart(SoapOutInterceptor.java:156)
at org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.handleMessage(SoapOutInterceptor.java:81)
at org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.handleMessage(SoapOutInterceptor.java:61)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:531)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:464)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:367)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:320)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:89)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
I tried even add an interceptor, but I didn't know how to pass the ticketId.
Any idea?
If you are creating a DOM element, you shouldn't set a databinding into the Header object. You only need the Databinding if you are using a JAXB (or other) type. CXF handles DOM elements directly.
This is how I have the method working (for some reason, with the previous approach the header wasn't written):
private static void addHeader(String ticketId) {
try {
SOAPFactory sf = SOAPFactory.newInstance();
SOAPElement userIdHeaderElement = sf.createElement(new QName("http://www.stats.com/", "UserIdHeader"));
SOAPElement ticketIdElement = sf.createElement(new QName(null, "TicketId"));
ticketIdElement.addTextNode(ticketId);
userIdHeaderElement.addChildElement(ticketIdElement);
List<Header> headers = new ArrayList<Header>();
Header dummyHeader = new Header(new QName("http://www.stats.com/"), userIdHeaderElement);
headers.add(dummyHeader);
BindingProvider.class.cast(service).getRequestContext().put(Header.HEADER_LIST, headers);
} catch (SOAPException e) {
e.printStackTrace();
}
}

How do I test a Spring-WS webservice that Requires soapHeaders?

I have a spring-ws (2.0.2) service I have implemented that requires some custom elements in the soap header. I am trying to use Spring's MockWebServiceClient to generate a valid request to test the dispatcher, marshallers, etc.
The problem I am getting is that the MockWebSerivce only seems to support the Soap Body (the payload).
How can I access the soap request being generated to get the right headers into it?
If there is a better library for doing this other than Spring's MockWebServiceClient, that would be fine too.
Related Links:
http://forum.springsource.org/showthread.php?101708-MockWebServiceClient-amp-WS-Security
Add SoapHeader to org.springframework.ws.WebServiceMessage
I had the similar problem when I wanted to test spring web service with Security, I ended up using the Spring Interceptors to modify the header before they reach end point, I enabled the interceptors only for testing.
Create an interceptor, I implemented the SmartEndpointInterceptor, You can use the other interceptors if you choose
public class ModifySoapHeaderInterceptor implements
SmartEndpointInterceptor
{
//WSConstants.WSSE_NS;
private static final String DEFAULT_SECURITY_URL = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private static final String SECURITY_TAG = "Security";
private static final String SECURITY_PREFIX = "wsse";
private static final String USER_NAME_TOKEN = "UsernameToken";
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws Exception
{
SaajSoapMessage saajSoapMessage(SaajSoapMessage)messageContext.getRequest());
SOAPHeader soapHeader = saajSoapMessage.getSaajMessage().getSOAPPart()
.getEnvelope().getHeader();
//you can modify header's as you choose
Name headerElement = saajSoapMessage.getSaajMessage().getSOAPPart()
.getEnvelope()
.createName(SECURITY_TAG, SECURITY_PREFIX, DEFAULT_SECURITY_URL);
SOAPHeaderElement soapHeaderElement = soapHeader
.addHeaderElement(headerElement);
SOAPElement usernameToken = soapHeaderElement.addChildElement(
USER_NAME_TOKEN, SECURITY_PREFIX);
SOAPElement userNameElement = usernameToken.addChildElement("Username",
SECURITY_PREFIX);
userNameElement.addTextNode("userid");//you can inject via spring
SOAPElement passwordElement = usernameToken.addChildElement("Password",
SECURITY_PREFIX);
passwordElement.addTextNode("password");
return true;
}
}
Configure this interceptor in spring context
<sws:interceptors>
<bean class="prasanna.ws.security.wss4j.ModifySoapHeaderInterceptor"/>
</sws:interceptors>
This will add the necessary security headers to the message before it reaches the end point, You can still use MockWebServiceClient to test your web service.
As you noted, the MockWebServiceClient sendRequest() method only sets up the SOAP body with the payload given it. It does not touch the SOAP header.
To set up the SOAP header as well you can create a class that implements the RequestCreator interface and sets the SOAP header. Pass an instance of this class to the sendRequest() method.
For example:
class SoapActionCreator implements RequestCreator {
private final Source payload;
public SoapActionCreator(Source payload) {
this.payload = payload;
}
#Override
public WebServiceMessage createRequest(WebServiceMessageFactory webServiceMessageFactory)
throws IOException {
WebServiceMessage webServiceMessage =
new PayloadMessageCreator(payload).createMessage(webServiceMessageFactory);
SoapMessage soapMessage = (SoapMessage) webServiceMessage;
SoapHeader header = soapMessage.getSoapHeader();
// Add an Action element to the SOAP header
StringSource headerSource = new StringSource(
"<wsa:Action xmlns:wsa=\"http://www.w3.org/2005/08/addressing\">https://example.com/foo/bar</wsa:Action>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
return webServiceMessage;
}
}
Then use SoapActionCreator like this:
SoapActionCreator soapActionCreator = new SoapActionCreator(requestPayload);
mockClient.sendRequest(soapActionCreator).
andExpect(payload(responsePayload));
where requestPayload is the SOAP request body and responsePayload is the entire SOAP response (header and body).
I did find the smock library which does what I really wanted: just takes a text file with the whole request in it.
http://code.google.com/p/smock/wiki/SpringWs
It supports the same request and response matchers as the spring provided stuff. It also keeps my tests very self contained. (Rather than a whole new class that would only be used in my test cases.)

JAX-WS Password Type PasswordText

I've got a simple command line Java JAX-WS app to test a SOAP request, but the server is expecting the Password Type to be PasswordText and I'm stumped on how to set this...
The code looks like so:
#WebServiceRef
private static final HelloService helloService = new HelloService(url, new QName(
URL, "HelloService"));
public static void main(final String... args) {
try {
final HelloPort helloPort = helloService.getHelloPort();
final BindingProvider hB = ((BindingProvider) helloPort);
hB.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
END_POINT_ADDRESS);
hB.getRequestContext().put(BindingProvider.USERNAME_PROPERTY,
USERNAME);
hB.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,
PASSWORD);
...
I've tested the request using SOAP-UI so I know it's working. Any help on setting the password type would be appreciated.
Thanks.
That will set the username and password for Basic HTTP authentication. If you've tested it in SoapUI, I'm guessing the 'PasswordText' value you speak of is the 'WSS-Password Type' in the request details pane. That sets WSS security, not HTTP security.
With JAX-WS in Java6 you need to attach a SOAPHandler to inject the WSS-Usertoken into the SOAP Header. There are plenty of bits and bobs about this round the net, but I couldn't find one single link to post, so here's some code instead to get you going...
To add a handler you need something like:
final Binding binding = ((BindingProvider) servicePort).getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null)
handlerList = new ArrayList<Handler>();
handlerList.add(new SecurityHandler());
binding.setHandlerChain(handlerList); // <- important!
Then the SecurityHandler class will do the deed. Handlers are general things and get called for both successful messages and for faults, but perhaps more importantly they get called in both message directions - for the outgoing request and then again for the incoming response. You only want to handle outgoing messages. So you'll need something like:
public final class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
...
#Override
public boolean handleMessage(final SOAPMessageContext msgCtx) {
// Indicator telling us which direction this message is going in
final Boolean outInd = (Boolean) msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Handler must only add security headers to outbound messages
if (outInd.booleanValue()) {
try {
// Get the SOAP Envelope
final SOAPEnvelope envelope = msgCtx.getMessage().getSOAPPart().getEnvelope();
// Header may or may not exist yet
SOAPHeader header = envelope.getHeader();
if (header == null)
header = envelope.addHeader();
// Add WSS Usertoken Element Tree
final SOAPElement security = header.addChildElement("Security", "wsse",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
final SOAPElement userToken = security.addChildElement("UsernameToken", "wsse");
userToken.addChildElement("Username", "wsse").addTextNode("MyWSSUsername");
userToken.addChildElement("Password", "wsse").addTextNode("MyWSSPassword");
} catch (final Exception e) {
LOG.error(e);
return false;
}
}
return true;
}
...
// Other required methods on interface need no guts
}
I've made a few assumptions here, but hopefully it'll get you going!
Kind regards.
If you implement SOAPHandler interface, the method msgCtx.getMessage() will render the entire XML, and if you are working with big files you will have Out of Memory errors. I tested with UsernameToken authentication on JAX-WS client and it works:
String SECURITY_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
String PASSWORD_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
String AUTH_PREFIX = "wss";
MyService service = new MyService();
MyServicePort port = service.getMyServicePort();
try {
SOAPFactory soapFactory = SOAPFactory.newInstance();
SOAPElement security = soapFactory.createElement("Security", AUTH_PREFIX, SECURITY_NS);
SOAPElement uToken = soapFactory.createElement("UsernameToken", AUTH_PREFIX, SECURITY_NS);
SOAPElement username = soapFactory.createElement("Username", AUTH_PREFIX, SECURITY_NS);
username.addTextNode("username");
SOAPElement pass = soapFactory.createElement("Password", AUTH_PREFIX, SECURITY_NS);
pass.addAttribute(new QName("Type"), PASSWORD_TYPE);
pass.addTextNode("password");
uToken.addChildElement(username);
uToken.addChildElement(pass);
security.addChildElement(uToken);
Header header = Headers.create(security);
((WSBindingProvider) port).setOutboundHeaders(header);
// now, call webservice
} catch (SOAPException ex) {
ex.printStackTrace();
}
Edit: You should add the "rt.jar" from jre to classpath.

Categories