Apache CXFs unterschätzte Stärke
Apache CXF ist neben Spring MVC und anderen bekannten Frameworks bei der Webserviceentwicklung immer ein Kandidat für die Umsetzung der technischen Schnittstelle. Meistens werden die Konkurrenten aus verschiedenen (und guten) Gründen bevorzugt. Es gibt allerdings ein Szenario, in dem sein Einsatz entscheidende Vorteile bringt und man durchaus überlegen sollte, seine Nachteile in Kauf zu nehmen.
CXFs Unique Selling Point
CXF ermöglicht es, Webservice-Schnittstellen nahezu vollständig als Java-Code zu definieren. Auf Client- und Serverseite wird als Webservicedefinition das exakt gleiche Java-Interface verwendet. Die Serverseite implementiert dieses Interface und die Clientseite verwendet es wie jede andere Implementierung eines Interfaces. CXF sorgt für alles was hinter dieser Schnittstelle passiert. Das bedeutet, dass die tatsächliche Kommunikation vollständig gekapselt ist und Entwickler keinen REST- oder SOAP-Webservice implementieren müssen.
Die glänzende Nische
Aus dem USP ergibt sich ein Einsatzgebiet für CXF, in dem es allen anderen Frameworks voraus ist: bei der Entwicklung interner Webservice-Schnittstellen. Mit intern ist hier gemeint, dass sowohl der Client als auch der Server in der eigenen Hand liegen und die Schnittstelle von keinem Dritten verwendet werden soll. Da auf beiden Seiten CXF verwendet werden muss, um von den Vorteilen zu profitieren, ergibt sich auch, dass es sich um zwei Java-Ökosysteme (respektive Kotlin) handeln muss.
Ein eher diskussionswürdiger Umstand ist, wenn Client- und Servercode dann noch in der gleichen Codebase, beziehungsweise gleichen Repository, vorliegen und entwickelt werden. Der hieraus entstehende Vorteil ist, dass Änderungen an der Schnittstelle bereits zur Compile-Zeit die Anpassung beider Seiten erzwingen. Ob eine gemeinsame Codebase nur für diesen Vorteil herbeigeführt werden sollte, muss kritisch abgewogen werden. Es existieren jedoch Systeme, in denen dieser Umstand sowieso aus der Vergangenheit besteht. Sind Client und Server in unterschiedlichen Repositories abgelegt, sollten Änderungen der Schnittstelle spätestens in der CI-Pipeline beziehungsweise dem Build-Prozess auffallen.
Der Vorteil im Detail
Ist CXF in einem Szenario wie oben einmal eingerichtet, unterscheidet sich die Implementierung der Schnittstelle kaum noch von einem normalen Java-Interface. Bei der Wahl der technischen Kommunikation werden zwei Implementierungen geboten. Zum einen kann SOAP gewählt werden, welches bei der Jax-WS-Implementierung von CXF verwendet wird. Der Vorteil bei Jax-WS ist, dass das Java-Interface keiner weiteren Anpassung bedarf. Fällt die Wahl auf REST – und somit auf Jax-RS –, benötigt das Interface ein paar Annotationen.
Die Einfachheit von CXF-Schnittstellen am Beispiel
Das folgende Beispiel zeigt eine einfache Definition eines Jax-RS Interface, der serverseitigen Implementierung und der clientseitigen Verwendung. Dabei wird deutlich, wie simpel die Schnittstellendefinition ist, wenn man bedenkt, dass diese schlussendlich über HTTP durchgeführt wird.
Aufgrund der Natur der Java-Interfaces und der gemeinsamen Codebase werden Änderungen an der Schnittstelle immer bereits zur Compilezeit zur Client- und Serverseite propagiert und erzwingen nötige Anpassungen. Hierdurch werden Laufzeitfehler durch unterschiedliche Schnittstellenversionen ausgeschlossen. Dieser Vorteil kommt besonders in der frühen Phase einer Entwicklung zur Geltung, da sich Schnittstellen in dieser Zeit besonders häufig ändern.
Das folgende Beispiel veranschaulicht, wie einfach eine Schnittstelle mit einem fertig konfigurierten CXF gestaltbar ist. Auf die Konfiguration wird im folgenden Code-Beispiel der Client- und Serverseite verzichtet. Hierzu sind im Internet ausreichend Beispiele zu finden – im Gegensatz zu anderen Dingen was CXF angeht, dazu später mehr.
Das Interface
Das gemeinsame Interface wird bei der Implementierung mit Jax-RS durch den Java-EE-REST-Endpoint-Annotationen ergänzt. Diese sind im Java SDK enthalten. Bei der Verwendung von Jax-WS werden diese nicht benötigt und würden das Interface weiter vereinfachen.
Listing 1: Interface
@WebService
public interface IHelloWorld {
@GET
@Path("/sayHello")
@Produces(MediaType.APPLICATION_JSON)
HelloMessage sayHello() throws NotInRightMoodException;
@POST
@Path("/setMood")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
Result setMood mood);
}
Serverseitige Implementierung
Die serverseitige Implementierung unterscheidet sich nicht von der Implementierung eines typischen Interfaces.
Listing 2: Serverseitige Implementierung
public class HelloWorldWebservice implements IHelloWorld{
private Mood myMood;
@Override
public HelloMessage sayHello() throws NotInRightMoodException {
if(myMood.isRight()){
return new HelloMessage("hello!");
}
throw new NotInRightMoodException();
}
@Override
public Result setMood(Mood mood) {
return new Result(myMood = mood);
}
}
Die Konfiguration des Webservices erfolgt durch folgende Dependency:
Listing 3: Dependency im Server
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
</dependency>
Auf ein detailliertes Konfigurationsbeispiel wird verzichtet, hierfür gibt es im Internet ausreichend gute Beispiele.
Clientseitige Verwendung
Der Webservice wird im Client wie jeder andere Service verwendet und unterscheidet sich auch in keiner Weise von diesen. Es ist nicht ersichtlich, dass die Kommunikation zu einem entfernten Service erfolgt.
Listing 4: Clientseitige Verwendung
public class HelloWorldClient {
private final IHelloWorld helloWorldWebservice;
public HelloWorldClient(IHelloWorld helloWorldWebservice) {
this.helloWorldWebservice = helloWorldWebservice;
}
public HelloMessage sayHello() {
try{
return helloWorldWebservice.sayHello();
}catch (NotInRightMoodException e){
LOGGER.error("'Hello World'-Service is not in the right mood to say hello", e);
}
}
public Result setGoodMood() {
return helloWorldWebservice.setMood(new Mood("good"));
}
}
Zur Konfiguration des Clients wird folgende Dependency benötigt:
Listing 5: Dependency im Client
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
</dependency>
Auf ein detailliertes Konfigurationsbeispiel wird hier verzichtet, da auch hierfür im Internet ausreichend gute Beispiele zu finden sind.
Das Zusammenspiel
In Abb. 1 sind die Komponenten auf Server- und Client-Seite dargestellt, die von CXF verwaltet werden. Die Kapselung der Kommunikation setzt sich auf den folgenden Bestandteilen zusammen:
- In der Server-Anwendung instanziert CXF eine implementierende Klasse des Interfaces IHelloWorld. Die Klasse benötigt keinen weiteren Code, welcher ihn explizit als Webservice deklariert, er erscheint wie ein beliebiger Service.
- CXF erzeugt auf der Server-Seite einen Webservice-Endpoint, der sich nach außen nicht von denen anderer Frameworks unterscheidet und könnte von einem beliebigen Client-Framework angesprochen werden.
- In der Client-Anwendung instanziert CXF das IHelloWorld-Interface. In den Konfigurationen werden dabei die Endpoint-Adresse und etwaige weitere Parameter angegeben. Die Instanz der Klasse kann wieder jede andere Bean zum Beispiel mit Spring verwendet werden.
- CXF kapselt alles, was mit der Kommunikation zu tun hat. Vom Serviceaufruf im Client bis zum Aufruf der Interface-Implementierung auf dem Server muss ein Entwickler sich um nichts kümmern.
Die Schattenseiten
CXF bietet grundsätzlich alles, was man von einem modernen Framework erwartet. Die Integration zum Beispiel mit Spring ist gegeben, sodass Webservice-Endpoints und -Clients in den Kontexten initialisiert werden können. Vieles ist mit den CXF-Mechanismen zu implementieren und sollten Spezialfälle erforderlich sein, ist das Framework auch flexibel genug diese abzubilden.
Wo sind dann also die Probleme? Kurz und auf den Punkt gebracht: die offizielle Dokumentation und der Mangel an guten Beispielen im Internet. Neue Frameworks lernt man allerdings am einfachsten durch eben diese beiden Hilfsmittel. Eine gute Dokumentation erlaubt das Lernen anhand einer eigenen kleinen Beispielanwendung. Wohingegen gute fertige Beispiele für den Einsatz eines Frameworks dessen Funktionsweise gut nachvollziehen lässt.
Wer regelmäßig neue Frameworks nutzt und/oder kennenlernen möchte, weiß den Wert dieser beiden Komponenten zu schätzen und sobald ein Framework diese nicht bietet, hat es schon von vornherein einen schweren Stand, fast unabhängig davon, wie gut es eigentlich ist.
Wer sich mit CXF auseinandersetzen möchte, stößt schnell auf die offizielle Seite der Apache Software Foundation und bekommt dort zwar viele, aber sehr unstrukturierte Informationen, geliefert [1]. Derart schlechte Dokumentationen demotivieren Entwickler heutzutage sehr schnell, da diese durch die guten Äquivalente zum Beispiel von Spring und allen seinen Frameworks schon seit Jahren so viel Besseres gewohnt ist. Sucht man in dem Wust an Informationen Hilfe zu fortgeschrittenen Anwendungsfällen, wie zum Beispiel das Handling von Netzwerkfehlern oder die Implementierung von Fallbacks, wird man nur schwer fündig, falls die Information überhaupt vorhanden ist. Beim weiteren Durchsuchen des Internets tritt ebenfalls schnell Ernüchterung auf, da die Ergebnisse nicht die Erwartungen erfüllen, die man heutzutage an Beispiele für Frameworks stellt. Dadurch erweist sich die Einarbeitung in CXF verhältnismäßig schwer und man kommt schnell zu dem Schluss, dass ein anderes Framework attraktiver ist.
Jetzt mögen diese Nachteile profan klingen, allerdings haben sie realistisch gesehen nicht nur Einfluss auf die eigene Einarbeitung. Dieser Umstand wird auch alle nachfolgenden Entwicklerinnen und Entwickler treffen, die sich später um die Weiterentwicklung und Wartung kümmern müssen. Hier wird wieder mehr Aufwand für die Einarbeitung benötigt. Das ist im Prozess der Abwägung von Frameworks zwar nur ein Argument von vielen, kann aber durchaus entscheidend sein.
Eine Meinung über CXF To-Go
CXF ist nicht das populärste Webservice-Framework und dafür gibt es unter anderem die diskutierten Gründe. Es existieren jedoch Szenarien, in denen es entscheidende Vorteile für sich beanspruchen und dadurch in Abwägungen gegenüber anderen Webservice-Frameworks punkten kann. Zum Beispiel im diskutierten Szenario dieses Artikels sollte man den anfänglichen Aufwand gegen die Vorteile abwägen. Wer sich einmal mit CXF angefreundet hat und in den Genuss der Einfachheit seiner Webservices gekommen ist, wird es bei der nächsten Evaluation der Frameworks für die Webservices sicher im Hinterkopf haben.