

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Gestione errori
<a name="errorhandling"></a>

**Topics**
+ [TryCatchFinally Semantica](#errorhandling.trycatchfinally)
+ [Annullamento](#test.cancellation.resources)
+ [Annidato TryCatchFinally](#errorhandling.nested)

Il costrutto `try`/`catch`/`finally` in Java semplifica la gestione degli errori ed è quindi utilizzato diffusamente. Consente di associare gestori di errori a un blocco di codice. Internamente, ciò avviene aggiungendo ulteriori metadati sui gestori di errori allo stack di chiamate. Quando viene generata un'eccezione, il runtime cerca un gestore di errori associato nello stack di chiamate e lo richiama; se non lo trova, propaga l'eccezione fino alla catena di chiamate.

Questo processo è appropriato per il codice sincrono, ma la gestione degli errori in programmi distribuiti e asincroni è più complesso. Poiché una chiamata asincrona ritorna immediatamente, il chiamante non è presente nello stack di chiamate quando viene eseguito il codice asincrono. Ciò significa che le eccezioni non gestite nel codice asincrono non possono essere gestite dal chiamante nel modo usuale. In genere, le eccezioni generate nel codice asincrono sono gestite passando lo stato di errore a un callback che viene passato a un metodo asincrono. Se in alternativa si utilizza `Future<?>`, viene restituito un errore quando tenti di accedervi. Questo processo non è ideale in quanto il codice che riceve l'eccezione (il callback o il codice che utilizza `Future<?>`) non dispone del contesto della chiamata originale e può non essere in grado di gestire l'eccezione in modo adeguato. Inoltre, in un sistema asincrono distribuito in cui i componenti sono eseguiti simultaneamente, possono verificarsi più errori contemporaneamente. Questi errori possono essere di tipo e gravità differenti e devono essere gestiti in modo appropriato.

Anche la pulizia delle risorse dopo una chiamata asincrona risulta alquanto complessa. A differenza del codice sincrono, non è possibile utilizzarlo try/catch/finally nel codice chiamante per ripulire le risorse perché il lavoro iniziato nel blocco try potrebbe essere ancora in corso quando viene eseguito il blocco finally.

Il framework fornisce un meccanismo che rende la gestione degli errori nel codice asincrono distribuito simile e quasi altrettanto semplice di quella di 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();
    }
  };
}
```

Il funzionamento della classe `TryCatchFinally` e delle relative varianti, ovvero `TryFinally` e `TryCatch`, è simile a quello dei blocchi Java `try`/`catch`/`finally`. Tale classe consente di associare i gestori di eccezioni a blocchi di codice di flusso di lavoro che possono essere eseguiti come task asincroni e remoti. Il metodo `doTry()` è equivalente, a livello di logica, al blocco `try`. Il framework esegue automaticamente il codice in `doTry()`. Un elenco di oggetti `Promise` può essere passato al costruttore di `TryCatchFinally`. Il metodo `doTry` sarà eseguito quanto tutti gli oggetti `Promise `passati al costruttore diventano pronti. Se un'eccezione viene generata dal codice richiamato in modo asincrono da `doTry()`, tutto il lavoro in sospeso in `doTry()` viene annullato e `doCatch()` viene chiamato per gestire l'eccezione. Ad esempio, nell'elenco qui sopra, se `downloadImage` genera un'eccezione, `createThumbnail` e `uploadImage` verranno annullati. Infine, `doFinally()` viene chiamato quando tutto il lavoro asincrono risulta terminato (completato, non riuscito o annullato). Questo metodo può essere utilizzato per la pulizia delle risorse. Puoi inoltre nidificare queste classi in base alle esigenze aziendali.

Quando un'eccezione è restituita in `doCatch()`, il framework fornisce uno stack di chiamate logiche che include chiamate asincrone e remote. Ciò può rivelarsi utile per il debug, soprattutto se hai dei metodi asincroni che chiamano altri metodi asincroni. Ad esempio, un'eccezione da downloadImage genererà un'eccezione come quella riportata di seguito:

```
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 Semantica
<a name="errorhandling.trycatchfinally"></a>

L'esecuzione di un programma AWS Flow Framework per Java può essere visualizzata come un albero di rami in esecuzione simultanea. Una chiamata a un metodo asincrono, a un'attività e a `TryCatchFinally` crea un nuovo ramo in tale struttura. Ad esempio, il flusso di lavoro di elaborazione di immagini può essere rappresentato dalla struttura ad albero illustrata di seguito.

![\[Struttura di esecuzione asincrona\]](http://docs.aws.amazon.com/it_it/amazonswf/latest/awsflowguide/images/trycatchfinally.png)


Un errore in un ramo dell'esecuzione comporterà la rimozione di quel ramo, proprio come un'eccezione provoca la rimozione dello stack di chiamate in un programma Java. La rimozione risale lungo il ramo di esecuzione fino a che l'errore viene gestito o viene raggiunta la radice della struttura ad albero, nel qual caso l'esecuzione di flusso di lavoro viene terminata.

Il framework segnala gli errori che si verificano durante l'elaborazione di task come eccezioni. Associa i gestori di eccezioni (metodi `doCatch()`) definiti in `TryCatchFinally` a tutti i task creati dal codice nel metodo `doTry()` corrispondente. Se un'attività fallisce, ad esempio a causa di un timeout o di un'eccezione non gestita, verrà sollevata l'eccezione appropriata e verrà invocata la corrispondente per gestirla. `doCatch()` A tal fine, il framework collabora con Amazon SWF per propagare gli errori remoti e li resuscita come eccezioni nel contesto del chiamante.

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

Quando si verifica un'eccezione nel codice sincrono, il controllo passa direttamente al blocco `catch`, ignorando il codice rimanente nel blocco `try`. Per esempio: 

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

In questo codice, se `b()` genera un'eccezione, `c()` non viene mai richiamato. Facciamo un raffronto con un flusso di lavoro:

```
new TryCatch() {

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

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

In questo caso, le chiamate a `activityA`, `activityB` e `activityC` hanno esito positivo e comportano la creazione di tre task che vengono eseguiti in modo asincrono. Supponiamo che successivamente il task per `activityB` restituisca un errore. Questo errore viene registrato nella cronologia da Amazon SWF. Per gestirlo, il framework dapprima tenterà di annullare tutti gli altri task originati nell'ambito dello stesso `doTry()`; in questo caso, `activityA` e `activityC`. Quanto tutti i task risultano terminati (annullati, non riusciti o completati), il metodo `doCatch()` appropriato verrà richiamato per gestire l'errore.

A differenza dell'esempio sincrono, dove `c()` non è mai stato eseguito, `activityC` è stato richiamato e un task è stato pianificato per l'esecuzione. Di conseguenza, il framework effettuerà un tentativo per annullarlo, ma non è garantito che tale operazione riesca. L'annullamento non è certo in quanto l'attività può essere già stata completata, può ignorare la richiesta di annullamento o può non riuscire a causa di un errore. Il framework garantisce tuttavia che la chiamata del metodo `doCatch()` verrà effettuata solo dopo il completamento di tutti i task avviati dal metodo `doTry()` corrispondente. Garantisce inoltre la chiamata di `doFinally()` solo dopo il completamento di tutti i task avviati da `doTry()` e `doCatch()`. Se, ad esempio, le attività dell'esempio precedente dipendono l'una dall'altra, ad esempio `activityB` dipende da `activityA` e `activityC` da`activityB`, l'annullamento `activityC` sarà immediato perché non è programmato in Amazon SWF fino al `activityB` completamento:

```
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();
    }
};
```

### Heartbeat dell'attività
<a name="errorhandling.activity.heartbeat"></a>

Il meccanismo di cancellazione cooperativa di AWS Flow Framework for Java consente di annullare senza problemi le attività in corso. Quando si avvia l'annullamento, i task bloccati o in attesa di essere assegnati a un lavoratore vengono annullati automaticamente. Se, tuttavia, un task è già assegnato a un lavoratore, il framework richiederà all'attività di annullarlo. L'implementazione di attività deve gestire in modo esplicito queste richieste di annullamento. Ciò viene eseguito mediante la segnalazione dell'heartbeat dell'attività.

La segnalazione dell'heartbeat consente all'implementazione di attività di comunicare l'avanzamento di un task di attività in corso, il che è utile per il monitoraggio, e all'attività di verificare l'esistenza di richieste di annullamento. Il metodo `recordActivityHeartbeat` genera un'eccezione `CancellationException` se un annullamento è stato richiesto. L'implementazione di attività può rilevare questa eccezione e agire sulla richiesta di annullamento oppure può ignorare la richiesta non tenendo conto dell'eccezione. Per soddisfare la richiesta di cancellazione, l'attività deve eseguire l'eventuale pulizia desiderata e quindi generare di nuovo `CancellationException`. Quando questa eccezione viene generata a partire da un'implementazione di attività, il framework registra che il task di attività è stato completato con lo stato annullato.

L'esempio seguente mostra un'attività che scarica ed elabora immagini. L'attività genera l'heartbeat dopo l'elaborazione di ogni immagine e se viene richiesto l'annullamento, esegue la pulizia e genera di nuovo l'eccezione per confermare l'annullamento.

```
@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;
        }
    }
}
```

La segnalazione dell'heartbeat dell'attività non è necessaria, ma è consigliata se l'attività è a esecuzione prolungata o se esegue operazioni dispendiose che intendi annullare in condizioni di errore. Devi chiamare `heartbeatActivityTask` periodicamente a partire dall'implementazione di attività.

In caso di timeout dell'attività, verrà generata l'eccezione `ActivityTaskTimedOutException` e `getDetails` sull'oggetto eccezione restituirà i dati passati all'ultima chiamata a `heartbeatActivityTask` riuscita per il task di attività corrispondente. L'implementazione di flusso di lavoro può utilizzare queste informazioni per determinare l'avanzamento prima del timeout del task di attività.

**Nota**  
Non è consigliabile eseguire il battito cardiaco troppo frequentemente perché Amazon SWF può limitare le richieste di heartbeat. Consulta la [Amazon Simple Workflow Service Developer Guide](https://docs.aws.amazon.com/amazonswf/latest/developerguide/) per conoscere i limiti imposti da Amazon SWF.

### Annullamento esplicito di un task
<a name="errorhandling.canceltask"></a>

Oltre alle condizioni di errore, vi sono altri casi in cui puoi annullare esplicitamente un task. Ad esempio, è possibile che un'attività per l'elaborazione di pagamenti mediante una carta di credito debba essere annullata se l'utente annulla l'ordine. Il framework ti consente di annullare esplicitamente i task creati nell'ambito di una classe `TryCatchFinally`. Nell'esempio seguente, il task di pagamento viene annullato se si riceve un segnale durante l'elaborazione del pagamento.

```
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);
        }
    }
}
```

### Ricezione di notifiche relative a task annullati
<a name="errorhandling.canceltask.notification"></a>

Se un task viene completato quando lo stato è annullato, il framework informa la logica di flusso di lavoro generando un'eccezione `CancellationException`. Se un'attività viene completata quando lo stato è annullata, un record viene creato nella cronologia e il framework chiama il metodo `doCatch()` appropriato con un'eccezione `CancellationException`. Come mostrato nell'esempio precedente, quando il task di elaborazione del pagamento viene annullato, il workflow riceve un'eccezione `CancellationException`. 

Un'eccezione `CancellationException` non gestita viene propagata nel ramo di esecuzione come avviene con qualsiasi altra eccezione. Tuttavia, il metodo `doCatch()` riceverà l'eccezione `CancellationException` solo se non vi sono altre eccezioni nell'ambito, in quanto la priorità delle altre eccezioni è superiore a quella dell'annullamento. 

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

Puoi nidificare la classe `TryCatchFinally` in funzione delle tue esigenze. Poiché ognuno `TryCatchFinally` crea un nuovo ramo nell'albero di esecuzione, è possibile creare ambiti annidati. Le eccezioni nell'ambito padre comporteranno tentativi di annullamento di tutti i task avviati dalle classi `TryCatchFinally` nidificate nell'ambito. Tuttavia, le eccezioni in una classe `TryCatchFinally` nidificata non vengono propagate automaticamente al padre. Se desideri propagare un'eccezione da una classe `TryCatchFinally` nidificata alla classe `TryCatchFinally` che la contiene, devi generare di nuovo l'eccezione in `doCatch()`. In altre parole, solo le eccezioni non gestite sono propagate, esattamente come i blocchi Java `try`/`catch`. Se annulli una classe `TryCatchFinally` nidificata chiamando il metodo Cancel, la classe `TryCatchFinally` nidificata verrà annullata ma non la classe `TryCatchFinally` che la contiene.

![\[Annidato TryCatchFinally\]](http://docs.aws.amazon.com/it_it/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);
    }
};
```