Webframeworks für JVM-Sprachen: Rails gegen Grails

Seite 3: REST und asynchrone Verarbeitung

Inhaltsverzeichnis

ERB (Embedded Ruby), eine Mischung aus beispielsweise HTML mit eingebettetem Ruby, ist Rails' View-Technik und sorgt zusammen mit Template Partials und Helper-Funktionen für wartbare Views. Grails' Gegenstück dazu sind Sitemesh als Layout Engine und Groovy Server Pages (GSP) für Markup und GSP-Tags (ähnlich JSP/ASP). Komplexer Code lässt sich in selbstgeschriebenen GSP-Tags (Taglibs) verstecken.

Die Präprozessor-Verarbeitung von SASS oder CoffeeScript, die Komprimierung von Bildern oder JavaScript in für das Web geeignete Größen sowie Abhängigkeiten zu weiteren Komponenten decken die mitgelieferten Asset-Pipeline Plug-ins ab. Nur wer noch feingranularer testen und optimieren will, muss sich noch selbst mit Bower, Grunt oder Gulp die Hände beschmutzen.

class SimpleTagLib {
/**
* @attr bye Begrüßung ('true') oder Verabschiedung ('false')
*/
def helloOrGoodbye = { attrs, body ->
out << (attrs.bye == 'true' ? "Tschüss " : "Hallo ") << body()
}
}
// in der GSP Datei, Ausgabe: "Tschüss Jochen"
<g:helloOrGoodbye bye="true">Jochen</g:helloOrGoodbye>

Bereits zu einer Zeit (~2007), als SOAP noch schwer angesagt war, hat Rails konsequent auf REST gesetzt. Heutzutage helfen sowohl Rails als auch Grails auf ihre Art beim Exponieren und Konsumieren von Ressourcen, beispielsweise initial durch Restful CRUD Scaffolding, Routing oder Content-Negotation, aber auch durch Model-Serialisierung. Viel Zeit fließt erfahrungsgemäß in die Integration und Anpassung der Schnittstellen, sodass die bereitgestellten Features mit ihren Annahmen zu Hypermedia, Content-Type, Versionierung oder technischer Dokumentation nur teilweise nützlich sind.

Das neuzeitlich-schlanke Rails liefert viele seiner im REST-Kontext gebrauchten Funktionen nur noch über Gems und überlässt die Auswahl dem Entwickler. Ein Beispiel dafür ist ActiveResource, früher Bestandteil des Rails-Core und probates Mittel, um zwei Rails-Applikationen per RESTful HTTP über eine ActiveRecord ähnliche API kommunizieren zu lassen.

Grails hat im Auslieferungszustand einen mächtigen Funktionsumfang. Zum Beispiel können Entwickler Domain-Klassen einfach per Annotation in eine Ressource verwandeln. Wird es spezifischer, erfolgt die Implementierung im Controller, der wiederum Funktionen des RestfulController und diverser, anpassbarer Converter, Renderer und Binder (zum Beispiel für HAL-JSON), nutzen kann. So sieht etwa Content Negotiation in Grails wie folgt aus:

// Neuen Mime-Type und Renderer in resources.groovy 
// und Config.groovy definieren
def campaignMimeType = new MimeType("application/vnd.campaign+json")
beans = {
campaignRenderer(JsonRenderer, Campaign, campaignMimeType)
}
// Aus dem Controller, via JsonRenderer für den neuen Mime-Type, rendern
respond Campaign.get(1L)

HTTP Requests sind oft der Flaschenhals einer Webanwendung, daher sollte man nur das Nötigste zum schnellen Rendern der Seite tun und lang laufende Aufgaben auf nicht blockende Nebenschauplätze verlegen.

Frühe Versionen von Rails waren nicht thread safe und so wurde der gesamte Request vom Webserver (bspw. Mongrel) mit einer Mutex-Klasse ummantelt. Das ist lange her, aber genau wie das Parallelisierungsproblem in MRI kommt dieser Punkt in Diskussionen immer wieder auf. Blockende HTTP-Zugriffe nach Außen lassen sich so refakturieren, dass sie die Rack-Middleware-Schicht (Webserver API) direkt verarbeitet.

Unter JRuby gibt es keinen Grund, die Java-Concurrency-Bibliotheken nicht zu nutzen. Allerdings gibt auch unabhängige Gems wie Sidekiq (Celluloid, Threads, Redis), um Tasks asynchron abzuarbeiten. Grails erspart die Suche nach entsprechenden Bibliotheken und bietet mit dem integrierten GPars-Framework eine mächtige Async API (Promise Pattern), die beispielsweise Services durch Verwenden der @DelegateAsync-Annotation einen asynchronen Klon hinzufügt. GORM bietet für viele seiner Funktionen entsprechende *async-Varianten und die Action Response lässt sich als PromiseMap direkt aus dem Controller zurückgeben:

import static grails.async.Promises.*
import Campaign

class CampaignController {
def index() {
// return grails.async.PromiseMap
tasks campaigns: Campaign.async.list(),
totalCampaigns: Campaign.async.count(),
otherValue: task { 2 * 2 }
}
}

Nicht nur in Enterprise-Projekten ist entkoppelte, skalierbare und komponentenübergreifende Kommunikation über Message Services gewünscht. Im Rails-Umfeld kann man dafür ebenfalls Sidekiq einsetzen, aber eigentlich sind beliebige JMS-Provider (Java Message Service) unter JRuby beziehungsweise Groovy denkbar. Task Scheduler, die Aufgaben zu festgelegten Zeiten starten, sind eine weitere Möglichkeit.

In Rails kann man tatsächlich einem Betriebssystem-Job die Ausführung eines Rake Task überlassen oder man setzt eines der dazu verfügbaren Gems (zum Beispiel Whenever) ein. Noch einfacher geht es mit dem Quartz-Plug-in in Grails.