Von Mule nach Java und zurück

Keine Kommentare

Da Mule weitgehend aus Java besteht, überraschen die vielfältigen Kombinationsmöglichkeiten mit Java nicht. Einige bekannte – und weniger bekannte – Varianten stelle ich in den folgenden Abschnitten vor.

Eine Kleinigkeit aber vorweg: Einige werden sich nach der Einleitung die Frage stellen, welche Sprache sonst noch im Mule-Kern vorkommt: Es ist Scala, das in einigen der Enterprise-Komponenten (DataWeave, Batch) vorkommt. Wenn Mule läuft, befindet sich die Scala-Laufzeitbibiliothek also immer auch auf dem Classpath. Wer möchte, kann daher auch Scala statt Java verwenden. Ich beschäftige mich heute aber “nur” mit Java.

Expression Language

Mule erlaubt an beinahe allen Stellen in seinen Flows statt fester Werte sogenannte MEL-Expressions. MEL steht für “Mule Expression Language”. Eine MEL-Expression wird mit #[] eingerahmt. MEL sieht aus wie viele andere Skriptsprachen, sie ist stark an C- bzw. Java-Syntax angelehnt. Die genaue Beschreibung befindet sich bei Mule: MEL-Reference.
Wenn eine MEL-Expression in einem Flow ausgewertet wird, so liegen einige Objekte bereits im Scope, z.B. message für die Nachricht oder payload für die Nutzlast der Nachricht. Mule-Kenner wissen bereits: Die Payload ist immer ein Java-Objekt (genauer: eine Instanz eines Objektes). Bei POJOs kann mit Punkt-Notation auf die Attribute zugegriffen werden, im Hintergrund ruft der MEL-Interpreter die zugehörigen Getter auf. So könnte man aus einem String mit

<set-payload value="#[payload.bytes]" doc:name="getBytes()" />

den Inhalt als Byte-Array auslesen. (Das Beispiel ist konstruiert und gefährlich: Es gibt zwar eine Methode getBytes() in String, die ist jedoch kein echter “Getter”. Außerdem verlassen wir uns hier auf das Default-Encoding, was grundsätzlich keine gute Idee ist.) Anschließend hätten wir in der Mule-Message ein Byte-Array statt eines Strings. Mit

<logger level="INFO" message="Paylod type is: #[payload.getClass()]" doc:name="getClass()" />

ließe sich das auch direkt prüfen. Nebenbei habe ich gezeigt, wie Methoden aufgerufen werden.
Man kann natürlich auch Methoden mit Parametern aufrufen, um zum Beispiel alle x zum u zu machen:

<set-payload value="#[payload.replace('x', 'u')]" doc:name="'x' to 'u'"/>

Oder – wenn man die Welt verdrehen möchte – geht das mit Apache Commons 3 und einer statischen Methode aus StringUtils:

<set-payload value="#[org.apache.commons.lang3.StringUtils.reverse(payload)]" doc:name="reverse" />

Ich habe mich hier auf Klassen beschränkt, die bereits im Classpath vorhanden sind. Natürlich darf man auch eigene Klassen benutzen, die man im Verzeichnis src/main/java implementiert.

Java-Component

Die nächste Variante sind die sogenannten Java Components. Sie existieren in zwei Geschmacksrichtungen: entweder als einfache POJOs oder als Implementierung des Mule-Interfaces org.mule.api.lifecycle.Callable.
Fangen wir mit einem POJO an:

public class SimplePojo {
  public String xToU(String text) {
    return text.replace('x', 'u');
  }
}

Den Code können wir per XML-Schnipsel aufrufen:

<component class="de.codecentric.components.SimplePojo" />

Wenn in der Klasse nur eine Public-Methode mit einem Parameter existiert, so ruft Mule diese auf. Existieren mehrere Methoden, wird es komplizierter: Dann kommen sogenannte Entry-Point-Resolver ins Spiel. Wer es genauer wissen möchte: Unter Configuring-Components in der Mule-Doku stehen die Details.
POJOs bieten die üblichen Vorteile: Sie sind extrem lose an Mule gekoppelt (genau genommen überhaupt nicht) und sie lassen sich in JUnit-Tests ohne Abhängigkeiten zu irgendwelchem Mule-Code testen. Da die Mule-XML-Konfiguration nur eine Variante einer Spring-Konfiguration darstellt, können Components übrigens auch als Spring-Beans instanziiert werden, mit den bekannten Möglichkeiten von Spring: Factories, Scopes, etc. Standardmäßig sind die Components Singletons (und müssen daher auch thread-safe implementiert sein).
Die zweite Variante der Components implementieren das Mule-Interface Callable. Über diesen Weg kommt man auch an einige Innereien von Mule heran:

public class SimpleCallable implements Callable {
  @Override
  public Object onCall(MuleEventContext eventCtx) throws Exception {
    return ((String)eventCtx.getMessage().getPayload()).replace('u', 'x');
  }
}

Wie man sich denken kann, wird aus dem Flow onCall() aufgerufen. Darin hat man Zugriff auf den MuleEventContext, die MuleMessage und den MuleContext. Über die MuleMessage lassen sich nicht nur die Payload auslesen und bearbeiten, sondern auch die Properties in den verschiedenen Mule-Scopes. Der Nachteil besteht darin, dass Tests schwieriger sind, da man sich erst einen MuleEventContext bauen muss.
Braucht man nur den MuleContext und möchte bei den “einfachen” Methoden bleiben, kann man auch – in einem POJO – MuleContextAware implementieren. Nachdem eine Instanz der Component erstellt wurde, ruft Mule die Methode setMuleContext(MuleContext ctx) auf, so dass man sich den Context in einer Member-Variablen merken kann.

Java-Transformer

Einen Java-Transformer ruft man aus der XML-Konfiguration ähnlich wie eine Component auf:

<custom-transformer class="de.codecentric.SimpleTransformer" doc:name="Java"/>
Roger Butenuth

Dr. Roger Butenuth hat in Karlsruhe Informatik studiert und anschließend in Paderborn promoviert (Kommunikation in Parallelrechnern). Er hat langjährige Erfahrung in der Projekt- und Produktentwicklung.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.