Bug 1074368

Summary: Schema element generated from exception class doesn't honor @XmlElement annotation
Product: [JBoss] JBoss Enterprise Application Platform 6 Reporter: Kyle Lape <klape>
Component: Web ServicesAssignee: Jim Ma <ema>
Status: CLOSED EOL QA Contact: Rostislav Svoboda <rsvoboda>
Severity: medium Docs Contact:
Priority: urgent    
Version: 6.2.0CC: ema, jbliznak, kkhan
Target Milestone: DR9   
Target Release: EAP 6.4.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Known Issue
Doc Text:
A bug has been found in this release of JBoss EAP 6 wherein Schema generated from exception classes do not honor the @XmlElement annotation. This issue will be resolved in a future release of the product.
Story Points: ---
Clone Of: Environment:
Last Closed: 2019-08-19 12:45:19 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
simple webservice with annotated exception
none
Several usecases with various @XmlElement attributes none

Description Kyle Lape 2014-03-10 02:51:59 UTC
The schema element generated from exception class always has the nillable="true" attribute, and doesn't honor @XmlElement.

Comment 1 Scott Mumford 2014-04-22 23:23:41 UTC
Flagged as Known Issue as ticket unresolved at the time of writing the release note.

Comment 3 Jan Blizňák 2014-07-03 08:31:50 UTC
If I stick to the summary (comment #0), there should be two issues fixed:

1) schema element for field of exception type always has the nillable="true" attribute

2) schema element for field of exception type doesn't honor @XmlElement


ad1)

@WebFault
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class PairException extends Exception {

  private static final long serialVersionUID = 1L;

  public PairException(String message) {
    super(message);
    init();
  }

  private long age = 20L;
  private Pair<Integer, Boolean> info = new Pair<Integer, Boolean>(1, false);
  private List<Boolean> flags = null;

  //.... constructors, getters, setters
}

on 6.3.0.ER3 WSDL generated contains:

<xs:complexType name="PairException">
    <xs:sequence>
        <xs:element name="age" nillable="true" type="xs:long"/>
        <xs:element maxOccurs="unbounded" minOccurs="0" name="flags" type="xs:boolean"/>
        <xs:element name="info" nillable="true" type="tns:pair"/>
        <xs:element minOccurs="0" name="message" type="xs:string"/>
    </xs:sequence>
</xs:complexType>

on 6.3.0.ER8 WSDL generated contains:

<xs:complexType name="PairException">
    <xs:sequence>
        <xs:element minOccurs="0" name="age" type="xs:long"/>
        <xs:element maxOccurs="unbounded" minOccurs="0" name="flags" type="xs:boolean"/>
        <xs:element minOccurs="0" name="info" type="tns:pair"/>
        <xs:element minOccurs="0" name="message" type="xs:string"/>
    </xs:sequence>
</xs:complexType>



ad2)

@WebFault
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class MyException extends Exception
{

    private static final long serialVersionUID = -1L;

    @XmlElement(nillable = false, required = true)
    private String nonBoth = "i can't be either nil nor skipped";

    @XmlElement(nillable = false, required = false)
    private List<Integer> minZero = null;

    @XmlElement(nillable = true)
    private Integer nilInt = null;

    @XmlElement(nillable = true)
    private String nilString = null;

    @XmlElement(nillable = true, required = false)
    private List<String> minZeroNilString = null;

   //.... constructors, getters, setters

}

On 6.3.0.ER8 the generated WSDL contains:

<xs:element name="MyException" type="tns:MyException"/>
  <xs:complexType name="MyException">
    <xs:sequence>
      <xs:element minOccurs="0" name="message" type="xs:string"/>
      <xs:element maxOccurs="unbounded" minOccurs="0" name="minZero" type="xs:int"/>
      <xs:element maxOccurs="unbounded" minOccurs="0" name="minZeroNilString" type="xs:string"/>
      <xs:element minOccurs="0" name="nilInt" type="xs:int"/>
      <xs:element minOccurs="0" name="nilString" type="xs:string"/>
      <xs:element minOccurs="0" name="nonBoth" type="xs:string"/>
    </xs:sequence>
</xs:complexType>


From above testing, only the (1) is fixed.
So verification failed on 6.3.0.ER8 as there is only a partial fix.

Comment 4 Jan Blizňák 2014-07-03 08:49:58 UTC
Additional info:

the main part of fix is in <cxf>/rt/databinding/jaxb/src/main/java/org/apache/cxf/jaxb/JAXBSchemaInitializer.java 
method #addElement(...):

if (isArray) {
    el.setMinOccurs(0);
    el.setMaxOccurs(Long.MAX_VALUE);
} else {
    if (xmlElementAnno == null) {
        el.setMinOccurs(0);
        el.setNillable(false);
    } else {
        el.setNillable(xmlElementAnno.nillable());
        int minOccurs = xmlElementAnno.required() ? 1 : 0;
        el.setMinOccurs(minOccurs);
    }
}



>> if (xmlElementAnno == null) 
seems to be always true, like the field has no such annotation

Comment 5 Jim Ma 2014-07-04 05:28:26 UTC
Kyle, Can you please look at this issue if the fix for the first issue(exception type always has the nillable="true" attribute) as Jan pointed above has resolved customer issue ? If it works , I'd think we can mark this issue as resolved and make it included in release; we create another JBWS issue to track another issue(doesn't honor @XmlElement) ?

Comment 6 Kyle Lape 2014-07-21 16:55:38 UTC
Looking at the history of bz1040703, this BZ seemed to be opened based on a comment you made there (comment 11).  Therefore it's safe to split this into two BZs and fix the second half in a CP release if needed.

Comment 7 Jim Ma 2014-11-06 07:23:19 UTC
We have upgraded cxf to 2.7.13 and this fix is already included in EAP 6.4: https://bugzilla.redhat.com/show_bug.cgi?id=1157479.

Comment 8 Jan Blizňák 2014-12-02 12:59:44 UTC
After discussion with Jim, both cases stated in #c03 should be verified.

1) schema element for field of exception type always has the nillable="true" attribute
2) schema element for field of exception type doesn't honor @XmlElement


ad 1) was fixed back in 6.3.0.ER8, so that is fine with 6.4.0.DR11

ad 2) unable to get nillable, minOccurs and maxOccurs information from @XmlElement


It seems that these attributes are propageted to generated schema (example with public fields and no getters/setters, the version with private fields and annotated getters generates the same schema):


@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class AnnotatedPublicFieldsException extends Exception {

    public AnnotatedPublicFieldsException() {
    }

    public String requiredD_nillableD_String;

    @XmlElement(nillable = false, required = true)
    public String requiredT_nillableF_String;

    @XmlElement(nillable = false, required = false)
    public String requiredF_nillableF_String;

    @XmlElement(nillable = true, required = true)
    public String requiredT_nillableT_String;

    @XmlElement(nillable = true, required = false)
    public String requiredF_nillableT_String;

    @XmlElement(nillable = false, required = false)
    public List<Integer> requiredF_nillabelF_IntegerList;

    @XmlElement(nillable = true, required = false)
    public List<String> requiredF_nillabelT_StringList = null;

    @XmlElement(name="renamed_requiredD_nillableD_String")
    public String requiredD_nillableD_renamed_String;


<xs:element name="AnnotatedPublicFieldsException" type="tns:AnnotatedPublicFieldsException"/>
<xs:complexType name="AnnotatedPublicFieldsException">
  <xs:sequence>
    <xs:element minOccurs="0" name="##default" type="xs:string"/>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="##default" type="xs:int"/>
    <xs:element name="##default" type="xs:string"/>
    <xs:element name="##default" nillable="true" type="xs:string"/>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="##default" type="xs:string"/>
    <xs:element minOccurs="0" name="##default" nillable="true" type="xs:string"/>
    <xs:element minOccurs="0" name="message" type="xs:string"/>
    <xs:element minOccurs="0" name="renamed_requiredD_nillableD_String" type="xs:string"/>
    <xs:element minOccurs="0" name="requiredD_nillableD_String" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

So it reads the nillable/required attributes at least for simple type. The list obviously doesn't propagate the nillable attribute. And the obvious issue here is that the name of property is not generated properly unless set explicitely (by spec it should fallback to attribute name from Java http://docs.oracle.com/javaee/6/api/javax/xml/bind/annotation/XmlElement.html#name%28%29 ).

Comment 9 Jan Blizňák 2014-12-02 14:53:44 UTC
I tried to generate schema for the same also with RI JAXWS (https://jax-ws.java.net/2.2.8/) to generate schema with command:

./jaxws-ri/bin/wsgen.sh -d . -wsdl -cp /home/jbliznak/dev/ws/bz1074368/helloworld-ws/target/classes/ com.redhat.ws.HelloWorldServiceImpl


For shortened version  with public fields only from previous comment, it didn't process these fields:

<xs:complexType name="AnnotatedPublicFieldsException">
  <xs:sequence>
    <xs:element name="message" type="xs:string" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>


So I used the full version:

@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class AnnotatedGettersException extends Exception {

    public AnnotatedGettersException() {
    }

    private String requiredD_nillableD_String;

    private String requiredT_nillableF_String;

    private String requiredF_nillableF_String;

    private String requiredT_nillableT_String;

    private String requiredF_nillableT_String;

    private List<Integer> requiredF_nillabelF_IntegerList;

    private List<String> requiredF_nillabelT_StringList = null;

    private String requiredD_nillableD_renamed_String;

    private String requiredF_nillableT_renamed_String;

    private String requiredT_nillableF_renamed_String;

    public String getRequiredD_nillableD_String() {
        return requiredD_nillableD_String;
    }
    public void setRequiredD_nillableD_String(String requiredD_nillableD_String) {
        this.requiredD_nillableD_String = requiredD_nillableD_String;
    }

    @XmlElement(nillable = false, required = true)
    public String getRequiredT_nillableF_String() {
        return requiredT_nillableF_String;
    }
    public void setRequiredT_nillableF_String(String requiredT_nillableF_String) {
        this.requiredT_nillableF_String = requiredT_nillableF_String;
    }

    @XmlElement(nillable = false, required = false)
    public String getRequiredF_nillableF_String() {
        return requiredF_nillableF_String;
    }
    public void setRequiredF_nillableF_String(String requiredF_nillableF_String) {
        this.requiredF_nillableF_String = requiredF_nillableF_String;
    }

    @XmlElement(nillable = true, required = true)
    public String getRequiredT_nillableT_String() {
        return requiredT_nillableT_String;
    }
    public void setRequiredT_nillableT_String(String requiredT_nillableT_String) {
        this.requiredT_nillableT_String = requiredT_nillableT_String;
    }

    @XmlElement(nillable = true, required = false)
    public String getRequiredF_nillableT_String() {
        return requiredF_nillableT_String;
    }
    public void setRequiredF_nillableT_String(String requiredF_nillableT_String) {
        this.requiredF_nillableT_String = requiredF_nillableT_String;
    }

    @XmlElement(nillable = false, required = false)
    public List<Integer> getRequiredF_nillabelF_IntegerList() {
        return requiredF_nillabelF_IntegerList;
    }
    public void setRequiredF_nillabelF_IntegerList(List<Integer> requiredF_nillabelF_IntegerList) {
        this.requiredF_nillabelF_IntegerList = requiredF_nillabelF_IntegerList;
    }

    @XmlElement(nillable = true, required = false)
    public List<String> getRequiredF_nillabelT_StringList() {
        return requiredF_nillabelT_StringList;
    }
    public void setRequiredF_nillabelT_StringList(List<String> requiredF_nillabelT_StringList) {
        this.requiredF_nillabelT_StringList = requiredF_nillabelT_StringList;
    }

    @XmlElement(name="renamed_requiredD_nillableD_String")
    public String getRequiredD_nillableD_renamed_String() {
        return requiredD_nillableD_renamed_String;
    }
    public void setRequiredD_nillableD_renamed_String(String requiredD_nillableD_renamed_String) {
        this.requiredD_nillableD_renamed_String = requiredD_nillableD_renamed_String;
    }

    @XmlElement(name="renamed_requiredF_nillableT_String", nillable = true, required = false)
    public String getRequiredF_nillableT_renamed_String() {
        return requiredF_nillableT_renamed_String;
    }
    public void setRequiredF_nillableT_renamed_String(String requiredF_nillableT_renamed_String) {
        this.requiredF_nillableT_renamed_String = requiredF_nillableT_renamed_String;
    }

    @XmlElement(name="renamed_requiredT_nillableF_String", nillable = false, required = true)
    public String getRequiredT_nillableF_renamed_String() {
        return requiredT_nillableF_renamed_String;
    }
    public void setRequiredT_nillableF_renamed_String(String requiredT_nillableF_renamed_String) {
        this.requiredT_nillableF_renamed_String = requiredT_nillableF_renamed_String;
    }
}

And this is what I get:

<xs:complexType name="AnnotatedGettersException">
  <xs:sequence>
    <xs:element name="message" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredD_nillableD_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredD_nillableD_renamed_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredF_nillabelF_IntegerList" type="xs:int" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="requiredF_nillabelT_StringList" type="xs:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="requiredF_nillableF_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredF_nillableT_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredF_nillableT_renamed_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredT_nillableF_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredT_nillableF_renamed_String" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredT_nillableT_String" type="xs:string" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

And it looks like even RI JAXWS doesn't honor @XmlElement at all for Exception attributes, it gives the same result for the code above with @XmlElement annotations removed.

Comment 10 Jan Blizňák 2014-12-04 13:53:48 UTC
Created attachment 964677 [details]
simple webservice with annotated exception

I must set this to failedQA. Although @XmlElement attributes 'required' and 'nillable' are honored for Exception properties, it leads to 'name' be initialized with "##default" which leads to invalid schema and such webservice can't be then consumed.


Simple example:

@WebService(
        serviceName = "HelloWorldService",
        portName = "HelloWorld",
        endpointInterface = "com.redhat.ws.processable.HelloWorldService",
        targetNamespace = "http://www.redhat.com/HelloWorld"
)
public class HelloWorldService {
    @WebMethod
    public String get() throws AnnotatedException { return null; }
}

public class AnnotatedException extends Exception {

    public AnnotatedGettersException() {}

    private String aString;

    @XmlElement(nillable = true, required = true)
    public String getaString() {
        return aString;
    }

    public void setaString(String aString) {
        this.aString = aString;
    }
}


Schema:
<xs:complexType name="AnnotatedException">
  <xs:sequence>
    <xs:element name="##default" nillable="true" type="xs:string"/>
    <xs:element minOccurs="0" name="message" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

 
Just trying consume WS with SoapUI:
Error: The value '##default' is an invalid name.

Comment 11 Jan Blizňák 2014-12-04 14:14:35 UTC
Created attachment 964681 [details]
Several usecases with various @XmlElement attributes

I prepared small project with various combination of required/nillable/name attributes of @XmlElement.
I don't think that all of them are valid usecases, I just made the combinations I can think of to see the results. For comparison I always made pairs of Exception type and non-Exception type with the same fields/annotations, because non-Exception types seems to be processed correctly.

The project can generate two deployments. 
The first one is processable (WSDL and schema can be generated) by both JBossWS and RI JAX-WS. 
The second one is the case which (I would say) is not a valid usecase. It uses @XmlAccessorType(XmlAccessType.FIELD) and have annotated getters for private fields. It fails to be processed by both JBossWS and RI JAX-WS. I added it just to compare, because the same class written as Exception (just 'extends Exception') is processed by both implementations without failure.

This is what I did to compare JBossWS with RI JAX-WS:

0. download https://jax-ws.java.net/2.2.8/ & extract to <jaxws-ri-dir>
1. mvn -P processable install 
2. deploy <project-dir>/target/jboss-helloworld-ws.war to running EAP (I used 6.4.0.DR12)
3. curl http://localhost:8080/jboss-helloworld-ws?wsdl -o <output-dir>/jbossws.wsdl
4. <jaxws-ri-dir>/bin/wsgen.sh -d <output-dir> -wsdl -cp <project-dir>/target/classes/ com.redhat.ws.processable.HelloWorldServiceImpl
5. compare <output-dir>/jbossws.wsdl and <output-dir>/HelloWorldService_schema1.xsd

To test the failing scenario, just replace "processable" with "failing" in steps 1 & 4)

Comment 12 Jan Blizňák 2014-12-04 14:48:15 UTC
The issues/differences (ignoring the name="##default" stated before) I can see on JBossWS with schemas for non-exception and exception types:

public class AnnotatedGetters[Type | Exception extends Exception] {

    private List<Integer> requiredF_nillabelF_IntegerList;
    private List<String> requiredF_nillabelT_StringList = null;

    @XmlElement(nillable = false, required = false)
    public List<Integer> getRequiredF_nillabelF_IntegerList() {
        return requiredF_nillabelF_IntegerList;
    }

    @XmlElement(nillable = true, required = false)
    public List<String> getRequiredF_nillabelT_StringList() {
        return requiredF_nillabelT_StringList;
    }

    //setters, constructor, other properties..
}

<xs:complexType name="annotatedGettersType">
  <xs:sequence>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="requiredF_nillabelF_IntegerList" type="xs:int"/>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="requiredF_nillabelT_StringList" nillable="true" type="xs:string"/>
    <!-- skipped other elements -->
    </xs:sequence>
</xs:complexType>


<xs:complexType name="AnnotatedGettersException">
  <xs:sequence>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="##default" type="xs:int"/>
    <xs:element maxOccurs="unbounded" minOccurs="0" name="##default" type="xs:string"/>
    <xs:element minOccurs="0" name="message" type="xs:string"/>
    <!-- skipped other elements -->
  </xs:sequence>
</xs:complexType>

For the record RI JAX-WS schemas for the same:

<xs:complexType name="annotatedGettersType">
  <xs:sequence>
    <xs:element name="requiredF_nillabelF_IntegerList" type="xs:int" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="requiredF_nillabelT_StringList" type="xs:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    <!-- skipped other elements -->
  </xs:sequence>
</xs:complexType>

<xs:complexType name="AnnotatedGettersException">
  <xs:sequence>
    <xs:element name="message" type="xs:string" minOccurs="0"/>
    <xs:element name="requiredF_nillabelF_IntegerList" type="xs:int" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="requiredF_nillabelT_StringList" type="xs:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    <!-- skipped other elements -->
  </xs:sequence>
</xs:complexType>



We can see that the 'nillable' attribute for exception property of collection type seems to not be taken into account in JBossWS implementation.
As I already stated in comment #c9, RI JAX-WS does not process @XmlElement on exception properties, so it gives us only defaults. Furthermore it is not consistent in default attributes for element representing collection (always 'nillable="true"' for exception property vs. correctly honoring 'nillable' on POJO property).

Comment 15 Red Hat Bugzilla 2023-09-14 02:04:43 UTC
The needinfo request[s] on this closed bug have been removed as they have been unresolved for 1000 days