

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Fehlerbehandlung
<a name="errorhandling"></a>

**Topics**
+ [TryCatchFinally Semantik](#errorhandling.trycatchfinally)
+ [Abbruch](#test.cancellation.resources)
+ [Verschachtelt TryCatchFinally](#errorhandling.nested)

Das Konstrukt `try`/`catch`/`finally` in Java vereinfacht die Fehlerbehandlung und wird sehr häufig eingesetzt. Es ermöglicht die Verknüpfung von Fehler-Handlern mit einem Codeblock. Dies geschieht intern durch die Anhäufung von Metadaten zu den Fehler-Handlern auf dem Aufruf-Stack. Wird eine Ausnahme ausgelöst, sucht die Laufzeit beim Aufruf-Stack nach einem zugehörigen Fehler-Handler und ruft diesen auf. Wird kein passender gefunden, wird die Ausnahme an die Aufruf-Kette weitergegeben.

Dies funktioniert gut bei synchronem Code. Die Fehlerbehandlung in asynchronen und verteilten Programmen stellt jedoch einige Herausforderungen dar. Da ein asynchroner Aufruf sofort zurückkehrt, befindet sich der Aufrufer nicht auf der Aufrufliste, wenn der asynchrone Code ausgeführt wird. Das bedeutet, dass nicht behandelte Ausnahmen in einem asynchronen Code vom Aufrufer nicht in der üblichen Weise behandelt werden können. In der Regel werden Ausnahmen, die in einem asynchronen Code auftreten, behandelt, indem der Fehlerstatus an ein Callback übergeben wird, das an die asynchrone Methode übermittelt wird. Alternativ erfolgt bei Verwendung von `Future<?>` die Meldung eines Fehlers, wenn Sie versuchen, darauf zuzugreifen. Dies ist keineswegs ideal, da dem Code, der die Ausnahme empfängt (das Callback oder den Code, das bzw. der `Future<?>` verwendet), der Kontext des ursprünglichen Aufrufs fehlt und er die Ausnahme möglicherweise nicht adäquat behandeln kann. Darüber hinaus kann es bei einem verteilten asynchronen System, bei dem mehrere Komponenten parallel ausgeführt werden, gleichzeitig zu mehreren Fehlern kommen. Dabei kann es sich um unterschiedliche Fehlertypen von unterschiedlichem Schweregrad handeln, die alle entsprechend behandelt werden müssen.

Das Bereinigen einer Ressource nach einem asynchronen Aufruf ist ebenfalls schwierig. Im Gegensatz zu synchronem Code können Sie den Code try/catch/finally im aufrufenden Code nicht verwenden, um Ressourcen zu bereinigen, da die im Try-Block eingeleitete Arbeit möglicherweise noch andauert, wenn der Finally-Block ausgeführt wird.

Das Framework bietet einen Mechanismus, der die Fehlerbehandlung in verteiltem asynchronem Code der von Java ähnelt und fast so einfach wie die von Java ist. 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();
    }
  };
}
```

Die `TryCatchFinally`-Klasse und deren Varianten `TryFinally` und `TryCatch` funktionieren ähnlich wie Javas `try`/`catch`/`finally`. Mit dieser Lösung können Sie Ausnahme-Handler mit Blöcken von Workflow-Code verknüpfen, die als asynchrone und Remote-Aufgaben ausgeführt werden können. Die `doTry()`-Methode entspricht logisch dem `try`-Block. Das Framework führt den Code automatisch in `doTry()` aus. Eine Liste von `Promise`-Objekten kann an den Konstruktor von `TryCatchFinally` übergeben werden. Die `doTry`-Methode wird ausgeführt, wenn alle `Promise `-Objekte, die an den Konstruktor übergeben wurden, bereit sind. Wird eine Ausnahme von einem Code ausgelöst, der asynchron innerhalb von `doTry()` aufgerufen wurde, werden alle Vorgänge in `doTry()` abgebrochen und `doCatch()` aufgerufen, um die Ausnahme zu behandeln. Wenn beispielsweise in der obigen Auflistung `downloadImage` eine Ausnahme auslöst, dann werden `createThumbnail` und `uploadImage` abgebrochen. Wenn alle asynchronen Vorgänge beendet wurden (abgeschlossen, fehlgeschlagen oder abgebrochen), wird abschließend `doFinally()` aufgerufen. Es kann zum Bereinigen von Ressourcen verwendet werden. Sie können diese Klassen auch gemäß Ihren Anforderungen verschachteln.

Wenn eine Ausnahme in `doCatch()` gemeldet wird, stellt das Framework einen vollständigen logischen Aufruf-Stack mit asynchronen und Remote-Aufrufen bereit. Dies kann beim Debuggen nützlich sein, insbesondere bei asynchronen Methoden, die andere asynchrone Methoden aufrufen. Eine Ausnahme von downloadImage führt beispielsweise zu einer Ausnahme wie der folgenden:

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

Die Ausführung eines Programms AWS Flow Framework für Java kann als Baum gleichzeitig ausgeführter Zweige visualisiert werden. Durch den Aufruf einer asynchronen Methode, einer Aktivität oder `TryCatchFinally` wird eine neue Verzweigung in dieser Baumstruktur der Ausführung angelegt. Der Bildverarbeitungs-Workflow beispielsweise ist in Form einer Baumstruktur auf folgender Abbildung zu sehen.

![\[Baumstruktur der asynchronen Ausführung\]](http://docs.aws.amazon.com/de_de/amazonswf/latest/awsflowguide/images/trycatchfinally.png)


Ein Fehler in einer Verzweigung der Ausführung führt zu einer Entladung der Verzweigung, genau wie eine Ausnahme die Entladung eines Aufruf-Stacks in einem Java-Programm verursacht. Dieser Vorgang setzt sich fort, bis entweder der Fehler behandelt oder der Stamm erreicht ist. In diesem Fall wird die Workflow-Ausführung beendet.

Das Framework meldet Fehler, die bei der Verarbeitung von Aufgaben auftreten, als Ausnahmen. Es verknüpft die Ausnahme-Handler (`doCatch()`-Methoden), die in `TryCatchFinally` definiert sind, mit allen Aufgaben, die vom Code im entsprechenden `doTry()` erstellt wurden. Wenn eine Aufgabe fehlschlägt, z. B. aufgrund eines Timeouts oder einer unbehandelten Ausnahme, wird die entsprechende Ausnahme ausgelöst und die entsprechende wird aufgerufen, um sie zu behandeln. `doCatch()` Um dies zu erreichen, arbeitet das Framework mit Amazon SWF zusammen, um Remote-Fehler zu verbreiten und sie als Ausnahmen im Kontext des Aufrufers wieder aufleben zu lassen.

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

Tritt eine Ausnahme im asynchronen Code auf, springt das Steuerelement direkt zum `catch`-Block und überspringt den verbleibenden Code im `try`-Block. Zum Beispiel: 

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

Bei diesem Code wird, wenn `b()` eine Ausnahme auslöst, `c()` niemals aufgerufen. Vergleichen Sie dies mit einem Workflow:

```
new TryCatch() {

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

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

Hier werden Aufrufe von `activityA`, `activityB` und `activityC` erfolgreich zurückgegeben und führen zur Erstellung dreier Aufgaben, die asynchron ausgeführt werden. Angenommen, die Aufgabe für `activityB` verursacht zu einem späteren Zeitpunkt einen Fehler. Dieser Fehler wird von Amazon SWF in der Historie aufgezeichnet. Aus diesem Grund versucht das Framework zunächst alle anderen Aufgaben abzubrechen, die aus dem Bereich desselben `doTry()` stammen. In diesem Fall sind das `activityA`und `activityC`. Nach Beendigung aller Aufgaben (durch Abbrechen, Fehlschlagen oder erfolgreichem Abschließen), wird die entsprechende `doCatch()`-Methode aufgerufen, um den Fehler zu behandeln.

Im Gegensatz zum synchronen Beispiel, bei dem `c()` niemals ausgeführt wurde, wurde `activityC` hier aufgerufen. Zudem wurde eine Aufgabe für die Ausführung eingeplant. Deshalb versucht das Framework einen Abbruch, für dessen Erfolg es aber keine Garantie gibt. Der Abbruch kann nicht garantiert werden, da die Aktivität möglicherweise bereits abgeschlossen ist, die Abbruchanforderung ignoriert oder fehlschlägt. Das Framework garantiert jedoch, dass `doCatch()` nur aufgerufen wird, wenn alle Aufgaben, die über das entsprechende `doTry()` gestartet wurden, abgeschlossen sind. Es garantiert zudem, dass `doFinally()` nur aufgerufen wird, wenn alle Aufgaben, die vom `doTry()`- und `doCatch()`-Block gestartet wurden, abgeschlossen sind. Wenn die Aktivitäten im obigen Beispiel beispielsweise voneinander abhängen, beispielsweise von `activityA` und `activityC` von, dann erfolgt die Stornierung von `activityC` sofort`activityB`, da sie erst in Amazon SWF geplant ist, wenn Folgendes `activityB` abgeschlossen ist: `activityB`

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

### Aktivitäts-Heartbeat
<a name="errorhandling.activity.heartbeat"></a>

Mit dem kooperativen Stornierungsmechanismus von AWS Flow Framework for Java können Aufgaben während des Fluges problemlos storniert werden. Wird der Abbruch ausgelöst, werden blockierte Aufgaben oder Aufgaben, die darauf warten, zu einem Worker zugewiesen zu werden, automatisch abgebrochen. Wenn eine Aufgabe aber bereits einem Worker zugewiesen wurde, fordert das Framework den Abbruch der Aktivität an. Ihre Aktivitätsimplementierung muss diese Abbruchanforderungen explizit behandeln können. Dies geschieht durch das Übermitteln von Heartbeats Ihrer Aktivität.

Durch das Senden von Heartbeats ist die Aktivitätsimplementierung in der Lage, den Fortschritt einer andauernden Aufgabe zu melden. Dies unterstützt die Überwachung und ermöglicht der Aktivität zu prüfen, ob Abbruchanforderungen vorliegen. Die `recordActivityHeartbeat`-Methode löst bei Anforderung eines Abbruchs eine `CancellationException` aus. Die Aktivitätsimplementierung kann diese Ausnahme abfangen und auf die Abbruchanforderung reagieren oder die Anforderung durch "Verschlucken" der Ausnahme ignorieren. Um der Abbruchanforderung Rechnung zu tragen, sollte die Aktivität die gewünschte Bereinigung vornehmen, sofern erforderlich, und dann `CancellationException` erneut auslösen. Wird diese Ausnahme von einer Aktivitätsimplementierung ausgelöst, erfasst das Framework, dass die Aktivitätsaufgabe im abgebrochenen Status beendet wurde.

Das folgende Beispiel zeigt eine Aufgabe, bei der Bilder heruntergeladen und verarbeitet werden. Es kommt nach jeder Verarbeitung eines Bilds zu einem Heartbeat. Wird ein Abbruch gefordert, wird bereinigt und die Ausnahme zur Bestätigung des Abbruchs erneut ausgelöst.

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

Das Senden von Aktivitäts-Heartbeats ist nicht erforderlich, wird aber empfohlen, wenn die Ausführung der Aktivität lange dauert oder dabei kostenintensive Operationen ausgeführt werden, die im Falle eines Fehlers abgebrochen werden sollten. Sie sollten `heartbeatActivityTask` periodisch von der Aktivitätsimplementierung aus aufrufen.

Kommt es bei der Ausführung der Aktivität zu einer Zeitüberschreitung, wird die `ActivityTaskTimedOutException` ausgelöst und `getDetails` auf dem Ausnahmeobjekt gibt für die entsprechende Aktivitätsaufgabe die Daten zurück, die an den letzten erfolgreichen Aufruf von `heartbeatActivityTask` übergeben wurden. Die Workflow-Implementierung kann anhand dieser Informationen feststellen, wie weit die Ausführung fortgeschritten war, ehe es zu einer Zeitüberschreitung bei der Aktivitätsaufgabe kam.

**Anmerkung**  
Es empfiehlt sich nicht, zu häufig Heartbeat-Anfragen zu drosseln, da Amazon SWF Heartbeat-Anfragen drosseln kann. Informationen zu den von [Amazon SWF festgelegten Beschränkungen finden Sie im Amazon Simple Workflow Service Developer Guide](https://docs.aws.amazon.com/amazonswf/latest/developerguide/).

### Explizites Abbrechen einer Aufgabe
<a name="errorhandling.canceltask"></a>

Abgesehen von Fehlerbedingungen gibt es noch andere Fälle, in denen eine Aufgabe explizit abzubrechen ist. So muss beispielsweise eine Aktivität zur Verarbeitung von Zahlungen mit der Kreditkarte abgebrochen werden, wenn der Benutzer den Auftrag storniert. Das Framework ermöglicht das explizite Abbrechen von Aufgaben, die mit `TryCatchFinally` erstellt wurden. Im folgenden Beispiel wird die Zahlungsaufgabe abgebrochen, wenn während der Verarbeitung der Zahlung ein Signal empfangen wird.

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

### Empfangen von Benachrichtigungen über abgebrochene Aufgaben
<a name="errorhandling.canceltask.notification"></a>

Wird eine Aufgabe im abgebrochenen Status beendet, informiert das Framework die Workflow-Logik durch Auslösen einer `CancellationException`. Wenn eine Aktivität im abgebrochenen Status beendet wird, wird ein Datensatz zum Verlauf hinzugefügt und das Framework ruft das erforderliche `doCatch()` mit einer `CancellationException` auf. Wie im vorherigen Beispiel gezeigt, empfängt der Workflow eine `CancellationException`, wenn die Aufgabe der Zahlungsverarbeitung abgebrochen wird. 

Eine unbehandelte `CancellationException` wird wie jede andere Ausnahme in der Ausnahmeverzweigung weiter nach oben gereicht. Die `doCatch()`-Methode empfängt die `CancellationException` aber nur, wenn es im Scope keine weitere Ausnahme gibt. Andere Ausnahmen werden höher priorisiert als der Abbruch. 

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

Sie können `TryCatchFinally` gemäß Ihren Anforderungen verschachteln. Da jeder Zweig in der Ausführungsstruktur einen neuen Zweig `TryCatchFinally` erstellt, können Sie verschachtelte Bereiche erstellen. Ausnahmen im übergeordneten Scope führen zu Abbruchversuchen bei allen Aufgaben, die durch ein verschachteltes `TryCatchFinally`' in ihnen initiiert wurden. Allerdings werden Ausnahmen in einem verschachtelten `TryCatchFinally` nicht automatisch an das übergeordnete Element weitergegeben. Wenn Sie eine Ausnahme aus einem verschachtelten `TryCatchFinally` an das enthaltene `TryCatchFinally` weitergeben möchten, sollten Sie die Ausnahme in `doCatch()` erneut auslösen. Anderes ausgedrückt: Nur unbehandelte Ausnahmen steigen wie Javas `try`/`catch`-Konstrukt auf. Wenn Sie ein verschachteltes `TryCatchFinally` durch Aufruf der Abbruchmethode abbrechen, wird das verschachtelte `TryCatchFinally` abgebrochen, aber nicht automatisch auch das enthaltene `TryCatchFinally`.

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