Java Web Services: JAXB e JAX-WS em Axis2

Use as tecnologias JAXB 2.x e JAX-WS 2.x padrão Java para serviços da Web do Axis2

Apache Axis2 suporta uma gama de tecnologias de ligação de dados, incluindo o padrão Java™ oficial, JAXB 2.x. O Axis2 também suporta o padrão java para a configuração de serviço da Web, JAX-WS 2.x, como uma alternativa para suas próprias técnicas de configuração customizada. Dennis Sosnoski continua sua série da coluna Java Web Services mostrando como usar cada um desses padrões Java com Axis2 e discutindo algumas das limitações do suporte atual de Axis2 para eles.

Dennis Sosnoski, Consultant, Sosnoski Software Solutions, Inc.

Author photoDennis Sosnoski é um consultor e instrutor especializado em XML e serviços da Web baseados em Java (este link reside fora de ibm.com). Sua experiência em desenvolvimento de software profissional se estende por mais de 30 anos, sendo que nos últimos 10 focou tecnologias XML e Java do lado do servidor. Dennis é o desenvolvedor líder da estrutura de software livre JiBX XML Data Binding (este link reside fora de ibm.com) e a estrutura de serviços da Web associada JiBX/WS (este link reside fora de ibm.com), assim como um committer na estrutura de serviços da Web Apache Axis2 (este link reside fora de ibm.com). Também foi um dos membros do Grupo de Especialistas para as especificações JAX-WS 2.0 e JAXB 2.0. O material para a série Serviços da Web Java é baseado nas aulas de treinamento de Dennis.



15/Set/2009

Sobre esta série

Os serviços de Web são uma parte crucial do papel desempenhado pela tecnologia Java na computação empresarial. Nesta série de artigos, o consultor de serviços de XML e Web Dennis Sosnoski cobre as principais estruturas e tecnologias importantes para desenvolvedores Java que utilizam serviços de Web. Acompanhe a série para manter-se informado com relação aos mais recentes desenvolvimentos da área e saber como utilizá-los para auxiliá-lo em seus projetos de programação.

O Apache Axis original teve como base o primeiro padrão Java para serviços da Web, JAX-RPC. Essa não foi uma grande abordagem, visto que JAX-RPC restringiu o projeto interno do código Axis e contribuiu para problemas de desempenho e falta de flexibilidade. JAX-RPC também fez algumas suposições sobre a direção do desenvolvimento dos serviços da Web que não deram certo.

No momento em que o esforço do Axis2 foi iniciado, uma substituição para JAX-RPC já estava nos planos, portanto, o Axis2 foi projetado para ser flexível o suficiente a fim de implementar o suporte relativo à substituição dos serviços da Web padrão no topo da estrutura base. As versões recentes do Axis2 implementaram suporte para ligação de dados de JAXB 2.x Java XML padrão e o Java Web Services de JAX-WS 2.x padrão que substituíram JAX-RPC. Este artigo mostra como usar JAXB e JAX-WS com Axis2 e identifica algumas das limitações do suporte atual do Axis2's para esses padrões.

JAXB em Axis2

Axis2 implementa suporte para JAXB 2.x como uma das alternativas de ligação de dados que podem ser escolhidas na geração do código de uma definição de serviço do Web Services Description Language (WSDL) com WSDL2Java. (Consulte "Java Web Services: ligação de dados do Axis2" para uma discussão das alternativas principais.) Assim como com grande parte das alternativas, a geração de código do WSDL que usa JAXB 2.x cria um conjunto de classes de ligação e um conjunto de classes de modelo de dados. As classes de ligação, incluindo um stub do lado do cliente e um receptor de mensagens do lado do servidor, interagem entre o código do aplicativo e Axis2. As classes de modelo de dados representam os dados da mensagem real.

JAXB 2.x usa anotações nas classes de modelo de dados para controlar como os dados são convertidos para e de XML. A abordagem de anotações permitirá usar implementações de JAXB diferentes no tempo de execução sem a necessidade de alterar o código-fonte ou recompilar as classes. Fica por conta da implementação de JAXB acessar as informações da anotação das classes de modelo de dados e aplicar essas anotações na conversão para e de XML.

O download do código (consulte Download) apresenta um aplicativo de amostra para mostrar o uso de JAXB no Axis2, no diretório jaxb. Esse aplicativo é outra versão do serviço de gerenciamento de biblioteca único usado em artigos anteriores desta série (incluindo a comparação de ligação de dados em "Ligação de dados do Axis2"). A definição do serviço do WSDL estabelece quatro operações:

  • getBook para recuperar os dados de um determinado livro identificados pelo International Standard Book Number (ISBN)
  • getBooksByType para recuperar os detalhes de todos os livros de um determinado tipo
  • getTypes para localizar os tipos de livros disponíveis
  • addBook para adicionar um novo livro à biblioteca

Listagem 1 mostra uma versão bem editada do WSDL, com apenas as porções relevantes à operação getBook incluída:

Listagem 1. WSDL do serviço de biblioteca
<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>

O suporte de JAXB do Axis2 deve ser ampliado para gerar métodos de operação não empacotados (em que os valores empacotados em uma mensagem são convertidos em parâmetros de método para conveniência de programação — novamente, consulte "Java Web Services: ligação de dados do Axis2" para uma discussão entre as interfaces empacotadas e não empacotadas.) Mas o suporte não empacotado que usa a ferramenta WSDL2Java não funciona para esse exemplo no código do Axis2 atual ou das versões mais recentes. Pelo menos por enquanto, os métodos da operação empacotada são a única forma de usar JAXB com a geração de código do Axis2 (mas, veja a discussão do JAX-WS resumidamente para uma abordagem alternativa). Com a interface de operação empacotada, cada método de serviço usa um único parâmetro de objeto que corresponda à mensagem de entrada da operação e retorna um objeto que corresponda à mensagem de saída da operação.

O código fornecido fornece a implementação real do serviço e de um cliente de teste e está configurado para funcionar com as classes geradas pela execução de WSDL2Java. Assim como em relação ao código de amostra de artigos anteriores desta série, o download contém os arquivos build.properties e build.xml usados para criar a amostra com Apache Ant (no diretório jaxb). Primeiro, é preciso editar o arquivo build.properties para definir o caminho para a instalação do Axis2 (e para alterar outras configurações, se isso for necessário para o seu sistema). Em seguida, apenas digite ant em um console aberto para o diretório jaxb para executar WSDL2Java, compilar o código fornecido e o gerado e gerar o arquivo AAR para implementação do servidor. Para testá-lo, primeiro implemente o arquivo AAR gerado para a sua instalação do servidor do Axis2 e, em seguida, digite ant run no console.

Uso de JAXB no lado do cliente

O cliente de teste cria uma instância do stub de serviço que usa os parâmetros de terminal de serviço enviados como argumentos de linha de comando e depois executa uma sequência de cinco chamadas de serviço:

  1. Obtenha as informações para um livro.
  2. Obter os tipos de livros na biblioteca.
  3. Adicione um livro à biblioteca (que falhará se o livro já estiver presente, assim como ocorrerá quando o cliente for executado mais de uma vez sem reiniciar o servidor).
  4. Se a última etapa for concluída com êxito, tente adicionar outro livro com o mesmo ISBN (o que deverá falhar).
  5. Obtenha as informações para todos os livros de um determinado tipo.

Listagem 2 mostra o código de teste do cliente completo. É possível ver a natureza empacotada da interface de serviço nos objetos do wrapper usados para cada operação, como no objeto GetTypes necessário em uma chamada para a operação getTypes (embora não exista nenhum dado de entrada para essa operação) e o objeto GetTypesResponse retornado pela chamada.

Listagem 2. Código do cliente do teste do JAXB
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() + '\'');
        }
    }
}

Se você comparar a Listagem 2 com os exemplos de código do cliente em "Java Web Services: Ligação de dados do Axis2", você verá que ela é muito similar aos exemplos empacotados de JiBX e Axis Data Binding (ADB), sendo que a principal diferença é que as classes do wrapper do JAXB usam listas do tipo Java 5 em vez de arrays (uma alternativa que também é suportada pela ligação de dados do JiBX, mas não por ADB).

Uso no lado do servidor

O código do lado do servidor para o serviço de biblioteca é composto por um par de classes, um que realmente implemente o manuseio da biblioteca e outro que se adapte à interface de serviço esperada por Axis2. O código da implementação real é quase idêntico nas várias ligações de dados, apenas com alterações mínimas de acordo com a necessidade das representações de modelo de dados do aplicativo gerado. A Listagem 3 mostra a classe de interface de serviço mais interessante. Assim como no cliente, a interface empacotada requer que o código do aplicativo extraia dados dos objetos do wrapper recebidos e construa objetos do wrapper a serem enviados.

Listagem 3. Código do servidor do JAXB
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: ligação de dados do Axis2" não mostra o código da interface do servidor para as várias ligações de dados do artigo, mas se comparar a Listagem 3 com os downloads de código reais desse artigo, você verá que ele tem correspondência bem próxima das amostras empacotadas de ADB e JiBX, novamente diferente apenas pelo uso de listas do tipo Java 5 em vez de arrays.

Classes do modelo de dados do JAXB

A Listagem 4 mostra algumas das classes do modelo de dados do JAXB geradas pela execução de WSDL2Java (com a maioria dos comentários excluídos, exceto alguns deixados como amostras de representação). As classes do modelo de dados geradas são as mesmas para cliente e servidor, embora elas sejam geradas separadamente pelo build do projeto. As classes mostradas são as usadas para a chamada getBook no código do cliente da Listagem 2 e no código do servidor da Listagem 3. As anotações (mostradas em negrito) em cada definição de classe e a maior parte das definições de campo fornecem informações de configuração usadas por JAXB para controlar a conversão de objetos para e de XML.

Listagem 4. Classes do modelo de dados do JAXB
@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;
    }
}

A anotação @XmlAccessorType é usada em um pacote ou nível de classe para controlar como os valores são acessados na classe — como todos os campos, todas as propriedades com os métodos de acesso get/set, apenas campos públicos e propriedades ou apenas quando especificada por uma anotação individual. A anotação @XmlType é usada em uma classe ou definição numérica correspondente a um tipo de esquema para informar a JAXB o nome e o espaço de nomes do tipo de esquema (se houver), a ordem dos valores na representação de tipo e, opcionalmente, como construir instâncias da classe que um método de fábrica. A anotação @XmlRootElement é usada em uma classe ou definição de numeração correspondente a uma definição de elemento global para determinar o nome e o espaço para nomes do elemento global. As anotações @XmlElement e @XmlAttribute são usadas em valores (como campos ou como métodos de propriedade JavaBean) para determinar o nome do elemento ou do atributo e outras características.

Todas as anotações usadas pelo JAXB estão no pacote javax.xml.bind.annotation, incluindo muitas outras anotações além das usadas no código gerado para essa única amostra. JAXB oferece suporte à geração do código dos esquemas, como nesse caso, e à inicialização a partir do código. Algumas das anotações e opções (como as que lidam com factories de objetos e com métodos de serializador/desserializador) são usadas apenas quando iniciadas pelo código.

Problemas de JAXB em Axis2

WSDL2Java chama o compilador de ligação de XJC incluso na implementação de referência do JAXB para gerar o código do modelo de dados, para que, na maioria dos casos, a geração do código de modelo de dados seja independente de Axis2. Você gerará o mesmo modelo de dados se executar o compilador de ligação de JAXB XJC diretamente nos esquemas usados por um serviço da Web. Infelizmente, a correspondência de impedância entre WSDL2Java e XJC não é sempre perfeita e isso causa alguns problemas.

Um problema está relacionado ao modo como os esquemas estão estruturados em documentos WSDL. A forma original do WSDL do serviço de biblioteca usava um único documento que incorporava dois esquemas separados, um para os elementos da mensagem do WSDL e outro para os dados de aplicativo (informações de livro e tipo). O esquema de elemento da mensagem importava apenas o esquema de dados de aplicativo via referência de espaço para nomes, conforme permitido pelo WSDL. Esse WSDL com esquema incorporados funciona bem com WSDL2Java usando a ligação de dados de ADB ou JiB, mas, com JAXB, ele faz uma exceção ser lançada durante o processamento dos esquemas. Separar o esquema de dados de aplicativo em um arquivo à parte e especificar o nome de arquivo na importação do esquema permitia que WSDL2Java processasse o esquema corretamente usando ligação de JAXB.

Outro problema é que XJC oferece inúmeras opções de geração de código, assim como de personalizações, para controlar os detalhes da geração de código para determinados componentes de esquema, — mas WSDL2Java não apresenta nenhuma forma de enviar essas opções ou personalizações a XJC, portanto, a geração de código é sempre executada com configurações padrão. Se for preciso usar qualquer uma das personalizações ou opções de geração de código, será necessário executar XJC separadamente de WSDL2Java. Infelizmente, não há maneira de usar um modelo de dados de JAXB gerado separadamente na geração de código de WSDL2Java. Se precisar usar um modelo de dados de JAXB personalizado, sua melhor abordagem provavelmente será executar WSDL2Java para gerar seu próprio modelo de dados JAXB e substituir nas classes de modelo de dados geradas separadamente, e modifique manualmente o código de acordo com a necessidade para corrigir tudo junto. Como alternativa, é possível usar JAX-WS, conforme descrito na próxima seção, o que lhe permite continuar usando WSDL2Java integralmente, mas com algumas limitações importantes.


Usando JAX-WS em Axis2

Embora JAXB possa ser usado apenas como alternativa à técnica de ligação de dados de Axis2, as diferenças em relação à JAX-WS são muito mais profundas. JAX-WS é uma abordagem totalmente diferente para definir serviços da Web e substitui completamente a configuração de lado do servidor e lado do cliente de Axis2 padrão. Gere código de JAX-WS a partir de WSDL usando a ferramenta WsImport inclusa na implementação de referência de JAX-WS (que deve ser baixada separadamente de Axis2; consulte Recursos) em vez de WSDL2Java. Até mesmo o mecanismo de implementação é diferente da abordagem de arquivo AAR normalmente usada com Axis2.

O download do código oferece uma segunda versão do mesmo aplicativo de amostra usado anteriormente, sendo que este foi modificado para mostrar o uso de JAX-WS em Axis2. O código está no diretório jaxws do download e possui seu próprio WSDL, build.properties e build.xml. O WSDL para essa versão de JAX-WS é essencialmente o mesmo que o usado para JAXB, apresentado na Listagem 1. A diferença principal no WSDL é que ele usa um esquema incorporado para os dados de aplicativo, o que não funcionava para WSDL2Java ao se utilizar a ligação de dados de JAXB.

Ao gerar código a partir de WSDL usando a ferramenta WsImport de JAX-WS, você obtém o mesmo modelo de dados e as mesmas classes de wrapper de JAXB de quando se usa WSDL2Java para geração de código de JAXB. As diferenças estão no código de ligação, que, no caso de JAX-WS, consiste em uma interface de serviço gerada e em uma classe de construtor de serviços no lado do cliente. A classe de interface, mostrada na Listagem 5 (levemente reformatada e com apenas um comentário de método deixado como exemplo), define os métodos correspondentes às operações definidas no WSDL. Os códigos do servidor e do cliente usam essa interface. As várias anotações na interface oferecem todas as informações de configuração necessárias que JAX-WS usa para relacionar a interface a um serviço e os métodos de interface a operações desse serviço.

Listagem 5. Interface de serviço gerada por JAX-WS
@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
    ;
}

A ferramenta WsImport reconhece o WSDL fornecido como correspondente à convenção "empacotada" e automaticamente gera uma interface de serviço não empacotada. É possível ver o efeito disso na Listagem 5 pela forma como os métodos usam valores individuais como parâmetros de entrada e diretamente retornam qualquer tipo que seja apropriado, em vez de usar uma camada de objetos do wrapper (embora os objetos do wrapper ainda sejam gerados e usados em segundo plano pelo tempo de execução de JAX-WS).

O código fornecido novamente fornece a implementação real do serviço e de um cliente de teste. Para testar isso, é preciso editar o arquivo build.properties fornecido a fim de definir os caminhos para a instalação do Axis2 e a implementação de referência do JAX-WS (consulte Recursos). Quando isso tiver sido feito, digite ant em um console aberto para o diretório jaxws para executar a geração de código de JAX-WS a partir de WSDL, compilar o código fornecido e criar um arquivo JAR para implementação de servidor. Para executar o cliente de teste, copie o arquivo JAR gerado no diretório WEB-INF/servicejars de instalação do servidor do Axis2 e digite ant run no console.

Uso de JAX-WS no lado do cliente

Listagem 6 mostra o código de teste do cliente completo. Se comparar isso com a Listagem 2, você verá a diferença entre uma interface empacotada e não empacotada, com a não empacotada sendo muito mais amigável ao programador.

Listagem 6. Código do cliente do teste do JAX-WS
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() + '\'');
        }
    }
}

A manipulação no lado do cliente do JAX-WS geralmente espera ter acesso ao WDSL de serviço no tempo de execução e usa WSDL para inicializar a ligação do servidor. Caso saiba que o WSDL do serviço de destino estará sempre disponível diretamente a partir do servidor no tempo de execução e que o servidor estará sempre no mesmo endereço, será preciso apenas fornecer o URL de WSDL a WsImport e fazer com que ele codifique permanentemente o URL no código gerado. Para os trabalhos mais importantes, é melhor usar uma cópia local do WSDL e substituir o endereço de serviço de destino no tempo de execução se ele for diferente do que está no WSDL. O arquivo de construção fornecido usa essa abordagem, e a parte do código da Listagem 6 apresentada em negrito mostra como é possível alterar o endereço de serviço no tempo de execução sem modificar o WSDL.

Uso de JAX-WS no lado do servidor

A versão JAX-WS do código do lado do servidor é mostrada na Listagem 7. A anotação @WebService na classe de implementação (apresentada em negrito) relaciona o código de implementação a uma determinada interface de serviço da Web. Essa anotação na classe de implementação permite substituir as configurações da anotação correspondente na interface de serviço gerada (Listagem 5). Nesse caso, a anotação define os nomes do serviço e da porta e também fornece o local da definição de serviço de WSDL (e Axis2 aparentemente espera estar relacionado ao root do caminho da classe ou a um URL absoluto).

Listagem 7. Código do servidor do JAX-WS
@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();
    }
}

O restante do código da Listagem 7 é apenas uma versão não empacotada do código mostrado no exemplo de JAXB (empacotado) na Listagem 3.

Problemas de JAX-WS em Axis2

Axis2 executa muito bem o básico da manipulação do JAX-WS, mas ele tem algumas limitações. A mais significativa é a falta de suporte para WS-Security ou outras tecnologias de extensão de serviços da Web quando se utiliza JAX-WS em Axis2 (embora os servidores de aplicativos construídos em torno de Axis2 possam implementar sua própria forma de configurar e usar WS-Security). Essa é uma limitação severa para aplicativos corporativos e, por não ter vantagens compensadoras, exceto uma abordagem de configuração um pouco mais simples, não parece haver um bom motivo para usar JAX-WS em Axis2 neste momento.


Mudando do Axis2

Neste artigo, você viu os conceitos básicos de uso dos padrões Java JAXB 2.x e JAX-WS 2.x com Axis2. O suporte de JAXB no Axis2 tem algumas limitações, mas, pelo menos para esquemas mais simples que não exigem personalizações, ele representa uma alternativa útil a outras abordagens de ligação de dados oferecidas por Axis2. O suporte de JAX-WS é mais limitado e atualmente é útil apenas para serviços mais simples que não exigem o uso de WS-Security ou de qualquer outra funcionalidade com valor agregado.

Até agora, os artigos desta série enfocaram a estrutura Apache Axis2. A cobertura de JAXB e JAX-WS cria um excelente ponto de partida para se observar algumas outras estruturas de Java Web Services de software livre que também suportem esses padrões. No próximo mês, a coluna procurará trabalhar com a estrutura Metro Web Services desenvolvida pela Sun em conjunto com as implementações de referência de JAXB e JAX-WS. Ela também mostrará mais a fundo o uso e os recursos de JAX-WS.


Download

DescriçãoNomeTamanho
Source code for this articlej-jws8.zip57KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre
ArticleID=431567
ArticleTitle=Java Web Services: JAXB e JAX-WS em Axis2
publish-date=09152009