

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Tratamento de erros
<a name="errorhandling"></a>

**Topics**
+ [TryCatchFinally Semântica](#errorhandling.trycatchfinally)
+ [Cancelamento](#test.cancellation.resources)
+ [Aninhado TryCatchFinally](#errorhandling.nested)

O constructo `try`/`catch`/`finally` no Java simplifica o tratamento de erros e é usado de forma ubíqua. Ele permite associar manipuladores de erros a um bloco de código. Internamente, isso funciona inserindo metadados adicionais sobre os manipuladores de erro na pilha de chamada. Quando uma exceção é gerada, o tempo de execução examina a pilha de chamada para localizar um manipulador de erros associado e o invocar, e se nenhum manipulador de erros apropriado for localizado, ele propagará a exceção para cima na cadeia de chamada.

Isso funciona bem para código síncrono, mas a manipulação de erros em assíncronos e em programas distribuídos impõe desafios adicionais. Como uma chamada assíncrona retorna imediatamente, o chamador não está na pilha de chamadas quando o código assíncrono é executado. Isso significa que as exceções não tratadas no código assíncrono não podem ser tratadas pelo chamador na forma habitual. Normalmente, as exceções originadas em código assíncrono são tratadas passando o estado de erro para um retorno de chamada que é passado para o método assíncrono. Como alternativa, se um `Future<?>` estiver sendo usado, ele relatará um erro quando você tentar acessá-lo. Isso não é o ideal porque o código que recebe a exceção (o retorno de chamada ou o código que usa o `Future<?>`) não tem o contexto da chamada original e não pode controlar adequadamente a exceção. Além disso, em um sistema assíncrono distribuído, com componentes que são executados simultaneamente, mais de um erro pode ocorrer simultaneamente. Esses erros podem ser de tipos e de severidades diferentes e precisam ser tratados de forma adequada.

A limpeza de um recurso após uma chamada assíncrona também é difícil. Ao contrário do código síncrono, você não pode usá-lo try/catch/finally no código de chamada para limpar recursos porque o trabalho iniciado no bloco try ainda pode estar em andamento quando o bloco final for executado.

A estrutura fornece um mecanismo que torna o tratamento de erros em código assíncrono distribuído semelhante e quase tão simples quanto o do Java. try/catch/finally

```
ImageProcessingActivitiesClient activitiesClient
     = new ImageProcessingActivitiesClientImpl();

public void createThumbnail(final String webPageUrl) {

  new TryCatchFinally() {

    @Override
    protected void doTry() throws Throwable {
      List<String> images = getImageUrls(webPageUrl);
      for (String image: images) {
        Promise<String> localImage
            = activitiesClient.downloadImage(image);
        Promise<String> thumbnailFile
            = activitiesClient.createThumbnail(localImage);
        activitiesClient.uploadImage(thumbnailFile);
      }
    }

    @Override
    protected void doCatch(Throwable e) throws Throwable {

      // Handle exception and rethrow failures
      LoggingActivitiesClient logClient = new LoggingActivitiesClientImpl();
      logClient.reportError(e);
      throw new RuntimeException("Failed to process images", e);
    }

    @Override
    protected void doFinally() throws Throwable {
      activitiesClient.cleanUp();
    }
  };
}
```

A classe `TryCatchFinally` e suas variantes, `TryFinally` e `TryCatch`, funcionam de forma semelhante ao `try`/`catch`/`finally` do Java. Usando-a, você pode associar manipuladores de exceção a blocos de código de fluxo de trabalho que podem executar como tarefas assíncronas e remotas. O método `doTry()` é logicamente equivalente ao bloco `try`. A estrutura executa automaticamente o código em `doTry()`. Uma lista de objetos `Promise` pode ser passada para o construtor de `TryCatchFinally`. O método `doTry` será executado quando todos os objetos `Promise `passados para o construtor estiverem prontos. Se uma exceção for gerada pelo código que foi invocado de forma assíncrona em `doTry()`, qualquer trabalho pendente em `doTry()` será cancelado e `doCatch()` será chamado para tratar a exceção. Por exemplo, na lista acima, se `downloadImage` lançar uma exceção, `createThumbnail` e `uploadImage` serão cancelados. Finalmente, `doFinally()` é chamado quando todo o trabalho assíncrono for feito (concluído, com falha ou cancelado). Ele pode ser usado para limpeza de recursos. Você também pode aninhar essas classes para atender às suas necessidades.

Quando uma exceção é relatada em `doCatch()`, a estrutura fornece uma pilha de chamada lógica completa que inclui chamadas assíncronas e remotas. Isso pode ser útil ao depurar, especialmente se você tiver métodos assíncronos que chamam outros métodos assíncronos. Por exemplo, uma exceção de downloadImage produzirá uma exceção como esta:

```
RuntimeException: error downloading image
  at downloadImage(Main.java:35)
  at ---continuation---.(repeated:1)
  at errorHandlingAsync$1.doTry(Main.java:24)
  at ---continuation---.(repeated:1)
…
```

## TryCatchFinally Semântica
<a name="errorhandling.trycatchfinally"></a>

A execução de um programa AWS Flow Framework para Java pode ser visualizada como uma árvore de ramificações em execução simultânea. Uma chamada para um método assíncrono, para uma atividade e para o próprio `TryCatchFinally` cria uma nova ramificação nessa árvore de execução. Por exemplo, o fluxo de trabalho de processamento de imagem pode ser visualizado como na árvore mostrada na figura a seguir.

![\[Árvore de execução assíncrona\]](http://docs.aws.amazon.com/pt_br/amazonswf/latest/awsflowguide/images/trycatchfinally.png)


Um erro em uma ramificação de execução causa o desenrolamento da ramificação, assim como uma exceção causa o desenrolamento da pilha de chamada em um programa Java. O desenrolamento continua movendo a movimentação da ramificação de execução para cima até que o erro seja tratado ou a raiz da árvore seja acessada, nesse caso a execução do fluxo de trabalho é encerrada.

A estrutura relata erros que acontecem durante o processamento de tarefas como exceções. Ela associa os manipuladores de exceção (métodos `doCatch()`) definidos em `TryCatchFinally` com todas as tarefas criadas pelo código no `doTry()` correspondente. Se uma tarefa falhar, por exemplo, devido a um tempo limite ou a uma exceção não tratada, a exceção apropriada será levantada e o `doCatch()` correspondente será invocado para tratá-la. Para conseguir isso, a estrutura trabalha em conjunto com o Amazon SWF para propagar erros remotos e ressuscitá-los como exceções no contexto do chamador.

## Cancelamento
<a name="test.cancellation.resources"></a>

Quando ocorre uma exceção em código síncrono, o controle salta diretamente para o bloco `catch`, ignorando qualquer código restante no bloco `try`. Por exemplo: 

```
try {
    a();
    b();
    c();
}
catch (Exception e) {
    e.printStackTrace();
}
```

Neste código, se `b()` gerar uma exceção, `c()` nunca será invocado. Compare isso com um fluxo de trabalho:

```
new TryCatch() {

    @Override
    protected void doTry() throws Throwable {
        activityA();
        activityB();
        activityC();
    }

    @Override
    protected void doCatch(Throwable e) throws Throwable {
        e.printStackTrace();
    }
};
```

Nesse caso, todas as chamadas para `activityA`, `activityB` e `activityC` retornam com êxito e resultam na criação de três tarefas que serão executadas assincronamente. Digamos que posteriormente a tarefa para a `activityB` resulte em um erro. Esse erro é registrado no histórico pelo Amazon SWF. Para tratar esse erro, a estrutura primeiro tentará cancelar todas as outras tarefas originadas no escopo do mesmo `doTry()`, nesse caso, `activityA` e `activityC`. Quando todas essas tarefas forem concluídas (canceladas, com falha ou concluídas com êxito), o método `doCatch()` apropriado será invocado para tratar o erro.

Ao contrário do exemplo síncrono, onde `c()` nunca foi executado, `activityC` foi invocada e uma tarefa foi programada para execução. Portanto, a estrutura fará uma tentativa de cancelá-la, mas não há garantia de que ela será cancelada. O cancelamento não pode ser garantido porque a atividade pode já ter sido concluída, pode ignorar a solicitação de cancelamento ou pode falhar devido a um erro. Contudo, a estrutura fornece a garantia de que `doCatch()` é chamado somente depois que todas as tarefas iniciadas no `doTry()` correspondente foram concluídas. Também garante que `doFinally()` seja chamado somente depois que todas as tarefas iniciadas em `doCatch()` e `doTry()` foram concluídas. Se, por exemplo, as atividades no exemplo acima dependerem umas das outras, digamos que `activityB` dependa de `activityA` e `activityC` de `activityB`, o cancelamento de `activityC` será imediato porque não está programado no Amazon SWF até que `activityB` seja concluído:

```
new TryCatch() {

    @Override
    protected void doTry() throws Throwable {
        Promise<Void> a = activityA();
        Promise<Void> b = activityB(a);
        activityC(b);
    }

    @Override
    protected void doCatch(Throwable e) throws Throwable {
        e.printStackTrace();
    }
};
```

### Pulsação de atividade
<a name="errorhandling.activity.heartbeat"></a>

O mecanismo de cancelamento cooperativo do AWS Flow Framework for Java permite que as tarefas de atividades em voo sejam canceladas normalmente. Quando o cancelamento é acionado, as tarefas que foram bloqueadas ou estão aguardando para serem atribuídas a um operador são canceladas automaticamente. Se, no entanto, uma tarefa já estiver atribuída a um operador, a estrutura solicitará que a atividade seja cancelada. A implementação da atividade deve tratar explicitamente essas solicitações de cancelamento. Isso é feito relatando a pulsação da atividade.

Relatar a pulsação permite que a implementação da atividade relate o progresso de uma tarefa de atividade em andamento, o que é útil para o monitoramento e permite que a atividade verifique se há solicitações de cancelamento. O método `recordActivityHeartbeat` gerará uma `CancellationException` se um cancelamento tiver sido solicitado. A implementação da atividade pode capturar essa exceção e responder à solicitação de cancelamento, ou pode ignorar a solicitação engolindo a exceção. Para honrar a solicitação de cancelamento, a atividade deve executar a limpeza desejada, se houver, e, em seguida gerar a `CancellationException` novamente. Quando essa exceção é gerada em uma implementação de atividade, a estrutura registra que a tarefa de atividade foi concluída em estado cancelado.

O exemplo a seguir mostra uma atividade que faz download e processa imagens. Se houver pulsação depois do processamento de cada imagem e se o cancelamento for solicitado, ele limpará e gerará a exceção novamente para reconhecer o cancelamento.

```
@Override
public void processImages(List<String> urls) {
    int imageCounter = 0;
    for (String url: urls) {
        imageCounter++;
        Image image = download(url);
        process(image);
        try {
            ActivityExecutionContext context
                 = contextProvider.getActivityExecutionContext();
            context.recordActivityHeartbeat(Integer.toString(imageCounter));
        } catch(CancellationException ex) {
            cleanDownloadFolder();
            throw ex;
        }
    }
}
```

Relatar a pulsação da atividade não é necessário, mas é recomendável se a atividade for de longa execução ou estiver executando operações caras que você deseja cancelar em condições de erros. Você deve chamar `heartbeatActivityTask` periodicamente na implementação da atividade.

Se o tempo limite da atividade for esgotado, `ActivityTaskTimedOutException` será gerada, e `getDetails` no objeto de exceção retornará os dados passados para a última chamada bem-sucedida para `heartbeatActivityTask` para a tarefa de atividade correspondente. A implementação do fluxo de trabalho pode usar essas informações para determinar quanto de progresso foi feito antes do tempo limite da tarefa de atividade ter esgotado.

**nota**  
Não é uma boa prática fazer heartbeat com muita frequência, pois o Amazon SWF pode aplicar o controle de utilização às solicitações de heartbeat. Consulte o [Guia do desenvolvedor do Amazon Simple Workflow Service](https://docs.aws.amazon.com/amazonswf/latest/developerguide/) para obter informações sobre os limites impostos pelo Amazon SWF.

### Cancelamento explícito de uma tarefa
<a name="errorhandling.canceltask"></a>

Além das condições de erro, há outros casos em você pode cancelar explicitamente uma tarefa. Por exemplo, uma atividade para processar pagamentos usando um cartão de crédito pode precisar ser cancelada se o usuário cancelar o pedido. A estrutura permite que você cancele explicitamente tarefas criadas no escopo de um `TryCatchFinally`. No exemplo a seguir, a tarefa de pagamento será cancelada se um sinal for recebido enquanto o pagamento estava sendo processado.

```
public class OrderProcessorImpl implements OrderProcessor {
    private PaymentProcessorClientFactory factory
        = new PaymentProcessorClientFactoryImpl();
    boolean processingPayment = false;
    private TryCatchFinally paymentTask = null;

    @Override
    public void processOrder(int orderId, final float amount) {
        paymentTask = new TryCatchFinally() {

            @Override
            protected void doTry() throws Throwable {
                processingPayment = true;

                PaymentProcessorClient paymentClient = factory.getClient();
                paymentClient.processPayment(amount);
            }

            @Override
            protected void doCatch(Throwable e) throws Throwable {
                if (e instanceof CancellationException) {
                    paymentClient.log("Payment canceled.");
                } else {
                    throw e;
                }
            }

            @Override
            protected void doFinally() throws Throwable {
                processingPayment = false;
            }
        };

    }

    @Override
    public void cancelPayment() {
        if (processingPayment) {
            paymentTask.cancel(null);
        }
    }
}
```

### Recebimento de notificação de tarefas canceladas
<a name="errorhandling.canceltask.notification"></a>

Quando uma tarefa é concluída em estado cancelado, a estrutura informa a lógica do fluxo de trabalho gerando uma `CancellationException`. Quando uma atividade é concluída em estado cancelado, é feito um registro no histórico, e a estrutura chama o `doCatch()` apropriado com uma `CancellationException`. Conforme mostrado no exemplo anterior, quando a tarefa de processamento de pagamento é cancelada, o fluxo de trabalho recebe uma `CancellationException` 

Uma `CancellationException` não tratada é propagada para cima na ramificação de execução como qualquer outra exceção. No entanto, o método `doCatch()` receberá a `CancellationException` apenas se não houver outra exceção no escopo. Outras exceções têm prioridade mais alta que um cancelamento. 

## Aninhado TryCatchFinally
<a name="errorhandling.nested"></a>

Você pode aninhar `TryCatchFinally`s para atender às suas necessidades. Como cada uma `TryCatchFinally` cria uma nova ramificação na árvore de execução, você pode criar escopos aninhados. As exceções no escopo pai provocarão tentativas de cancelamento de todas as tarefas iniciadas pelos `TryCatchFinally`s aninhados dentro dele. No entanto, as exceções em `TryCatchFinally` aninhado não são propagadas automaticamente para o pai. Para propagar uma exceção de um `TryCatchFinally` aninhado para o `TryCatchFinally` que o contém, você deve gerar novamente a exceção em `doCatch()`. Ou seja, apenas as exceções não tratadas são movidas para acima, assim como o `try`/`catch` do Java. Se você cancelar um `TryCatchFinally` aninhado chamando o método de cancelamento, o `TryCatchFinally` aninhado será cancelado, mas o `TryCatchFinally` que o contém não será cancelado automaticamente.

![\[Aninhado TryCatchFinally\]](http://docs.aws.amazon.com/pt_br/amazonswf/latest/awsflowguide/images/nested.png)


```
new TryCatch() {
    @Override
    protected void doTry() throws Throwable {
        activityA();

        new TryCatch() {
            @Override
            protected void doTry() throws Throwable {
                activityB();
            }

            @Override
            protected void doCatch(Throwable e) throws Throwable {
                reportError(e);
            }
        };

        activityC();
    }

    @Override
    protected void doCatch(Throwable e) throws Throwable {
        reportError(e);
    }
};
```