1
votes

I am trying to learn about JAX-WS, in particular how to use a complex type. In all three books on Java EE that I have access to, they mention that it is possible, but do not give any example... Strangely, neither search on the web finds one that is complete - including both service and client, but maybe I just cannot locate it.

Here is the service class:

package userobj;    
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;

@WebService(serviceName = "UserObj")
public class UserObj 
{    
    @WebMethod(operationName = "sum")
    public int sum(@WebParam(name = "obj") Two obj) 
    {
        return obj.getA() + obj.getB();
    }
}

and the class for the corresponding complex type Two:

package userobj;
public class Two 
{           
        private int a, b;
        public int getA() { return a; }
        public void setA(int newA) { a = newA; }
        public int getB() { return b; }
        public void setB(int newB) { b = newB; }
}

When I try to use this in a client webservice application, Glassfish4.1 automatically generates all the classes from the WSDL, but the generated class Two looks like this:

package uowsapp;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for two complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="two">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "two")
public class Two {


}

If I manually insert the content of the original class Two to this one, then the application works as expected: UserObjWSApp, but this is probably not intended use case... because Clean&Build on this project regenerates the Two.java and breaks the project. Is there some way I can make sure that Netbeans8.0.2 will generate a proper complex type from the WSDL that it generated itself?

As suggested by maress in his answer, the Two class should follow the requirements of JAXB, however, modifying it like this:

package userobj;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="Two")
public class Two 
{
        @XmlElement
        private int a;
        @XmlElement
        private int b;

        public int getA() { return a; }
        public void setA(int newA) { a = newA; }
        public int getB() { return b; }
        public void setB(int newB) { b = newB; }
}

results in the following exception during the app with service on deploy, and the deploy fails:

com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Class has two properties of the same name "a"
    this problem is related to the following location:
        at public int userobj.Two.getA()
        at userobj.Two
        at public userobj.Two userobj.jaxws.Sum.obj
        at userobj.jaxws.Sum
    this problem is related to the following location:
        at private int userobj.Two.a
        at userobj.Two
        at public userobj.Two userobj.jaxws.Sum.obj
        at userobj.jaxws.Sum

and the same for the b property. Then, commenting-out all the getters and setters and making the members public so that they can be accessed directly results in the same behavior as before - the generated class Two has no members. And the same happens when I move the @XmlElement annotation in front of the getA() and getB() methods: deploy ok, WSDL contains a, b, but generated Two does not. Any ideas what else could one try, please?

3

3 Answers

1
votes

jax-ws uses jaxb under the hood to generate the schema.

Hence, your classes must be jaxb compliant for them to generate a valid complextype.

For your two, it is very simple:

@XmlRootElement(name="Two")
public class Two {

  @XmlElement
  private int a;
  @XmlElement
  private int b;

  public int getA() {return a;}
  public void setA(int a) {this.a = a;}
  public int getB() {return b;}
  public void setB(int b) {this.b = b}
}
1
votes

Following your modification, the problem you're now facing is related to the @XmlAccessorType annotation (or lack thereof) on your data types. The annotation determines how JAXB is going to access, and therefore be able to map the fields on your defined type. The annotation provides four options, defined by the @XmlAccessType annotation : FIELD, NONE, PROPERTY and PUBLIC_MEMBER.

The default is PUBLIC_MEMBER and what this means to JAXB is : map all non static, non transient fields and all javabean-standard property types (i.e. getXXX and setXXX pairs). Because you have both int a; and getA(), it amounts to having 2 fields of the same name - a.

What you now need to do is define an XmlAccessorType to clear up the confusion:

@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name="Two")
public class Two {
//
}

I've chosen PROPERTY here because it's more in line with the Javabeans convention, but it also confers other advantages, as outlined in Blaise Doughan's blog post(granted these do not really apply to your use case)

0
votes

The code was correct, the annotation @XMLRootElement was not required, neither the @XmlElement one.

The only reason why the NetBeans did not generate the proper class Two with the fields and accessors was that it was using an old copy of WSDL. I did not realize that Clean&Build - even though it deletes the old generated source code and regenerates it anew - does not download a current version of WSDL. It always used an old version of WSDL from my very first try, where the fields a and b were not included. To re-download the WSDL, open the project in Project Browser - open "Web Service References" item and right-click the specific web service reference (in this case UserObj) and say Refresh... and tick the "also WSDL..." checkbox in the upcoming dialog

With refreshing the WSDL, both the original version without JAXB annotations and the version with JAXB annotations worked well, and generated a complete class Two:

package uowsapp;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for two complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="two">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="a" type="{http://www.w3.org/2001/XMLSchema}int"/>
 *         &lt;element name="b" type="{http://www.w3.org/2001/XMLSchema}int"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "two", propOrder = {
    "a",
    "b"
})
public class Two {

    protected int a;
    protected int b;

    /**
     * Gets the value of the a property.
     * 
     */
    public int getA() {
        return a;
    }

    /**
     * Sets the value of the a property.
     * 
     */
    public void setA(int value) {
        this.a = value;
    }

    /**
     * Gets the value of the b property.
     * 
     */
    public int getB() {
        return b;
    }

    /**
     * Sets the value of the b property.
     * 
     */
    public void setB(int value) {
        this.b = value;
    }

}