JAXB - XML e Java de mãos dadas

Por Guilherme de Almeida Moreira em 27/02/08

Você já participou de um projeto que precisou ler um arquivo de configuração em xml? Já precisou consumir um xml e transformá-lo em objeto? O que você usou? Quem já trabalhou com xml sabe da dificuldade que podemos encontrar pelo caminho, e é esse tipo de dificuldade que a especificação Java Architecture for XML Binding ou simplesmente JAXB tenta resolver.

Imagine a seguinte situação: Precisarmos enviar os dados contidos em um objeto para um outro servidor. Temos muitas opções para fazer o envio, como por exemplo colocar essas informações em um arquivo de texto seguindo uma máscara pré-definida. Porém apenas as aplicações que conhecessem essa máscara entenderiam os dados, e perdemos portabilidade. Usando xml a situação já é outra: qualquer aplicação, independende de linguagem, entenderá os dados contidos no arquivo xml.

Antes de falarmos sobre o JAXB vamos primeiro conferir alguns conceitos:

XML
XML é uma linguagem de marcação que serve para guardar dados de uma forma estruturada. Essa estrutura é definida pelo próprio usuário ou por um schema. Um xml é um arquivo de texto puro, portanto independente de plataforma, por isso é muito utilizado para transmitir dados entre diferentes aplicações e sistemas. Exemplo:
carro.xml


<?xml version="1.0" encoding="UTF-8"?>
<carro>
  <nome>Fusca</nome>
  <portas>2</portas>
  <motoristas>
    <motorista>
      <nome>Guilherme</nome>
    </motorista>
    <motorista>
      <nome>Leonardo</nome>
    </motorista>
  </motoristas>
</carro>

XSD
XSD é o schema citado na seção anterior, ele define quais são as regras que a estrutura do xml deve seguir, possibilitando a validação desse xml. Exemplo:


<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="carro" type="Carro" />
  <xsd:complexType name="Carro">
    <xsd:sequence>
      <xsd:element name="nome" type="xsd:string" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="portas" type="xsd:int" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="motoristas" type="Motorista" minOccurs="0"
        maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Motorista">
    <xsd:sequence>
      <xsd:element name="nome" minOccurs="1" maxOccurs="1"
        type="xsd:string" nillable="false"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

O primeiro ponto da especificação apresenta uma ferramenta chamada Binding Compiler, cuja função é transformar um xsd em um conjunto de classes que tenham uma estrutura compatível com a estrutura do xml que esse xsd define.

No XSD de exemplo definimos a seguinte estrutura: Um elemento carro deve ter um elemento nome e um elemento motoristas (do tipo Motorista), seguindo essa ordem, primeiro nome e depois motoristas. Depois definimos o tipo Motorista que deve conter apenas um nome.

O Binding Compiler é independente da implementação do JAXB, ou seja, quem define como ele será executado é quem implementa a especificação, porém a maioria e inclusive a própria RI(Reference Implementation) cria um comando que pode ser chamado pela linha de comando do Sistema Operacional, o xjc. Por exemplo no Linux:

xjc carro.xsd -d src -p br.com.caelum

Se você já está usando o Java 6, o JAXB já vêm junto com o JDK.

Com esse comando o Binding Compiler gera três classes: Carro.java, Motorista.java e a ObjectFactory.java. As classes Carro e Motorista seguem a estrutura do xsd.

Gerando e Lendo XML
A segunda parte da especificação define o que temos que fazer para transformar objetos em xml e vice-versa. A API do JAXB é quem se responsabiliza por essas transformações.

Transformando objetos em xml
O processo de transformar um objeto em xml é chamado de Marshal. Com o JAXB para transformar um objeto em xml precisamos de um JAXBContext, esse context é quem fornecerá o Marshaller. O Marshaller é quem finalmente transforma um objeto (JAXBElement) em xml. O JAXBElement contém o objeto de verdade a ser serializado e algumas propriedades do xml. É aqui que entra a importância do ObjectFactory criado pelo Binding Compiler, ele é responsável por criar uma instância do JAXBElement apropriada para o tipo de objeto a ser serializado.


  JAXBContext context = JAXBContext.newInstance("br.com.caelum");
  Marshaller marshaller = context.createMarshaller();
  JAXBElement<Carro> element = new ObjectFactory().createCarro(carro);
  marshaller.marshal(element, System.out);

Parseando xml em objetos java
Para fazer o caminho contrário, ou seja popular um objeto java com dados de um xml também precisamos de um JAXBContext, porém agora temos que pegar um Unmarshaller. O Unmarshaller recebe um arquivo xml e devolve um JAXBElement contendo um objeto populado.


  JAXBContext context = JAXBContext.newInstance("br.com.caelum");
  Unmarshaller unmarshaller = context.createUnmarshaller();
  JAXBElement<Carro> element = (JAXBElement<Carro>unmarshaller.
    unmarshal(new File("resources/carro.xml"));
  Carro carro = element.getValue();

Conclusão
O JAXB facilita muito a vida dos programadores java, fazendo o consumo e criação de xml menos trabalhosos. Essa API também fornece outros recursos como, validação, geração de schema (a partir de classes java, cria um xsd), opções para trabalhar com Namespace e etc. Comente nesse post outras oções do JAXB e outras bibliotecas que você usa no seu dia-a-dia.

Os 7 hábitos dos desenvolvedores de WebServices altamente eficazes

Por Paulo Silveira em 18/02/08

Esta semana tive o prazer de palestrar no Café Com Tapioca, evento do CeJUG realizado em Fortaleza. Estavam presentes dois conhecidos evangelistas da Sun: Reggie Hutcherson e Simon Ritter.


DSC01766 DSC01773
DSC01770 DSC01763

O agradecimento fica ao Rafael Carneiro, do PortalJava e do CeJUG, pela organização geral do evento. Agradeço também à equipe do grupo Fortes pela minha vinda ao Ceará: ao Clavius Tales, Igo Coelho, Antônio Israel, Ronaldo Moreira, Tiago Moraes, Rodrigo Maia e tantos outros. Vale reparar como a comunidade java cearense é ativa nos blogs! Nesse sábado também ministrei umworkshop sobre Arquitetura e Design Java aqui em Fortaleza.

Durante esses dias aqui, muitas pessoas perguntaram bastante sobre webservices: em especial JSON e Rest. Compilei alguns dos pontos que foram recorrentes nas discussões e considerados como boas práticas:

Cuidado com a granularidade - a granularidade do seu serviço não pode ser muito fina, caso contrário seus seviços sofrerão dos mesmos problemas que o EntityBean do EJB2 exposto remotamente: uma enorme quantidade de requisições serão disparadas para executar pequenas tarefas, como getters! Seus serviços devem realizar uma quantidade significativa de tarefas, para evitar um número alto de roundtrips!

Exponha serviços, não dados - é comum ouvir a frase “Vou criar webservices para expor os meus dados“. O grande perigo aqui é deixar toda a lógica de negócio na mão do cliente, o que descentralizará seu serviço e forçará o cliente a realizar muitas requisições ao servidor. Devemos expor serviços, e não dados em sua forma mais crua.

Nunca parsear WSDL/SOAP manualmente - antes das ferramentas que trabalham com webservices terem atingido sua maturidade, era muito comum ver por aí as pessoas gerando o SOAP manualmente, através de bibliotecas XML ou às vezes até mesmo concatenando String com o uso de StringBuffer! O SOAP é o protocolo de comunicação, e assim como quando você usa RMI/CORBA e você não enxerga absolutamente nada do JRMP/IIOP, você nunca deveria ter contato direto com o protocolo de comunicação! O SOAP e o WSDL devem ser utilizados por ferramentas, e não pela sua própria aplicação. Hoje em dia qualquer ferramenta como o Apache Axis, Apache CXF, ou mesmo o wsimport, que já vem no JDK 6, auxilia nossa tarefa de gerar Stubs que sabem trabalhar com o SOAP, sem que você nem mesmo precise ve-lo um dia. Se você está usando um servidor de aplicação, esta tarefa é ainda mais fácil, até mesmo para fazer o deploy do serviço.

Não enviar XML dentro de XML - outra prática comumente encontrada em aplicações antigas que usam Webservices: dentro do SOAP é enviado um outro XML como um dos parâmetros, então mesmo usando ferramentas para gerar os stubs você fica com uma String que dentro dela há um XML e este precisa ser parseado por sua própria aplicação. Isto é uma má interpretação do conceito que “Webservices é comunicação via XML“. Concordo que em alguns raros casos isso possa fazer sentido, como por exemplo se o valor do que você quer realmente é um outro XML, mas no geral isto é feito sem necessidade nenhuma, como é o famoso caso do WebService MS Office dos Correios do Brasil.

Considerar protocolo binário - são muitas as reclamações de que o SOAP acaba sendo um XML grande e pesado para ser transportado. Hoje em dia há muitas formas de contornar isso, como o uso do padrão Fast InfoSet para compressão do XML. Uma outra forma seria o uso de protocolos proprietários, como o AMF da Adobe, que é uma opção comum no uso do Flex.

Considerar não usar WSDL/SOAP - pelas diversas críticas a burocracia exagerada do WSDL/SOAP, muitas pessoas estão optando por usar algo mais simples, como o bom e velho XML (POX), JSON ou até mesmo uma forma qualquer de estruturar dados. Caso você precise de simplicidade e velocidade no desenvolvimento com outras plataformas, essa é uma boa opção. Cuidado: muita gente está considerando qualquer webservice que não use WSDL/SOAP como sendo REST. Os webservices do flickr e da Amazon são um bom exemplo: tudo é via GET (as vezes POST) e não há recurso identificado pela URI. Na verdade ele usa apenas um esquema HTTP+XML (POX), considerado apenas em parte como RESTful. Você pode ver que esses webservices são muito diferentes do modelo RESTful do Atom Publishing Protocol. Criar um protocolo realmente REST como este não é fácil: porém teremos esse trabalho bastante simplificado com a JAX-RS (JSR 311), e já podemos ver isso através do Glassfish Jersey.

Considerar usar JSON - JSON é um formato que tem ganho muita popularidade. Não é a toa: além de ser um simples para debugar, parsear e gerar, com javascript basta fazer um eval em um resultado JSON que ele já estará pronto para usar. É uma excelente opção para o consumo via AJAX e criação de mashups. JSON tem ganho bastante força na comunidade Flex, tornando-o uma ótima opção como ponte entre Flex e Java. A escolha por JSON abre portas para muitos tipos diferentes de clientes, e em especial o browser, que é nossa plataforma global.

WebServices sem servidor de aplicação no java 6

Por Nico Steppat em 11/07/07

Criando WebServices num servidor de aplicação como glassfish ou jboss ficou muito fácil. Basta escrever um Session Bean Stateless e usar as anotações do pacote javax.ws que JavaSE 6 já fornece. Veja o exemplo:

@Stateless
@Remote(Oi.class)
@WebService
public class OiMundo {
  
  @WebMethod
  public String oi(String nome) {
    return "Hallo " + nome;
  }
}

public interface Oi 
  String oi(String nome);
}

Pronto, fazendo deploy dessa classe no servidor do aplicação cria automaticamente o WebService e disponibiliza o WSDL. Aqui tem uma discussão mais ampla sobre webservices em um servidor de aplicação.

Sem servidor de aplicação

Com javaSE 6 nas mãos você nem precisa de um servidor de aplicação, nem mesmo um web server! Basta usar as anotações @WebService e @WebMethod na sua classe e a ferramenta apt (annotation processing tool – não o apt do Debian!, encontra-se na pasta bin do jdk) para gerar um WebService.

A conhecida classe:

@WebService
public class OiMundo {
  
  @WebMethod
  public String oi(String nome) {
    return "Hallo " + nome;
  }
}

O comando (imagina a classe OiMundo está no pacote br.com.caelum.ws):

$apt br/com/caelum/ws/OiMundo.java

gera um pacote br.com.caelum.ws.javaws com duas classes, uma classe Oi.java para os parâmetros do WebService (no nosso caso String nome do método oi) e outra OiResponse.java que representa o retorno.

Falta publicar o serviço/endpoint e iniciá-lo:

public class PublicaService {
  
  public static void main(String[] args){
    OiMundo service = new OiMundo();
    Endpoint endpoint = Endpoint.publish("http://localhost:8080/oi", service);        
  }
}

Pronto, o WebService está rodando sem servidor de aplicação nem web container. Tudo isso com javaSE 6 jdk. Para acessar a wsdl dele, usa-se o URL:

http://localhost:8080/oi?wsdl

Claro que isso não substitui o servidor por questões de performance, configuração e manutenção, mas é útil na hora de desenvolver e testar um serviço rapidamente.

Cliente

Para criar o cliente do serviço, javaSE 6 já vem com as ferramentas necessárias. Precisamos usar o wsimport (na pasta bin do jdk) para gerar as classes do cliente. Com o serviço rodando, fazemos na linha de comando:

$wsimport -keep -p br.com.caelum.cliente http://localhost:8080/oi?wsdl

A opção keep não apaga os arquivos fontes e p gera as classes dentre do pacote especificado.

Para chamar o serviço pelas classes geradas, escreva a seguinte classe dentro do pacote br.com.caelum.cliente:


public class TesteServico {
  
  public static void main(String[] args) {
    
  br.com.caelum.cliente.OiMundo port = new OiMundoService().getOiMundoPort();
  System.out.println(port.oi("Johann"));
  }
}

Repare que a classe OiMundo foi gerada pelo wsimport e é uma interface, não confundir com o serviço OiMundo que é uma implementação.

WebServices no WebContainer

Num WebContainer, usando Tomcat ou Jetty, é preciso usar um framework para criar um WebService. Já foi mencionado o Xfire nesse mesmo blog (que agora faz parte do projeto CXF), mas existem outros como o AXIS da Apache ou Metro do java.net. Metro é a implementação usada pelo Glassfish.

Vamos usar o Metro para criar um WebService dentro numa aplicação web usando Tomcat.

Faça o download do metro, instale e copie os jars da pasta metro/lib para a pasta tomcat/shared/lib, ou então para o lib exclusivo do seu projeto. São os seguintes:

  • webservices-api.jar
  • webservices-extra.jar
  • webservices-tools.jar
  • webservices-extra-api.jar
  • webservices-rt.jar
  • wstx-services.war

Crie um aplicação web comum e adicione a classe TchauMundo no seu projeto com as anotações:

@WebService
public class TchauMundo {

  @WebMethod
  public String tchau(String nome) {
    return "Tschüss " + nome;
  }
}

Gere novamente o serviço e execute o seguinte comando na pasta src do seu projeto:

$apt br/com/caelum/ws/TchauMundo.java

As classes geradas devem ficar na pastas WEB-INF/classes.

Agora é preciso registrar um servlet e listener que recebe as requisições do WebService. O web.xml:

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>webServiceServlet</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webServiceServlet</servlet-name>
<url-pattern>/tchau</url-pattern>
</servlet-mapping>
</web-app>

Além disso, falta um outro xml (mais um!) com a definição do serviço/endpoint. O arquivo sun-jaxws.xml deve estar na pasta WEB-INF. Veja o exemplo:


<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint
name='tchauMundo'
implementation='br.com.caelum.ws.TchauMundo'
url-pattern='/tchau'/>
</endpoints>

Fazendo o deploy e acessando a seguinte url:

http://localhost:8080/ws/tchau

temos o nosso serviço:

WebServices sem servidor de aplicação

A entrada de uma API para produzir e consumir webservices no Java SE 6 gerou muita polêmica, por inflar o tamanho da Java RE e ser pouco necessário na maioria dos projetos. Apesar disso, ter uma API unificada, além de ferramentas facilitadoras, possibilita uma mais fácil adoção de webservices por parte dos desenvolvedores Java.