Labs SD >

Web Services III

Objectivos

Índice:


Limite de tempo

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:

 


Operações unidirecionais

As operações unidirecionais 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 unidirecionais.

 


Operações assíncronas

Até agora vimos exemplos de invocações remotas síncronas: o cliente faz um pedido ao servidor e fica bloqueado à espera da resposta. Caso o cliente não pretenda ficar bloqueado à espera da resposta do servidor, é possível fazê-lo através de uma invocação assíncrona. Neste caso o cliente faz o pedido, continua a executar, e vai receber a resposta mais tarde.

A 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 síncrona 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 duas formas alternativas de programar um cliente que recorre a chamadas assíncronas. De seguida explicamos cada uma.

Invocação com polling

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, periodicamente, 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
    Response<EchoResponse> response = port.echoAsync(name);

    while (!response.isDone()) {
        Thread.sleep(10 /* milliseconds */);

        /* while waiting for response do something else... */
    	System.out.print(".");
    	System.out.flush();
    }

    System.out.print("Asynchronous call result: ");
    System.out.println(response.get().getReturn());

Invocação com callback

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 objeto é 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) {
	    // do something else while waiting..
    	Thread.sleep(10 /* milliseconds */);
    	System.out.print("."); 
    	System.out.flush();
    }

Em ambos os casos, a resposta é obtida invocando o método response.get(), que pode lançar uma exceção caso esta tenha sido retornada pelo método remoto. Caso o método remoto retorne Void, este método lançará 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. Apenas é necessário um stub diferente.

 


Exercício

O objetivo deste exercício é adaptar o código do módulo pts do projeto de forma a que as chamadas passem a ser feitas assincronamente. Como será discutido nas aulas teóricas, esta extensão permitirá implementar o protocolo de replicação da segunda parte do projeto de forma mais eficiente. Hoje estamos a aprender a técnica das chamadas assíncronas.

  1. Clonar o código da primeira entrega do projeto.
    Esta cópia vai ser usada apenas para este exercício.
  2. Criar pasta jaxws (caso ainda não exista) na diretoria: pts-ws-cli/src
  3. Adicionar um ficheiro binding.xml à pasta jaxws, semelhante ao apresentado acima, que ativa a opção enableAsyncMapping.
  4. Execute o comando de geração de código: O código gerado no stub vai conter métodos com o sufixo Async, que permitem fazer as chamadas assíncronas.
  5. Modificar a definição da classe PointsClient para remover a declaração de implementação da interface PortType e as anotações @Override.
        public class PointsClient {
            
    Este passo evita ter que implementar os métodos assíncronos para todas as operações.
    Neste exercício apenas vamos usar a operação pointsBalance que devolve o saldo de pontos.

Invocação com polling

  1. Adicionar o seguinte método à classe PointsClient:
  2.     public Response<PointsBalanceResponse> pointsBalanceAsync(String userEmail) {
            return port.pointsBalanceAsync(userEmail);
        }
        
  3. Adicionar no método main() da classe PointsClientApp
  4.     import javax.xml.ws.Response;
        import java.util.concurrent.ExecutionException;
    
        // ... (acrescentar no fim do método)
    
        String userEmail = "user@example.com";
        client.activateUser(userEmail);
        // asynchronous call with polling
        Response<PointsBalanceResponse> response = client.pointsBalanceAsync(userEmail);
    
        while (!response.isDone()) {
            Thread.sleep(10 /* milliseconds */);
            System.out.print(".");
    		System.out.flush();
        }
    
        try {
            System.out.println("(Polling) asynchronous call result: " + response.get().getReturn());
        } catch (ExecutionException e) {
            System.out.println("Caught execution exception.");
            System.out.print("Cause: ");
            System.out.println(e.getCause());
        }
        
  5. Na pasta pts-ws-cli fazer:
  6. Lançar o servidor pts-ws:
  7. Lançar o cliente pts-ws-cli:
  8. Analisar o resultado
  9. Experimentar introduzir um atraso no pts-ws, recompilar, executar novamente e comparar o resultado
  10. Compare a invocação síncrona com a invocação assíncrona com polling. Em ambos se espera pela resposta. Qual é a diferença entre ambos?

Invocação com callback

  1. Adicionar o seguinte método à classe PointsClient:
  2.     public Future<?> pointsBalanceAsync(String userEmail, AsyncHandler<PointsBalanceResponse> asyncHandler) {
            return port.pointsBalanceAsync(userEmail, asyncHandler);
        }
        
  3. Adicionar na classe PointsClientApp:
  4.     import javax.xml.ws.Response;
    
        public class PointsClientApp {
            static boolean received = false;
            
            public static void main(String[] args) throws Exception {
        
                // ... (acrescentar no fim do método)
    
                String userEmail1 = "user@example.com";
                client.activateUser(userEmail1);
                client.addPoints(userEmail1, 1100);
    
                // asynchronous call with callback
                client.pointsBalanceAsync(userEmail1, new AsyncHandler<PointsBalanceResponse>() {
                    @Override
                    public void handleResponse(Response<PointsBalanceResponse> response) {
                        try {
                            System.out.println();
                            System.out.print("(Callback) Asynchronous call result arrived: ");
                            System.out.println(response.get().getReturn());
                            received = 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 (!received) {
                    Thread.sleep(10 /* milliseconds */);
                    System.out.print(".");
                    System.out.flush();
                }
        
  5. Na pasta pts-ws-cli fazer:
  6. Lançar o servidor pts-ws:
  7. Lançar o cliente pts-ws-cli:
  8. Porque é que a variável received necessita de ser static?
  9. Quais as diferenças entre usar callback em vez de polling?

© Docentes de Sistemas Distribuídos, Dep. Eng. Informática, Técnico Lisboa