Mule cxf client and wcf service interaction - web-services

I have the workflow like
<flow name="testmulewcfFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/GetData" doc:name="HTTP"/>
<set-payload value="#[message.inboundProperties.'http.query.params'.value]" doc:name="Set Payload"/>
<cxf:jaxws-client port="CXFWebservicePort" operation="GetData" serviceClass="com.mulesoft.wcfconsumer.IService1" doc:name="CXF"/>
<object-to-string-transformer doc:name="Object to String"/>
</flow>
And very simple wcf service which I want to use from mule anypoint studio
public class Service1 : IService1
{
public string GetData(string value)
{
return string.Format("You entered: {0}", value);
}
}
But I when I make a call: http://localhost:8081/GetData?value=hello
I get the error:
Unexpected wrapper element {http://tempuri.org/}GetData found. Expected {http://tempuri.org/}GetDataResponse.. Failed to route event via endpoint: org.mule.module.cxf.CxfOutboundMessageProcessor. Message payload is of type: PushbackInputStream
What am I doing wrong?

That issue you are facing due to not implementing an outbound endpoint at the end after cxf:jaxws-client
Put an out bound endpoint at the end that will call your external web service with a http:request component :- http://www.mulesoft.org/documentation/display/current/HTTP+Connector

Related

How do I set the WS-Addressing MessageId header when using CXF with Apache Camel?

I'm invoking a web service that requires WS-Addressing SOAP headers. I'm using Apache Camel with CXF to invoke the web service. When I configure the CXF endpoint with the web service's WSDL, it's smart enough to automatically add WS-Adressing SOAP headers, but I need to set a custom MessageId.
Here is the message that is currently being sent:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<ws:international xmlns:ws="http://www.w3.org/2005/09/ws-i18n">
<ws:locale xmlns:ws="http://www.w3.org/2005/09/ws-i18n">en_CA</ws:locale>
</ws:international>
<fram:user wsa:IsReferenceParameter="true" xmlns:fram="http://wsbo.webservice.ephs.pdc.ibm.com/Framework/" xmlns:wsa="http://www.w3.org/2005/08/addressing">BESTSystem</fram:user>
<Action soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">http://webservice.ephs.pdc.ibm.com/Client/QueryHumanSubjects</Action>
<MessageID soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6</MessageID>
<To soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">https://panweb5.panorama.gov.bc.ca:8081/ClientWebServicesWeb/ClientProvider</To>
<ReplyTo soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<ns2:queryHumanSubjectsRequest xmlns:ns2="http://wsbo.webservice.ephs.pdc.ibm.com/Client/" xmlns:ns3="http://wsbo.webservice.ephs.pdc.ibm.com/FamilyHealth/">
<!-- stuff -->
</ns2:queryHumanSubjectsRequest>
</soap:Body>
</soap:Envelope>
As you can see, the MessageId value is "urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6". I need to set a custom value.
I tried adding the MessageId header they way I add the other headers like "international" and "user", but some part of the framework overrides the value.
// Note this doesn't work! Something overrides the value. It works for other headers.
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
List<SoapHeader> headers = CastUtils.cast((List<?>) in.getHeader(Header.HEADER_LIST));
SOAPFactory sf = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
QName MESSAGE_ID_HEADER = new QName("http://www.w3.org/2005/08/addressing", "MessageID", "wsa");
SOAPElement messageId = sf.createElement(MESSAGE_ID_HEADER);
messageId.setTextContent("customValue");
SoapHeader soapHeader = new SoapHeader(MESSAGE_ID_HEADER, messageId);
headers.add(soapHeader);
}
The CXF website has some documentation on how to set WS-Addressing headers, but I don't see how to apply it to Apache Camel. The Apache Camel CXF documentation doesn't specifically mention WS-Addressing either.
The documentation links you posted actually do have the information you need, although it's not immediately obvious how to apply it to Camel.
The CXF documentation says that:
The CXF org.apache.cxf.ws.addressing.impl.AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:
AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
add.setValue("http://localhost:9090/decoupled_endpoint");
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
((BindingProvider)port).getRequestContext()
.put("javax.xml.ws.addressing.context", maps);
Note that it sets the addressing properties on the "RequestContext".
The Apache Camel documentation says that:
How to propagate a camel-cxf endpoint’s request and response context
CXF client API provides a way to invoke the operation with request and response context. If you are using a camel-cxf endpoint producer to invoke the outside web service, you can set the request context and get response context with the following code:
CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
public void process(final Exchange exchange) {
final List<String> params = new ArrayList<String>();
params.add(TEST_MESSAGE);
// Set the request context to the inMessage
Map<String, Object> requestContext = new HashMap<String, Object>();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
exchange.getIn().setBody(params);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
}
});
The above example has some stuff we don't need, but the important thing is that it shows us how to set the CXF Request Context.
Put them together and you get:
#Override
public void process(Exchange exchange) throws Exception {
AttributedURIType messageIDAttr = new AttributedURIType();
messageIDAttr.setValue("customValue");
AddressingProperties maps = new AddressingProperties();
maps.setMessageID(messageIDAttr);
Map<String, Object> requestContext = new HashMap<>();
requestContext.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT, requestContext);
}
// org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES = "javax.xml.ws.addressing.context"
// org.apache.cxf.endpoint.Client.REQUEST_CONTEXT = "RequestContext"
Warning: In my route, I invoke multiple different web services sequentially. I discovered that after setting the RequestContext as shown above, Camel started using the same RequestContext for all web services, which resulted in an error: "A header representing a Message Addressing Property is not valid and the message cannot be processed". This is because the incorrect "Action" header was used for all web service invocations after the first.
I traced this back to Apache Camel using a "RequestContext" Exchange property, separate from the header we set, which apparently takes priority over the header. If I remove this property prior to calling subsequent web services, CXF automatically fills in the correct Action header.
if your problem not solved, I suggest you to combine your cxf service with custom interceptor. it easy to work with your soap message. like this:
<bean id="TAXWSS4JOutInterceptorBean" name="TAXWSS4JOutInterceptorBean" class="com.javainuse.beans.SetDetailAnswerInterceptor " />
<cxf:cxfEndpoint id="CXFTest" address="/javainuse/learn"
endpointName="a:SOATestEndpoint" serviceName="a:SOATestEndpointService"
serviceClass="com.javainuse.SOATestEndpoint"
xmlns:a ="http://javainuse.com">
<cxf:binding>
<soap:soapBinding mtomEnabled="false" version="1.2" />
</cxf:binding>
<cxf:features>
<wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
</cxf:features>
<cxf:inInterceptors>
<ref bean="TAXWSS4JInInterceptorBean" />
</cxf:inInterceptors>
<cxf:inFaultInterceptors>
</cxf:inFaultInterceptors>
<cxf:outInterceptors>
<ref bean="TAXWSS4JOutInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>
and in the interceptor you can set soap headers like this:
public class SetDetailAnswerInterceptor extends WSS4JOutInterceptor {
public SetDetailAnswerInterceptor() {
}
#Override
public void handleMessage(SoapMessage mc) {
AttributedURIType value = new AttributedURIType();
value.setValue("test");
((AddressingProperties) mc.get("javax.xml.ws.addressing.context.outbound")).setMessageID(value);
}
}

Empty soap envelope in WSO2 axis2 module

I'm working on custom axis2 module for wso2 esb. Right now I'm using code from https://docs.wso2.com/display/ESB490/Writing+an+Axis2+Module
and I have a problem with incoming requests. It doesn't matter what request I send because it always looks like this:
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body/></soapenv:Envelope>
On the other hand OutFlow works more or less as it should - response looks ok but instead of "out" its direction is set as "in". If I'm not mistaken invoke method will be called for requests and revoke for responses - am I right? In my case both are using invoke. Any ideas what I'm doing wrong?
Edit:
My handler code:
public class LogHandler extends AbstractHandler implements Handler {
private Logger log = Logger.getLogger(LogHandler.class.toString());
#Override
public void init(HandlerDescription handlerDescription) {
super.init(handlerDescription);
}
public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
System.out.println("invoked: " + msgContext.getEnvelope().toString() + "\n");
log.info("invoked: " + msgContext.getEnvelope().toString() + "\n");
return InvocationResponse.CONTINUE;
}
public void revoke(MessageContext msgContext) {
log.info("revoked: " + msgContext.getEnvelope().toString() + "\n");
}
}
Module:
public class LoggingModule implements Module {
private static final Log log = LogFactory.getLog(LoggingModule.class);
// initialize the module
public void init(ConfigurationContext configContext, AxisModule module) throws AxisFault {
}
public void engageNotify(AxisDescription axisDescription) throws AxisFault {
}
// shutdown the module
public void shutdown(ConfigurationContext configurationContext) throws AxisFault {
}
public String[] getPolicyNamespaces() {
return null;
}
public void applyPolicy(Policy policy, AxisDescription axisDescription) throws AxisFault {
}
public boolean canSupportAssertion(Assertion assertion) {
return true;
}
}
module.xml:
<module name="sample-logging" class="pl.wso2.logging.LoggingModule">
<InFlow>
<handler name="InFlowLogHandler" class="pl.wso2.logging.LogHandler">
<order phase="loggingPhase"/>
</handler>
</InFlow>
<OutFlow>
<handler name="OutFlowLogHandler" class="pl.wso2.logging.LogHandler">
<order phase="loggingPhase"/>
</handler>
</OutFlow>
</module>
In my wso2 proxy I use Payload Mediator to create response and then return it using Respond Mediator.
For given request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<aa>blahblah</aa>
</soapenv:Body>
</soapenv:Envelope>
there two thing logged:
request from InFlow
invoked: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlso
ap.org/soap/envelope/"><soapenv:Body/></soapenv:Envelope>
and response from OutFlow
invoked: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlso
ap.org/soap/envelope/"><soapenv:Body><m:checkpriceresponse xmlns:m="http://services.samples/xsd"><m:
code>dsadsa</m:code></m:checkpriceresponse></soapenv:Body></soapenv:Envelope>
As per https://axis.apache.org/axis2/java/core/docs/modules.html#Step2_:_LogHandler ...
"public void invoke(MessageContext ctx);" is the method that is called
by the Axis2 engine when the control is passed to the handler. "public
void revoke(MessageContext ctx);" is called when the handlers are
revoked by the Axis2 engine."
which means since you are calling same handler in both InFlow and OutFlow the same invoke() method should be getting triggered for both the request and the response. If you want different logics to be executed for requests and responses maybe you should write separate handlers for request and response.
After debugging everything I've found that while request was parsed in InFlow, instead of using its soap message, new one (empty) was created. Thankfully it's possible to access proper request using soap tracer handler (or just its code).

Mule CXF SOAP service - Validate against XSD and send custom response instead of Soap fault

I have a Mule flow where I exposed a SOAP service using Mule's CXF inbound endpoint. I configured validationEnabled="true" and also wsdlLocation="path-to\my\wsdl". With this configuration of CXF inbound endpoint, it is able to validate the incoming SOAP request and throw a SOAP fault in case there are schema validation errors. So far so good.
Now I want to customise the SOAP Fault response in case of schema validation errors.
I don't want to send SOAP Fault at all, instead I would like to send something like below in the response body
<errorCode>123</errorCode>
<errorDescription>some error description</errorDescription>
Can any one please tell me how I can achieve this?
If you are exposing a SOAP web service and want to have a validation of incoming SOAP message against the schema and put custom message, then one of the best way is to use mulexml:schema-validation-filter
for example the following code :-
<mulexml:schema-validation-filter name="Schema_Validation" schemaLocations="yourSchema.xsd" returnResult="true" doc:name="Schema Validation" />
<flow name="ServiceFlow" >
<http:listener config-ref="HTTP_Listener_Configuration" path="mainData" doc:name="HTTP Connector"/>
<message-filter onUnaccepted="ValidationFailFlow" doc:name="filter to validate xml against xsd" throwOnUnaccepted="true" >
<filter ref="Schema_Validation"/>
</message-filter>
<cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" validationEnabled="true" doc:name="SOAP"/>
<component class="com.test.services.schema.maindata.v1.Impl.MainDataImpl" doc:name="JavaMain_ServiceImpl"/>
</flow>
and the create a sub flow to create your custom message
<errorCode>123</errorCode>
<errorDescription>some error description</errorDescription>
:-
<sub-flow name="ValidationFailFlow" >
<logger message="SOAP Request is not valid!!" level="INFO" doc:name="Logger"/>
<set-payload value="<errorCode>123</errorCode><errorDescription>Soap Validation fail!!!/errorDescription>" doc:name="Set Payload" mimeType="application/xml"/>
</sub-flow>
So now if validation is failing then it will route to your sub flow and show your custom message
note, you can create your custom message using set payload or Java class or XSLT or anything you wish :)
for more reference on mulexml:schema-validation-filter refer :- https://docs.mulesoft.com/mule-user-guide/v/3.7/schema-validation-filter

Securing SOAP webservice, wildfly, authentication error

I've got a jax-ws webservice which I need to secure. When I start wildfly everything seems to load nicely, but when I try to send a request via SOAPUI tool, the server returns Error with body element - unauthorized. Relevant parts of code below.
Webservice itself:
#WebService
#Stateless
#RolesAllowed("testrole")
#SecurityDomain("test-domain")
public class Test {
#WebMethod
#WebResult(name = "HelloResponse")
public String sayHello(#WebParam(name = "username") String name) {
return "Hello " + name;
}
}
standalone.xml:
<security-domain name="test-domain" cache-type="default">
<authentication>
<login-module code="org.jboss.security.auth.spi.UserRolesLoginModule"
flag="required">
<module-option name="userProperties" value="test-domain-users.properties" relative-to="jboss.server.config.dir"/>
<module-option name="rolesProperties" value="test-domain-roles.properties" relative-to="jboss.server.config.dir"/>
</login-module>
</authentication>
</security-domain>
jboss-web.xml:
<jboss-web>
<security-domain>test-domain</security-domain>
</jboss-web>
test-domain-users.properties and test-domain-roles.properties look like this:
testuser=testpassword, testuser=testrole

Consume a simple web service using mule

I am trying to consume a public service using Mule + apache cxf. The service is available at http://www.html2xml.nl/Services/Calculator/Version1/Calculator.asmx?WSDL
This is a very simple service which does basic arithmetic operations. I am trying to call the operation "Add" here. My mule configuration is as below
<flow name="calculator" doc:name="calculator">
<stdio:inbound-endpoint system="IN" doc:name="STDIO"/>
<custom-transformer class="com.calculator.transformer.CalculatorClient" doc:name="Java"/>
<outbound-endpoint address="http://localhost:28081/service/Calculator?WSDL" exchange-pattern="request-response" doc:name="HTTP">
<cxf:jaxws-client clientClass="com.calculator.wsdl.Calculator" enableMuleSoapHeaders="true" port="CalculatorHttpPost" wsdlLocation="classpath:/wsdl/Calculator.wsdl" operation="Add">
<cxf:inInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
</cxf:outInterceptors>
</cxf:jaxws-client>
</outbound-endpoint>
<transformer ref="CalculatorResponse" doc:name="Transformer Reference"/>
<mulexml:jaxb-object-to-xml-transformer name="CalculatortoXML" jaxbContext-ref="myJaxbCal" />
<stdio:outbound-endpoint system="OUT" doc:name="STDIO"/>
</flow>
Before calling the client class i added a transformer as below. This just sets the 2 numbers to add.
Code
package com.calculator.transformer;
import org.mule.api.MuleMessage;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractMessageTransformer;
import com.calculator.wsdl.Add;
public class CalculatorClient extends AbstractMessageTransformer {
#Override
public Object transformMessage(MuleMessage message, String outputEncoding)
throws TransformerException {
Add add= new Add();
add.setA(3);
add.setB(3);
return add;
}
}
Once i start mule i receive the error.Not sure what i am doing wrong.
ERROR 2014-01-16 01:09:46,237 [[weatherproject].calculator.stage1.02] org.mule.exception.DefaultMessagingExceptionStrategy:
Message : wrong number of arguments. Failed to route event via endpoint: org.mule.module.cxf.CxfOutboundMessageProcessor. Message payload is of type: Add
Code : MULE_ERROR--2
Exception stack is:
1. wrong number of arguments (java.lang.IllegalArgumentException)
sun.reflect.NativeMethodAccessorImpl:-2 (null)
2. wrong number of arguments. Failed to route event via endpoint: org.mule.module.cxf.CxfOutboundMessageProcessor. Message payload is of type: Add (org.mule.api.transport.DispatchException)
org.mule.module.cxf.CxfOutboundMessageProcessor:148 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transport/DispatchException.html)
Root Exception stack trace:
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
you have mentioned http://localhost:28081/service/Calculator?WSDL as your address and I suppose it should be http://localhost:28081/service/Calculator .
This post helped me to solve the problem
Mule SOAP client wrapper as parameter instead of object array
By using the JAXB bindings as suggested CXF will generate the wrapper objects.