Amazon IoT für Java

Mit Java ins Internet der Dinge: AWS IoT Device SDK for Java

Tam Hanna

© Shutterstock / Immersion Imagery

Cloud-Anbieter haben schon früh festgestellt, dass man mit dem reinen Vermieten von Servern nicht reich wird. Wirkliche High-Margin-Geschäfte lassen sich nur durch das Anbieten von Value Added Services machen. Amazon bietet beispielsweise einen hauseigenen IoT-Server an, der auf dem MQTT-Protokoll basiert. Mit dem AWS IoT Device SDK for Java steht nun ein Entwicklungskit zur Verfügung, das Java-basierten Systemen die Interaktion mit eben diesem erlaubt.

Die Idee, Java in einem Embedded-System einzusetzen, wirkt auf den ersten Blick geradezu schockierend. In der heutigen Zeit ist man als Entwickler allerdings gut beraten, an dieser Stelle alte Ängste zu den Akten zu legen. Das beste Beispiel dafür ist der Spotter aus dem Hause UnME2 (Abb. 1).

Abb. 1: Dieses Produkt basiert auf Java

Abb. 1: Dieses Produkt basiert auf Java

In Zeiten vielkerniger Prozessrechner mit vollständiger Unterstützung von Linux oder Windows ist es kein Problem, Desktopcode und Java Runtimes auch unterwegs einzusetzen. Der wichtigste Vorteil der Verwendung von Java im Embedded-Bereich dürfte mit Sicherheit die höhere Kommunalität sein. Wenn man Servercode nicht nochmals am Client neu schreiben muss, lassen sich Systeme – wie der in Abbildung 1 gezeigte Bus Tracker – schneller realisieren. Die Verwendung einer Backend-Infrastruktur von der Stange schafft dann weitere Ersparnisse.

Amazon prescht vor

Zunächst möchte ich mit der ersten Version des SDK arbeiten – die bereitstehende zweite Variante setzt auf eine Gruppe nativer Module, ist deshalb schwieriger zu deployen und soll uns erst später beschäftigen.

Im ersten Schritt benötigen wir eine Eclipse IDE, in der wir ein neues Projekt auf Basis der Maven-Paketverwaltung anlegen. Die Verwendung von Maven ist insofern keine Marotte, als Amazon das AWS IoT Device SDK for Java auf einer Gruppe von Unterstützungsbibliotheken (beispielsweise für MQTT) aufbaut. Zudem kann das manuelle Zusammenbauen einer kompilierfähigen Umgebung aus .jar-Dateien schnell in Arbeit ausarten.

Zum einfachen Erstellen des Projektskeletts wählen wir die Checkbox Create a simple Project aus, um ein Vorlagenprojekt zu erzeugen und die Archetypauswahl zu deaktivieren. Vergeben Sie danach noch einen Namen und eine Gruppen-ID, um die sonst leere Projektmappe anzulegen. Im nächsten Schritt müssen wir die Bereitstellung von AWS IoT Device SDK for Java befehligen. Hierzu öffnen wir die XML-Datei, und fügen das in Listing 1 dargestellte Snippet ein.

<project ...>
  ...
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-iot-device-sdk-java</artifactId>
    <version>1.3.4</version>
  </dependency>
  </dependencies>
</project>

Den Rest der Arbeit erledigt Maven an dieser Stelle für uns; im Rahmen der nächsten Kompilation bekommen wir, unter der Voraussetzung einer bestehenden Internetverbindung, die Bereitstellung der diversen Bibliothekskomponenten für uns erledigt.

Nachrichten austauschen

Hocheffiziente Systeme mit sehr weiter Verbreitung verwenden oft proprietäre Netzwerkprotokolle: Das ist vernünftig, da das Einsparen einiger Cents bei Millionen von Einheiten im Feld frei nach Bob Crantall zu insgesamt nicht unerheblichen Einsparungen führt. Gerade bei kleineren Systemen ist es allerdings oft vernünftiger, ein einfach zu handhabendes Netzwerkprotokoll zu verwenden und etwas mehr für die Internetverbindung zu bezahlen.

Alle Cloud-Anbieter setzen auf MQTT – unter anderem, weil die Cloud-Systeme mit den eingehenden Nachrichteninformationen unabhängig von der jeweiligen Payload makeln können. Bedenken Sie, dass der Nachrichtenkanal bzw. Nachrichtentyp bei korrekter Verwendung des Protokolls Teil des Protokollheaders ist.

Als Erstes wollen wir unser Java-AWS-System deshalb befähigen, Nachrichten in die Cloud abzufeuern. Für die eigentliche Kommunikation mit dem Server stellt uns das SDK zwei Kommunikationswege zur Verfügung. Während für MQTT (over TLS 1.2, Port 8883) eine vollwertige Zertifikatsdatei erforderlich ist, kommt MQTT Over WebSocket (Port 443) mit einem gewöhnlichen und im Code als Konstante anlegbaren Identifikationsstring aus. Da das vor allem für kleine Versuche bequemer ist, wollen wir sie in diesem Artikel anwenden. Zu beachten gilt allerdings, dass das Speichern von Konstanten in Produktivcode eine Sicherheitslücke darstellt.

Die Credentials müssen wir im im Backend beschaffen, weshalb wir hier die IDE verlassen und uns mit einem Browser Ihrer Wahl beim Amazon AWS Backend anmelden. Klicken Sie danach auf das Services-Tab auf der linken oberen Bildschirmkante, um die Liste aller verfügbaren Dienste zu öffnen. Suchen Sie danach nach dem String IAM, um die betreffende Konsole zu aktivieren. Wählen Sie die Option Access management, dann Users, um mit Klick auf Add user den Prozess zur Erzeugung eines neuen Benutzerkontos loszutreten. Im Feld Access Type entscheiden wir uns für die Option Enables an access key ID and secret access key for the AWS API, CLI, SDK, and other development tools.

Im nächsten Schritt müssen Sie dem IAM-Benutzer Rechte zuweisen. Amazon möchte Sie an dieser Stelle zur Nutzung der Gruppenfunktion nötigen, die die Verwaltung größerer Benutzerkohorten erleichtert. Sofern wir es allerdings nur mit einer Demo zu tun haben, wechseln wir stattdessen in die Rubrik Attach existing policies directly und suchen nach der Policy AdministratorAccess. Weisen Sie diese danach dem neu anzulegenden Benutzer zu.

Die restlichen Einstellungen sind für uns nicht relevant – wichtig ist nur, dass wir sowohl einen Access Key als auch einen Secret Access Key erhalten. Danach können wir auch schon in die IDE zurückkehren, wo wir unserem Projekt einen Einsprungpunkt verpassen und diesen nach dem in Listing 2 gezeigten Schema beleben.

public static void main(String[] args) {
  String clientEndpoint = "<prefix>.iot.<region>.amazonaws.com";
  String clientId = "TamsGeraetInEclipse";
  AWSIotMqttClient client = new AWSIotMqttClient(clientEndpoint, clientId, "ACCESSKEY", "SECRETKEY", null);
  try {
    client.connect();
  } catch (AWSIotException e) {
    e.printStackTrace();
  }
}

Die Client-ID ist dabei ein von Ihnen festzulegender String – wichtig ist nur, dass jede gleichzeitig stattfindende Verbindung ihren eigenen String mitbringen muss. Ansonsten begegnet uns hier im Allgemeinen nichts Überraschendes – ersetzen Sie die Strings XXX und XXX durch die weiter oben in der IAM-Konsole angelegten. Der fünfte Wert Null ist normalerweise für ein Session-Token vorgesehen, das wir hier wegen der Verwendung konstanter Log-in-Credentials allerdings nicht berücksichtigen. Nun fehlt noch der Endpoint, der den URL des anzusprechenden Amazon-Event-Arbeiters beschreibt. Auch Sie müssen im Backend ertrotzen, was die Rückkehr zum Browser verlangt. AWS zeigt sich hier insofern verwirrender als Azure, als der von uns zu verwendende Dienst auf den Namen AWS IoT Core hört. Der eigentliche Endpoint-URL findet sich dann auch nicht im Hauptmenü. Sie müssen links auf Settings klicken, um dann im Textfeld Custom Endpoint einen nach dem Schema aaaaaavel-ats.iot.eu-west-1.amazonaws.com aufgebauten URL abernten zu können.

An dieser Stelle können wir unser Programm erstmals zur Ausführung freigeben. Das AWS SDK liefert seinem Anwender von Haus aus umfangreiche Logging-Informationen, was zum in Abbildung 2 gezeigten Schirmbild führt. Klicken Sie danach im Debugger-Fenster auf das Stoppsymbol, um die Verbindung zwischen der Endstelle und unserem Java-Client zu trennen.

Abb. 2: Eclipse informiert uns über den erfolgreichen Verbindungsaufbau

Abb. 2: Eclipse informiert uns über den erfolgreichen Verbindungsaufbau

Nachrichten auf Wanderschaft

Die Ausgabe der Logging-Informationen erledigt Amazon löblicherweise nicht von Hand, sondern setzt dabei auf das Java.Util.Logging-Paket. Daraus folgt für Sie als Entwickler, dass Sie das Verhalten des Loggers anpassen können. Der Datenaustausch zwischen den verschiedenen mit AWS IoT Core verbundenen Geräten erfolgt ausschließlich über das MQTT-Protokoll. Hinter der Abkürzung verbirgt sich der String MQ Telemetry Transport. Dabei handelt es sich um ein niedrigschwelliges Nachrichtenaustauschsystem, das das bequeme Transferieren von Nachrichten erlaubt. Im Prinzip arbeitet der Server dabei mit nach dem Schema a/b/c aufgebauten Meldungskanälen. Ein Kanal entsteht dabei automatisch, wenn ein Client Interesse an den in ihnen abgefeuerten Informationen anmeldet – eine eingehende Nachricht stellt der Server automatisch an all jene zu, die daran Interesse haben.

Dass es im Hintergrund fortgeschrittene Funktionen wie QOS und Co. gibt, wollen wir an dieser Stelle schon aus didaktischen Gründen ignorieren. Zum Versenden einer ersten Nachricht benötigen wir dann den nachfolgenden Code, der innerhalb des Try-Catch-Blocks für den Verbindungsaufbau unterkommt:

try {
  client.connect();
  String topic = "sus/tamoggemon/thermodaten";
  String payload = "22";
  client.publish(topic, AWSIotQos.QOS0, payload);
} catch (AWSIotException e) {

Unter Vernachlässigung der QOS finden wir hier nur zwei Strings, die einerseits den zu verwendenden Ereigniskanal und andererseits die eigentliche Payload übertragen. Das API erlaubt dann das synchrone Übertragen der Informationen. An dieser Stelle können wir überprüfen, ob die Ausführung das gewünschte Ergebnis bringt. Hierzu reicht es aus, den in Abbildung 3 gezeigten Monitor des AWS-Backends zu nutzen. Er informiert uns über die Art der stattgefundenen Transaktionen.

Abb. 3: Der IoT-Monitor wird im Allgemeinen schnell aktualisiert

Abb. 3: Der IoT-Monitor wird im Allgemeinen schnell aktualisiert

Regel in der Cloud

Wir hatten weiter oben festgestellt, dass die Nutzung des MQTT-Protokolls kein Selbstzweck von Amazon bzw. anderen Cloud-Anbietern ist. Vielmehr geht es den Unternehmen darum, eine mehr oder weniger standardisierte Form der anzuwendenden Payload zu erreichen. Im Backend lassen sich so nämlich Regeln anlegen, die beim Eintreffen bestimmter Nachrichten – ohne weiteres Zutun von Infrastruktur auf Seiten des Entwicklers – Aktionen setzen.

Der Großteil der AWS-Dokumentation beschreibt das Festlegen von Regeln unter Verwendung der als CLI bezeichneten Kommandozeile: Ein sehr leistungsfähiges Werkzeug, dessen Anwendung und Einarbeitung den Rahmen dieses Artikels allerdings sprengen würde. Wir kehren stattdessen zum AWS IoT Backend zurück und klicken im links eingeblendeten Menü nun auf die Option Act. Das Backend blendet daraufhin ein aus zwei Optionen bestehendes Untermenü ein, in dem wir auf die Rules-Option klicken und dann eine neue Regel anlegen.

Im ersten Schritt müssen Sie dabei einen Namen und eine Beschreibung vergeben, um danach die zu verwendende SQL-Version zu spezifizieren. Das in Abbildung 4 gezeigte Auswahlfenster ist eine kleine Besonderheit des AWS-IoT-Diensts: Amazon erlaubt Ihnen die Auswahl verschiedener Versionen des SQL-Interpreters.

Abb. 4: Auswahl des SQL Interpreters

Abb. 4: Auswahl des SQL Interpreters

AWS IoT geht im Allgemeinen davon aus, dass die angelieferten Nachrichten nach dem folgenden Schema eine Art JSON-Datenbank darstellen:

{
  "reported": {
    "moisture" : <moisture-reading>,
    "temp" : <temperature-reading>
  }
}

Da wir uns derzeit allerdings nicht mit komplizierten Regeln aufhalten wollen, reicht es aus, nach dem Schema SELECT * FROM 'sus/tamoggemon/thermodaten' ein Select All anzulegen.

Im nächsten Schritt scrollen wir nach unten, um eine oder mehrere Aktionen hinzuzufügen. Der einfachste Weg hierzu ist das Anklicken des Actions-Buttons, in dem Sie aus einer Gruppe von Amazon vorgefertigten AWS-Aktionen wählen dürfen. Wir entscheiden uns hier für die Option Republish a message to an AWS IoT Topic und scrollen ganz nach unten, um in die nächste Seite des für die Konfiguration der Regel verantwortlichen Assistenten zu wechseln. Dort dürfen wir einen Topic String festlegen, der den Namen des Kanals festlegt, in dem die Informationen auszuliefern sind. In den folgenden Schritten wollen wir dabei mit dem Wert sus/tamoggemon/thermoantworten arbeiten.

Wichtig ist, dass das AWS Backend auch in diesem Bereich mit vollständiger Rechteverwaltung arbeitet – die Rule Engine darf nur auf jene Dienste zugreifen, die der Entwickler durch das Zuweisen der als Role bezeichneten Benutzerrollen autorisiert.

Wer mit einem jungfräulichen AWS-Konto arbeitet, muss an dieser Stelle aber nicht zurückkehren. Sie können auch auf die Option Create Rule klicken, um direkt eine neue Regel zu bestellen. Hierzu ist im ersten Schritt ein Name nötig; das Einfügen der notwendigen Berechtigungen erledigt das Backend für uns.

An dieser Stelle können wir unsere AWS-Regel jedenfalls speichern, woraufhin wir wieder im allgemeinen Regelkonfigurationsfenster landen. Scrollen Sie dort dann ganz nach unten, um die neue Regel anzulegen und zu speichern.

Informationen entgegennehmen

Zur Überprüfung des korrekten Funktionierens könnten wir theoretisch einen Client programmieren. Fürs Erste klicken wir stattdessen im links eingeblendeten Menü auf die Option Test, um den im Backend enthaltenen MQTT-Testclient zu aktivieren. Er erlaubt Ihnen das Anmelden bei mehr oder weniger beliebigen Topics. Im nächsten Schritt kehren wir in Eclipse zurück, wo wir das Programm abermals anwerfen und eine Message abfeuern. Der Lohn der Mühen ist das Aufscheinen der eingehenden Nachricht. Aufgrund der geringen Komplexität der vorliegenden Regel war nicht davon auszugehen, dass es bei ihrer Realisierung zu Problemen kommt. Hakt es bei Ihnen in der Praxis, finden Sie eine Gruppe hilfreicher Werkzeuge.

Als Nächstes wollen wir dafür sorgen, dass die per MQTT eingehenden Messages auch unter Nutzung des AWS IoT Device SDK for Java-SDK abgeerntet werden können. Das ist insofern sinnvoll, als Java-Code immer noch sehr gerne für Data-Mining-Aufgaben eingesetzt wird. Während man das tatsächliche „Einsammeln“ der Temperatur- oder sonstigen Informationen vielleicht eher mit einem stromsparenden ESP32 bewerkstelligt, ist es bequem, wenn der für die Visualisierung zuständige Prozessrechner die Informationen direkt durch Auswertung des MQTT-Datenstroms bekommen könnte.

Die Entgegennahme von MQTT-Nachrichten setzt eine MessageWorker-Klasse voraus, die von der im SDK enthaltenen Klasse AWSIotTopic abzuleiten ist. Die einfachste lebensfähige Version sieht folgendermaßen aus:

public class MessageRXWorker extends AWSIotTopic{
  public MessageRXWorker(String topic) {
    super(topic);
    // TODO Auto-generated constructor stub
  }
 
}

Wer die weiter oben angeschnittenen QOS-Funktion ebenfalls verwenden möchte, setzt stattdessen auf einen etwas komplizierteren Konstruktor. Für uns relevant ist hier allerdings die Methode onMessage, die für das Entgegennehmen der eigentlichen vom Server eingehenden MQTT-Nachrichten sorgen muss:

public class MessageRXWorker extends AWSIotTopic{
  @Override
  public void onMessage(AWSIotMessage message) {
    System.out.println(message.getStringPayload());
  }
}

Unsere Implementierung beschränkt sich darauf, die Payload als String zu betrachten und in die Systemkonsole auszugeben. Als Nächstes müssen wir eine Instanz in der Hauptklasse anlegen:

public class Worker {
  static MessageRXWorker topicW;

Die eigentliche Belebung und Anmeldung erfolgt so, wie man es erwarten würde:

try {
  client.connect();
  String topicName = "sus/tamoggemon/thermoantworten";
  topicW = new MessageRXWorker(topicName);
  client.subscribe(topicW);

Achten Sie darauf, den Aufruf von subscribe vor dem weiter oben abgedruckten Nachrichtensendecode zu platzieren. Danach können Sie das Programm auch schon ausführen, um sich am in Abbildung 5 gezeigten Verhalten zu erfreuen.

Abb. 5: Der Nachrichtenspiegel funktioniert

Abb. 5: Der Nachrichtenspiegel funktioniert

Weitere Experimente

An dieser Stelle haben wir einen grundlegenden Datenaustausch unter Verwendung des AWS SDK realisiert. Das wahrscheinlich am häufigsten verwendete Zusatzfeature dürften Wildcards sein: spezielle Zeichen, die die zum Anmelden einer Subscription verwendeten Strings modifizieren und so eine höhere Reichweite erreichen. Ein Klassiker wäre beispielsweise das Symbol +, das Sie nach dem Schema a/+/c verwenden dürfen. Es blendet eine Hierarchieebene aus, weshalb Nachrichten aus a/b/c und a/dddd/c gleichermaßen ankommen würden.

Bei Geräten mit schlechter Internetverbindung ist es oft wünschenswert, wenn auch das Absenden der Informationen mit einem asynchronen API erfolgt. Dazu müssen Sie nicht auf das Java Threading API zurückgreifen – das SDK bringt auch hierfür eine Abhilfe mit, die ich Ihnen ebenfalls kurz demonstrieren möchte:

public class MyMessage extends AWSIotMessage {
  public MyMessage(String topic, AWSIotQos qos, String payload) {
    super(topic, qos, payload);
  }

Analog zu weiter oben beginnt unsere Arbeit auch hier mit der Erzeugung einer IotMessage-Klasse. Sie bekommt diesmal einen umfangreicheren Konstruktor eingeschrieben und bringt zudem eine Gruppe weiterer Ereignis-Handler mit:

@Override
  public void onSuccess() {    }
 
  @Override
  public void onFailure() {    }
 
  @Override
  public void onTimeout() {    }
}

Das eigentliche Versenden der Nachricht ist dann wieder gewöhnliche OOP:

long timeout = 3000;                    // milliseconds
MyMessage message = new MyMessage(topic, qos, payload);
client.publish(message, timeout);

Fazit

Das AWS IoT Device SDK for Java ermöglicht Entwicklern das komfortable Erzeugen von Anwendungen, die die in der AWS-Infrastruktur ausgetauschten Nachrichten sowohl emittieren als auch konsumieren können. Aus der Logik folgt, dass sich diese Messages natürlich auch im Backend bzw. mit anderen AWS-Diensten verarbeiten lassen.

Unsere Reise durch die faszinierende Welt von Amazon IoT ist damit aber noch nicht am Ende. Beim nächsten Mal nutzen wir die als Device Shadow bezeichnete Funktion, um Informationen auch beim Abbrechen der Internetverbindung aufrechtzuerhalten. Bleiben Sie bei uns – denn es bleibt spannend.

Geschrieben von
Tam Hanna
Tam Hanna
Tam Hanna befasst sich seit der Zeit des Palm IIIc mit der Programmierung und Anwendung von Handcomputern. Er entwickelt Programme für diverse Plattformen, betreibt Onlinenewsdienste zum Thema und steht unter tamhan@tamoggemon.com für Fragen, Trainings und Vorträge gern zur Verfügung.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: