I am working on Axis1.4 and generated java clients from wsdl.
I am looking for a good working example which shows how to log complete SOAP Request and Response. I did see couple of examples where handleRequest, handleResponse methods are used but I failed to understand how to make use/invoke these handlers from my java classes to capture request/response.
For an example, Here is how I am invoking a service and get the response. I am trying to understand on how to capture the full SOAP XML Request/Response in my code.
//locate service
FundEditorServiceImplServiceLocator locator = new FundEditorServiceImplServiceLocator();
FundEditorService service = locator.getFundEditorServiceImplPort();//invoke service
FundInfo response = service.getFundInfo(fundInfoSpec);
You have to use handlers.
HandlerRegistry hr = locator.getHandlerRegistry();
List<HandlerInfo> handlerChain = hr.getHandlerChain((QName) locator.getPorts().next());
HandlerInfo hi = new HandlerInfo();
hi.setHandlerClass(MyHandlerClass.class);
handlerChain.add(hi);
For every handler class, you have to generate a HandlerInfo object. Register it on the chain and it should work. The MyHandlerClass should be a sub class of javax.xml.rpc.handler.GenericHandler. There you have to implement handleResonse() and/or handleRequest(). You can investigate the SOAPMessage (get it from the parameter MessageContext of the methods) and log things to your preferred logging framework.
Related
I'm using Camel and have generated code from a WSDL using CXF. I generated a client stub and the implementation appears like this:
SetDeviceDetailsv4 port = ss.getSetDeviceDetailsv4Port();
com.vodafone.gdsp.ws.SetDeviceDetailsv4_Type _setDeviceDetailsv4_parameters = null;
com.vodafone.gdsp.ws.GdspHeader _setDeviceDetailsv4_gdspHeader = null;
com.vodafone.gdsp.ws.SetDeviceDetailsv4Response _setDeviceDetailsv4__return = port.setDeviceDetailsv4(_setDeviceDetailsv4_parameters, _setDeviceDetailsv4_gdspHeader);
System.out.println("setDeviceDetailsv4.result=" + _setDeviceDetailsv4__return);
As you one can see, the port takes two parameters and returns the response, which I want to delegate back to my Camel Route. What's the best way to implement this in Camel? I already have my CXF Enpoint defined, I'm just struggling with the DSL Routing part of it. Should I add a processor like what is found in this link? Apache Camel and web services
Thanks
You can use jax-ws client (implement as bean) and use it in camel DSL. JAX-WS client bean definition takes service class/interface and allow you configure additional properties like SSL config & etc. In route, we can use it as bean. It takes JAXB generated Request object (WSDL request object) as input and returns the JAXB generated Response Object (WSDL response object). To convert you pojo to JAXB classes, Dozer framework can be used or custom mapping can be also used.
Jax-WS client is also flexible to take XML as request and response. In that case, properties need to be set as DATAFORMAT as PAYLOAD.
I'm not sure if this is the correct way to do it but I added both of my "input" objects as a Camel Header, then I wrote a processor that grabbed what I needed and put the two objects that the service call needed as parameters.
public void process(Exchange exchange) throws Exception {
Message inMessage = exchange.getIn();
gdspHeader = inMessage.getHeader(GDSP_HEADER, com.vodafone.gdsp.ws.GdspHeader.class);
commModule = inMessage.getHeader(COMM_MODULE_HEADER, resmed.hi.ngcs.datastore.model.CommModule.class);
SetDeviceDetailsv4_Type deviceDetails = createSetDeviceDetailsv4(commModule);
List<Object> params = new ArrayList<>();
params.add(deviceDetails);
params.add(gdspHeader);
inMessage.setBody(params);
}
`
I want to set a dynamically generated HTTP header on each SOAP JAX-WS request.
If I wanted to set the same HTTP header on each JAX-WS request I could use the technique here, i.e.
public class MyApplicationClass {
// Inject an instance of the service's port-type.
#WebServiceRef(EchoService.class)
private EchoPortType port;
// This method will invoke the web service operation and send transport headers on the request.
public void invokeService() {
// Set up the Map that will contain the request headers.
Map<String, Object> requestHeaders = new HashMap<String, Object>();
requestHeaders.put(“MyHeader1”, “This is a string value”);
requestHeaders.put(“MyHeader2”, new Integer(33));
requestHeaders.put(“MyHeader3”, new Boolean(true));
// Set the Map as a property on the RequestContext.
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(com.ibm.websphere.webservices.Constants.REQUEST_TRANSPORT_PROPERTIES, requestHeaders);
// Invoke the web services operation.
String result = port.echoString(“Hello, world!”);
}
}
However, here I want to use a different HTTP header for each request. Essentially I want to include a X-RequestId header or similar with a random value, so the receiving server can distinguish between requests duplicated on a timeout either by the Java client or (worse) an inline HTTP proxy.
Moreover, it JAX-WS retries the same call, I don't want it to regenerate the header (obviously).
Note that my application is already covered in the equivalent of port.echoString (lots of calls to the web service). I can't manually change the header in front of each such call because:
they share the same binding provider, and this would not be thread-safe (i.e. user A could change the header, user B could change the header, then user A could call, then user B could call, and the same header be passed)
this would require modification all over the code.
What I want to do is add something to the class that serialises each request, to add the header at serialisation time.
Questions that are related but are not duplicates:
java web service client, adding http headers - does not use JAX-WS binding, i.e. each call has to be made manually
How to add header to SOAP request? - SOAP headers not HTTP headers
As for the unique value aspect, you can use the JDK's UUID class to create a GUID:
requestHeaders.put("X-RequestId", java.util.UUID.randomUUID().toString());
As for the clarified thread safety concern, based on the JAX-WS specification (JSR-224) section 9.3 I'd suggest using a JAX-WS client handler to do this as the handler spec identifies a thread safe mechanism: MessageContext:
9.3.3 Handler Implementation Considerations
Handler instances may be pooled by a JAX-WS runtime system. All
instances of a specific handler are considered equivalent by a JAX-WS
runtime system and any instance may be chosen to handle a particular
message. Different handler instances may be used to handle each
message of an MEP. Different threads may be used for each handler in a
handler chain, for each message in an MEP or any combination of the
two. Handlers should not rely on thread local state to share
information. Handlers should instead use the message context, see section 9.4.
You can write one central handler class and attach it to the BindingProvider's handler chain to avoid changing all the places you invoke the service operation across the application. You can add a handler to the handler chain programmatically or via the #HandlerChain annotation companion to #WebServiceRef
This post describes using the handler framework's MessageContext to set outbound http headers like you want. However in your case you want to set the X-RequestId with the UUID value discussed above.
Having a SOAP based web service with XML payload, how can I grab the XML payload of the service response in JAX-WS >2.0 client?
I can see how this question will be marked as duplicate, but bear with me.
It seems that the options are:
Use Dispatch API. However this would require me to go low(er) level and create the request payload manually, which I want to avoid.
Use handler infrastructure of the JAX-WS to grab the payload and possibly pass it via MessageContext property back to the client, but this will still unmarshall the XML into JAXB Object tree, which I want to avoid.
So, how can I:
Call a web service using SEI - no messing with creating XML request from scratch
Grab the 'RAW' XML response without the JAXB unmarshalling happening
Sounds simple enough, right?
Among the non-goals the JAX-WS 2 specification says that
'Pluggable data binding JAX-WS 2.0 will defer data binding to JAXB[10]; it is not a goal to provide a
plug-in API to allow other types of data binding technologies to be used in place of JAXB. However,
JAX-WS 2.0 will maintain the capability to selectively disable data binding to provide an XML based
fragment suitable for use as input to alternative data binding technologies.'
So, you should be able to have access to the raw payload.
The following example is also in the specification :
4.3.5.5 Asynchronous, Callback, Payload-Oriented
class MyHandler implements AsyncHandler<Source> {
...
public void handleResponse(Response<Source> res) {
Source resMsg = res.get();
// do something with the results
}
}
Source reqMsg = ...;
Service service = ...;
Dispatch<Source> disp = service.createDispatch(portName,
Source.class, PAYLOAD);
MyHandler handler = new MyHandler();
disp.invokeAsync(reqMsg, handler);
Since the specification also states that a Binding has its own HandlerChain (see chapter 9) you should be able to remove the JAXB handler from the chain, so that JAXB marshalling/unmarshalling doesn't get involved.
I am very much confused about the concept behind the QName.
lets take for example (I have taken these examples from http://www.mkyong.com/):
ServerInfoService sis = new ServerInfoService();
ServerInfo si = sis.getServerInfoPort();
System.out.println(si.getServerName());
And with QName :
URL url = new URL("http://localhost:8888/ws/image?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "ImageServerImplService");
Service service = Service.create(url, qname);
ImageServer imageServer = service.getPort(ImageServer.class);
Now my question is :
1) Is there any concepts based on which we have to decide which type of client we can write
2) What is the purpose or additional benefits in using QName because all I can see here is that it increases complexity as compared to simple client.
Here is what i know:-
It depends on how you would want to make use of your client to invoke the web service.
The first approach
ServerInfoService sis = new ServerInfoService();
ServerInfo si = sis.getServerInfoPort();
is the plain simple proxy generation approach; where-in you use a tool like wsimport to generate proxies/stubs to your SEI(Service Endpoint Interface)/web-service interfaces and invoke methods on it like any other java method call. Is mostly used in clients where you simply need to invoke methods on the web-service without getting into granular details.
The QName or rather the Service approach offer finer controls over how the client and webservice communicate. JAXWS 2.0 introduced something called as a Provider interface which was an alternative to your SEI which basically let a client communicate at the XML message level and provide a dynamic representation/view of your web-service to the client. More here. The primary use of Service API is mostly to create Dispatch instances which basically let a client dispatch to a speicific port(method qualified using QName api) using JAXB messages as XML payloads.
Other uses of Service api let a client call methods on the webservice asynchronously; provide access to handlers; etc. A good example of using the Service and QName approach to help you understand further and to relate to what i have said is this link here:- Dispatching Web Service Calls.
This being said there is a lot more to know and understand; but hope this gives you a start.
I need to create the dynamic client to call web services, which can call web services with Service Mode as Service.Mode.PAYLOAD as well as Service.Mode.MESSAGE. I have created the Dispatcher as:
Dispatch<Source> sourceDispatch =service.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
But this can invoke the services with Service Mode PAYLOAD only. Please suggest me the way how can I previously determine the Service Mode from WSDL link (service Mode parser code) before creating Dispatch instance?
The mode does not depend on the WSDL.
If you want to pass to sourceDispatch.invoke(T msg) an entire SOAP message use mode.MESSAGE. If you only want to pass the PAYLOAD (the body) use mode.PAYLOAD, and invoke will wrap it in a message for you.
The mode also determines wether invoke returns you a message or the payload.