seeseekey.net - Invictus Deus Ex Machina

Möchte man in einer Android-App einen Alert (in etwa das Äquivalent zu einer MessageBox) erzeugen, so kann der AlertDialog dafür genutzt werden. Dieser stellt dabei einen Builder zur Verfügung mit welchem der Dialog schnell erstellt ist:

AlertDialog dialog = new AlertDialog.Builder(view.getContext()).create();

dialog.setTitle("Warnung");
dialog.setMessage("Bild konnte nicht gesendet werden.");

dialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
    new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }
    });

dialog.show();

In dem Quelltext wird eine neue Instanz des AlertDialog-Builders mit dem Kontext des aktuellen View angelegt. Mit Hilfe des Builders wird der Titel und die Nachricht des Dialogs festgelegt. Anschließend wird ein OK-Button definiert und dieser mit einer Funktionalität versehen. Danach wird der Dialog mittels der show()-Methode angezeigt.

Vor einigen Tagen begegneten mir einige Dateien, deren Inhalts größtenteils aus der Zeichenfolge EF BF BD EF BF BD EF BF BD (hexadizimal) bestand. Eigentlich sollte in den entsprechenden Dateien binäre Daten enthalten sein. Damit stellte sich nun die Frage: Was war passiert?

EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD

Auf den ersten Blick sah das ganze so aus, als ob ein Großteil der Datei durch Datenmüll ersetzt wurde. Schaut man sich die Zeichenfolge allerdings genauer an, so wird man feststellen das sich die Folge EF BF BD immer und immer wieder wiederholt. Bei dieser Zeichenfolge handelt es sich um die hexadezimale Schreibweise des Unicode-Zeichens für den Replacement Character welcher meist durch eine Raute mit einem Fragezeichen (�) dargestellt wird.

Eindeutiger wäre das Problem gewesen, wenn die erzeugten Dateien mit der Zeichenfolge EF BB BF begonnen hätte. Dabei handelt es sich um das Byte Order Mark für eine UTF-8 kodierte Datei. Damit wäre gleich klar geworden, das die enthaltenen Daten nicht zu einer Datei mit binären Inhalten passen. Doch wie sind diese Dateien nun entstanden? Der Ursprung der Dateien ist in einer Java-Applikation zu finden, welche diese Dateien erstellt. Diese kopierte die Daten von A nach B, im Quelltext (man ignoriere das fehlende try with resources) könnte das so ausgesehen haben:

FileInputStream fileInputStream  = new FileInputStream("binary.dat");
FileWriter fileWriter = new FileWriter("binary-copied.dat");

int byteData;

while ((byteData = fileInputStream.read()) != -1) {
    fileWriter.write(byteData);
}

fileInputStream.close();
fileWriter.close();

Hier wird ein FileInputStream geöffnet und dieser Stück für Stück mit einem FileWriter in die Zieldatei geschrieben. Genau an dieser Stelle entsteht das Problem – der FileWriter ist nämlich ein zeichenbasierter Writer, das bedeutet das sämtliche Zeichen, die mit diesem geschrieben werden, kodiert werden. Wenn nun bei dieser Kodierung ein Zeichen gefunden wird, welches nicht im Unicode abgebildet werden kann, so erhält dieses Zeichen den Wert EF BF BD – besagter Replacement Character. Damit ist dann auch erklärt warum die binären Dateien hauptsächlich nur noch aus diesen Zeichen bestanden. Die echten Daten wurden beim Kopiervorgang größtenteils schlicht und ergreifend in den Replacement Character konvertiert, da sich für diese Daten keine Entsprechung im Unicode fand.

Den freien Suchserver Elasticsearch kann man, wie das Wort Suchserver es dezent andeutet, als Server betreiben. Allerdings ist es manchmal nicht gewünscht einen dedizierter Server zu betreiben. In einem solchen Fall kann man den Elasticsearch-Server in eine Java-Applikation einbetten. Der sicherlich häufigste Fall für eine solche Einbettung ist dabei die Nutzung zu Testzwecken (z.B. Unit-Tests zum Test der Suchergebnisse). Im ersten Schritt sollte in das Java-Projekt die entsprechende Abhängigkeit zum Projekt hinzugefügt werden. In diesem Beispiel wird dabei auf einem auf Maven basierenden Projekt ausgegangen – in diesem muss die pom.xml entsprechend erweitert werden:

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>2.3.4</version>
</dependency>

Damit wurde Elasticsearch dem Projekt hinzugefügt. Nun muss der eigentliche Server im Java-Projekt gestartet werden. Dafür werden nur wenige Zeilen Quellcode benötigt:

Settings.Builder elasticsearchSettings = Settings.settingsBuilder()
    .put("http.enabled", "true")
    .put("path.data", "data")
    .put("path.home", "home");

Node node = nodeBuilder()
    .local(true)
    .settings(elasticsearchSettings.build())
    .node();

In diesem Beispiel werden im ersten Schritt die Einstellungen für Elasticsearch definiert. Dabei wird unter anderem der HTTP-Modus aktiviert. Wenn dieser deaktiviert ist, ist die Kommunikation per HTTP nicht mehr möglich. Stattdessen kann dann nur noch die Kommunikation über das Transport-Interface genutzt werden. Dieses Interface wird im Normalfall für die interne Kommunikationen zwischen einzelnen Elasticsearch-Clustern genutzt – allerdings ist eine Nutzung mit dem Elasticsearch-Java-Client ebenfalls möglich. Mit dem aktivierten Modus, ist die Entwicklung einfacher (z.B. für den Einsatz von Analyse-Tools) und die Konfiguration näher an der Praxis. Standardmäßig horcht der Elasticsearch-Server dabei auf dem Port 9200 – in diesem Beispiel wäre er über die URL:

http://localhost:9200

erreichbar. Nachdem die Einstellungen angelegt wurden, werden diese dem NodeBuilder übergeben, welcher schlussendlich den Elasticsearch-Server hochfährt. Nachdem dieser hochgefahren wurde, kann die Nutzung des selben beginnen.

Ein REST-API von Hand entwickelt, benötigt eine Dokumentation, ein entsprechenden Server und eventuell einige Clients als Referenz. Einfacher wird es mit einem Framework wie Swagger. Unter Zuhilfenahme der Beschreibungssprache YAML können mit Hilfe des Frameworks REST-APIs, Dokumentation, Server und Clients generiert werden.

Der Swagger Editor

Der Swagger Editor

Doch Swagger versteht sich nicht nur als Framework, sondern auch als Spezifikation. Begonnen wurde mit der Entwicklung bereits im 2010; die Swagger Specification trägt seit Anfang Januar 2012 offiziell den Namen OpenAPI Specification und beschreibt eine maschinenlesbare Interfacedefinitionen einer REST-API. Ähnliches wurde unter anderem schon mit WSDL und WADL versucht – alles Konzepte bzw. Beschreibungsprachen welche an ihren eigenen Limitationen gescheitert sind und wenn überhaupt nur noch sporadisch genutzt werden.

Betreut und weiterentwickelt wird die Spezifikation nun von der Open API Initiative, zu der namenhafte Firmen wie Google, PayPal, IBM, Atlassian und Microsoft gehören. Die Spezifikation als solche ist freie Software und auf GitHub zu finden. Sie ist unter der Apache Lizenz lizenziert. Aktuell ist die Spezifikation in der Version 2.0 veröffentlicht.

Auf der offiziellen Webseite von Swagger findet sich ein Editor, mit welchem APIs definiert werden können und anschließend exportiert werden können. Der Editor kann dabei Server unter anderem in den Sprachen bzw. für die Framworks Haskel, Jax-RS, Node.js, Python, Rails und PHP erzeugen. Bei den Clients ist die Auswahl noch größer. Diese können in C#, HTML, Dart, Go, Groovy, Java, Javascript, Objective C, Perl, PHP, Ruby, Scala, Swift und vielen weiteren Sprachen erzeugt werden.

Neben dem Editor kann für die Erzeugung von Clients auch der Swagger Codegen genutzt werden. Dabei handelt es sich um eine Java-Anwendung um die Clients lokal auf dem eigenen Rechner zu erzeugen. Der Editor und viele weitere Tools rund um Swagger sind ebenfalls auf GitHub zu finden. – auch diese sind freie Software, welche unter der Apache Lizenz stehen.

Statische Codeanalyse ist eine feine Sache. Sie weißt auf Fehler und Probleme schon zur Entwicklungszeit hin. Dafür gibt es unter anderem Systeme wie SonarQube. Wem ein solchen System zu groß ist, der kann SonarLint nutzen, welches statische Codeanalyse lokal in der gewünschten IDE liefert.

sonarlint.org/intellij/

sonarlint.org/intellij/

Für IntelliJ IDEA findet man die passende Version dabei unter sonarlint.org/intellij/. Daneben gibt es auch Versionen für Eclipse und Visual Studio. SonarLint wird dabei mit einem vorgefertigtem Regelsatz geliefert und kann nach der Installation gleich genutzt werden. SonarLint ist unter der GPL in Version 3 (bzw. der LGPL) lizenziert und damit freie Software.

Unter Java ist es mögliche alle Klassen eines Packages als ganzes zu importieren. Im Quelltext sieht das dann so aus:

import com.example.project.engine.*

Viele IDEs wie auch IntelliJ IDEA fügen diese Wildcard-Imports zu einem Projekt, wenn mehr als eine Klasse (abhängig von den Einstellungen) aus dem Namespace genutzt wird. Unter Umständen ist dieses Verhalten allerdings nicht gewünscht. Möchte man das Verhalten unter IntelliJ IDEA deaktivieren, muss dies in den Einstellungen geschehen.

Die entsprechende Seite in den Einstellungen

Die entsprechende Seite in den Einstellungen

Dort gibt es unter Editor -> Code Style -> Java -> Imports die Einstellung ab wie vielen Klassen ein Wildcard-Import genutzt werden soll. Stellt man hier die Zahl entsprechend hoch ein, so werden keine Wildcards-Imports mehr genutzt.

Wenn man die Verbindung von Klassen und deren Komponenten analysieren möchte, so kann man dies in vielen Java-IDEs mit Hilfe eines automatisch erzeugten Klassendiagrames. Verfügt die eigene IDE nicht über eine solche Funktionalität, so kann das Werkzeug Class Visualizer genutzt werden, welches unter class-visualizer.net zu finden ist.

Der Class Visualizer nach dem Start

Der Class Visualizer nach dem Start

Das Tool lädt dabei eine Reihe von Klassen aus einer JAR-Datei, bzw. aus einem Verzeichnis und analysiert diese anschließend. Danach kann die Klassenhierarchie durchsucht und die einzelnen Klassen analysiert werden. Dabei werden Ableitungen, Berechtigungen und vieles mehr übersichtlich darstellt, so das man auch komplexe Abhängigkeiten schnell durchschaut hat. Bei der Software handelt es sich um Freeware.

Wenn man in der IDE Intellij IDEA von JetBrains eine neue Java-Klasse erzeugt, so wird ein entsprechender Header erzeugt, aus welchem hervorgeht, wer genau diese Klasse angelegt hat. Zum Abschalten des Verhaltens müssen die Einstellungen geöffnet werden.

Die entsprechenden Einstellungen

Die entsprechenden Einstellungen

In den Einstellungen befindet sich der Punkt File and Code Templates. In diesem Punkt gibt es den Tab Includes. Wird dort der Punkt File Header ausgewählt, kann dieser entsprechend der eigenen Wünsche konfiguriert z.B. komplett entfernt werden.

Nutzt man Maven in einem Projekt, so wird dieses Projekt eine Reihe von Abhängigkeiten in Form von Bibliotheken haben. Manchmal kommt es vor, das eine Bibliothek in mehreren Versionen von unterschiedlichen Bibliotheken eingebunden wird und das dass Hauptprojekt die falsche Version nutzt. Im Terminal kann man mittels des Befehls:

mvn dependency:tree

den Abhängigkeitsbaum des Projektes anzeigen lassen. Bei kleinere Projekten sieht der Baum dabei noch übersichtlich aus:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ netty-example ---
[INFO] de.uulm.vs:netty-example:jar:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] \- org.jboss.netty:netty:jar:3.2.2.Final:compile

Bei größeren Projekten kann man mit Hilfe des Abhängigkeitsbaum ermitteln, welche Abhängigkeiten woher kommen und eventuelle Probleme mit diesen lösen.

Wenn man unter Java eine JSON-Datei schreiben möchte, so kann man dies natürlich von Hand tun. Einfacher und effizienter ist es allerdings wenn man das ganze mit der Bibliothek GSON erledigt. Dabei handelt sich um eine von Google entwickelte Bibliothek, welche ursprünglich für den internen Gebrauch bei Google gedacht war. Mit dieser ist neben vielen anderen Dingen unter anderem auch die Serialisierung von Java-Objekten in JSON möglich. Möchte man eine JSON-Datei manuell schreiben so nutzt man den von der Bibliothek bereitgestellten JsonWriter:

JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(new File("test.json")), StandardCharsets.UTF_8));

writer.setIndent("  "); // definiert die Einrückung

writer.beginObject();
writer.name("test");
writer.value("123");
writer.endObject();

writer.close();

Im ersten Beispiel wird ein einfaches Objekt in eine JSON-Datei geschrieben, was in der Datei schlussendlich so aussieht:

{
  "test": "123"
}

Natürlich ist es auch möglich beliebig viele Objekte in eine Datei zu schreiben und Arrays zu nutzen:

JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(new File("C:\\Temp\\test.json")), StandardCharsets.UTF_8));

writer.setIndent("  "); // definiert die Einrückung

writer.beginObject();
writer.name("persons");

writer.beginArray();

writer.beginObject();
writer.name("firstname");
writer.value("Hans");
writer.name("lastname");
writer.value("Meiser");
writer.endObject();

writer.beginObject();
writer.name("firstname");
writer.value("Luna");
writer.name("lastname");
writer.value("Bottom");
writer.endObject();

writer.endArray();

writer.endObject();

writer.close();

Heraus kommt dabei eine JSON Datei welche so aussieht:

{
  "persons": [
    {
      "firstname": "Hans",
      "lastname": "Meiser"
    },
    {
      "firstname": "Luna",
      "lastname": "Bottom"
    }
  ]
}

Zu finden ist die Bibliothek auf GitHub. Lizenziert ist die GSON-Bibliothek unter der Apache Lizenz und damit freie Software.