Java Web services: JAXB and JAX-WS in Axis2

Use Java-standard JAXB 2.x and JAX-WS 2.x technologies for Axis2 Web services

Apache Axis2 supports a range of data-binding technologies, including the official Java™ standard, JAXB 2.x. Axis2 also supports the Java standard for Web service configuration, JAX-WS 2.x, as an alternative to its own custom configuration technique. Dennis Sosnoski continues his Java Web services column series by demonstrating how you can use each of these Java standards with Axis2 and discussing some of the limitations of Axis2's current support for them.

Share:

Dennis Sosnoski, Architecture Consultant and Trainer, Sosnoski Software Solutions, Inc.

Author photoDennis Sosnoski is a consultant and trainer specializing in Java-based XML and Web services. His professional software development experience spans more than 30 years, with the last 10 focused on server-side XML and Java technologies. Dennis is the lead developer of the open source JiBX XML Data Binding framework and the associated JiBX/WS Web services framework, as well as a committer on the Apache Axis2 Web services framework. He was also one of the Expert Group members for the JAX-WS 2.0 and JAXB 2.0 specifications. The material for the Java Web Services series is based on Dennis' training classes.



15 September 2009

Also available in Chinese Russian Vietnamese Portuguese

About this series

Web services are a crucial part of Java technology's role in enterprise computing. In this series of articles, XML and Web services consultant Dennis Sosnoski covers the major frameworks and technologies that are important to Java developers using Web services. Follow the series to stay informed of the latest developments in the field and aware of how you can use them to aid your programming projects.

The original Apache Axis was based on the first Java standard for Web services, JAX-RPC. This turned out not to be a great approach, because JAX-RPC constrained the internal design of the Axis code and contributed to both performance issues and a lack of flexibility. JAX-RPC also made some assumptions about the direction of Web services development that turned out to be wrong.

By the time the Axis2 effort was started, a replacement for JAX-RPC was already in the works, so Axis2 was designed to be flexible enough to implement support for the replacement Web services standard on top of the base framework. Recent versions of Axis2 have implemented support for both the JAXB 2.x Java XML data-binding standard and the JAX-WS 2.x Java Web services standard that replaced JAX-RPC. This article shows how to use JAXB and JAX-WS with Axis2 and identifies some of the limitations of Axis2's current support for these standards.

JAXB in Axis2

Develop skills on this topic

This content is part of a progressive knowledge path for advancing your skills. See Building and deploying JAX-WS web services

Axis2 implements support for JAXB 2.x as one of the data-binding alternatives you can choose when generating code from a Web Services Description Language (WSDL) service definition with WSDL2Java. (See "Java Web Services: Axis2 Data Binding" for a discussion of the other main alternatives.) As with most of the other alternatives, generating code from WSDL using JAXB 2.x creates both a set of linkage classes and a set of data model classes. The linkage classes, including a client-side stub and a server-side message receiver, interface between your application code and Axis2. The data model classes represent the actual message data.

JAXB 2.x uses annotations in the data model classes to control how data is converted to and from XML. The annotations approach allows you to use different JAXB implementations at run time without needing to change your source code or recompile your classes. It's up to the JAXB implementation to access the annotation information from the data model classes and apply these annotations when converting to and from XML.

The code download (see Download) provides a sample application to demonstrate JAXB usage in Axis2, in the jaxb directory. This application is another version of the simple library-management service used in previous articles in this series (including the data-binding comparison in "Axis2 Data Binding"). The WSDL service definition defines four operations:

  • getBook to retrieve the details for a particular book identified by International Standard Book Number (ISBN)
  • getBooksByType to retrieve the details for all books of a particular type
  • getTypes to find the types of books available
  • addBook to add a new book to the library

Listing 1 shows a heavily edited version of the WSDL, with only the portions relevant to the getBook operation included:

Listing 1. Library service WSDL
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
  
    <schema elementFormDefault="qualified"
        targetNamespace="http://ws.sosnoski.com/library/wsdl"
        xmlns="http://www.w3.org/2001/XMLSchema">
      
      <import namespace="http://ws.sosnoski.com/library/types"
          schemaLocation="types.xsd"/>
        
      <element name="getBook">
        <complexType>
          <sequence>
            <element name="isbn" type="string"/>
          </sequence>
        </complexType>
      </element>
      
      <element name="getBookResponse">
        <complexType>
          <sequence>
            <element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
          </sequence>
        </complexType>
      </element>
      ...
    
    </schema>

  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>

  <wsdl:message name="getBookResponse">
    <wsdl:part element="wns:getBookResponse" name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">

    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...

  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">

    <wsdlsoap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operation name="getBook">
    
      <wsdlsoap:operation soapAction="urn:getBook"/>
      
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      
    </wsdl:operation>
    ...

  </wsdl:binding>

  <wsdl:service name="jaxb-library">

    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location="http://localhost:8080/axis2/services/jaxb-library"/>
    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

Axis2's JAXB support is supposed to extend to generating unwrapped operation methods (where values wrapped within a message are converted to method parameters for programming convenience — again, see "Java Web Services: Axis2 Data Binding" for a discussion of wrapped vs. unwrapped interfaces). But the unwrapped support using the WSDL2Java tool does not work for this example in either the current Axis2 code or the last few releases. At least for now, wrapped operation methods are the only way to use JAXB with Axis2 code generation (but see the JAX-WS discussion, shortly, for an alternative approach). With the wrapped-operation interface, each service method takes a single object parameter matching the input message for the operation and returns an object matching the output message for the operation.

The supplied code gives the actual implementation of both the service and a test client, set up to work with the classes generated by running WSDL2Java. As with the sample code for previous articles in this series, the download includes build.properties and build.xml files used to build the sample with Apache Ant (in the jaxb directory). You first need to edit the build.properties file to set the path to your Axis2 installation (and to change other settings, if necessary for your system). Then you can just type ant on a console open to the jaxb directory to run WSDL2Java, compile both supplied and generated code, and build the AAR file for server deployment. To try it out, first deploy the generated AAR file to your Axis2 server installation and then type ant run on the console.

Client-side JAXB usage

The test client creates an instance of the service stub using service endpoint parameters passed in as command-line arguments, and then executes a sequence of five service calls:

  1. Get the information for a book.
  2. Get the types of books in the library.
  3. Add a book to the library (which fails if the book is already present, as will happen when the client is run more than once without restarting the server).
  4. If the last step succeeded, try to add another book with the same ISBN (which should always fail).
  5. Get the information for all books of a particular type.

Listing 2 shows the complete test client code. You can see the wrapped nature of the service interface in the wrapper objects used for each operation, such as the GetTypes object required on a call to the getTypes operation (even though there is no input data for this operation) and the GetTypesResponse object returned by the call.

Listing 2. JAXB test client code
public class WebServiceClient
{
    public static void main(String[] args) throws Exception {
        
        // check for required command line parameters
        if (args.length < 3) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.jaxb.WebServiceClient host port path");
            System.exit(1);
        }
        
        // create the client stub
        String target = "http://" + args[0] + ":" + args[1] + args[2];
        System.out.println("Connecting to " + target);
        JaxbLibraryStub stub = new JaxbLibraryStub(target);
        
        // retrieve a book directly
        String isbn = "0061020052";
        GetBook gb = new GetBook();
        gb.setIsbn(isbn);
        GetBookResponse gbr = stub.getBook(gb);
        BookInformation book = gbr.getGetBookReturn();
        if (book == null) {
            System.out.println("No book found with ISBN '" + isbn + '\'');
        } else {
            System.out.println("Retrieved '" + book.getTitle() + '\'');
        }
        
        // retrieve the list of types defined
        GetTypesResponse gtr = stub.getTypes(new GetTypes());
        List<TypeInformation> types = gtr.getGetTypesReturn();
        System.out.println("Retrieved " + types.size() + " types:");
        for (int i = 0; i < types.size(); i++) {
            TypeInformation type = types.get(i);
            System.out.println(" '" + type.getName() + "' with " +
                type.getCount() + " books");
        }
        
        // add a new book
        String title = "The Dragon Never Sleeps";
        isbn = "0445203498";
        try {
            AddBook ab = new AddBook();
            ab.setType("scifi");
            ab.setIsbn(isbn);
            ab.getAuthor().add("Cook, Glen");
            ab.setTitle(title);
            stub.addBook(ab);
            System.out.println("Added '" + title + '\'');
            title = "This Should Not Work";
            ab.setTitle(title);
            stub.addBook(ab);
            System.out.println("Added duplicate book - should not happen!");
        } catch (AddDuplicateFault e) {
            System.out.println("Failed adding '" + title +
                "' with ISBN '" + isbn + "' - matches existing title '" +
                e.getFaultMessage().getBook().getTitle() + '\'');
        }
        
        // get all books of a type
        GetBooksByType gbbt = new GetBooksByType();
        gbbt.setType("scifi");
        GetBooksByTypeResponse gbbtr = stub.getBooksByType(gbbt);
        List<BookInformation> books = gbbtr.getGetBooksByTypeReturn();
        System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
        for (int i = 0; i < books.size(); i++) {
            System.out.println(" '" + books.get(i).getTitle() + '\'');
        }
    }
}

If you compare Listing 2 with the client code examples in "Java Web Services: Axis2 Data Binding," you'll see that it's very similar to the JiBX and Axis Data Binding (ADB) wrapped examples, mainly differing in that the JAXB wrapper classes use Java 5 typed lists rather than arrays (an alternative that is also supported by JiBX data binding, but not by ADB).

Server-side usage

The server-side code for the library service consists of a pair of classes, one that actually implements the library handling and another that adapts to the service interface expected by Axis2. The actual implementation code is almost identical across the different data bindings, with only minor changes as needed for the generated application data model representations. Listing 3 shows the more interesting service-interface class. As on the client, the wrapped interface requires the application code to extract data from received wrapper objects and construct wrapper objects to be sent.

Listing 3. JAXB server code
public class JaxbLibraryImpl extends JaxbLibrarySkeleton
{
    private final BookServer m_server;
    
    public JaxbLibraryImpl() {
        m_server = new BookServer();
    }
    
    public AddBookResponse addBook(AddBook req) throws AddDuplicateFault {
        BookInformation prior = m_server.getBook(req.getIsbn());
        if (prior == null) {
            BookInformation book = new BookInformation();
            book.getAuthor().addAll(req.getAuthor());
            book.setIsbn(req.getIsbn());
            book.setTitle(req.getTitle());
            book.setType(req.getType());
            AddBookResponse rsp = new AddBookResponse();
            rsp.setAddBookReturn(m_server.addBook(book));
            return rsp;
        } else {
            AddDuplicateFault e =
                new AddDuplicateFault("Book already present with matching ISBN");
            AddDuplicate ad = new AddDuplicate();
            ad.setBook(prior);
            e.setFaultMessage(ad);
            throw e;
        }
    }

    public GetBookResponse getBook(GetBook req) {
        BookInformation book = m_server.getBook(req.getIsbn());
        GetBookResponse rsp = new GetBookResponse();
        rsp.setGetBookReturn(book);
        return rsp;
    }

    public GetBooksByTypeResponse getBooksByType(GetBooksByType req) {
        GetBooksByTypeResponse rsp = new GetBooksByTypeResponse();
        rsp.getGetBooksByTypeReturn().addAll(m_server.getBooksByType(req.getType()));
        return rsp;
    }

    public GetTypesResponse getTypes(GetTypes req) {
        GetTypesResponse rsp = new GetTypesResponse();
        rsp.getGetTypesReturn().addAll(m_server.getTypes());
        return rsp;
    }
}

"Java Web Services: Axis2 Data Binding" doesn't show the server interface code for the different data bindings in the article, but if you compare Listing 3 with the actual code downloads from that article, you'll see that it closely matches the ADB and JiBX wrapped samples, again differing only by the use of Java 5 typed lists rather than arrays.

JAXB data model classes

Listing 4 shows some of the JAXB data model classes generated by running WSDL2Java (with most of the generated comments deleted, except for a couple left in as representative samples). The generated data model classes are the same for client and server, even though they're generated separately by the project build. The classes shown are those used for the getBook call in both the Listing 2 client code and the Listing 3 server code. The annotations (shown in bold) on each class definition and most of the field definitions supply configuration information that JAXB uses to control the conversion of objects to and from XML.

Listing 4. JAXB data model classes
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "isbn"
})
@XmlRootElement(name = "getBook")
public class GetBook {

    @XmlElement(required = true)
    protected String isbn;

    /**
     * Gets the value of the isbn property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getIsbn() {
        return isbn;
    }

    /**
     * Sets the value of the isbn property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setIsbn(String value) {
        this.isbn = value;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookInformation", propOrder = {
    "author",
    "title"
})
public class BookInformation {

    protected List<String> author;
    @XmlElement(required = true)
    protected String title;
    @XmlAttribute(required = true)
    protected String type;
    @XmlAttribute(required = true)
    protected String isbn;

    public List<String> getAuthor() {
        if (author == null) {
            author = new ArrayList<String>();
        }
        return this.author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String value) {
        this.title = value;
    }

    public String getType() {
        return type;
    }

    public void setType(String value) {
        this.type = value;
    }
    
    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String value) {
        this.isbn = value;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "getBookReturn"
})
@XmlRootElement(name = "getBookResponse")
public class GetBookResponse {

    protected BookInformation getBookReturn;

    public BookInformation getGetBookReturn() {
        return getBookReturn;
    }

    public void setGetBookReturn(BookInformation value) {
        this.getBookReturn = value;
    }
}

The @XmlAccessorType annotation is used on a package or class level to control how values are accessed from the class — as all fields, as all properties with get/set access methods, as only public fields and properties, or only when specified by an individual annotation. The @XmlType annotation is used on a class or enum definition matching a schema type to tell JAXB the name and namespace of the schema type (if any), the order of values in the type representation, and optionally how to construct instances of the class using a factory method. The @XmlRootElement annotation is used on a class or enum definition matching a global-element definition to give the name and namespace of the global element. The @XmlElement and @XmlAttribute annotations are used on values (as fields, or as JavaBean property methods) to give the element or attribute name and other characteristics.

All the annotations used by JAXB are in the javax.xml.bind.annotation package, including many other annotations beyond those used in code generated for this simple sample. JAXB supports both generating code from schemas, as in this case, and starting from code. Some of the annotations and options (such as those dealing with object factories and with serializer/deserializer methods) are only used when starting from code.

JAXB issues in Axis2

WSDL2Java calls the XJC binding compiler included in the JAXB reference implementation in order to generate the data model code, so in most respects the data model code generation is independent of Axis2. You'd generate the same data model if you ran the JAXB XJC binding compiler directly on the schemas used by a Web service. Unfortunately, the impedance matching between WSDL2Java and XJC is not always perfect, and that leads to some problems.

One problem relates to how schemas are structured in WSDL documents. The original form of the WSDL for the library service used a single document that incorporated two separate schemas, one for the WSDL message elements and one for the application data (book and type information). The message-element schema just imported the application-data schema via a namespace reference, as allowed by WSDL. This WSDL with embedded schemas works fine with WSDL2Java using ADB or JiBX data binding, but with JAXB it causes an exception to be thrown during the processing of the schemas. Separating the application-data schema out into a separate file and specifying the file name on the schema import allowed WSDL2Java to process the schema correctly using JAXB binding.

Another issue is that XJC provides a number of code-generation options, as well as extensive customizations to control the details of code generation for particular schema components — but WSDL2Java doesn't provide any way of passing these options or customizations through to XJC, so the code generation is always run with default settings. If you need to use any of the code-generation options or customizations, you may need to run XJC separately from WSDL2Java. Unfortunately, there's no way to use a separately generated JAXB data model in WSDL2Java code generation. If you need to use a customized JAXB data model, your best approach would probably be to run WSDL2Java to generate its own JAXB data model, then substitute in your separately generated data model classes, and hand-modify code as necessary to patch everything together. Alternatively, you could use JAX-WS, as described in the next section, which allows you to skip using WSDL2Java entirely, but with some major limitations.


Using JAX-WS in Axis2

While JAXB can be used as just another alternative data-binding technique for Axis2, the differences with JAX-WS are much deeper. JAX-WS is an entirely different approach to defining Web services, and it completely replaces the standard Axis2 server-side and client-side configuration. You generate JAX-WS code from WSDL using a WsImport tool, included in the JAX-WS reference implementation (which must be downloaded separately from Axis2; see Resources), rather than WSDL2Java. Even the deployment mechanism is different from the AAR-file approach normally used with Axis2.

The code download provides a second version of the same sample application used earlier, this one modified to demonstrate JAX-WS usage in Axis2. Its code is in the jaxws directory of the download, and it has its own WSDL, build.properties, and build.xml. The WSDL for this JAX-WS version is essentially the same as that used for JAXB, shown in Listing 1. The main difference in the WSDL is that it uses an embedded schema for the application data, which didn't work for WSDL2Java using JAXB data binding.

When you generate code from WSDL using JAX-WS's WsImport tool, you get the same JAXB data model and wrapper classes as when you use WSDL2Java for JAXB code generation. The differences are in the linkage code, which in the case of JAX-WS consists of a generated service interface and a client-side service-builder class. The interface class, shown in Listing 5 (slightly reformatted, and with only one method comment left in as an example), defines the methods matching operations defined in the WSDL. Both client and server code use this interface. The extensive annotations in the interface provide all the necessary configuration information JAX-WS uses to relate the interface to a service and the interface methods to operations of that service.

Listing 5. JAX-WS generated service interface
@WebService(name = "Library", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@XmlSeeAlso({
    ObjectFactory.class
})
public interface Library
{
    /**
     * 
     * @param isbn
     * @return
     *     returns com.sosnoski.ws.library.jaxws.BookInformation
     */
    @WebMethod(action = "urn:getBook")
    @WebResult(name = "getBookReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getBook",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBook")
    @ResponseWrapper(localName = "getBookResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBookResponse")
    public BookInformation getBook(
        @WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String isbn);

    @WebMethod(action = "urn:getBooksByType")
    @WebResult(name = "getBooksByTypeReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getBooksByType",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBooksByType")
    @ResponseWrapper(localName = "getBooksByTypeResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetBooksByTypeResponse")
    public List<BookInformation> getBooksByType(
        @WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String type);

    @WebMethod(action = "urn:getTypes")
    @WebResult(name = "getTypesReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "getTypes",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetTypes")
    @ResponseWrapper(localName = "getTypesResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.GetTypesResponse")
    public List<TypeInformation> getTypes();

    @WebMethod(action = "urn:addBook")
    @WebResult(name = "addBookReturn",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl")
    @RequestWrapper(localName = "addBook",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.AddBook")
    @ResponseWrapper(localName = "addBookResponse",
        targetNamespace = "http://ws.sosnoski.com/library/wsdl",
        className = "com.sosnoski.ws.library.jaxws.AddBookResponse")
    public boolean addBook(
        @WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String type,
        @WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String isbn,
        @WebParam(name = "author",
            targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        List<String> author,
        @WebParam(name = "title", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
        String title)
        throws AddDuplicateFault
    ;
}

The WsImport tool recognizes the supplied WSDL as matching the "wrapped" convention and automatically generates an unwrapped service interface. You can see the effect of this in Listing 5 by the way the methods take individual values as input parameters and directly return whatever type is appropriate, rather than using a layer of wrapper objects (though the wrapper objects are still generated, and used behind the scenes by the JAX-WS runtime).

The supplied code again gives the actual implementation of both the service and a test client. To try it out, you need to edit the supplied build.properties file to set the paths to both your Axis2 installation and an installation of the JAX-WS reference implementation (see Resources). Once that's done, type ant on a console open to the jaxws directory to run JAX-WS code generation from WSDL, compile the supplied code, and build a JAR file for server deployment. To run the test client, copy the generated JAR file to the WEB-INF/servicejars directory of your Axis2 server installation and then type ant run on the console.

Client-side JAX-WS usage

Listing 6 shows the complete test client code. If you compare this with Listing 2, you'll see the difference between an unwrapped and a wrapped interface, with the unwrapped being much more programmer-friendly.

Listing 6. JAX-WS test client code
public class WebServiceClient
{
    public static void main(String[] args) throws Exception {
        
        // check for required command line parameters
        if (args.length < 3) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.jaxws.WebServiceClient host port path");
            System.exit(1);
        }
        
        // create the client stub
        JaxwsLibrary service = new JaxwsLibrary();
        Library stub = service.getLibrary();
        
        // set the actual endpoint address
        String target = "http://" + args[0] + ":" + args[1] + args[2];
        System.out.println("Connecting to " + target);
        BindingProvider provider = (BindingProvider)stub;
        provider.getRequestContext().
            put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, target);
        
        // retrieve a book directly
        String isbn = "0061020052";
        BookInformation book = stub.getBook(isbn);
        if (book == null) {
            System.out.println("No book found with ISBN '" + isbn + '\'');
        } else {
            System.out.println("Retrieved '" + book.getTitle() + '\'');
        }
        
        // retrieve the list of types defined
        List<TypeInformation> types = stub.getTypes();
        System.out.println("Retrieved " + types.size() + " types:");
        for (int i = 0; i < types.size(); i++) {
            TypeInformation type = types.get(i);
            System.out.println(" '" + type.getName() + "' with " +
                type.getCount() + " books");
        }
        
        // add a new book
        String title = "The Dragon Never Sleeps";
        isbn = "0445203498";
        try {
            List<String> authors = new ArrayList<String>();
            authors.add("Cook, Glen");
            stub.addBook("scifi", isbn, authors, title);
            System.out.println("Added '" + title + '\'');
            title = "This Should Not Work";
            stub.addBook("scifi", isbn, authors, title);
            System.out.println("Added duplicate book - should not happen!");
        } catch (AddDuplicateFault e) {
            System.out.println("Failed adding '" + title +
                "' with ISBN '" + isbn + "' - matches existing title '" +
                e.getFaultInfo().getBook().getTitle() + '\'');
        }
        
        // get all books of a type
        List<BookInformation> books = stub.getBooksByType("scifi");
        System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
        for (int i = 0; i < books.size(); i++) {
            System.out.println(" '" + books.get(i).getTitle() + '\'');
        }
    }
}

JAX-WS client-side handling generally expects to have access to the service WSDL at run time, and it uses the WSDL to initialize the server linkage. If you know the WSDL for the target service will always be available directly from the server at run time, and that the server will always be at the same address, you can just give the WSDL URL to WsImport and have it hard-code the URL into the generated code. For most serious work, it's better to use a local copy of the WSDL and then override the target service address at run time if it's different from the one in the WSDL. The supplied build file takes this approach, and the portion of the Listing 6 code shown in bold demonstrates how you can change the service address at run time without modifying the WSDL.

Server-side JAX-WS usage

The JAX-WS version of the server-side code is shown in Listing 7. The @WebService annotation on the implementation class (shown in bold) relates the implementation code to a particular Web service interface. This annotation on the implementation class allows you to override settings from the corresponding annotation in the generated service interface (Listing 5). In this case, the annotation sets the service and port names and also gives the location of the WSDL service definition (which Axis2 apparently expects to be either relative to the root of the class path, or an absolute URL).

Listing 7. JAX-WS server code
@javax.jws.WebService(endpointInterface="com.sosnoski.ws.library.jaxws.Library",
portName="library", targetNamespace="http://ws.sosnoski.com/library/wsdl",
wsdlLocation="com/sosnoski/ws/library/jaxws/library.wsdl", serviceName="JaxwsLibrary")
public class JaxwsLibraryImpl implements Library
{
    private final BookServer m_server;
    
    public JaxwsLibraryImpl() {
        m_server = new BookServer();
    }
    
    public boolean addBook(String type, String isbn, List<String> author, String title)
        throws AddDuplicateFault {
        BookInformation prior = m_server.getBook(isbn);
        if (prior == null) {
            BookInformation book = new BookInformation();
            book.getAuthor().addAll(author);
            book.setIsbn(isbn);
            book.setTitle(title);
            book.setType(type);
            return m_server.addBook(book);
        } else {
            AddDuplicate ad = new AddDuplicate();
            ad.setBook(prior);
            AddDuplicateFault e =
                new AddDuplicateFault("Book already present with matching ISBN", ad);
            throw e;
        }
    }

    public BookInformation getBook(String isbn) {
        return m_server.getBook(isbn);
    }

    public List<BookInformation> getBooksByType(String type) {
        return m_server.getBooksByType(type);
    }

    public List<TypeInformation> getTypes() {
        return m_server.getTypes();
    }
}

The rest of the Listing 7 code is just an unwrapped version of that shown in the (wrapped) JAXB example in Listing 3.

JAX-WS issues in Axis2

Axis2 does very well at the basics of JAX-WS handling, but it does have some limitations. The most significant is lack of support for WS-Security or other Web services extension technologies when you use JAX-WS in Axis2 (though application servers built around Axis2 may implement their own way of configuring and using WS-Security). This is a severe limitation for enterprise applications, and without any compensating advantages except a somewhat simpler configuration approach there doesn't seem to be much reason to use JAX-WS in Axis2 at present.


Moving on from Axis2

In this article, you've seen the basics of using the JAXB 2.x and JAX-WS 2.x Java standards with Axis2. The JAXB support in Axis2 has some limitations, but at least for simple schemas that don't require customizations it provides a useful alternative to the other data-binding approaches that Axis2 supports. The JAX-WS support is more limited and is currently only useful for simple services that don't require WS-Security or any other added-value functionality.

So far the articles in this series have focused on the Apache Axis2 framework. The JAXB and JAX-WS coverage makes a great jumping-off point for looking at some of the other open source Java Web services frameworks that also support these standards. Next month the column will look at working with the Metro Web Services framework developed by Sun in conjunction with the JAXB and JAX-WS reference implementations. It'll also dig more deeply into JAX-WS usage and features.


Download

DescriptionNameSize
Source code for this articlej-jws8.zip57KB

Resources

Learn

  • Apache Axis2: The Axis 2 home has information on Axis2 development and status, along with links for mailing lists, issue tracking, and downloads.
  • "Java Web Services, Part 3: Axis2 Data Binding" (Dennis Sosnoski, developerWorks, July 2007): This article discusses the main data-binding options for Axis2. The code used for that article is the basis of the sample code for this column.
  • JAXB Reference Implementation: This is the home page for the JAXB reference implementation.
  • JAX-WS Reference Implementation: Here's the home page for the JAX-WS reference implementation.
  • Axis2 Jira: This site is the place to look for known issues with Axis2. For issues relating to data-binding alternatives, including JAXB, check the databinding component category. For JAX-WS issues, check the jaxws component category.
  • Browse the technology bookstore for books on these and other technical topics.
  • developerWorks Java technology zone: Find hundreds of articles about every aspect of Java programming.

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, SOA and web services, Open source
ArticleID=428424
ArticleTitle=Java Web services: JAXB and JAX-WS in Axis2
publish-date=09152009