Labs SD >
O exemplo abaixo demonstra como configurar os tempos de espera pelas respostas de Web Services. No fim do tempo, caso a resposta não tenha sido recebida, é lançada uma exceção.
O JAX-WS distingue dois timeouts distintos:
Exemplo:
As operações unidireccionais de Web Service são aquelas que não enviam resposta, logo o programa cliente é desbloqueado mal a mensagem de pedido saia através da rede. Este exemplo demonstra como definir operações unidireccionais.
Em situações em que o cliente não pretenda ficar bloqueado à espera da resposta do servidor (podendo-a receber posteriormente, quando o cliente assim o desejar), é possível fazê-lo através de uma invocação assíncrona.
Uma possível forma de fazer invocações assíncronas é através dos métodos com sufixo Async. Para que estes métodos sejam gerados é necessário do lado do cliente indicar um ficheiro de binding, na pasta src/jaxws. Os stubs assim gerados passam a incluir tanto os métodos para invocação sincrona como assíncrona.
<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://java.sun.com/xml/ns/jaxws" > <bindings node="wsdl:definitions"> <enableAsyncMapping>true</enableAsyncMapping> </bindings> </bindings>
Existem 2 formas alternativas de programar um cliente que recorre a chamadas assíncronas. De seguida explicamos cada uma.
Para uma invocação assíncrona, o cliente deve executar um método com o sufixo Async e de seguida usar o método Response.isDone() para verificar se a resposta já chegou. Nesta solução o cliente invoca o método remoto sem ficar bloqueado, ficando responsável por verificar, periódicamente, se o servidor já respondeu (através do objecto Response). Só depois da resposta ter chegado, pode então o cliente obter o seu resultado através do objecto Response.
// asynchronous call with polling (too slow to wait for it) Responseresponse = port.echoAsync(name); while (!response.isDone()) { Thread.sleep(100 /* milliseconds */); /* while waiting for response do other calls... */ String result = port.fastEcho(name); System.out.print("Synchronous call result: "); System.out.println(result); } System.out.println("Async->" + response.get().getReturn());
Um outro modelo de funcionamento é o registo de um objecto de callback do tipo AsyncHandler aquando da execução da chamada assíncrona. Quando a resposta chega, um método desse objecto é invocado. Este funcionamento é semelhante ao atendimento de interrupções num processador. A rotina de callback corresponde à rotina de atendimento da interrupção. O receber uma resposta do servidor corresponde à interrupção.
static boolean finished = false; ... // asynchronous call with callback port.echoAsync(name, new AsyncHandler() { @Override public void handleResponse(Response response) { try { System.out.println(); System.out.print("Asynchronous call result arrived: "); System.out.println(response.get().getReturn()); finished = true; } catch (InterruptedException e) { System.out.println("Caught interrupted exception."); System.out.print("Cause: "); System.out.println(e.getCause()); } catch (ExecutionException e) { System.out.println("Caught execution exception."); System.out.print("Cause: "); System.out.println(e.getCause()); } } }); while (!finished) { Thread.sleep(100); System.out.print("."); // do something usefull while waiting... System.out.flush(); }
Em ambos os casos, a resposta é obtida invocando o método response.get() que lança uma exceção caso esta tenha sido retornada pelo método remoto. Caso o método remoto retorne Void, este método lancará uma NullPointerException. Caso contrário, o objecto retornado pode ser obtido com o método getReturn().
Consultar o exemplo de Cliente-Servidor com invocações assíncronas:
Repare que não é preciso alterar o servidor para que o cliente possa fazer invocações assíncronas.
Na segunda parte do projeto os saldos dos utilizadores serão replicados, de acordo com um protocolo de quorum consensus, sendo os servidores de estação os gestores de réplicas, e o servidor Binas o cliente deste sistema replicado. Desta forma, as estações devem implementar duas novas operações: getBalance e setBalance.
Este exercício tem como objetivo guiar cada grupo nos primeiros passos para resolver a segunda parte do projeto. Mais precisamente, sugerimos que comecem por implementar as duas novas operações oferecidas por cada gestor de réplica; e modificar o Binas para invocar essas operações quando pretende aceder ao saldo de um utilizador.
Para introduzir as novas operações getBalance e setBalance é necessário estender o contrato WSDL do servidor de estação.
Mais precisamente:
Compete a cada grupo prever as exceções que fazem sentido para cada operação.
Passos recomendados:
Para começar, por simplificação, sugere-se que se comece por concretizar as invocações das operações getBalance e setBalance com chamadas síncronas; o Binas deve aguardar pelas respostas de todas as estações, ou seja, de acordo com a figura seguinte:
Bom trabalho!
© Docentes de Sistemas Distribuídos,
Dep. Eng. Informática,
Técnico Lisboa