APIs e Microsserviços

19 abr, 2011

Serviços da Web Java: entendendo e modelando o WSDL 1.1

Publicidade

Os serviços da Web para aplicativos corporativos dependem muito do
uso de definições de serviço. As definições de serviço especificam um
contrato básico entre o provedor de serviços e quaisquer possíveis
clientes, detalhando os tipos de funções que são fornecidas pelo serviço
e as mensagens trocadas como parte de cada função.

Os provedores de
serviços e consumidores são livres para implementar as suas extremidades
da troca como quiserem, desde que as mensagens que eles enviam
correspondam à definição de serviço. O uso de definições de serviço que
especificam trocas de mensagem XML é o que diferencia os serviços da Web
das tecnologias anteriores de programação distribuída.

Foram propostas várias técnicas para definir serviços da Web, mas a
abordagem mais usada é o WSDL 1.1. O WSDL 1.1 tem algumas desvantagens –
como uma estrutura complexa demais – que o tornam quase ilegível para
os “não iniciados”. Também não tem uma definição consagrada formal. O
resultado disso são sucessivos “acréscimos” para esclarecimento que
preencheram algumas lacunas do documento de especificação original.

Em
resposta a isso, as pilhas de serviços da Web tendem a processar
documentos WSDL 1.1 da forma mais flexível possível. Essa flexibilidade
pode dificultar ainda mais o entendimento do WSDL 1.1, porque os
desenvolvedores podem ver uma variedade ampla de estruturas WSDL mas
não têm uma orientação em relação à melhor abordagem.

Neste artigo, você aprenderá a entender os documentos WSDL 1.1 e verá
as primeiras partes de um modelo Java para verificar documentos WSDL e
convertê-los para um formato padrão.

Entendendo o WSDL 1.1

O WSDL 1.1, publicado no início de 2001, foi substituído tecnicamente
pela recomendação WSDL 2.0 do W3C publicado em 2007. O WSDL 2.0 oferece
uma estrutura mais limpa que a do WSDL 1.1 e mais flexibilidade.
Entretanto, o WSDL 2.0 vive o dilema do ovo e da galinha. 

O WSDL 2.0
não é amplamente utilizado porque não tem um suporte amplo e, como não é
amplamente utilizado, os implementadores de pilhas de serviços da Web
não sofrem pressão para oferecer suporte a ele. O WSDL 1.1, embora tenha
falhas, é suficientemente bom para a maioria dos propósitos.

A especificação original WSDL 1.1 era imprecisa em relação à
quantidade de recursos a ser usados. Como o foco do WSDL era o trabalho
com definições de serviço de SOAP, ele também incluiu o suporte para
alguns recursos de SOAP (como codificação rpc) que posteriormente foram
considerados indesejáveis.

O Web Services Interoperability Organization
(WS-I) tratou desses problemas no Basic Profile (BP), que definiu boas
práticas para serviços da Web usando SOAP e WSDL. O BP 1.0 foi aprovado
em 2004 e atualizado como BP 1.1 em 2006. Neste artigo, abordarei o WSDL
1.1 com base nas diretrizes do WS-I BP, ignorando recursos efetivamente
descontinuados como a codificação rpc para SOAP.

As definições do esquema XML têm a finalidade de definir a estrutura
dos documentos XML. O WSDL 1.1 incluiu uma descrição de esquema na
especificação original, mas esse esquema não correspondia às descrições
de texto em vários aspectos. Isso foi corrigido em uma versão modificada
do esquema posteriormente, mas o documento do WSDL 1.1 não foi
atualizado para refletir a alteração.

Em seguida, o grupo WS-I BP
decidiu fazer ainda mais mudanças no esquema WSDL; portanto, criou
aquilo que parece ser a versão das boas práticas desse esquema confuso.
Os documentos escritos para uma versão do esquema geralmente não são
compatíveis com outras versões (apesar de usar o mesmo namespace), mas,
felizmente, a maioria das ferramentas de serviços da Web basicamente
ignora o esquema e aceita qualquer coisa que pareça razoável. 

Nem mesmo a versão WS-I BP do esquema WSDL 1.1 ajuda muito a garantir
que os documentos WSDL 1.1 sigam a especificação. O esquema não reflete
todas as restrições do WS-I BP, particularmente no que diz respeito à
ordem dos componentes.

Além disso, o esquema XML não consegue manipular
muitos tipos de restrições declaradas facilmente nos documentos (tais
como atributos alternativos e elementos de extensão obrigatórios
provenientes de esquemas separados).

Portanto, o ato de certificar-se de
que um documento WSDL 1.1 siga a especificação WSDL 1.1 (conforme as
mudanças realizadas pelo WS-I BP) envolve muito mais do que a ativação
da validação do esquema XML. Voltarei a esse tópico mais adiante neste
artigo. Primeiro revisarei a estrutura das descrições de serviço do WSDL
1.1.

Uso de namespaces

Este artigo usa:

  • O prefixo wsdl para representar o namespace do WSDL 1.1 http://schemas.xmlsoap.org/wsdl/.
  • O prefixo soap para o namespace http://schemas.xmlsoap.org/wsdl/soap/ usado pela extensão do SOAP 1.1 para o WSDL 1.1
  • O prefixo xs para o namespace http://www.w3.org/2001/XMLSchema usado para definições de esquema XML

Componentes da descrição

Os documentos WSDL 1.1 usam um elemento-raiz fixo, chamado convenientemente de <wsdl:definitions>.
Dentro desse elemento-raiz, um elemento filho “passivo” (que só faz
referência a documentos WSDL 1.1) e cinco elementos filho “ativos” (os
que realmente contribuem para a descrição do serviço) são definidos no
namespace do WSDL 1.1:

  • <wsdl:import>: faz referência a um documento WSDL 1.1 separado, com descrições a ser incorporadas a esse documento.
  • <wsdl:types>: define tipos ou elementos XML usados para troca de mensagens.
  • <wsdl:message>: define a mensagem em si, em termos de tipos ou elementos XML.
  • <wsdl:portType>: define um conjunto abstrato de operações implementadas por um serviço.
  • <wsdl:binding>: define a implementação real de um
    <wsdl:portType>, usando formatos e protocolos específicos.
  • <wsdl:service>: define o serviço como um todo, geralmente incluindo um ou mais elementos <wsdl:port> com informações de acesso para elementos <wsdl:binding>.

Também há um elemento <wsdl:document> que pode ser usado para fins de documentação, como o primeiro filho do elemento <wsdl:definitions> e também como o primeiro filho de qualquer um dos elementos acima.

Normalmente, uma descrição de serviço completa requer pelo menos um desses elementos, com exceção de <wsdl:import>, mas não é necessário que todos estejam no mesmo documento. É possível usar <wsdl:import>
para montar uma descrição WSDL completa a partir de diversos
documentos, oferecendo a você a flexibilidade de dividir descrições para
que se adaptem à sua organização.

Por exemplo, os três primeiros
elementos da descrição (<wsdl:types>, <wsdl:message> e <wsdl:portType>)
em conjunto fornecem uma descrição completa da interface de serviço
(talvez definida por uma equipe de arquitetura). Portanto, talvez faça
sentido mantê-los separados dos elementos voltados para a implementação <wsdl:binding> e <wsdl:service>.
Todas as principais pilhas de serviços da Web suportam a divisão de descrições em diversos documentos WSDL.

A Listagem 1 e a Listagem 2
apresentam uma amostra de uma descrição do serviço WSDL dividida em
dois documentos WSDL, com os componentes da descrição da interface no
arquivo BookServerInterface.wsdl e os componentes de implementação em
BookServerImpl.wsdl.

A Listagem 1 mostra BookServerInterface.wsdl:

Listagem 1. BookServerInterface.wsdl

<wsdl:definitions ... xmlns:tns="http://sosnoski.com/ws/library/BookServerInterface"
targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
<wsdl:document>Book service interface definition.</wsdl:document>
<wsdl:types>
<xs:schema ...
targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
<xs:import namespace="http://sosnoski.com/ws/library/types"
schemaLocation="book-types.xsd"/>
...
</xs:schema>
</wsdl:types>
<wsdl:message name="getBookMessage">
<wsdl:part name="part" element="tns:getBook"/>
</wsdl:message>
<wsdl:message name="getBookResponseMessage">
<wsdl:part name="part" element="tns:getBookResponse"/>
</wsdl:message>
...
<wsdl:message name="addBookMessage">
<wsdl:part name="part" element="tns:addBook"/>
</wsdl:message>
<wsdl:message name="addBookResponseMessage">
<wsdl:part name="part" element="tns:addBookResponse"/>
</wsdl:message>
<wsdl:message name="addDuplicateFault">
<wsdl:part name="fault" element="tns:addDuplicate"/>
</wsdl:message>
<wsdl:portType name="BookServerPortType">
<wsdl:documentation>
Book service implementation. This creates an initial library of books when the
class is loaded, then supports method calls to access the library information
(including adding new books).
</wsdl:documentation>
<wsdl:operation name="getBook">
<wsdl:documentation>
Get the book with a particular ISBN.
</wsdl:documentation>
<wsdl:input message="tns:getBookMessage"/>
<wsdl:output message="tns:getBookResponseMessage"/>
</wsdl:operation>
...
<wsdl:operation name="addBook">
<wsdl:documentation>Add a new book.</wsdl:documentation>
<wsdl:input message="tns:addBookMessage"/>
<wsdl:output message="tns:addBookResponseMessage"/>
<wsdl:fault message="tns:addDuplicateFault" name="addDuplicateFault"/>
</wsdl:operation>
</wsdl:portType>
</wsdl:definitions>

A Listagem 2 mostra BookServerImpl.wsdl. O elemento <wsdl:import> perto do começo importa a descrição da interface de BookServerInterface.wsdl.

Listagem 2. BookServerImpl.wsdl

<wsdl:definitions ... xmlns:ins="http://sosnoski.com/ws/library/BookServerInterface"
xmlns:tns="http://sosnoski.com/ws/library/BookServer"
targetNamespace="http://sosnoski.com/ws/library/BookServer">
<wsdl:document>
Definition of actual book service implementation.
</wsdl:document>
<wsdl:import namespace="http://sosnoski.com/ws/library/BookServerInterface"
location="BookServerInterface.wsdl"/>

<wsdl:binding name="BookServerBinding" type="ins:BookServerPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="getBook">
<soap:operation soapAction="urn:getBook"/>
<wsdl:input>
<soap:body/>
</wsdl:input>
<wsdl:output>
<soap:body/>
</wsdl:output>
</wsdl:operation>
...
<wsdl:operation name="addBook">
<soap:operation soapAction="urn:addBook"/>
<wsdl:input>
<soap:body/>
</wsdl:input>
<wsdl:output>
<soap:body/>
</wsdl:output>
<wsdl:fault name="addDuplicateFault">
<soap:fault name="addDuplicateFault"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BookServer">
<wsdl:port name="BookServerPort" binding="tns:BookServerBinding">
<soap:address location="http://localhost:8080/cxf/BookServer"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Além das definições de elementos (e atributos) no namespace do WSDL 1.1,
o WSDL 1.1 também define elementos de extensão. Esses elementos de
extensão devem se encaixar em locais específicos das descrições de
serviço do WSDL 1.1 para fornecer informações adicionais necessárias
para um tipo de serviço específico.

Os únicos elementos de extensão do
WSDL 1.1 que ainda são amplamente usados são os que estão relacionados a
ligações de SOAP 1.1 (vistos na Listagem 2, dentro dos elementos <wsdl:binding> e <wsdl:service>), que foram definidas pela especificação WSDL 1.1 original e as
ligações do SOAP 1.2, definidas por uma especificação separada em 2006.

Detalhes dos componentes

O elemento <wsdl:types> quebra todas as definições XML usadas para mensagens, na forma de um ou mais elementos <xs:schema>. O WSDL permite alternativas ao esquema XML para essas definições, mas a
maioria das pilhas só suporta o esquema XML.

Os elementos <xs:schema> podem usar <xs:import> e/ou <xs:include>
para incorporar outros esquemas externos ao WSDL, se você desejar (e
podem fazer referência a esquemas separados contidos no mesmo WSDL).

Já que um único elemento <wsdl:types> pode conter qualquer quantidade de definições de esquema, nunca há um motivo para usar mais de um elemento <wsdl:types> em um documento WSDL. Na Listagem 1, o elemento <wsdl:types> está perto da parte superior de BookServerInterface.wsdl.

Com exceção de <wsdl:import> e <wsdl:types>, todos os outros componentes de nível superior de um documento WSDL são nomeados individualmente pelo uso de um atributo name obrigatório.

Se você usa um atributo targetNamespace no elemento de raiz do documento <wsdl:definitions>
(algo que, de modo geral, você deve fazer, por ser uma boa prática),
os nomes desses componentes são definidos no namespace de destino. Isso
significa que, ao definir o nome, você só dá a parte simples ou “local”
do nome, mas as referências a esse componente devem qualificar o nome
com um prefixo de namespace ou namespace padrão.

A Figura 1 shows mostra
as ligações mais importantes entre os componentes do WSDL. As linhas
contínuas representam referências qualificadas aos nomes e as linhas
pontilhadas representam nomes para identificação sem a qualificação do
namespace:

Figura 1. Ligações entre componentes do WSDL

As mensagens, representadas por elementos <wsdl:message> estão no “coração” das descrições do serviço WSDL. Os elementos <wsdl:message> são as descrições dos dados XML trocados entre o cliente e o provedor de serviços.

Cada <wsdl:message> contém zero ou mais (normalmente um) elementos filho <wsdl:part>. Cada elemento part requer o seu próprio atributo name (exclusivo dentro de <wsdl:message>) e um atributo element ou type que faz referência à definição de esquema dos dados XML. Vários elementos <wsdl:message> são mostrados na Listagem 1, depois do elemento <wsdl:types> em BookServerInterface.wsdl.

Os elementos <wsdl:portType> definem a interface abstrata para um serviço, em termos de mensagens enviadas do/para o serviço. Os elementos <wsdl:portType> contém qualquer quantidade de elementos filho <wsdl:operation>.

Cada elemento filho <wsdl:operation> precisa do seu próprio atributo name (que o WS-I BP exige que seja exclusivo dentro de <wsdl:portType>)
e contém um elemento filho ou mais que descrevem as mensagens usadas
pela operação. Os elementos filho têm três tipos, representando tipos de
uso diferentes:

  • <wsdl:input>: dado enviados do cliente para o provedor de serviços como entrada para a operação
  • <wsdl:output>: dados retornados para o cliente pelo provedor de serviços como resultado da operação
  • <wsdl:fault>: dados retornados para o cliente pelo provedor de serviços quando há um erro no processamento

O WSDL 1.1 define vários padrões de interação entre o cliente e o
provedor de serviços, conforme o representado por sequências diferentes
de elementos filho <wsdl:input> e <wsdl:output>, mas nem todos os padrões estão suficientemente bem definidos para ser
implementados.

O WS-I BP restringe os padrões a apenas dois:

  • Operações
    de solicitação-resposta com um <wsdl:input> seguido por um <wsdl:output>
  • Operações unidirecionais com apenas um <wsdl:input>

No caso de operações de solicitação-resposta (o tipo que é, de longe, o mais comum), os elementos <wsdl:input> e <wsdl:output> podem ser seguidos por qualquer quantidade de elementos <wsdl:fault>.

Cada elemento <wsdl:input>, <wsdl:output> ou <wsdl:fault> faz referência a uma descrição de mensagem por meio do atributo obrigatóriomessage
. Essa referência é qualificada pelo namespace, portanto, geralmente
precisa incluir um prefixo.

É possível ver exemplos disso na Listagem 1, com o elemento <wsdl:input message=”tns:getBookMessage”/> usado na descrição da operação getBook. (O prefixo tns é definido no elemento de raiz <wsdl:definitions> com o mesmo URI do namespace que o atributo targetNamespace).

É possível considerar <wsdl:portType> como equivalente lógico de uma interface Java na maioria dos aspectos, com os elementos <wsdl:operation> equivalentes aos métodos, os elementos <wsdl:input> como parâmetros de método, os elementos <wsdl:output> como retornos de método e os elementos <wsdl:fault>
como exceções verificadas.

A geração de código Java a partir do WSDL
usa essas correspondências, da mesma forma que a maioria das ferramentas
que gera WSDL a partir do código Java já existente.

<wsdl:binding> representam uma instância da interface abstrata definida por um <wsdl:portType>, visto na Listagem 2 no início de BookServerImpl.wsdl. O atributo type dá o nome qualificado do tipo de porta implementado pela ligação.

Os elementos filho de <wsdl:binding> dão os detalhes da implementação do tipo de porta. Os elementos filho do namespace do WSDL correspondem aos de <wsdl:portType> e devem usar os mesmos valores de referências de name, e não referências qualificadas pelo namespace, como acontece com a referência de <wsdl:portType>.

A Figura 1 mostra essa conexão no nível de <wsdl:operation> com linhas pontilhadas. A mesma conexão por nome se aplica aos elementos filho <wsdl:input>/<wsdl:output>/<wsdl:fault> dos elementos <wsdl:operation>.
Apesar dessa reutilização dos nomes de elemento, o conteúdo desses elementos é muito diferente quando eles são filhos de <wsdl:binding> em vez do elemento <wsdl:portType>.

<wsdl:binding> é onde as extensões definidas pelo WSDL entram em cena. O elemento filho <soap:binding>
é usado na definição de um serviço SOAP (o único tipo de serviço
permitido pelo WS-I BP, embora o WSDL 1.1 também permita ligações HTTP).

Esse elemento <soap:binding> usa o atributo obrigatório transport para definir o tipo de transporte usado pela ligação (HTTP, como mostra o valor http://schemas.xmlsoap.org/soap/http na Listagem 2, é a única opção permitida pelo WS-I BP).

O atributo opcional style permite escolher entre rpc e estilos de documento para a representação de dados XML (cujo padrão mais comum é document , que corresponde às mensagens que usam definições de elemento de esquema, e não definições de tipo).

Dentro de cada filho de <wsdl:operation> de <wsdl:binding>, um elemento <soap:operation> pode ser usado para especificar um valor de SOAPAction
para identificar solicitações que chamam essa operação (e talvez
também substituir a opção de rpc ou estilo de documento determinada pelo
elemento <soap:binding>, embora o WS-I BP proíba esse uso).

Cada elemento filho <wsdl:input>/<wsdl:output>/<wsdl:fault> contém outro elemento de extensão que, no caso da Listagem 2, é sempre <soap:body>
(que indica que os dados da mensagem são enviados no corpo da mensagem
SOAP. Também é possível enviar dados e até mesmo falhas nos cabeçalhos
SOAP, embora eu considere essa prática inadequada) para um <wsdl:input> ou <wsdl:output> ou o equivalente <soap:fault> usado com um <wsdl:fault>.

O componente final de uma descrição do serviço de WSDL é o elemento <wsdl:service>, que é formada por um agrupamento de elementos <wsdl:port>.
Cada elemento <wsdl:port> associa um endereço de acesso com um <wsdl:binding>. O endereço de acesso é fornecido pelo elemento de extensão aninhado <soap:address>.

SOAP 1.1 x 1.2

O SOAP 1.1 tem sido usado amplamente para serviços da Web desde que a
especificação foi publicada em 2000. O SOAP 1.2 foi desenvolvido com um
suporte mais amplo do segmento de mercado por meio do W3C e publicado
como padrão oficial do W3C em 2007.

O SOAP 1.2 é mais bem documentado e
mais limpo que o SOAP 1.1 – alguns dos aspectos mais feios do 1.1 foram
removidos cirurgicamente. Apesar da estrutura mais limpa, para a maioria
dos serviços da Web, na prática há pouca diferença entre os dois.

Provavelmente, o recurso mais significativo do SOAP 1.2 é o fato de ser a
única forma suportada oficialmente de usar o suporte melhorado para
anexos de SOAP fornecido pelo XML-binary Optimized Packaging (XOP) e
pelo Message Transmission Optimization Mechanism (MTOM) de SOAP.

Eu
venho usando o SOAP 1.1 na série
Serviços da Web Java até
hoje, porque algumas pilhas mais antigas não suportam o SOAP 1.2, mas o
1.2 provavelmente é uma opção melhor para o desenvolvimento de novos
serviços da Web. 

Trabalhando com o WSDL

Com todas as variações de esquemas e regras referentes a documentos
do WSDL 1.1, não é de se estranhar que muitos documentos não
correspondam à forma de boas práticas definidas pelo WS-I BP. O suporte
de todas as pilhas de serviços da Web para muitas variações das formas
de boas práticas ajudou a perpetuar o uso de construções desatualizadas
ou incorretas, o que leva à disseminação de más práticas em todo o
segmento de mercado.

E eu certamente não estou imune a esse contágio. Ao revisar os documentos WSDL que forneci como código de amostra para
esta série, eu me surpreendi ao constatar que nenhum estava totalmente
correto.

Portanto, quando eu me propus a escrever este artigo, pensei que
seria bom incluir uma ferramenta que as pessoas pudessem usar para
verificar documentos WSDL com relação às regras de boas práticas.

Partindo daí, a transformação dos documentos WSDL na forma de boas
práticas pareceu um passo pequeno a ser dado, desde que o WSDL não tenha
erros. Isso acabou dando muito mais trabalho do que eu havia planejado
originalmente, e os detalhes completos do modelo serão desenvolvidos nos
dois artigos seguintes desta série.

Foram desenvolvidos diversos modelos diferentes para trabalhar com
documentos WSDL na linguagem Java, inclusive o amplamente usado Web
Services Description Language for Java Toolkit (WSDL4J), a implementação
de referência do JSR 110.
Aparentemente nenhum desses modelos servia para o que eu queria fazer,
por causa dos objetivos duplos de primeiramente ler documentos WSDL em
qualquer forma razoável e reportar tanto erros quanto variações de boas
práticas e, em segundo lugar, escrever documentos WSDL sem erros e
reformatados de forma condizente com as boas práticas.

O WSDL4J, por
exemplo, não mantinha a ordem dos elementos a partir da entrada, para
que eu pudesse relatar problemas de ordem e também não trata definições
de esquema – portanto, não podia ser usado diretamente para verificar as
referências a partir dos elementos <wsdl:part>.
Portanto, eu pude escolher entre estabelecer objetivos de forma mais
realista ou escrever o meu próprio modelo. Obviamente, optei por
escrever meu próprio modelo.

Validação x verificação

Uso o termo verificação neste artigo para designar a verificação para ver se o documento WSDL está correto, porque o termo alternativo validação
geralmente é usado, no contexto dos documentos XML, para designar a
verificação de documentos com relação a uma definição de esquema.

Modelo de WSDL

Eu já tinha implementado um modelo de WSDL parcial para uso com a
ligação de dados JiBX, como parte do projeto JiBX/WS. Esse modelo foi
projetado somente para saída e envolve um número relativamente pequeno
de classes que, em alguns casos, combinam dados de elementos aninhados
da estrutura XML do WSDL (o <wsdl:message> combinado com um único filho de <wsdl:part>, o <wsdl:input>, <wsdl:output> e <wsdl:fault> dentro de um <wsdl:binding> combinado com um elemento <soap:body> ou <soap:fault>
e assim por diante).

Essa estrutura de classe compacta facilitou o
desenvolvimento do subconjunto de documentos WSDL suportados pela
estrutura, mas no início quando pensei em basear uma ferramenta de
verificação e reestruturação naquele modelo, percebi que o suporte para
entradas de um WSDL que poderia estar mal estruturado exigiria um modelo
mais próximo da representação XML.

A geração de códigos a partir do esquema WS-I BP para WSDL 1.1 era
outra opção. Quando examinei essa possibilidade, percebi que o ato de
simplesmente usar as classes geradas diretamente seria uma bagunça,
porque o esquema inclui tipos redundantes e algumas construções
estranhas usadas para representar os diversos padrões de troca de
mensagens (na época, alguns deles eram proibidos pelo texto do WS-I BP).

Então, acabei desenvolvendo as classes manualmente, embora fosse
possível obter um resultado final praticamente igual se eu começasse com
o código gerado a partir do esquema e eliminasse a duplicação e a
complexidade desnecessária.

A ligação de dados JiBX suporta diversas
ligações para as mesmas classes – portanto, eu pude configurar a ligação
de entradas para tratar toda a variedade de opções permitidas por
qualquer versão do WSDL 1.1 e configurar a ligação de saídas para
realizar a saída do WSDL somente de forma compatível com as boas
práticas.

A Listagem 3 mostra parte da classe Definitions, que corresponde ao elemento-raiz <wsdl:definitions>:

Listagem 3. Classe Definitions (parcial)

public class Definitions extends ElementBase
{
/** Enumeration of child elements, in expected order. */
static enum AddState {
invalid, imports, types, message, portType, binding, service };

/** List of allowed attribute names. */
public static final StringArray s_allowedAttributes =
new StringArray(new String[] { "name", "targetNamespace" });

/** Validation context in use. */
private ValidationContext<ElementBase,Definitions> m_validationContext;

/** Current state (used for checking order in which child elements are added). */
private AddState m_state;

/** Name for this definitions. */
private String m_name;

/** Target namespace for WSDL. */
private String m_targetNamespace;

/** List of all import child elements. */
private List<Import> m_imports = new ArrayList<Import>();

/** List of all types child elements. */
private List<Types> m_types = new ArrayList<Types>();

/** List of all message child elements. */
private List<Message> m_messages = new ArrayList<Message>();

/** List of all portType child elements. */
private List<PortType> m_portTypes = new ArrayList<PortType>();

/** List of all binding child elements. */
private List<Binding> m_bindings = new ArrayList<Binding>();

/** List of all services child elements. */
private List<Service> m_services = new ArrayList<Service>();

/** Map from qualified name to message in this definition. */
private Map<QName,Message> m_nameMessageMap =
new HashMap<QName,Message>();

/** Map from qualified name to port type in this definition. */
private Map<QName,PortType> m_namePortTypeMap =
new HashMap<QName,PortType>();

/** Map from qualified name to message in this definition. */
private Map<QName,Binding> m_nameBindingMap =
new HashMap<QName,Binding>();

/** Map from qualified name to service in this definition. */
private Map<QName,Service> m_nameServiceMap =
new HashMap<QName,Service>();
...
/**
* Check state transitions between different types of child elements.
* If the elements are not in the expected order,
* this flags the first out-of-order element for reporting.
* @param state new add state
* @param comp element component
*/
private void checkAdd(AddState state, ElementBase comp) {
if (m_state != state) {
if (m_state == null || (m_state != AddState.invalid &&
state.ordinal() > m_state.ordinal())) {

// advanced on to another type of child element
m_state = state;

} else if (state.ordinal() < m_state.ordinal()) {

// report child element out of order
m_validationContext.addWarning
("Child element of wsdl:definitions out of order", comp);
m_state = AddState.invalid;
}
}
}
...
/**
* Add an unmarshalled wsdl:message child element. This also indexes the message by
* name for validation access.
*
* @param child
*/
public void addMessage(Message child) {
checkAdd(AddState.message, child);
m_messages.add(child);
addName(child.getName(), child, m_nameMessageMap);
}
...

A organização dos dados de elementos filho na Listagem 3
mostra como o modelo suporta entradas de forma geral e saídas de
acordo com as boas práticas. Em vez de usar uma lista única de elementos
filho de todos os tipos, ele usa listas separadas para cada tipo.

A
ligação JiBX de entrada trata os elementos filho como um conjunto não
ordenado, chamando um método de configuração específico para o tipo de
elemento sempre que um elemento filho é desserializado.

O método de
configuração inclui a instância em uma lista com tipos em vez de
substituir os valores anteriores, como se pode ver no método de
configuração addMessage() usado para os elementos filho <wsdl:message>. Cada método de configuração também executa uma verificação de estado
para capturar casos em que os elementos não estão na ordem esperada.

Os atributos e elementos de extensão (basicamente, qualquer atributo
ou elemento que não usa o namespace WSDL 1.1) são permitidos em qualquer
um dos elementos de WSDL. As configurações WS-Policy integradas nos
documentos WSDL dos artigos anteriores desta série são exemplos desses
elementos de extensão, bem como as referências reais a políticas.

A boa
prática em relação a esses elementos de extensão é que eles precedam os
elementos filho do namespace do WSDL 1.1, e essa é a forma como eles são
tratados na ligação de saída.

A ligação de entrada processa elementos
de extensão e atributos usando o código proveniente de uma classe base
das classes de elementos do WSDL, que não é mostrado na Listagem 3, e permite que os elementos estejam em qualquer ordem – gerando um aviso se eles seguem um elemento do namespace do WSDL 1.1.

O modelo trata elementos de extensão conhecidos usando ligações
separadas para cada namespace de extensão, cada um com o seu próprio
conjunto de classes. Abordarei a manipulação desses elementos de
extensão mais detalhadamente na próxima parte de Serviços da Web Java , que também fornecerá mais detalhes do código de origem.

Verificando o modelo

Parte da verificação básica dos dados de WSDL é realizada à medida
que objetos desserializados correspondentes a elementos são incluídos na
estrutura em árvore do documento WSDL, como mostra o código de addMessage() no final da Listagem 3.

Esse método usa o método checkAdd() para verificar a ordem dos elementos filho e o método addName() para se certificar de que um nome válido foi fornecido (o texto corresponde ao tipo de esquema de NCName
e o valor é exclusivo dentro do tipo de elemento) e para correlacionar
o nome ao objeto.

No entanto, isso é só a verificação das informações
mais básicas sobre o elemento de forma isolada. É necessário usar mais
código de verificação para verificar outras propriedades de cada
elemento e as inter-relações entre os elementos.

O JiBX permite chamar ganchos de extensão-usuário como parte dos
processos de serialização e desserialização. O modelo do WSDL usa um
gancho de extensão desse tipo, um método pós-configuração para executar
a lógica de verificação.

O método pós-configuração é chamado depois que
o processo de desserialização do objeto associado é concluído. Portanto, frequentemente é uma boa forma de executar checagens de
verificações de objetos.

No caso da verificação do WSDL, a abordagem
mais fácil é executar toda a verificação de objetos a partir de um único
método pós-configuração, para o elemento-raiz <wsdl:definitions>. Essa abordagem evita problemas de referências antecipadas a
componentes do documento WSDL quando os componentes não estão na ordem
esperada.

Mais extensões

Neste artigo, você viu os fundamentos da estrutura e uso do WSDL,
juntamente com a introdução a um modelo de dados Java para WSDL
destinado a suportar a verificação de documentos WSDL e a conversão
desses documentos para uma forma condizente com as boas práticas.

O próximo artigo da série
aprofunda este tópico, examinando os problemas frequentes ao escrever
asserções de política de WS e política de Segurança WS. Também tratará
do processamento do modelo de WSDL e verificação com mais profundidade,
incluindo a extensão do modelo para incluir asserções de política de WS e
política de Segurança WS integradas ao WSDL.

Recursos

Aprender

***

artigo publicado originalmente no developerWorks Brasil, por Dennis Sosnoski

Dennis Sosnoski é um consultor e instrutor especializado em XML e serviços da Web baseados em Java.
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 e a estrutura de serviços da Web associada JiBX/WS, assim como um committer na estrutura de serviços da Web Apache Axis2.
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.