Reverse engineering: How to generate SOAP Request XML in the backend? - java

I have the following classes:
WS Interface:
package com.mypackage;
import javax.ejb.Remote;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
#Remote
#SOAPBinding(style = Style.DOCUMENT)
#WebService(name = "MathService", targetNamespace = "http://mypackage.com/")
public interface MathServiceWS {
#WebResult(name = "result", targetNamespace = "http://mypackage.com/")
#RequestWrapper(localName = "addRequest", className = "AddRequest", targetNamespace = "http://mypackage.com/")
#WebMethod(action = "http://mypackage.com/add", operationName = "add")
#ResponseWrapper(localName = "addResponse", className = "AddResponse", targetNamespace = "http://mypackage.com/")
Long add(#WebParam(name = "add", targetNamespace = "http://mypackage.com/") AddBean add);
}
WS Implementation:
package com.mypackage;
import javax.ejb.Stateless;
import javax.jws.WebService;
#Stateless(mappedName = "MathService")
#WebService(serviceName = "MathService", endpointInterface = "com.mypackage.MathServiceWS", portName = "MathServicePort", targetNamespace = "http://mypackage.com/")
public class MathService implements MathServiceWS {
#Override
public Long add(AddBean add) {
Long first = new Long(add.getFirst().intValue());
Long second = new Long(add.getSecond().intValue());
return Long.valueOf(Math.addExact(first.longValue(), second.longValue()));
}
}
The bean:
package com.mypackage;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "Add",
namespace = "http://mypackage.com/",
propOrder = {
"first",
"second"
}
)
public class AddBean implements Serializable {
private static final long serialVersionUID = -7727938355039425419L;
#XmlElement(required = true)
private Integer first;
#XmlElement(required = true)
private Integer second;
public AddBean() {
}
public Integer getFirst() {
return first;
}
public void setFirst(Integer first) {
this.first = first;
}
public Integer getSecond() {
return second;
}
public void setSecond(Integer second) {
this.second = second;
}
}
After deploying this WS, when I'm adding the WSDL in SoapUI, the add method request is as follows after giving the user input:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:myp="http://mypackage.com/">
<soapenv:Header/>
<soapenv:Body>
<myp:addRequest>
<myp:add>
<first>1</first>
<second>2</second>
</myp:add>
</myp:addRequest>
</soapenv:Body>
</soapenv:Envelope>
Now I want to have the above SOAP request XML in my com.mypackage.MathService.add(AddBean) method with the given user input.
Using JAXB on com.mypackage.AddBean only generates partial request
The WebService Handlers is not useful to fulfill my requirement
Any pointer would be very helpful.

You may create an custom SOAPHandler object and can read the request payload and set it to SOAPMessageContext via custom property. Make sure you set the scope as application.
In your service class, inject the javax.xml.ws.WebServiceContext using #javax.annotation.Resource and access payload set via your custom property.
For example:
1. Create Handler and register it.
public class PopulateSOAPMessagePayloadHandler implements SOAPHandler<SOAPMessageContext> {
public static final String SOAP_MESSAGE_PAYLOAD = "__soap_message_payload";
#Override
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (!outboundProperty.booleanValue()) {
// for incoming:
ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
try {
smc.getMessage().writeTo(bout);
String payload = bout.toString(StandardCharsets.UTF_8.name());
smc.put(SOAP_MESSAGE_PAYLOAD, payload); //Set payload
smc.setScope(SOAP_MESSAGE_PAYLOAD, MessageContext.Scope.APPLICATION); //make it application scope
} catch (SOAPException | IOException e) {
e.printStackTrace();
// handle exception if needed
throw new WebServiceException(e);
}
}
return true;
}
// Other method (no-op) omitted
}
2. Get the payload
public class MathService implements MathServiceWS {
#Resource
private WebServiceContext context;
#Override
public Long add(AddBean add) {
String payload = (String) context.getMessageContext().get(SOAP_MESSAGE_PAYLOAD);
Long first = new Long(add.getFirst().intValue());
Long second = new Long(add.getSecond().intValue());
return Long.valueOf(Math.addExact(first.longValue(), second.longValue()));
}
}
Hope it helps.

You can get full control of the document easily. First lets setup the bean:
#XmlRootElement(name="addRequest")
#XmlAccessorType(XmlAccessType.FIELD) //Probably you don't need this line. it is by default field accessible.
public class AddBean implements Serializable {
private static final long serialVersionUID = -7727938355039425419L;
#XmlElement(name="first",required = true) //you don't need name attribute as field is already exactly the same as soap element
private Integer first;
#XmlElement(name="second",required = true) //you don't need name attribute as field is already exactly the same as soap element
private Integer second;
public AddBean() { }
//Getters and Setters
}
Now, I think this is the part you are looking for. To add custom namespace declarations and set prefix etc. If you are using org.springframework.ws.client.core.support.WebServiceGatewaySupport.getWebServiceTemplate to make SOAP request, then you can do the following:
public class WSCastorClient extends WebServiceGatewaySupport {
public CustomResponseObject callWebService(Addbean add) {
WebServiceTemplate wst = getWebServiceTemplate();
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.afterPropertiesSet();
wst.setMarshaller(marshaller);
wst.setUnmarshaller(marshaller);
wst.afterPropertiesSet();
CustomResponseObject response = (CustomResponseObject)
wst.marshallSendAndReceive(add, new
WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
SOAPMesage soapMEssage = saajSoapMessage.getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPHeader head = soapMessage.getSOAPHeader();
SOAPBody soapBody = soapMessage.getSOAPBody();
//Now you have full control of the soap header, body, envelope. You can add any namespace declaration, prefix, add header element, etc. You can add remove whatever you want.
soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix()); //clear whatever namespace is there
soapEnvelope.addNamespaceDeclaration("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
soapEnvelope.addNamespaceDeclaration("myp", "http://mypackage.com/");
soapEnvelope.setPrefix("soapenv");
soapHeader.setPrefix("soapenv");
soapBody.setPrefix("soapenv");
Document doc = saajSoapMessage.getDocument();
StringWriter sw = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(sw));
}
});
return response;
}
//close off other brackets if I forgot any.

Related

WSDL Client Calling Failed

First time working on SOAP services. I need to call this web service. http://demomt.weblite.com.my/TS_Services/SubmissionsService.asmx?WSDL
Here are the 2 out of 11 of the stubs that i have generated using wsimport
I tried for the whole day, but I keep getting this error message. Why? I suspect because i did not pass in the authentication header correctly. But at the same time i'm wondering how come the generated stub does not comes with Authentication header as a method param? is that normal? Need help. I'm so confusing.
Exception in thread "main"
com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client
received SOAP Fault from server: Server was unable to process request.
---> Object reference not set to an instance of an object. Please see the server log to find more detail regarding exact cause of the
failure.
WebLITETSServicesSoap.java
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
#WebService(name = "WebLITE_TSServicesSoap", targetNamespace = "TransactionalSubmissionsSvcs")
#XmlSeeAlso({
ObjectFactory.class
})
public interface WebLITETSServicesSoap {
#WebMethod(operationName = "GetTemplateList", action = "TransactionalSubmissionsSvcs/GetTemplateList")
#WebResult(name = "GetTemplateListResult", targetNamespace = "TransactionalSubmissionsSvcs")
#RequestWrapper(localName = "GetTemplateList", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.GetTemplateList")
#ResponseWrapper(localName = "GetTemplateListResponse", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.GetTemplateListResponse")
public String getTemplateList();
#WebMethod(operationName = "SendWithXML", action = "TransactionalSubmissionsSvcs/SendWithXML")
#WebResult(name = "SendWithXMLResult", targetNamespace = "TransactionalSubmissionsSvcs")
#RequestWrapper(localName = "SendWithXML", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.SendWithXML")
#ResponseWrapper(localName = "SendWithXMLResponse", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.SendWithXMLResponse")
public String sendWithXML(
#WebParam(name = "xmldoc", targetNamespace = "TransactionalSubmissionsSvcs")
SendWithXML.Xmldoc xmldoc);
#WebMethod(operationName = "SendWithJSON", action = "TransactionalSubmissionsSvcs/SendWithJSON")
#WebResult(name = "SendWithJSONResult", targetNamespace = "TransactionalSubmissionsSvcs")
#RequestWrapper(localName = "SendWithJSON", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.SendWithJSON")
#ResponseWrapper(localName = "SendWithJSONResponse", targetNamespace = "TransactionalSubmissionsSvcs", className = "transactionalsubmissionssvcs.SendWithJSONResponse")
public String sendWithJSON(
#WebParam(name = "emaillist", targetNamespace = "TransactionalSubmissionsSvcs")
String emaillist,
#WebParam(name = "params", targetNamespace = "TransactionalSubmissionsSvcs")
String params);
}
WebLITETSServices.java
import javax.xml.namespace.QName;
import javax.xml.ws.*;
import java.net.MalformedURLException;
import java.net.URL;
#WebServiceClient(name = "WebLITE_TSServices", targetNamespace = "TransactionalSubmissionsSvcs", wsdlLocation = "http://demomt.weblite.com.my/TS_Services/SubmissionsService.asmx?WSDL")
public class WebLITETSServices
extends Service
{
private final static URL WEBLITETSSERVICES_WSDL_LOCATION;
private final static WebServiceException WEBLITETSSERVICES_EXCEPTION;
private final static QName WEBLITETSSERVICES_QNAME = new QName("TransactionalSubmissionsSvcs", "WebLITE_TSServices");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("http://demomt.weblite.com.my/TS_Services/SubmissionsService.asmx?WSDL");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
WEBLITETSSERVICES_WSDL_LOCATION = url;
WEBLITETSSERVICES_EXCEPTION = e;
}
public WebLITETSServices() {
super(__getWsdlLocation(), WEBLITETSSERVICES_QNAME);
}
public WebLITETSServices(WebServiceFeature... features) {
super(__getWsdlLocation(), WEBLITETSSERVICES_QNAME, features);
}
public WebLITETSServices(URL wsdlLocation) {
super(wsdlLocation, WEBLITETSSERVICES_QNAME);
}
public WebLITETSServices(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, WEBLITETSSERVICES_QNAME, features);
}
public WebLITETSServices(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public WebLITETSServices(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
#WebEndpoint(name = "WebLITE_TSServicesSoap")
public WebLITETSServicesSoap getWebLITETSServicesSoap() {
return super.getPort(new QName("TransactionalSubmissionsSvcs", "WebLITE_TSServicesSoap"), WebLITETSServicesSoap.class);
}
#WebEndpoint(name = "WebLITE_TSServicesSoap")
public WebLITETSServicesSoap getWebLITETSServicesSoap(WebServiceFeature... features) {
return super.getPort(new QName("TransactionalSubmissionsSvcs", "WebLITE_TSServicesSoap"), WebLITETSServicesSoap.class, features);
}
private static URL __getWsdlLocation() {
if (WEBLITETSSERVICES_EXCEPTION!= null) {
throw WEBLITETSSERVICES_EXCEPTION;
}
return WEBLITETSSERVICES_WSDL_LOCATION;
}
}
I tried running with TestApplication
public class TestApplication {
public static void main(String[] args) throws MalformedURLException {
MailAdapterApplication adapterApplication = new MailAdapterApplication();
adapterApplication.run();
}
public void run() throws MalformedURLException {
WebLITETSServices services = new WebLITETSServices();
WebLITETSServicesSoap servicesSoap = services.getPort(WebLITETSServicesSoap.class);
Map<String, Object> req_ctx =((BindingProvider)servicesSoap).getRequestContext();
Map<String, List<String>> headers = new HashMap<>();
headers.put("Username",Collections.singletonList("sansun#weblite.com.my"));
headers.put("Password", Collections.singletonList("P#ssW0rd32!"));
headers.put("APIKey", Collections.singletonList("QdrLKxog"));
req_ctx.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
System.out.println(servicesSoap.getTemplateList());
}
}
Client received SOAP Fault from server: Server was unable to process request.
Object reference not set to an instance of an object.
I keep getting this error message. Why?
Indeed you did call the service, but the server returned an error code with error message, where the returned payload has no mapping to any expected return type or soap fault exception. There is no more information in the in the text you've provided.
It would be really usefull to check the response status code and payload which was really returned. How to log the respone payload depends on the webservice library you use (cxf, axis2, ??)
I suspect because i did not pass in the authentication header correctly.
You should confirm that suspission, from the information provided it is not possible to tell. Though if you did not provide any authentication information, it is very possible
But at the same time i'm wondering how come the generated stub does not comes with Authentication header as a method param? is that normal? Need help. I'm so confusing.
I assume under the "Authentication header" you mean AuthHeader header. You may need to enable header generation when invoking the wsimport, as well depends in th framework used. When using wsimport from the default JDK, try to use -XadditionalHeaders parameter

Access to web method parameters in JAX-WS SOAP handler?

I've developed a SOAP web service with Java7 and JAX-WS. This is an excerpt of the interface:
#WebService(name = "MyWebService",
targetNamespace = "http://www.something.com")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface MyWebServiceInterface
{
#WebMethod(operationName = "handleMsg",
action = "handleMsg")
#Oneway
void handleMsg(#WebParam(name = "MessageHeader",
targetNamespace = "http://www.something.com",
header = true,
partName = "header")
MessageHeader header,
#WebParam(name = "MessageBody",
targetNamespace = "http://www.soemthing.com",
partName = "body")
MessageType body);
}
I've implemented a custom SOAP handler for this web service (it work's fine) to do some additional stuff. In the method handleFault(..) I need to access the original MessageHeader of the web method (see interface above). How can this be done?
public class MyHandler implements SOAPHandler<SOAPMessageContext>
{
// ...
#Override
public boolean handleFault(final SOAPMessageContext context)
{
final Boolean outbound =
( Boolean ) context.get( MessageContext.MESSAGE_OUTBOUND_PROPERTY );
// handle only incoming message which do have a message set
if ( outbound != null && !outbound.booleanValue() && context.getMessage() != null )
{
MessageHeader header =
getOriginalHeaderOfFautlyMessage(); // <-- how can this be done?
}
}
}
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
Then you will have to extract your header node and unmarshall it.

How can I subscribe to incoming SSE events using Spring

I wrote a Spring RestController that returns a SseEmitter (for server-sent-event), and adds HATEOAS links to each event. Here is a simplified but working example of this controller:
package hello;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import hello.Greeting.Status;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
#RestController
public class GreetingController {
private static final Logger log = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
class GreetingRequestHandler implements Runnable {
private ResponseBodyEmitter emitter;
private Greeting greeting;
public GreetingRequestHandler(final ResponseBodyEmitter emitter, final Greeting greeting) {
this.emitter = emitter;
this.greeting = greeting;
}
#Override
public void run() {
try {
log.info(this.greeting.toString());
this.emitter.send(this.greeting);
Thread.sleep(5000);
if (Status.COMPLETE.equals(this.greeting.getStatus())) {
this.emitter.complete();
} else {
this.greeting.incrementStatus();
new Thread(new GreetingRequestHandler(this.emitter, this.greeting)).start();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
#RequestMapping(path = "/greeting")
public SseEmitter greeting(#RequestParam(value = "name", defaultValue = "World") final String name) {
SseEmitter emitter = new SseEmitter();
Greeting greeting = new Greeting(String.format(template, name));
greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
new Thread(new GreetingRequestHandler(emitter, greeting)).start();
log.info("returning emitter");
return emitter;
}
}
The Greeting class is the following:
package hello;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Greeting extends ResourceSupport {
private final String content;
private final static AtomicInteger idProvider = new AtomicInteger();
private int greetingId;
private Status status;
enum Status {
ENQUEUED,
PROCESSING,
COMPLETE;
}
#JsonCreator
public Greeting(#JsonProperty("content") final String content) {
this.greetingId = idProvider.addAndGet(1);
this.status = Status.ENQUEUED;
this.content = content;
}
public Status getStatus() {
return this.status;
}
protected void setStatus(final Status status) {
this.status = status;
}
public int getGreetingId() {
return this.greetingId;
}
public String getContent() {
return this.content;
}
#Override
public String toString() {
return "Greeting{id='" + this.greetingId + "', status='" + this.status + "' content='" + this.content + "', " + super.toString() + "}";
}
public void incrementStatus() {
switch (this.status) {
case ENQUEUED:
this.status = Status.PROCESSING;
break;
case PROCESSING:
this.status = Status.COMPLETE;
break;
default:
break;
}
}
}
This code works perfectly. If I try to reach the REST service using a web browser, I see events appearing with correct content and link.
The result looks like (each event appearing 5 seconds after the previous one):
data:{"content":"Hello, Kraal!","greetingId":8,"status":"ENQUEUED","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
data:{"content":"Hello, Kraal!","greetingId":8,"status":"PROCESSING","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
data:{"content":"Hello, Kraal!","greetingId":8,"status":"COMPLETE","_links":{"self":{"href":"http://localhost:8080/greeting?name=Kraal"}}}
Now I need to call this REST service and read these events from another Spring application... But I have no clue how to write the client code using Spring. This does not work as RestTemplate is designed for synchronous client side HTTP access...
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new Jackson2HalModule());
// required for HATEOAS
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
converter.setObjectMapper(mapper);
// required in order to be able to read serialized objects
MappingJackson2HttpMessageConverter converter2 = new MappingJackson2HttpMessageConverter();
converter2.setSupportedMediaTypes(MediaType.parseMediaTypes("application/octet-stream"));
converter2.setObjectMapper(mapper);
// required to understand SSE events
MappingJackson2HttpMessageConverter converter3 = new MappingJackson2HttpMessageConverter();
converter3.setSupportedMediaTypes(MediaType.parseMediaTypes("text/event-stream"));
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(converter);
converters.add(converter2);
converters.add(converter3);
// probably wrong template
RestTemplate restTemplate = new RestTemplate();
restTemplate = new RestTemplate(converters);
// this does not work as I receive events and no a single object
Greeting greeting = restTemplate.getForObject("http://localhost:8080/greeting/?name=Kraal", Greeting.class);
log.info(greeting.toString());
The error message I get is:
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'data': was expecting ('true', 'false' or 'null')
Indeed each event is a SSE event and starts with "data:"...
So the questions are:
what ObjectMapper module should I register in order to be able to map SSE with Jackson ?
how can I subscribe to incoming SSE events (observer pattern) using Spring ?
Thanks in advance.
Side note: As I'm struggling doing it using Spring I tried to do it using Jersey SSE support as follows. Using Jersey I receive events as expected, but then I can't cast them to a Greeting class (for the same reason as above I guess which is that I don't have the right converter module .):
Client client = ClientBuilder.newBuilder().register(converter).register(SseFeature.class).build();
WebTarget target = client.target("http://localhost:8080/greeting/?name=Kraal");
EventInput eventInput = target.request().get(EventInput.class);
while (!eventInput.isClosed()) {
final InboundEvent inboundEvent = eventInput.read();
if (inboundEvent == null) {
// connection has been closed
break;
}
// this works fine and prints out events as they are incoming
System.out.println(inboundEvent.readData(String.class));
// but this doesn't as no proper way to deserialize the
// class with HATEOAS links can be found
// Greeting greeting = inboundEvent.readData(Greeting.class);
// System.out.println(greeting.toString());
}
as per the documentation
you can use inboundEvent.readData(Class<T> type)

Java (web service - SOAP) - How do I add a SOAP handler on the client side and enable MTOM correct?

Java - JDK 1.6.0.7 - WSGEN -version: JAX-WS RI 2.2.3-b01-
I have the following problem:
SOAPBinding binding = (SOAPBinding)((BindingProvider)port).getBinding();
binding.setMTOMEnabled(true);
List<Handler> handlerChain = new ArrayList<Handler>();
handlerChain.addAll(binding.getHandlerChain());
handlerChain.add(new MyHandlerSecurity("admin", "admin"));
binding.setHandlerChain(handlerChain);
With this code the SoapHeader is correct, but the attachment is always a inline base64 text.
//List<Handler> handlerChain = new ArrayList<Handler>();
//handlerChain.addAll(binding.getHandlerChain());
//handlerChain.add(new MyHandlerSecurity("admin", "admin"));
//binding.setHandlerChain(handlerChain);
When handlerChain is commented out, you will see the attachment as an xop reference, but there is no SoapHeader and thus, the client is not authenticated...
How can I add a handler on the client side and enable MTOM correct?
Im not sure if i got the question right, but i think i had your same problem a couple of months ago, so here is my solution:
First you need a HeaderHandler class , wich creates the soap header element, it should look like this:
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
String AUTH_TK = "http://www.myurl.com:port/subdir/etc/";
String NOPREFIX="";//no prefix
String PREFIX_XMLNS="xmlns";
String value = "123456";
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
//<AuthorizationToken xmlns="http://www.myurl.com:port/subdir/etc/">
SOAPElement authorizationToken = header.addChildElement("AuthorizationToken", PREFIX_XMLNS, AUTH_TK);
//<Token>value</Token>
SOAPElement usernameToken =
authorizationToken.addChildElement("Token", NOPREFIX);
usernameToken.addTextNode(value);
//<Token>value</Token>
SOAPElement usernameToken =
authorizationToken.addChildElement("Token", PREFIX);
usernameToken.addTextNode(value);
} catch (Exception e) {
e.printStackTrace();
}
}
return outboundProperty;
}
public Set<QName> getHeaders() {
return null;
}
public void close(MessageContext arg0) {
}
public boolean handleFault(SOAPMessageContext arg0) {
return false;
}
}
After that you create a HeaderHandlerResolver to handle the header creation and insert it in a handler chain:
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
public class HeaderHandlerResolver implements HandlerResolver {
#SuppressWarnings("unchecked")
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
HeaderHandler hh = new HeaderHandler();
handlerChain.add(hh);
return handlerChain;
}
}
After that, you add in the Client:
try{
//new service instance (your service should be extending javax.xml.ws.Service;)
YourServiceProxy service = new YourServiceProxy();
//calls the header handler resolver ;)
service.setHandlerResolver(new HeaderHandlerResolver());
//get the service
YourService port = (YourService)service.getYourService();
//call the service
port.yourMethod()
} catch (Exception e) {
e.printStackTrace();
}
By the way, i didn't tested this particular header, i modified a previous header handler i had, so it may be not accurate, but i think it's pretty close, i really hope it helps you, try it out and tell us how it comes, i'll try to help you if it still doesn't works.

How to add soap header when making a soap request using the java objects generated by wsdl

I generated client java objects using JAX-WS RI. I am trying to make a SOAP request to a web service. Service requires authentication in the header which looks like below:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<xsd:authHeader>
<xsd:user>username#gmail.com</xsd:user>
<xsd:password>password1</xsd:password>
</xsd:authHeader>
</soapenv:Header>
<soapenv:Body>
<ns:searchAssetsParam>
<ns:includeSubfolders>true</ns:includeSubfolders>
<ns:resultsPage>2</ns:resultsPage>
</ns:searchAssetsParam>
</soapenv:Body>
</soapenv:Envelope>
The generated java objects have methods for calling the service, creating the objects and constructing the header. But, I am having trouble setting the header while making the call.
Here's the code that I am using:
IpsApiService service = new IpsApiService();
IpsApiPortType port = service.getIpsApiSoapPort();
SearchAssetsParam searchAssetsParam = buildSearchAssetsParam();
SearchAssetsReturn response = port.searchAssets(searchAssetsParam);
buildSearchAssetsParam() constructs the request object.
I created the header object as follows:
AuthHeader header = new AuthHeader();
header.setUser("username#gmail.com");
header.setPassword("password1");
How do I set this AuthHeader to the service request?
Thanks,
Venu
Once I had the same problem. I needed to modify the JAX-WS web service SOAP header at every request. To solve this problem I have created a handler like this:
public class MyHandler implements SOAPHandler<SOAPMessageContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
private String username;
private String password;
#Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage message = context.getMessage();
SOAPHeader header = message.getSOAPHeader();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
if (header == null) {
header = envelope.addHeader();
}
QName qNameUserCredentials = new QName("https://your.target.namespace/", "UserCredentials");
SOAPHeaderElement userCredentials = header.addHeaderElement(qNameUserCredentials);
QName qNameUsername = new QName("https://your.target.namespace/", "Username");
SOAPHeaderElement username = header.addHeaderElement(qNameUsername );
username.addTextNode(this.username);
QName qNamePassword = new QName("https://your.target.namespace/", "Password");
SOAPHeaderElement password = header.addHeaderElement(qNamePassword);
password.addTextNode(this.password);
userCredentials.addChildElement(username);
userCredentials.addChildElement(password);
message.saveChanges();
//TODO: remove this writer when the testing is finished
StringWriter writer = new StringWriter();
message.writeTo(new StringOutputStream(writer));
LOGGER.debug("SOAP message: \n" + writer.toString());
} catch (SOAPException e) {
LOGGER.error("Error occurred while adding credentials to SOAP header.", e);
} catch (IOException e) {
LOGGER.error("Error occurred while writing message to output stream.", e);
}
return true;
}
//TODO: remove this class after testing is finished
private static class StringOutputStream extends OutputStream {
private StringWriter writer;
public StringOutputStream(StringWriter writer) {
this.writer = writer;
}
#Override
public void write(int b) throws IOException {
writer.write(b);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
LOGGER.debug("handleFault has been invoked.");
return true;
}
#Override
public void close(MessageContext context) {
LOGGER.debug("close has been invoked.");
}
#Override
public Set<QName> getHeaders() {
LOGGER.debug("getHeaders has been invoked.");
return null;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
It adds the needed parameters to my SOAP header and it is invoked on every request. All you need to do is to modify handleMessage method to suit your needs.
It works for me by overriding the public void setAttribute(String namespace, String localName, String value) method.
import javax.xml.namespace.QName;
import org.apache.axis.Constants;
import org.apache.axis.message.SOAPHeaderElement;
#SuppressWarnings("serial")
public class ADESHeaderElement extends SOAPHeaderElement
{
public ADESHeaderElement(QName qname, Object value)
{
super(qname, value);
}
#Override
public void setAttribute(String namespace, String localName, String value)
{
if (!Constants.ATTR_MUST_UNDERSTAND.equals(localName))
{ // Or any other attribute name you'd want to avoid
super.setAttribute(namespace, localName, value);
}
}
}
Create header element like this:
ADESHeaderElement custheader = new ADESHeaderElement(qname, clientserv);
custheader.setActor(null);
When you create your service from classess generated by cxf, add custom interceptor
Service service = new MyService(wsdlURL, new QName("http://myservice.com/MyService/", "MyService"));
MyPort port = service.getMyPort();
Client client = ClientProxy.getClient(port);
// adding interceptor programmatically
client.getOutInterceptors().add(new MyHeaderHandler());
Your can extend AbstractSoapInterceptor to implement your custom interceptor to handle message.
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.jaxb.JAXBDataBinding;
import org.apache.cxf.phase.Phase;
import com.rpc.core.utils.DomainContext;
public class MyHeaderHandler extends AbstractSoapInterceptor {
/**
* Constructor
*/
public MyHeaderHandler() {
super(Phase.PRE_LOGICAL);
}
#Override
public void handleMessage(org.apache.cxf.binding.soap.SoapMessage message) throws org.apache.cxf.interceptor.Fault {
try {
message.getHeaders().add(new Header(new QName("MyCustomHeader"),"value", new JAXBDataBinding(String.class)));
} catch (JAXBException e) {
e.printStackTrace();
}
};
}
}
Yes, I did the same that Rangappa Tungal, following this example:
Service w = new ServiceLocator();
ServiceSoap ws = new ServiceSoapStub(new URL(w.getServiceSoapAddress()),w); Stub mystub = (Stub) ws;
AuthHeader up = new AuthHeader("user","pass");
mystub.setHeader("namespace", "AuthHeader", up);
ws.get***();
Link to the example!

Categories