

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.

# Abonnement-Workflow, Anleitung Teil 3: Implementieren der Aktivitäten
<a name="swf-sns-tutorial-implementing-activities"></a>

Wir implementieren nun die einzelnen Aktivitäten in unserem Workflow. Dazu beginnen wir mit einer Basisklasse, die einige gängige Funktionen für den Aktivitätscode bereitstellt.

**Topics**
+ [Definieren eines Basis-Aktivitätstyps](#defining-a-basic-activity-type)
+ [Definierend GetContactActivity](#defining-getcontactactivity)
+ [Definierend SubscribeTopicActivity](#defining-subscribetopicactivity)
+ [Definierend WaitForConfirmationActivity](#defining-waitforconfirmationactivity)
+ [Definierend SendResultActivity](#defining-sendresultactivity)
+ [Nächste Schritte](#implementing-activities-next-steps)

## Definieren eines Basis-Aktivitätstyps
<a name="defining-a-basic-activity-type"></a>

Beim Entwerfen des Workflows haben wir die folgenden Aktivitäten identifiziert:
+ `get_contact_activity`
+ `subscribe_topic_activity`
+ `wait_for_confirmation_activity`
+ `send_result_activity`

Jede dieser Aktivitäten werden wir nun implementieren. Da unsere Aktivitäten einige Funktionen gemeinsam haben werden, sollten wir ein wenig Grundlagenarbeit leisten und gemeinsamen Code erstellen, den sie gemeinsam nutzen können. Wir rufen es auf **BasicActivity**und definieren es in einer neuen Datei namens`basic_activity.rb`.

Wie bei den anderen Quelldateien fügen wir `utils.rb` hinzu, um auf die Funktion `init_domain` zuzugreifen und die Beispieldomäne einzurichten.

```
   require_relative 'utils.rb' 
```

Als Nächstes deklarieren wir die Basis-Aktivitätsklasse und einige gemeinsame Daten für die Aktivitäten. Wir speichern die [AWS::SimpleWorkflow::ActivityType](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityType.html)Instanz, den *Namen* und die *Ergebnisse* der Aktivität in den Attributen der Klasse.

```
   class BasicActivity

     attr_accessor :activity_type
     attr_accessor :name
     attr_accessor :results
```

Diese Attribute greifen auf Instanzdaten zu, die in der `initialize` Methode der Klasse definiert sind, die einen *Aktivitätsnamen* und eine optionale *Version* und Zuordnung von *Optionen* verwendet, die bei der Registrierung der Aktivität bei Amazon SWF verwendet werden.

```
     def initialize(name, version = 'v1', options = nil)

       @activity_type = nil
       @name = name
       @results = nil

       # get the domain to use for activity tasks.
       @domain = init_domain

       # Check to see if this activity type already exists.
       @domain.activity_types.each do | a |
         if (a.name == @name) && (a.version == version)
           @activity_type = a
         end
       end

       if @activity_type.nil?
         # If no options were specified, use some reasonable defaults.
         if options.nil?
           options = {
             # All timeouts are in seconds.
             :default_task_heartbeat_timeout => 900,
             :default_task_schedule_to_start_timeout => 120,
             :default_task_schedule_to_close_timeout => 3800,
             :default_task_start_to_close_timeout => 3600 }
         end
         @activity_type = @domain.activity_types.register(@name, version, options)
       end
     end
```

Wie bei der Registrierung des Workflow-Typs können wir einen bereits registrierten Aktivitätstyp abrufen, indem wir uns die Sammlung [activity\_types](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/Domain.html#activity_types-instance_method) der Domäne anschauen. Wird die Aktivität nicht gefunden, wird sie registriert.

Ebenso wie bei den Workflow-Typen können Sie *Standardoptionen* festlegen, die mit Ihrem Aktivitätstyp bei dessen Registrierung gespeichert werden.

Im letzten Schritt legen wir eine konsistente Ausführungsmethode für unsere Basisaktivität fest. Wir definieren eine `do_activity`-Methode, die eine Aktivitätsaufgabe verwendet. Wie oben dargestellt können wir die übergebene Aktivitätsaufgabe verwenden, um Daten über ihr Instance-Attribut `input` zu empfangen.

```
     def do_activity(task)
       @results = task.input # may be nil
       return true
     end
   end
```

Damit ist die Klasse abgeschlossen. **BasicActivity** Wir können sie nun verwenden, um unsere Aktivitäten auf einfache und konsistente Weise zu definieren.

## Definierend GetContactActivity
<a name="defining-getcontactactivity"></a>

Die erste Aktivität, die während einer Workflow-Ausführung ausgeführt wird`get_contact_activity`, ist, die Abonnementinformationen für Amazon SNS-Themen des Benutzers abzurufen.

Erstellen Sie eine neue Datei mit dem Namen `get_contact_activity.rb``yaml`, and require both, die wir verwenden, um eine Zeichenfolge für die Übergabe an Amazon SWF vorzubereiten`basic_activity.rb`, und die wir als Grundlage für diese **GetContactActivity**Klasse verwenden werden.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **GetContactActivity** provides a prompt for the user to enter contact
   # information. When the user successfully enters contact information, the
   # activity is complete.
   class GetContactActivity < BasicActivity
```

Da wir den Registrierungscode für die Aktivität eingegeben haben **BasicActivity**, **GetContactActivity**ist die `initialize` Methode dafür ziemlich einfach. Wir rufen einfach den Basisklassenkonstruktor mit dem Aktivitätsnamen `get_contact_activity` auf. Dies ist alles, was für die Registrierung unserer Aktivität erforderlich ist.

```
     # initialize the activity
     def initialize
       super('get_contact_activity')
     end
```

Wir definieren jetzt die `do_activity` Methode, die zur Eingabe der and/or E-Mail-Telefonnummer des Benutzers auffordert. 

```
     def do_activity(task)
       puts ""
       puts "Please enter either an email address or SMS message (mobile phone) number to"
       puts "receive SNS notifications. You can also enter both to use both address types."
       puts ""
       puts "If you enter a phone number, it must be able to receive SMS messages, and must"
       puts "be 11 digits (such as 12065550101 to represent the number 1-206-555-0101)."

       input_confirmed = false
       while !input_confirmed
         puts ""
         print "Email: "
         email = $stdin.gets.strip

         print "Phone: "
         phone = $stdin.gets.strip

         puts ""
         if (email == '') && (phone == '')
           print "You provided no subscription information. Quit? (y/n)"
            confirmation = $stdin.gets.strip.downcase
            if confirmation == 'y'
              return false
            end
         else
            puts "You entered:"
            puts "  email: #{email}"
            puts "  phone: #{phone}"
            print "\nIs this correct? (y/n): "
            confirmation = $stdin.gets.strip.downcase
            if confirmation == 'y'
              input_confirmed = true
            end
         end
       end

       # make sure that @results is a single string. YAML makes this easy.
       @results = { :email => email, :sms => phone }.to_yaml
       return true
     end
   end
```

Am Ende von `do_activity` platzieren wir die vom Benutzer erhaltene E-Mail-Adresse und Telefonnummer in eine Map und konvertieren diese mithilfe von `to_yaml` in eine YAML-Zeichenfolge. Dafür gibt es einen wichtigen Grund: Alle Ergebnisse, die Sie nach Abschluss einer Aktivität an Amazon SWF weitergeben, dürfen *nur Zeichenkettendaten* sein. Die Fähigkeit von Ruby, Objekte einfach in YAML-Zeichenfolgen und dann wieder zurück in Objekte zu konvertieren, ist für diesen Zweck sehr gut geeignet.

Damit ist die Implementierung von `get_contact_activity` abgeschlossen. Diese Daten werden bei der `subscribe_topic_activity`-Implementierung als Nächstes verwendet.

## Definierend SubscribeTopicActivity
<a name="defining-subscribetopicactivity"></a>

Wir werden uns nun mit Amazon SNS befassen und eine Aktivität erstellen, die die von generierten Informationen verwendet, `get_contact_activity` um den Benutzer für ein Amazon SNS SNS-Thema zu abonnieren.

Erstellen Sie eine neue Datei mit dem Namen `subscribe_topic_activity.rb`. Fügen Sie die gleichen Anforderungen wie bei `get_contact_activity` hinzu, deklarieren Sie Ihre Klasse und stellen Sie ihre `initialize`-Methode bereit.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **SubscribeTopicActivity** sends an SMS / email message to the user, asking for
   # confirmation.  When this action has been taken, the activity is complete.
   class SubscribeTopicActivity < BasicActivity

     def initialize
       super('subscribe_topic_activity')
     end
```

Nachdem wir nun den Code für die Einrichtung und Registrierung der Aktivität eingerichtet und registriert haben, werden wir etwas Code hinzufügen, um ein Amazon SNS SNS-Thema zu erstellen. Dazu verwenden wir die Methode [AWS::SNS::Client](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html)[create\_topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#create_topic-instance_method) des Objekts.

Fügen Sie die `create_topic` Methode zu Ihrer Klasse hinzu, die ein übergebenes Amazon SNS SNS-Client-Objekt verwendet.

```
     def create_topic(sns_client)
       topic_arn = sns_client.create_topic(:name => 'SWF_Sample_Topic')[:topic_arn]

       if topic_arn != nil
         # For an SMS notification, setting `DisplayName` is *required*. Note that
         # only the *first 10 characters* of the DisplayName will be shown on the
         # SMS message sent to the user, so choose your DisplayName wisely!
         sns_client.set_topic_attributes( {
           :topic_arn => topic_arn,
           :attribute_name => 'DisplayName',
           :attribute_value => 'SWFSample' } )
       else
         @results = {
           :reason => "Couldn't create SNS topic", :detail => "" }.to_yaml
         return nil
       end

       return topic_arn
     end
```

Sobald wir den Amazon-Ressourcennamen (ARN) des Themas haben, können wir ihn mit der Methode [set\_topic\_attributes](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#set_topic_attributes-instance_method) des Amazon SNS-Clients verwenden, um das Thema festzulegen *DisplayName*, was für den Versand von SMS-Nachrichten mit Amazon SNS erforderlich ist.

Schließlich definieren wir die `do_activity`-Methode. Zunächst sammeln wir alle Daten, die bei Terminierung der Aktivität über die `input`-Option übergeben wurden. Wie zuvor erwähnt muss diese als Zeichenfolge, erstellt mit `to_yaml`, übergeben werden. Beim Abrufen verwenden wir `YAML.load`, um die Daten in Ruby-Objekte umzuwandeln.

Hier ist der Anfang von `do_activity`, in den wir die Eingabedaten abrufen.

```
     def do_activity(task)
       activity_data = {
         :topic_arn => nil,
         :email => { :endpoint => nil, :subscription_arn => nil },
         :sms => { :endpoint => nil, :subscription_arn => nil },
       }

       if task.input != nil
         input = YAML.load(task.input)
         activity_data[:email][:endpoint] = input[:email]
         activity_data[:sms][:endpoint] = input[:sms]
       else
         @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml
         puts("  #{@results.inspect}")
         return false
       end

       # Create an SNS client. This is used to interact with the service. Set the
       # region to $SMS_REGION, which is a region that supports SMS notifications
       # (defined in the file `utils.rb`).
       sns_client = AWS::SNS::Client.new(
         :config => AWS.config.with(:region => $SMS_REGION))
```

Wenn wir keine Eingabedaten empfangen haben, gibt es nichts zu verarbeiten, also schlägt die Aktivität fehl.

Vorausgesetzt, dass alles in Ordnung ist, füllen wir unsere `do_activity` Methode weiter aus, rufen einen Amazon SNS SNS-Client mit dem ab und übergeben es an unsere `create_topic` Methode AWS SDK für Ruby, um das Amazon SNS SNS-Thema zu erstellen.

```
       # Create the topic and get the ARN
       activity_data[:topic_arn] = create_topic(sns_client)

       if activity_data[:topic_arn].nil?
         return false
       end
```

An dieser Stelle sei auf Folgendes hingewiesen:
+ Wir verwenden [https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/Core/Configuration.html#with-instance_method](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/Core/Configuration.html#with-instance_method), um die Region für unseren Amazon SNS SNS-Client festzulegen. Da wir SMS-Nachrichten versenden wollen, verwenden wir die SMS-fähige Region, die wir in `utils.rb` deklariert haben.
+ Wir speichern den ARN des Themas in unserer Map `activity_data`. Dies ist der Teil der Daten, die an die *nächste* Aktivität in unserem Workflow übergeben werden.

Schließlich abonniert diese Aktivität den Benutzer mithilfe der übergebenen Endpunkte (E-Mail und SMS) für das Amazon SNS-Thema. Wir fordern nicht vom Benutzer, dass er *beide Endpunkte* angibt, wir benötigen aber mindestens einen.

```
       # Subscribe the user to the topic, using either or both endpoints.
       [:email, :sms].each do | x |
         ep = activity_data[x][:endpoint]
         # don't try to subscribe an empty endpoint
         if (ep != nil && ep != "")
           response = sns_client.subscribe( {
             :topic_arn => activity_data[:topic_arn],
             :protocol => x.to_s, :endpoint => ep } )
           activity_data[x][:subscription_arn] = response[:subscription_arn]
         end
       end
```

[AWS::SNS::Client.subscribe](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#subscribe-instance_method) verwendet das Thema ARN, das *Protokoll (das* wir geschickt als Map-Schlüssel für den entsprechenden Endpunkt getarnt haben`activity_data`).

Schließlich verpacken wir die Informationen für die nächste Aktivität erneut im YAML-Format, sodass wir sie an Amazon SWF zurücksenden können.

```
       # if at least one subscription arn is set, consider this a success.
       if (activity_data[:email][:subscription_arn] != nil) or (activity_data[:sms][:subscription_arn] != nil)
         @results = activity_data.to_yaml
       else
         @results = { :reason => "Couldn't subscribe to SNS topic", :detail => "" }.to_yaml
         puts("  #{@results.inspect}")
         return false
       end
       return true
     end
   end
```

Damit ist die Implementierung von `subscribe_topic_activity` abgeschlossen. Als Nächstes definieren wir `wait_for_confirmation_activity`.

## Definierend WaitForConfirmationActivity
<a name="defining-waitforconfirmationactivity"></a>

Sobald ein Benutzer ein Amazon SNS SNS-Thema abonniert hat, muss er die Abonnementanfrage noch bestätigen. In diesem Fall warten wir, bis der Benutzer entweder per E-Mail oder SMS bestätigt.

Die Aktivität, mit der der Benutzer das Abonnement bestätigt, heißt `wait_for_confirmation_activity`. Sie wird hier definiert. Erstellen Sie zunächst eine neue Datei namens `wait_for_confirmation_activity.rb`. Richten Sie sie so wie die vorherigen Aktivitäten ein.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **WaitForConfirmationActivity** waits for the user to confirm the SNS
   # subscription.  When this action has been taken, the activity is complete. It
   # might also time out...
   class WaitForConfirmationActivity < BasicActivity

     # Initialize the class
     def initialize
       super('wait_for_confirmation_activity')
     end
```

Als Nächstes beginnen wir mit der Definition der `do_activity`-Methode und rufen alle Eingabedaten in eine lokale Variable namens `subscription_data` ab.

```
     def do_activity(task)
       if task.input.nil?
         @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml
         return false
       end

       subscription_data = YAML.load(task.input)
```

Jetzt, wo wir das Thema ARN haben, können wir das Thema abrufen, indem wir eine neue Instanz von erstellen [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)und ihr den ARN übergeben.

```
       topic = AWS::SNS::Topic.new(subscription_data[:topic_arn])

       if topic.nil?
         @results = {
           :reason => "Couldn't get SWF topic ARN",
           :detail => "Topic ARN: #{topic.arn}" }.to_yaml
         return false
       end
```

Als Nächstes überprüfen wir das Thema, um zu schauen, ob der Benutzer das Abonnement über einen der Endpunkte bestätigt hat. Wir verlangen nur, dass ein Endpunkt bestätigt wurde, um die Aktivität als erfolgreich zu betrachten.

Ein Amazon SNS SNS-Thema verwaltet eine Liste der [Abonnements](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#subscriptions-instance_method) für dieses Thema, und wir können überprüfen, ob der Benutzer ein bestimmtes Abonnement bestätigt hat oder nicht, indem wir überprüfen, ob der ARN des Abonnements auf etwas anderes als `PendingConfirmation` gesetzt ist.

```
       # loop until we get some indication that a subscription was confirmed.
       subscription_confirmed = false
       while(!subscription_confirmed)
         topic.subscriptions.each do | sub |
           if subscription_data[sub.protocol.to_sym][:endpoint] == sub.endpoint
             # this is one of the endpoints we're interested in. Is it subscribed?
             if sub.arn != 'PendingConfirmation'
               subscription_data[sub.protocol.to_sym][:subscription_arn] = sub.arn
               puts "Topic subscription confirmed for (#{sub.protocol}: #{sub.endpoint})"
               @results = subscription_data.to_yaml
               return true
             else
               puts "Topic subscription still pending for (#{sub.protocol}: #{sub.endpoint})"
             end
           end
         end
```

Wenn wir für das Abonnement einen ARN erhalten, speichern wir diesen in den Ergebnisdaten der Aktivität, konvertieren ihn in YAML und geben von `do_activity` „true“ zurück. Dies weist darauf hin, dass die Aktivität erfolgreich abgeschlossen wurde.

Da das Warten auf die Bestätigung eines Abonnements eine Weile dauern kann, rufen wir gelegentlich die Aktivitätsaufgabe `record_heartbeat` auf. Dies signalisiert Amazon SWF, dass die Aktivität noch verarbeitet wird, und kann auch verwendet werden, um Updates über den Fortschritt der Aktivität bereitzustellen (wenn Sie etwas tun, z. B. Dateien verarbeiten, für das Sie den Fortschritt melden können).

```
         task.record_heartbeat!(
           { :details => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" })
         # sleep a bit.
         sleep(4.0)
       end
```

Damit endet unsere `while`-Schleife. Wenn wir irgendwie aus der While-Schleife geraten, melden wir einen Fehler und beenden die `do_activity`-Methode.

```
       if (subscription_confirmed == false)
         @results = {
           :reason => "No subscriptions could be confirmed",
           :detail => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" }.to_yaml
         return false
       end
     end
   end
```

Damit ist die Implementierung von `wait_for_confirmation_activity` abgeschlossen. Wir müssen nur noch eine Aktivität definieren: `send_result_activity`.

## Definierend SendResultActivity
<a name="defining-sendresultactivity"></a>

Wenn der Workflow so weit fortgeschritten ist, haben wir den Benutzer erfolgreich für ein Amazon SNS SNS-Thema angemeldet und der Benutzer hat das Abonnement bestätigt.

Unsere letzte Aktivität, `send_result_activity`, sendet dem Benutzer eine Bestätigung, dass er das Thema erfolgreichen abonniert hat. Dazu verwendet sie dieses Thema sowie den Endpunkt, über das der Benutzer das Abonnement bestätigt hat.

Erstellen Sie eine neue Datei namens `send_result_activity.rb`. Richten Sie sie so wie alle anderen bisherigen Aktivitäten ein.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **SendResultActivity** sends the result of the activity to the screen, and, if
   # the user successfully registered using SNS, to the user using the SNS contact
   # information collected.
   class SendResultActivity < BasicActivity

     def initialize
       super('send_result_activity')
     end
```

Unsere `do_activity` Methode beginnt ebenfalls auf ähnliche Weise, indem wir die Eingabedaten aus dem Workflow abrufen, sie aus YAML konvertieren und dann das Thema ARN verwenden, um eine [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)Instanz zu erstellen.

```
     def do_activity(task)
       if task.input.nil?
         @results = { :reason => "Didn't receive any input!", :detail => "" }
         return false
       end

       input = YAML.load(task.input)

       # get the topic, so we publish a message to it.
       topic = AWS::SNS::Topic.new(input[:topic_arn])

       if topic.nil?
         @results = {
           :reason => "Couldn't get SWF topic",
           :detail => "Topic ARN: #{topic.arn}" }
         return false
       end
```

Wenn wir das Thema haben, [veröffentlichen](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#publish-instance_method) wir eine Nachricht dafür (und stellen sie auf dem Bildschirm dar).

```
       @results = "Thanks, you've successfully confirmed registration, and your workflow is complete!"

       # send the message via SNS, and also print it on the screen.
       topic.publish(@results)
       puts(@results)

       return true
     end
   end
```

Wenn Sie unter einem Amazon SNS SNS-Thema veröffentlichen, wird die von Ihnen bereitgestellte Nachricht an *alle* abonnierten und bestätigten Endpunkte gesendet, die für dieses Thema existieren. Wenn der Benutzer also mit einer E-Mail-Adresse *und* einer Rufnummer für SMS bestätigt hat, erhält er zwei Bestätigungsmeldungen (eine an jedem Endpunkt).

## Nächste Schritte
<a name="implementing-activities-next-steps"></a>

Damit ist die Implementierung von `send_result_activity` abgeschlossen. Nun verbinden Sie alle diese Aktivitäten in einer Aktivitätsanwendung, die die Aktivitätsaufgaben verarbeitet und als Reaktion darauf Aktivitäten starten kann (siehe [Abonnement-Workflow, Anleitung Teil 4: Implementieren des Pollers für Aktivitätsaufgaben](swf-sns-tutorial-implementing-activities-poller.md)).