Einführung in das JBoss-Communityprojekt

JBoss goes Mobile – Mobile Anwendungen mit AeroGear
Kommentare

AeroGear ist ein JBoss-Communityprojekt, das den Fokus auf die mobile Entwicklung legt. Das Projekt bietet eine Bibliothek und verschiedene Utilities für Android, iOS und JavaScript. Wir werfen einen ersten Blick auf AeroGear.

Die Entwicklung von mobilen Anwendungen muss nicht unbedingt komplexer sein als nötig. Eine häufige Hürde ist die Bereitstellung einer Anwendung für verschiedene Plattformen. Für den Java-Entwickler scheint Android noch die gängigste Wahl zu sein, allerdings lauern auch hier Tücken, die nicht mit den Aufgabenstellungen von Swing- oder gar Enterprise-Java-Anwendungen zu vergleichen sind. Neue, oder zusätzliche Sprachen, wie JavaScript oder gar Objective-C können ebenfalls Probleme bereiten.

Unterschiedliche Frameworks

Ein durchaus größeres Problem als die verschiedenen Sprachen sind allerdings die völlig unterschiedlichen Frameworks, die es für die entsprechenden Plattformen gibt. Eine mobile Anwendung soll in erster Linie eine fachliche Anforderung umsetzen, doch diese gilt es in Quellcode zu formulieren. Für den Zugriff auf eine HTTP-Schnittstelle wird in Android häufig das HttpURLConnection-API genutzt, bei iOS-Anwendungen hingegen das AFNetworking Framework oder die systemeigene NSURLConnection-Klasse. Innerhalb von mobilen Webanwendungen wiederum kommt jQuery oder das XmlHttpRequest-Objekt zum Einsatz. Diese Frameworks haben völlig unterschiedliche APIs, was nicht nur daran liegt, dass sie in verschiedenen Programmiersprachen erstellt wurden. Diese Unterschiede machen es nicht leichter, eine fachliche Anforderung für die unterschiedlichen mobilen Plattformen zu programmieren.

AeroGear: Einheitliches API

Das AeroGear-Projekt bietet ein einheitliches API für Android, iOS und JavaScript. Die Konzepte der Bibliothek sind plattformübergreifend und somit leicht auf andere Plattformen zu übertragen. In der Praxis bedeutet das, dass das API dieselbe Funktionalität bereitstellt, dabei allerdings die Gegebenheiten der jeweiligen Programmiersprache honoriert. In Java wird ein Callback beispielsweise durch ein Interface ausgedrückt, bei Objective-C stattdessen ein so genannter Block (vergleichbar mit einem Closure) genutzt. Die derzeitigen Schwerpunkte von AeroGear liegen auf HTTP-Kommunikation, Offlinespeicherung und verschiedenen Sicherheitsthemen wie OTP oder Authentifizierung.

HTTP-REST-Zugriff

Für den Zugriff auf RESTful Web Services bietet AeroGear die Pipe-Schnittstelle. Sie abstrahiert den eigentlichen HTTP-Zugriff auf eine HTTP-Ressource und stellt im Wesentlichen CRUD-Funktionalität bereit. Sämtliche Pipe-Objekte werden mittels der Pipeline-Klasse erstellt, die ebenfalls die Verwaltung der einzelnen Pipes übernimmt. Nehmen wir an, es gibt einen REST-Service h ttp://server.com/cars, der über entsprechende CRUD-Funktionalität, wie hier beschrieben, funktioniert. Hier der Code für die AeroGear-Android-Plattform:

// set up the baseURL and the Pipeline "Factory":
URL baseURL = new URL("h ttp://server.com");
Pipeline pipeline = new Pipeline(baseURL);

// create Connection to "h ttp://server.com/cars"
Pipe carsPipe = pipeline.pipe(Car.class);

Innerhalb von drei Zeilen wird das Pipe-Objekt für den oben genannten URL erstellt. Anschließend können Daten vom REST-Service gelesen oder zu ihm übertragen werden (Listing 1).

// create "business object"
Car someCar = new Car(.........);

// save, via HTTP POST:
carsPipe.save(someCar, new Callback<Car>() {

  @Override
  public void onSuccess(Car serverData) {
    // work with the data....
  }
  @Override
  public void onFailure(Exception e) {
    // show error...
  }
});

Ein einfaches POJO-Objekt wird mittels der save-Methode zum Server geschickt. Die Tatsache, dass intern HTTP-POST verwendet wird, ist völlig transparent. Neben dem eigentlichen Objekt wird eine anonyme Implementierung des Callback-Interface übergeben. Im Falle eines erfolgreichen Speicherns (z. B. HTTP 201 (created)), wird die onSuccess()-Methode aufgerufen. Verschiedene Fehler werden innerhalb von onFailure() signalisiert.

Wird dieser Code aus einer Android Activity oder einem Fragment aufgerufen, muss man die Eigenschaften des Android Activity Lifecycle beachten. Bei einem eingehenden Anruf wird die Anwendung in den Hintergrund befördert, und (via HTTP) gelesene Daten können verloren gehen oder gar einen Fehler verursachen, wenn der Callback die UI der sich im Hintergrund befindenden Android Activity verändern will. Ähnliches passiert bei einer „Konfigurationsänderung“, wie beispielsweise dem Drehen des Telefons. Hier wird die aktuelle Android Activity zerstört und das Betriebssystem erzeugt eine neue. Die Daten der anonymen Callback-Implementierung gehen hier ebenfalls verloren. Auch hier bietet AeroGear Hilfe in Form von speziellen abstrakten Hilfsimplementierungen des Callback-Interface an.

Neben den CRUD-Features wird auch das Thema Hypermedia angegangen: AeroGears Pipe unterstützt verschiedene Arten für das Scrollen durch große Datenmengen („Pagination“). Dabei wird unter anderem der Web-Linking RFC umgesetzt (Listing 2).

...
ReadFilter readFilter = new ReadFilter();
readFilter.setLimit(2); // max. two cars per response

// HTTP GET for two cars
carsPipe.read(readFilter, new Callback<List<Car>>() {

  @Override
  public void onSuccess(List<Car> response) {
    PagedList<Car> pagedCars = (PagedList<Car>) response;

    // performs HTTP-GET for next items
    data.next(....callback.....);
    
  }
  @Override
  public void onFailure(Exception e) {
    // show error...
  }
});

Für die Pagination wird der read()-Methode neben dem üblichen Callback ein ReadFilter-Objekt mitgegeben. Neben diesen Parametern können weitere Feineinstellungen bei der Erzeugung des Pipe-Objekts mit der PageConfig-Klasse angegeben werden, wie beispielsweise die Info, welche Linking-Variante genutzt werden soll (z. B. RFC-5988 oder innerhalb des Response).

Hat der Server die (HTTP-)Anfrage der Android-Anwendung beantwortet, wird wie erwartet die onSuccess()-Methode aufgerufen. Hier wird das List-Ergebnis auf ein type-cast auf ein PagedList-Objekt vorgenommen. Der Aufruf von next() bezweckt das Versenden einer weiteren Anfrage an den Server. In diesem Beispiel werden die nächsten zwei Autos vom Server geladen.

Apple iOS

Das oben beschriebene Pipe-/Pipeline-API liegt auch für die iOS-Plattform vor. Auch hier bietet das Pipe-Objekt die bekannten read- oder save-Funktionen, allerdings angepasst an die „Besonderheiten“ von Objective-C (Listing 3).

// set up the baseURL and the Pipeline "Factory":
NSURL *baseURL = [NSURL URLWithString:@"http://server.com"];
AGPipeline *pipeline = [AGPipeline pipelineWithBaseURL:baseURL];
// create Connection to "http://server.com/cars"
id<AGPipe> carsPipe = [pipeline pipe:^(id<AGPipeConfig> config) {
  [config setName:@"cars"];
}];

Innerhalb weniger Zeilen wird das Pipe-Objekt für den gewünschten URL erstellt. Die Pipeline „Factory“ verfügt, wie in der Android-Variante, über eine pipe-Methode. Hier handelt es sich um einen so genannten Block (vergleichbar mit Java Closures (JDK 8)). Dieser Block bekommt genau ein Objekt übergeben, um die Konfiguration (z. B. Endpointname oder Authentifizierungsmodule) zu bestimmen. Anschließend können Daten vom REST-Service gelesen oder zu ihm übertragen werden:

// create "business object"
NSDictionary *car = @{@"maker": @"VW", @"type": @"Golf"};

// save, via HTTP POST:
[carsPipe save:car success:^(id responseObject) {
  // work with the data....
} failure:^(NSError *error) {
  // show error...
}];

Ähnlich wie in der Android-Variante wird ein „Auto“-Objekt zum Server geschickt. Statt einer eigenen Klasse wird eine Map genutzt, die das Auto repräsentiert. Die success– und failure-Callbacks sind ebenfalls mithilfe von Blocks realisiert. Beide Blocks haben jeweils genau ein Argument.

[ header = Seite 2: Xcode Template ]

Xcode Template

Abb. 1: Xcode Template

Das Starten von neuen Projekten ist oft müßig. In AeroGear gibt es deshalb ein Xcode Template, gezeigt in Abbildung 1. Dieses Template erzeugt ein einfaches Beispiel auf Basis der AeroGear-iOS-Bibliothek. Nachdem das Projekt erzeugt wurde, fehlt noch ein kleiner Schritt: Die Abhängigkeiten müssen mittels CocoaPods installiert werden. CocoaPods ist im weiten Sinne vergleichbar mit Apache Maven:

pod install

Mit diesem Aufruf werden die Abhängigkeiten geladen und ein so genannter Workspace wird erzeugt. Nach dem Öffnen dieses Workspace kann die Anwendung direkt auf dem eigenen Telefon gestartet werden. Der erste wichtige Schritt für eine eigene iOS-Anwendung.

Neben Android und iOS wird ebenfalls JavaScript für mobile Webanwendungen angeboten. Auch hier entspricht das API den bereits vorgestellten Konzepten. Für die Callbacks werden hier allerdings JavaScript-Funktionen verwendet:

jsCarsPipe.read({

  success: function( data ) {...}

});

Offline-Storage

Die verschiedenen Clientplattformen bieten unterschiedliche Möglichkeiten, Daten zu speichern:

  • Android: Memory, SQLite
  • iOS: Memory, PList, SQLite (in der Entwicklung)
  • JavaScript: Memory, SessionStorage, LocalStorage sowie Web SQL und IndexedDB (beide in der Entwicklung)

Auch hier bietet das Store-API plattformübergreifende Konzepte, wiederum angepasst an die Besonderheiten der verwendeten Programmiersprache, wie hier im Fall von JavaScript:

var userStore = AeroGear.DataManager({
  name: "users",
  type: "SessionLocal"
}).stores.users;

userStore.save(someObject);

2-Phasen-Authentifizierung und OTP

Neben einem gewöhnlichen Login-Module bietet AeroGear Support für One Time Password (OTP) (siehe hier). Mit der 2-Phasen-Authentifizierung kann der eigentliche Login um einen zusätzlichen Schutzmechanismus erweitert werden. Neben dem vom Benutzer gewählten Passwort ist ein generierter OTP-Token notwendig. In AeroGear wird die TOTP-(Time-Based-One-Time-Password-)Variante umgesetzt. Die Java-Bibliothek kann sowohl clientseitig (Android) als auch serverseitig eingesetzt werden. Der Server erzeugt dabei ein Secret und schickt dieses zum Client:

@javax.inject.Inject
@org.jboss.aerogear.security.auth.Secret
private Instance<String> secret;
....
Totp totp = new Totp(secret.get());

Der Client muss nun anhand des empfangenen Geheimnisses, beispielsweise via SMS, den Token für den zweiten Schritt der Anmeldung erzeugen (Android):

String secret = // received secret
// initialize OTP
Totp generator = new Totp(secret);
// generate token
String token = generator.now();

Der Client schickt diesen Token nun zum Server, damit dieser – nach erfolgreicher Validierung – den Login-Versuch frei gibt:

@javax.inject.Inject
@org.jboss.aerogear.security.auth.Secret
private Instance<String> secret;
...
Totp totp = new Totp(secret.get());
// check if the TOKEN was valid
boolean result = totp.verify(tokenFromMobileDevice);

Abbildung 2 zeigt den Prozess auf Basis des iOS-Client-Demos. Eine JavaScript-Variante ist derzeit in Arbeit.

Abb. 2: iOS-Client-Demo

AeroGear.NEXT

Die aktuelle Version 1.0 legt die Basis für kommende AeroGear-Features. Derzeit arbeiten die Entwickler an neuen Funktionalitäten wie beispielsweise „Data-Sync“, „Offline“ (z. B. CoreData für iOS oder ContentProvider Stores für Android) sowie auch plattformübergreifende Push Notificationen für APNs, GCM oder SimplePush. Der AeroGear Unified Push Server bietet eine einheitliche HTTP-Schnittstelle (und Java-API), um eine Push-Notification auf unterschiedliche Endgeräte zu senden (Abb. 3) (siehe auch hier bzw. hier).

Abb. 3: Push Notification

Eine Besonderheit hier ist die Entwicklung eines JavaScript-API für SimplePush, das außerhalb des FirefoxOS nutzbar ist. Neben der JavaScript-Bibliothek stellt das Projekt auch eine Implementierung des SimplePush-Protokolls bereit.

Die Version 1.1.0 im August beinhaltete die erste 0.8.0-Community-Version des AeroGear-Push-Kontexts, der unter anderem auch leicht auf OpenShift betrieben werden kann. In der zweiten Jahreshälfte 2013 darf mit weiteren Releases von AeroGear gerechnet werden. Die Entwickler freuen sich über Feedback und Diskussion auf der Mailingliste des Projekts.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -