Print XML content of SOAP message - java

I am using Apache CXF for my webservices. I've created an instance of AbstractSoapInterceptor. In its public void handleMessage(SoapMessage message) throws Fault method I would like to print the XML content of the intercepted message to the console. How can I achieve that?

Check this out and search for INBOUND INTERCEPTOR. Will place it here for reference...
public class InterceptorMensajeSOAPIn extends AbstractSoapInterceptor {
private static Logger log =
Logger.getLogger(InterceptorMensajeSOAPIn.class);
private SAAJInInterceptor saajIn = new SAAJInInterceptor();
public InterceptorMensajeSOAPIn(){
super(Phase.PRE_PROTOCOL);
getAfter().add(SAAJInInterceptor.class.getName());
}
public void handleMessage(SoapMessage message) throws Fault {
SOAPMessage soapMessage = getSOAPMessage(message);
try {
soapMessage.writeTo(System.out);
} catch (Exception e) {
e.printStackTrace();
}
}
private SOAPMessage getSOAPMessage(SoapMessage smsg){
SOAPMessage soapMessage = smsg.getContent(SOAPMessage.class);
if (soapMessage == null) {
saajIn.handleMessage(smsg);
soapMessage = smsg.getContent(SOAPMessage.class);
}
return soapMessage;
}
}

Any reason you cannot just use the LoggingInInterceptor that is shipped with CXF? You could just grab the code for that and use that as a basis, but in 2.3, the LoggingInInterceptor was enhanced to allow specifying a printstream and such to use, so it might "just work".

You can also use a feature for this: org.apache.cxf.feature.LoggingFeature:
<jaxws:endpoint ...>
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature"/>
</jaxws:features>
</jaxws:endpoint>

Related

Propagate exception from CXF interceptor to exception mapper

I've a flow where on CXF client I've jaxrs-in-interceptor, provider and exception mapper. In my case I'm catching bad response from client through in-interceptor and then I would like abort the cxf bus chain and throw a fault. Unfortunately I couldn't do it, cause in every situation exception thrown from interceptor is being only logged, but the main error (wrong json format) is propagated to exception mapper. I would like to avoid Exception mapper, but I don't know how. I'm using WebClient to implement interceptors like this:
#Component
public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
public MyInterceptor() {
super(POST_STREAM);
}
#Override
public void handleMessage(Message message) throws Fault {
if (message != null) {
//message.getExchange().setOneWay(true);
//message.getExchange().put(Exception.class, new MyException());
//message.getInterceptorChain().abort();
//message.setContent(Exception.class, new MyException());
//Endpoint ep = message.getExchange().get(Endpoint.class);
//message.getInterceptorChain().abort();
//if (ep.getInFaultObserver() != null) {
// ep.getInFaultObserver().onMessage(message);
//}
//throw new WebApplicationException( new MyException());
//message.setContent(Response.class, response);
throw new Fault(new MyException());
}
}
I read that I should implement jaxrs-filter cause exceptions thrown by interceptor are not propagated to exception mapper. Is it any way to do that in java thanks to WebClient implementation?
S client = create(url, clazz, list(jsonProvider(), providers));
WebClient.getConfig(client).getInInterceptors().add(new MyInterceptor());
I've also tried to use different phases on interceptor, but it also didn't work.
I have been researching and testing with your issue. The problem is that the exceptions thrown from the CXF interceptors escape the JAX-RS flow (see the answer of CXF team)
A Fault generated from interceptor can be catched implementing handleFault in the interceptor itself
public void handleFault(Message message) {
Exception e = message.getContent(Exception.class);
}
Or implementing a FaultListener and registering it at CXF Bus
WebClient.getConfig(client).getBus().getProperties().put("org.apache.cxf.logging.FaultListener",new MyFaultListener());
public class MyFaultListener implements FaultListener{
public boolean faultOccurred(final Exception exception,final String description,final Message message) {
//return false to avoid warning of default CXF logging interceptor
return false;
}
}
But you can not return custom response from interceptor or respond a Fault to client.
The workaround I have found to achieve the desired behaviour consist in replacing the Response with a custom object that could be processed by your usual method invokation, like an exceptionMapper
See CXF/ JAX-RS : Return Custom response from interceptor
Into Interceptor.handleMessage check the conditions you need and create a Response with custom status and entity. After this, stop the chain
public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
public MyInterceptor() {
super(Phase.POST_STREAM);
}
#Override
public void handleMessage(Message message) throws Fault {
if (message != null) {
//check the condition to raise the error
//build the custom Response replacing service call
Response response = Response
.status(Response.Status.BAD_REQUEST)
.entity("custom error")
.build();
message.getExchange().put(Response.class, response);
//abort interceptor chain in you want to stop processing or throw a Fault (catched by handleFault)
//message.getInterceptorChain().abort();
//throw new Fault (new MyException());
}
public void handleFault(Message messageParam) {
}
}
Add the ResponseExceptionMapper as provider when creating the JAXRS client
providers.add(new ResponseExceptionMapper<WebApplicationException>() {
#Override
public WebApplicationException fromResponse(Response r) {
return new WebApplicationException(r);
}
});
YourService proxy = JAXRSClientFactory.create(url, clazz,providers);
Client client = WebClient.client(proxy);
WebClient.getConfig(client).getInInterceptors().add(new MyInterceptor());
After this, a call to proxy.yourService() will raise a WebApplicationException if acomplish the interceptor check. You can catch it or rethrow in the desired way
try{
proxy.yourService();
}catch (WebApplicationException e){
}
Hope this helps
I fully agree with previous answer. My implementation looks like:
#Component
public class ServiceFailureInterceptor extends AbstractPhaseInterceptor<Message> {
private static final Logger LOG = LoggerFactory.getLogger(ServiceFailureInterceptor.class);
public ServiceFailureInterceptor() {
super(PRE_STREAM);
}
#Override
public void handleMessage(Message message) {
if (message != null) {
int responseCode = (int) message.get(Message.RESPONSE_CODE);
LogicException logicException = ErrorMapper.HTTP_STATUS_CODE_MAPPER.get(responseCode);
InputStream is = b2stream(MapperUtils.json().toBytes(logicException));
// clear old message & exchange
Exchange exchange = message.getExchange();
for (Class<?> contentFormat : message.getContentFormats()) {
message.setContent(contentFormat, null);
}
resetOrigInterceptorChain(message);
resetFault(exchange);
message.setContent(InputStream.class, is);
Message outMessage = createOutMessage(exchange, is);
prepareMessage(outMessage);
prepareMessage(message);
}
}
private void prepareMessage(Message message) {
message.put(Message.REQUESTOR_ROLE, true);
message.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
private Message createOutMessage(Exchange exchange, InputStream logicException) {
Endpoint ep = exchange.get(Endpoint.class);
Message outMessage = ep != null ? ep.getBinding().createMessage() : new MessageImpl();
outMessage.setContent(InputStream.class, logicException);
exchange.setOutMessage(outMessage);
outMessage.setExchange(exchange);
return outMessage;
}
private void resetFault(Exchange exchange) {
exchange.put(Exception.class, null);
}
private void resetOrigInterceptorChain(Message message) {
InterceptorChain chain = message.getInterceptorChain();
if (chain != null) {
for (Interceptor<?> interceptor : chain) {
chain.remove(interceptor);
}
chain.reset();
}
}
}
After setting this exception manually I'm going to ExceptionMapper implementation where my LogicException is consumed and response with exception is building. I cannot avoid Exception mapper when is declared as a provider through WebClient, so I've decided to use it and remapped Exception later.

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

CXF webservice : interceptor not triggered

We currently have a problem with CXF 2.7.3 on Jboss EAP 6.2 with a custom SoapFault exception.
The Subcode and its value is not displayed when we send a custom SoapFault:
Here is what we want from cxf:
<?xml version="1.0" encoding="UTF-8"?>
<tns:Fault
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://www.w3.org/2003/05/soap-envelope">
<tns:Code>
<tns:Value>tns:Sender</tns:Value>
<tns:Subcode>
<tns:Value>value</tns:Value>
</tns:Subcode>
</tns:Code>
<tns:Reason>
<tns:Text xml:lang="fr">
****
</tns:Text>
</tns:Reason>
<tns:Detail>
**Custom fault***
</tns:Detail>
</tns:Fault>
Here is what we have so far:
<?xml version="1.0" encoding="UTF-8"?>
<tns:Fault
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://www.w3.org/2003/05/soap-envelope">
<tns:Code>
<tns:Value>tns:Sender</tns:Value>
</tns:Code>
<tns:Reason>
<tns:Text xml:lang="fr">
****
</tns:Text>
</tns:Reason>
<tns:Detail>
**Custom fault***
</tns:Detail>
</tns:Fault>
The subcode is completely missing.
We tried to use a custom interceptor (extending from LoggingOutInterceptor or AbstractInterceptor) from CXF like this to intercept the custom fault:
public class SoapRequestInterceptor extends LoggingOutInterceptor {
private static Logger log = Logger.getLogger(SoapRequestInterceptor.class);
public SoapRequestInterceptor() {
super(Phase.MARSHAL);
}
public void handleMessage(SoapMessage message) throws Fault{
SoapMessage soapMessage = message.getContent(SoapMessage.class);
if (soapMessage != null) {
log.info( "request intercepted:" + soapMessage.toString());
}
}
}
The interceptor is not even called when we add him either to CXF bus or to jaxws interceptor (it’s added at the start of the application though since it gets through the constructor).
How can we intercept a custom soap Fault message and edit it in CXF?
Thanks a lot!
As asked here's the way we declare the interceptor in spring applicationContext.xml :
<cxf:bus>
<cxf:outFaultInterceptors>
<ref bean="soapRequestInterceptor" />
</cxf:outFaultInterceptors>
</cxf:bus>
<bean id="soapRequestInterceptor" class="fr.test.SoapRequestInterceptor" />
<jaxws:server serviceClass="fr.test.PriseEnChargeB2ServiceSP"
address="" serviceBean="#service">
<jaxws:binding>
<soap:soapBinding version="1.2" mtomEnabled="true" />
</jaxws:binding>
</jaxws:server>
Note : the interceptor is well instancied, but not called after a soap fault throw from our WS
The exception thrown at the end of our WS is this one :
public class PecSoapFaultException extends SoapFault {
private static final long serialVersionUID = 1L;
public TypeErreur erreur;
public PecSoapFaultException(String message, TypeErreur structure) {
super(message, new QName(""));
this.erreur = structure;
}
public PecSoapFaultException(String message, TypeErreur structure, QName faultcode) {
super(message, faultcode);
this.erreur = structure;
}
public PecSoapFaultException(String message, TypeErreur structure, QName faultcode,
QName subcode) {
super(message, faultcode);
this.setSubCode(subcode);
this.erreur = structure;
}
public TypeErreur getFaultInfo() {
return erreur;
}
The problem that your interceptor is not called is that you do not override the correct method. You should have your code like this:
#Override
public void handleMessage(Message message) throws Fault {
SoapMessage soapMessage = message.getContent(SoapMessage.class);
if (soapMessage != null) {
log.info( "request intercepted:" + soapMessage.toString());
}
}
Then your interceptor will be called. But as I said in my comment: in case of a fault the soapmessage is null. So you won't get any output in your log.

Camel return simple SoapFault without CXF/Spring-ws

I created a proxy-camel which accepts SOAP (over HTTP) and RESTful requests and forwards them to the correct web service. The Camel is unaware of message-structure, it doesn't know the WSDL or anything, it just knows if it is SOAP or not according to a http header. There is no CXF endpoint.
Further it does some Processing. Exception can occur inside there, for example when a service is not found or the url is invalid.
Is there an easy way to return a valid SOAPFault directly from this camel?
I tried to write a simple processor which is called onException. It looks like this:
.choice().when().header("SOAP").processRef(ExceptionToSoapProcessor())
The Processor that should transform any Exception into a SOAPFault looks like this
#Override
public void process(Exchange exchange) throws Exception {
Exception exception = (Exception) exchange.getProperty(Exchange.EXCEPTION_CAUGHT);
Integer responseCode = (Integer) exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE);
QName qName = SoapFault.FAULT_CODE_SERVER;
if (responseCode != null && responseCode < 500) {
qName = SoapFault.FAULT_CODE_CLIENT;
}
SoapFault fault = new SoapFault(exception.getMessage(), qName);
Message outMessage = exchange.getOut();
outMessage.setHeader(Message.RESPONSE_CODE, 500);
outMessage.setFault(true);
outMessage.setBody(fault);
exchange.setException(null);
exchange.removeProperty(Exchange.EXCEPTION_CAUGHT);
exchange.setProperty(Exchange.EXCEPTION_HANDLED, true);
}
But now I don't understand how I will marshal it, the response looks like this:
org.apache.cxf.binding.soap.SoapFault: Unauthorized
("Unauthorized" is the actual message)
PS: I used the dataformat SOAP before, but as mentioned, I don't have any ServiceInterface in this Camel.
I would move the handling of the error scenario to an onException() block. That way you can "declare" some of the behavior, like marking the exception as handled. IMHO makes it a little cleaner.
Just returning the SOAP fault would not result in a valid SOAP response. You have to build the complete message structure. I don't think there is a type converter for SOAP messages to a text stream, so you have to marshal the SOAP response yourself.
This is the code I am using to do the job:
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean beanType="some.package.WSHelper" method="createSOAPFaultServerError" />
</onException>
public static String createSOAPFaultServerError(final Exception cause) {
String result = null;
LOG.error("Creating SOAP fault, hiding original cause from client:", cause);
try {
SOAPMessage message = MessageFactory.newInstance().createMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPBody body = message.getSOAPBody();
SOAPFault fault = body.addFault();
fault.setFaultCode("Server");
fault.setFaultString("Unexpected server error.");
Detail detail = fault.addDetail();
Name entryName = envelope.createName("message");
DetailEntry entry = detail.addDetailEntry(entryName);
entry.addTextNode("The server is not able to complete the request. Internal error.");
result = soapMessage2String(message);
} catch (Exception e) {
LOG.error("Error creating SOAP Fault message", e);
}
return result;
}
private static String soapMessage2String(final SOAPMessage message) throws SOAPException, IOException {
String result = null;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
message.writeTo(outStream);
result = new String(outStream.toByteArray(), StandardCharsets.UTF_8);
return result;
}
HTH

How to extract SOAP header from a WS response using spring-ws and jaxb

We're using spring-ws 2.2 on our application to consume web services. We've been happily doing this for quite some time and everything is working fine, except that now I need to access the SOAP header in the response and I just can't find a way to do this.
We are using a WebServiceTemplate (from springs-ws) configured with a Jaxb2Marshaller. The jaxb files are generated from the wsdl using xjc. The header element in my responses look something like this:
<soapenv:Header>
<v1:ResponseHeader status="OK">
<v1:description>test</v1:description>
</v1:ResponseHeader>
</soapenv:Header>
In my java class, the code that parses the response looks like this (I've stripped some irrelevant code):
public CalculationData getValues(Integer id) throws IntegrationException {
WebServiceMessageCallback callback = createCallback(soapAction);
GetValuesRequest request = toGetValues(id);
GetValuesResponse response = null;
try {
response = (GetValuesResponse) webServiceTemplate.marshalSendAndReceive(request, callback);
} catch (SOAPFaultException fault) {
log.error("Soap fault occurred during getValues " + id);
throw new IntegrationException(fault);
}
CalculationData data = fromGetValues(response);
return data;
}
Please help me find a solution for extracting the information from the SOAP header out of the response. I must be able to parse the status code which is sent as an attribute.
By the way. I also have a ResponseHeader.java jaxb class which has been generated from the schemas.
Update from final changes:
This is how my handleResponse method looks like after inlining a ClientInterceptor implementation:
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> responseHeaderElements =
message.getSoapHeader().examineAllHeaderElements();
SoapHeaderElement header = null;
if (responseHeaderElements.hasNext()) {
header = responseHeaderElements.next();
} else {
log.error("Error! No ResponseHeader found in response.");
return false;
}
String responseCode = header.getAttributeValue(new QName(STATUS_QNAME));
responseMsg.put(RESPONSE_MSG_KEY, responseCode);
return true;
}
I tried getting the ResponseHeader element by QName, but that did not seem to work for some reason. However, I only expect to get one element in the soap header anyhow, is this will work fine.
For this use case, the best solution is to use a custom WebServiceMessageExtractor, as described here:
http://veithen.github.io/2015/01/03/spring-ws-extracting-soap-headers-from-responses.html
Implement a ClientInterceptor, specifically see handleResponse() method.
In order to access the Soap Headers, convert to a SoapMessage
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
QName v1ResponseHeaderQName = null;//todo
QName statusAttrQName = null;//todo
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> matchingHeaders = message.getSoapHeader().examineHeaderElements(v1ResponseHeaderQName);
String status = matchingHeaders.next().getAttributeValue(statusAttrQName);
}
then call webServiceTemplate.setInterceptors(..)
For some further examples of this stuff see AbstractWsSecurityInterceptor and its subclasses. Be aware however that those interceptors deal with replacing the request message, you just want to read the response message.
Problem is you are dealing with the raw soap message now so you've lost the nice spring marshalling and need to start dealing with namespaces (QNames) and w3c Dom stuff.
In order for the interceptor to pass the header back to the calling code, you could make the interceptor an anonymous inner class that is setup inside your getValues(...) method.
final Map<String,String> headers = new HashMap<>();
template.setInterceptors(new ClientInterceptor[]{new ClientInterceptor() {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
headers.put("foo", "bar");
return false;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
}});
template.marshalSendAndReceive(....);

Categories