Hi,
I found the cause of the problem to be a bug in this method in CXF (I have
version 2.4.1):
private Map<String, QName> createPayloadEleOpNameMap(BindingInfo
bindingInfo) {
Map<String, QName> payloadElementMap = new java.util.HashMap<String,
QName>();
for (BindingOperationInfo bop : bindingInfo.getOperations()) {
SoapOperationInfo soi =
(SoapOperationInfo)bop.getExtensor(SoapOperationInfo.class);
if (soi != null) {
if ("document".equals(soi.getStyle())) {
// if doc
if (bop.getOperationInfo().getInput() != null
&&
!bop.getOperationInfo().getInput().getMessageParts().isEmpty()) {
QName qn =
bop.getOperationInfo().getInput().getMessagePartByIndex(0)
.getElementQName();
payloadElementMap.put(qn.toString(),
bop.getOperationInfo().getName());
}
} else if ("rpc".equals(soi.getStyle())) {
// if rpc
payloadElementMap.put(bop.getOperationInfo().getName().toString(),
bop.getOperationInfo()
.getName());
}
}
}
return payloadElementMap;
}
The problem is that it requires the SoapOperationInfo to have a style
attribute, but in the W3C spec for WSDL it says the style attribute on the soap
operation is optional, specifically ' If the attribute is not specified, it
defaults to the value specified in the soap:binding element. If the
soap:binding element does not specify a style, it is assumed to be "document".'
So the code needs to check if the soi has a style and if not read it from the
binding and if not then set it as "document". This is not a problem in the
WSDLs generated by CXF (as I found out with a HelloWorld test) because it
creates these optional style attributes, but since W3C says people can generate
WSDLs without these (and I ran into one) I think it's worth fixing.
Unless someone disagrees, I'll create a defect for this and contribute a patch.
While reading code and stepping through the debugger to find out where this was
going wrong, I also found that if you use a SOAPMessage instead of a Source
then the following function fails (ignoring the exception) and your
ws-addressing action doesn't get set either- if you have any whitespace after
the soap:body element before your first payload element:
private String getPayloadElementName(SOAPMessage soapMessage) {
try {
SOAPElement element =
(SOAPElement)soapMessage.getSOAPBody().getChildElements().next();
return new QName(element.getNamespaceURI(),
element.getLocalName()).toString();
} catch (Exception e) {
//ignore
}
return null;
}
This fails because the .next() call at the end gets a text node instead of an
element object so the cast fails. So inexplicably your ws-addressing action
header doesn't get set in this case either. Unless someone disagrees, I'll
create a defect for this as well and contribute a patch.
Thanks so much for your help! I wouldn't have gone looking and found this out
without knowing it *should* work.
Jesse
-----Original Message-----
From: Jesse Pangburn
Sent: Wednesday, August 17, 2011 11:17 AM
To: 'Daniel Kulp'; [email protected]
Subject: RE: dispatch api with ws-addressing
Hi Dan,
Thanks for the suggestion, I'm trying to figure out one of those cases. I
changed the code to this to try a DOMSource instead:
Dispatch<Source> disp = service.createDispatch(new
QName("urn:ihe:iti:xds-b:2007", "DocumentRegistry_Port_Soap12"), Source.class,
Service.Mode.PAYLOAD, new AddressingFeature());
InputStream is =
IBServer.class.getResourceAsStream("RegistryQuery.xml");
Document doc = XMLUtils.parse(is);
DOMSource reqMsg = new DOMSource(doc);
Source response = disp.invoke(reqMsg);
DOMSource also sent the same wrong action header. I see that I get back a
StaxSource (CXF StaxSource, not javax StAXSource) in this case so I tried using
that as the input next:
Dispatch<Source> disp = service.createDispatch(new
QName("urn:ihe:iti:xds-b:2007", "DocumentRegistry_Port_Soap12"), Source.class,
Service.Mode.PAYLOAD, new AddressingFeature());
InputStream is =
IBServer.class.getResourceAsStream("RegistryQuery.xml");
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
XMLStreamReader xmlStreamReader =
xmlInputFactory.createXMLStreamReader(is, "UTF-8");
StaxSource staxSource = new StaxSource(xmlStreamReader);
Source response = disp.invoke(staxSource);
StaxSource also sent the same wrong action header. I also tried SAXSource:
InputSource inputSource = new InputSource(is);
SAXSource saxSource = new SAXSource(inputSource);
Source response = disp.invoke(saxSource);
And lastly I tried javax's StAXSource:
//StaxSource staxSource = new StaxSource(xmlStreamReader);
StAXSource staxSource = new StAXSource(xmlStreamReader);
Source response = disp.invoke(staxSource);
I thought maybe one of these was one of the cases you referred to, but assume I
misunderstood or am doing something else wrong. Or maybe this only works in
soap 1.1 wsdls or something?
Thanks,
Jesse
-----Original Message-----
From: Daniel Kulp [mailto:[email protected]]
Sent: Wednesday, August 17, 2011 10:29 AM
To: [email protected]
Cc: Jesse Pangburn
Subject: Re: dispatch api with ws-addressing
On Wednesday, August 17, 2011 12:15:53 PM Jesse Pangburn wrote:
> Hi,
> Thanks for the suggestion, that will work. I was hoping there was some way
> for CXF to do it automatically from the WSDL and the message body, but this
> manual step is at least simple and makes sense.
In SOME cases it can, but I'm not sure if StreamSource is one of them.
Another option is:
disp.getRequestContext().put(MessageContext.WSDL_OPERATION,
new QName(ns, "myOperation")
which will tell CXF what operation that dispatch is mapped to.
One note: you may want to switch from StreamSource to just "Source" if
possible. StreamSource is really the worse of the possible source types from
a performance standpoint. On the incoming side, we pretty much suck the
message into a byte[] or file and re-wrapper that with a new parser. Kind of
crappy. If you do Provider<Source>, you would likely get a subclass of
SAXSource which would allow us to maintain some level of streaming.
Dan
>
> Thank you,
> Jesse
>
> -----Original Message-----
> From: Aki Yoshida [mailto:[email protected]]
> Sent: Wednesday, August 17, 2011 2:09 AM
> To: [email protected]
> Subject: Re: dispatch api with ws-addressing
>
> Hi,
> you need to set the soap action in the dispatch client's request context.
> In your case, you need to add the following line:
>
> disp.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY,
> "urn:ihe:iti:2007:RegistryStoredQuery") ;
>
> Regards, aki
>
> 2011/8/17 Jesse Pangburn <[email protected]>:
> > Hi,
> > I'm trying to use the dispatch API and while I can make the service call
> > ok, the ws-address "action" header is wrong. Here's my code: URL
> > wsdlURL = new
> > URL("file:/home/jpangburn/Desktop/IHE/XDSb.Support.Materials.v9/wsdl/XD
> > S.b_DocumentRegistry.wsdl"); Service service = Service.create(wsdlURL,
> > new QName("urn:ihe:iti:xds-b:2007", "DocumentRegistry_Service"));
> > Dispatch<StreamSource> disp = service.createDispatch(new
> > QName("urn:ihe:iti:xds-b:2007", "DocumentRegistry_Port_Soap12"),
> > StreamSource.class, Service.Mode.PAYLOAD, new AddressingFeature());
> > InputStream is =
> > IBServer.class.getResourceAsStream("RegistryQuery.xml"); StreamSource
> > request = new StreamSource(is);
> > StreamSource response = disp.invoke(request);
> >
> > Basically, I try to create a service from a WSDL with the service QName,
> > then create a Dispatch from the port's QName while passing "new
> > AddressingFeature()" to turn on WS-Addressing. The action header comes
> > out like this: <Action
> > xmlns="http://www.w3.org/2005/08/addressing">http://cxf.apache.org/jaxw
> > s/dispatch/DocumentRegistry_PortType/InvokeRequest</Action>
> >
> > This is wrong because the SOAP12 binding's operation has the following:
> > <soap12:operation soapAction="urn:ihe:iti:2007:RegistryStoredQuery"/>
> >
> > So the header should be:
> > <Action
> > xmlns="http://www.w3.org/2005/08/addressing">urn:ihe:iti:2007:RegistryS
> > toredQuery</Action>
> >
> > All I can guess is that the Dispatch API doesn't know which operation
> > I'm calling so it makes up this generic action header for the port
> > instead of looking at the message I'm sending, determining the
> > operation called, and grabbing the correct soap action to use in the
> > action header. Am I doing something wrong or is this a limitation in
> > the Dispatch API's support for WS-Addressing?
> >
> > Thanks,
> > Jesse
--
Daniel Kulp
[email protected]
http://dankulp.com/blog
Talend - http://www.talend.com