Thank you very much Daniel.
The XmlAdapter is fixing the issue. I've used the java_first_jaxws sample
in the CXF distribution as a template for the implementation.
But I have added a check for null value at the beginning of marshal and
unmarshal functions (to avoid NullPointerException):
public StringStringMap marshal(Map<String, String> entries) throws
Exception{
if (entries==null){
return null;
}
[...]
}
public Map<String,String> unmarshal(StringStringMap ssm) throws
Exception {
if (ssm==null){
return null;
}
[...]
}
Regards.
Johann
2011/12/8 Daniel Kulp <[email protected]>
>
> You've hit one of those "funny" things with JAXB that I've never quite
> understood. Officially, JAXB does not support Maps. However, the JAXB
> runtime does kind of handle them as long as you don't specifically tell
> JAXB
> about them by annotating it with and JAXB annotation. As soon as you do,
> it
> barfs.
>
> The only way to handle this is to write an XmlAdapter that would map the
> Map
> into a class that JAXB would understand and then wire that in with
> @XmlJavaTypeAdapter along with your XmlElement annotation.
>
> You can see an example of how to handle maps like that in the
> java_first_jaxws
> sample in the CXF distribution.
>
> Dan
>
>
> On Wednesday, December 07, 2011 10:10:05 AM Default SPAM wrote:
> > Hi all,
> >
> > I would like to submit a question since I have noticed an inconsistency
> > between WSDL generated by "org.apache.cxf.tools.java2ws.JavaToWS" and
> > response sent by CXF.
> >
> > > I have a service like this:
> > @WebService(endpointInterface = "be.eft.cbkv3.ws.WSExampleService",
> > serviceName = "WSExampleService")
> > public class WSExampleServiceImpl implements WSExampleService{
> > protected final Log log= LogFactory.getLog(this.getClass());
> >
> > public WSExample getExample(String exampleId){
> > return new WSExample();
> > };
> > }
> >
> > > This service is returning a WSExample that is a simple bean that
> > > contains
> >
> > a Map:
> >
> > public class WSExample implements Serializable{
> >
> > /** identifier field */
> > private Long id;
> >
> > /**
> > * @return Returns the id.
> > * @hibernate.id column = "ID" length = "19"
> > * generator-class = "native"
> > */
> > public Long getId() {
> > return id;
> > }
> > public void setId(Long id) {
> > this.id = id;
> > }
> >
> > /** An object containing all business data related to the auto-action
> > */
> > private Map<String,String> arguments;
> >
> > /**
> > * @hibernate.map cascade= "all" table= "CBK_AUTOACTIONARGS"
> > * @hibernate.key column= "CBK_AUTOACTIONID"
> > foreign-key="AAARG_CBKAAID_FK"
> > * @hibernate.index column= "NAME" type= "java.lang.String"
> > * @hibernate.element column= "VALUE" type= "java.lang.String"
> > * @return the arguments
> > */
> > public Map<String, String> getArguments() {
> > return arguments;
> > }
> > public void setArguments(Map<String, String> arguments) {
> > this.arguments = arguments;
> > }
> > }
> >
> > > The WSDL generated looks like:
> > <?xml version="1.0" encoding="UTF-8"?>
> > <wsdl:definitions name="ExampleService" targetNamespace="
> > http://ws.cbkv3.eft.be/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
> > xmlns:tns="http://ws.cbkv3.eft.be/" xmlns:xsd="
> > http://www.w3.org/2001/XMLSchema" xmlns:soap="
> > http://schemas.xmlsoap.org/wsdl/soap/">
> > <wsdl:types>
> > <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="
> > http://ws.cbkv3.eft.be/" attributeFormDefault="unqualified"
> > elementFormDefault="unqualified" targetNamespace="
> http://ws.cbkv3.eft.be/">
> > <xs:complexType name="wsExample">
> > <xs:sequence>
> > <xs:element name="arguments">
> > <xs:complexType>
> > <xs:sequence>
> > <xs:element maxOccurs="unbounded" minOccurs="0" name="entry">
> > <xs:complexType>
> > <xs:sequence>
> > <xs:element minOccurs="0" name="key" type="xs:string"/>
> > <xs:element minOccurs="0" name="value" type="xs:string"/>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:element>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:element>
> > <xs:element minOccurs="0" name="id" type="xs:long"/>
> > </xs:sequence>
> > </xs:complexType>
> > <xs:element name="getExample" type="getExample"/>
> > <xs:complexType name="getExample">
> > <xs:sequence>
> > <xs:element minOccurs="0" name="exampleId" type="xs:string"/>
> > </xs:sequence>
> > </xs:complexType>
> > <xs:element name="getExampleResponse" type="getExampleResponse"/>
> > <xs:complexType name="getExampleResponse">
> > <xs:sequence>
> > <xs:element minOccurs="0" name="return" type="wsExample"/>
> > </xs:sequence>
> > </xs:complexType>
> > </xs:schema>
> > </wsdl:types>
> > <wsdl:message name="getExampleResponse">
> > <wsdl:part name="parameters" element="tns:getExampleResponse">
> > </wsdl:part>
> > </wsdl:message>
> > <wsdl:message name="getExample">
> > <wsdl:part name="parameters" element="tns:getExample">
> > </wsdl:part>
> > </wsdl:message>
> > <wsdl:portType name="WSExampleService">
> > <wsdl:operation name="getExample">
> > <wsdl:input name="getExample" message="tns:getExample">
> > </wsdl:input>
> > <wsdl:output name="getExampleResponse"
> > message="tns:getExampleResponse">
> > </wsdl:output>
> > </wsdl:operation>
> > </wsdl:portType>
> > <wsdl:binding name="WSExampleServiceSoapBinding"
> > type="tns:WSExampleService">
> > <soap:binding style="document" transport="
> > http://schemas.xmlsoap.org/soap/http"/>
> > <wsdl:operation name="getExample">
> > <soap:operation soapAction="" style="document"/>
> > <wsdl:input name="getExample">
> > <soap:body use="literal"/>
> > </wsdl:input>
> > <wsdl:output name="getExampleResponse">
> > <soap:body use="literal"/>
> > </wsdl:output>
> > </wsdl:operation>
> > </wsdl:binding>
> > <wsdl:service name="ExampleService">
> > <wsdl:port name="WSExampleServiceImplPort"
> > binding="tns:WSExampleServiceSoapBinding">
> > <soap:address location="
> http://localhost:9090/WSExampleServiceImplPort
> > "/>
> > </wsdl:port>
> > </wsdl:service>
> > </wsdl:definitions>
> >
> > > That's looks more or less fine, but once we receive a response from the
> >
> > server when there is no arguments (the WSExample#arguments is null) then
> we
> > receive something like:
> > <soapenv:Envelope xmlns:soapenv="
> http://schemas.xmlsoap.org/soap/envelope/"
> > xmlns:ws="http://ws.cbkv3.eft.be/">
> > <soapenv:Header/>
> > <soapenv:Body>
> > <ws:getExampleResponse>
> > <return>
> > <id>myId</id>
> > </return>
> > </ws:getExampleResponse>
> > </soapenv:Body>
> > </soapenv:Envelope>
> >
> > > This response is not "compliant" with the WSDL since <arguments> should
> >
> > be present, we should receive from server (running CXF):
> > <soapenv:Envelope xmlns:soapenv="
> http://schemas.xmlsoap.org/soap/envelope/"
> > xmlns:ws="http://ws.cbkv3.eft.be/">
> > <soapenv:Header/>
> > <soapenv:Body>
> > <ws:getExampleResponse>
> > <return>
> > <arguments>
> > </arguments>
> > <id>myId</id>
> > </return>
> > </ws:getExampleResponse>
> > </soapenv:Body>
> > </soapenv:Envelope>
> >
> > To workaround this, I can explicitely (and manually) add 'minOccurs="0"
> > nillable="true"' to 'arguments' element:
> > <xs:element name="arguments" minOccurs="0" nillable="true">
> >
> > That way, my WSDL is consistent with what CXF is returning.
> >
> > But, it means we also cannot rely on the WSDL dynamically generated (with
> > ?wsdl) once my service is deployed.
> >
> > I tried to add "@XmlElement(required=false, nillable=true)" annotation to
> > getArguments() function but I got an error like:
> > Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2
> > counts of IllegalAnnotationExceptions
> > [java] java.util.Map is an interface, and JAXB can't handle
> interfaces.
> > [java] this problem is related to the following location: [java]
> > at java.util.Map
> > [java] at public java.util.Map
> > be.eft.cbkv3.data.schema.WSExample.getArguments()
> > [java] at be.eft.cbkv3.data.schema.WSExample
> > [java] java.util.Map does not have a no-arg default constructor.
> > [java] this problem is related to the following location:
> > [java] at java.util.Map
> > [java] at public java.util.Map
> > be.eft.cbkv3.data.schema.WSExample.getArguments()
> > [java] at be.eft.cbkv3.data.schema.WSExample
> > [java] at
> >
> com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(Illega
> > lAnnotationsException.java:106) [...]
> >
> > It sounds logic that if "arguments can be null" (and it can) we can get
> > SOAP response with no <arguments> tag then CXF response sounds correct to
> > me.
> > But WSDL generated should then contains <... minOccurs="0"
> nillable="true">
> > instead of nothing (knowing that when such arguments are not specified,
> > defaults are <... minOccurs="1" nillable="false">.
> >
> > Any idea how to fix this inconsistency?
> >
> > Regards.
> > Johann
> >
> > NB: Tried with CXF 2.2.9 and CXF 2.5.0.
> --
> Daniel Kulp
> [email protected] - http://dankulp.com/blog
> Talend Community Coder - http://coders.talend.com
>