Blog
Blog

In unserem Blog stellen wir Ihnen Themen vor, die unsere Belegschaft und das Unternehmen bewegen.

Unsere Belegschaft setzt sich aus einer Menge von Menschen zusammen, die bereits viele Erfahrungen in unterschiedlichsten Branchen und Aufgabenfeldern sammeln konnten. Bei unserer tĂ€glichen Zusammenarbeit wird natĂŒrlich auch diskutiert, voneinander gelernt, Entscheidungen getroffen, großartige Dinge geschaffen aber auch Fehler gemacht. In unserem Blog möchten wir Ihnen davon erzĂ€hlen.

Vor allem aber möchten wir mit Ihnen sprechen und Ihre Meinung zu unseren BeitrĂ€gen erfahren. Wir wĂŒrden uns sehr freuen, von Ihnen zu hören.



Testen im Team mit OpenSSL

Im letzten Beitrag zum Thema "Testen im Team" haben wir beschrieben, wie wir eine Umgebung und Verfahren implementiert haben, um SoapUI in einem Team von Testern benutzen zu können.

Neben den Funktionstests an der SOAP-Schnittstelle unseres Testgegenstands sind weitere Tests erforderlich, die die Korrektheit der TLS-Verbindungen verifizieren. Bei einem Embedded System im Gesundheitsbereich ist eine fehlerfreie Implementierung der VerschlĂŒsselungsstandards unabdingbar. Entsprechend rigide sind die Anforderungen aus der Spezifikation und entsprechend vielfĂ€ltig ist der Detailreichtum der zu testenden Szenarien.

Weiterlesen...
 

Testen im Team

Im laufenden Projekt ging es darum, die SOAP Schnittstelle eines Embedded Systems im Gesundheitswesen zu testen. Bis zu fĂŒnf Kolleginnen und Kollegen sollten die Funktionstests durchfĂŒhren. In den Vorbesprechungen wurden zwei Entscheidungen herbeigefĂŒhrt: Die Open Source Variante von SoapUI sollte das Mittel der Wahl sein, um die Funktionstests umzusetzen. Außerdem wurde schnell klar, dass auch Mechanismen geschaffen werden mĂŒssen, um die Tests im Team effizient abwickeln zu können. Es galt, vier selbst gestellte Anforderungen umzusetzen:

Weiterlesen...
 

TIY - Test it yourself

Wer aufmerksam unseren Blog verfolgt und uns in Projekten erlebt hat, weiß, dass wir uns in den aktuellen Technologien bestens auskennen und mit geeigneten ArchitekturansĂ€tzen die bestmögliche Lösung fĂŒr unsere Kunden schaffen. Dabei kommt bei uns allerdings nie der ‘craftsmanship ‘ Ansatz (s. softwarecraftsmanship)  zu kurz . Als engagierte Software-Handwerker packen wir auch schonmal richtig zu. Als Beispiel dafĂŒr, wie wir tatkrĂ€ftig Hand anlegen und mit einfachen Mitteln schnelle Lösungen realisieren, sind nachfolgend zwei Szenarien aus der aktuellen Entwicklung aufgezeigt.

Laboraufbau auf die einfache Art

In einem unserer Entwicklungsprojekte soll der Nachweis erbracht werden, dass 42 eHealth-CardTerminals – einer  zentralen Einheit (Konnektor) zugeordnet und entsprechend konfiguriert – innerhalb einer vorgegeben Zeitspanne von diesem Konnektor erkannt und betriebsbereit im Netz verbunden werden können.

Aus organisatorischer Sicht ist in einem solchen Szenario bereits einiges zu beachten: 42 Kartenterminals beanspruchen von sich aus bereits einen bestimmten Raum (knapp 15 cm in der Breite), sie sind zudem mit je einem Netzteil und LAN-Kabel verbunden. DarĂŒber hinaus mĂŒssen sie wĂ€hrend der Inbetriebnahme zugĂ€nglich sein, so dass PIN-Eingaben am Tastenfeld möglich sind. Ein Rack fĂŒr diesen Einsatzzweck konnten wir auf die Schnelle nicht finden.

Als ‚echtes Kölner Traditionsunternehmen‘ sind bei uns natĂŒrlich neben einer Zapfgarnitur auch BierbĂ€nke und Tische jederzeit verfĂŒgbar. Was liegt also nĂ€her, als selbige fĂŒr den geforderten Testaufbau zu verwenden?

Die nachfolgenden Bilder zeigen, dass ein solcher Testaufbau mit BierbĂ€nken einfach und ĂŒbersichtlich,  vor allem aber auch schnell und preiswert zu realisieren ist. Zudem ein recyclefĂ€higer Ansatz


Apparatur 1 Apparatur 2

Apparatur 3 Apparatur 4

Testen, testen, testen

Arbeitet man im Testbereich, gibt es mitunter Situationen, die sich durch viele, immer gleich ablaufende, monotone Tasks auszeichnen, so etwa Regressionstests und Last- oder Performancetesting. Setzt man solche Tests auf und fĂŒhrt sie wiederholt aus, ist es nicht auszuschließen, dass einem nach kurzer Zeit ein altes Volkslied in eher bedrĂŒckender Art und Weise auf den Lippen liegt:

Stumpfsinn, Stumpfsinn, du mein VergnĂŒgen,
Stumpfsinn, Stumpfsinn, du meine Lust;
gĂ€b’s keinen Stumpfsinn, gĂ€b’s kein VergnĂŒgen,
gĂ€b’s keinen Stumpfsinn, gĂ€b’s keine Lust.

Stumpfsinn hat allerdings - insbesondere in agilen Projekten - keinen Platz und es muss Abhilfe geschaffen werden! Das folgende Beispiel zeigt auf, wie man der Monotonie ein Schnippchen schlagen und mit etwas handwerklichem Einsatz eine effektive Lösung schaffen kann.

Ab in die Werkstatt!

Im gleichen Kundenprojekt wie dem ersten Beispiel sind TestfÀlle definiert, in denen eine Karte mehrfach sequentiell in ein Kartenterminal gesteckt werden muss, um so die Ablaufzeiten in der Abarbeitung einzelner Kartenoperationen nachzuweisen bzw. zu optimieren.

100 und mehr sequentiell auszufĂŒhrende Steckaktionen an einem Kartenterminal: Das schreit ja förmlich nach Automatisierung!

Eine Apparatur, die das automatisierte Ziehen und Stecken einer Karte aus einer Testroutine heraus ermöglicht, ist schnell konzipiert. Rahmenbedingungen hierzu:

  • Ansteuerung via SOAP oder REST (als Testwerkzeug  sind vorwiegend SOAPUI und Groovy-Routinen im Einsatz)
  • Steuerung der mechanischen Anteile via Raspberry Pi (weil er sowohl hardwaretechnisch die Voraussetzungen bringt, als auch softwareseitig zahlreiche Moduln verfĂŒgbar sind. Und weil zudem noch  einer verfĂŒgbar ist
)
  • Antriebseinheit: Ein Servo aus dem Modellbau.

Teile dieser Apparatur lassen sich natĂŒrlich nicht mehr im BĂŒro erledigen, also heißt es erst einmal: ‚Ab in die Werkstatt!‘ (frei nach dem Holzwerker Guido Henn).

Der Architekturentwurf dieser Apparatur sieht  eine Grundplatte vor, welche die Antriebseinheit  (Servo) und den Adapter zur Befestigung und FĂŒhrung der eigentlichen Karte beinhaltet. Diese Grundplatte kann dann wiederum von unterschiedlichen Supporteinrichtungen in unterschiedlichen Winkeln und AbstĂ€nden zum Kartenterminal montiert werden.

Der Ja, aber - Faktor

Nachdem der erste Entwurf fertig ist und der Prototyp die Werkstatt verlassen hat erfolgt die Vorstellung im Team.

Und wie das immer so ist, wenn man einen Prototypen vorstellt kommen dann die ‚Ja, aber 
‘ – EinwĂ€nde.

  • Ja, aber der Apparat kann ja nur einen Typ von Kartenterminal 

  • Ja, aber man könnte statt des Raspberry doch auch 

  • Ja, aber ...

Ja, richtig! Denn in solchen Situationen hilft es oft, sich noch einmal  einige der bewĂ€hrten Philosophien aus der Unixwelt vor Augen zu fĂŒhren. Diese fokussieren zwar in erster Linie auf die Softwareentwicklung, haben aber auch fĂŒr diese Apparatur ihre Geltung. Eine kleine Auswahl:

  • Klein ist schön!
  • Gestalte jedes Programm so, dass es genau eine Aufgabe gut erledigt (mache nur eine Sache und mache sie gut)!
  • Erzeuge so bald wie möglich einen funktionierenden Prototyp!
  • Schlechter ist besser!
  • Und natĂŒrlich nicht zu vergessen die Regel 6: Es gibt keine Regel 6.

Der Prototyp erweist sich aufgrund des verwendeten Servos noch etwas ‚schwachbrĂŒstig‘, ein stĂ€rkerer Servo löst das Problem jedoch und die Apparatur funktioniert.

Ein paar Worte zur Technik

Der Betrieb des Servos wird durch eine externe 6 V- Spannung  sichergestellt (so wird der Raspberry entlastet, denn der benötigte Strom an den PINs liegt höher als 500 mA), deren Plusleitung direkt am Servo angeschlossen ist und deren Minuspol mit einem GRD-Pin des Raspberry verbunden ist.

Die Ansteuerung des Servo selbst erfolgt ĂŒber eine Steuerleitung, welche – ĂŒber einen 1 KOhm – Widerstand entkoppelt – an einen GPIO-Pin des Raspberry gefĂŒhrt ist.
Das zur Ansteuerung notwendige hardwarebasierte Pulsweitensignal wird durch ein frei verfĂŒgbares Pythonmodul (RPIO.PWM) realisiert. Die Steuerung selbst ist ebenfalls in Python implementiert und reduziert sich dann darauf, das Pulsweiten-Modul mit einem dedizierten PIN zu initialisieren und entsprechend dem gewĂŒnschten Drehwinkel einzustellen.

Auch der SOAPServer ist mit wenigen Python-Zeilen rasch erstellt und offeriert die Operationen MoveUp, MoveDown sowie MoveNeutral.

Die ‚Kalibrierung‘ der Apparatur ist durch ein eigenes Script realisiert, welches den Servo in eine definierte Position fĂ€hrt, so dass dann die KartenzufĂŒhrung  anhand einer Stellschraube fixiert werden kann.

Die Testsuite ist in diesem speziellen Fall als einfache Groovy-Routine realisiert. Diese Groovy-Routine nutzt fĂŒr alle anfallenden SOAP-Operationen die wslite-Bibliothek, so dass die eigentliche Testsuite mit wenigen Zeilen Code auskommt.

Wie im Video zu sehen, laufen die Tests nun automatisiert ab.


Begutachtet man nach getaner Arbeit sein Testsetting und liest im Testlog die EintrĂ€ge ‚Karte stecken, Lauf 92 von 100‘,  lĂ€sst man unwillkĂŒrlich seinen Blick zum Servo schweifen und sinniert still vor sich hin: ‚Stumpfsinn, Stumpfsinn, Du mein VergnĂŒgen!‘.

 

Mocking Frameworks verschlechtern das Software Design

Die wenig aufwendige Herstellung von Mocks hemmt das Hinterfragen des Designs

Vorteile testgetriebener Entwicklung

n-design entwickelt Software testgetrieben. Test Driven Development (TDD) ist fĂŒr uns nicht nur eine Methode zur Verringerung von Fehlern wĂ€hrend der Entwicklung und beim fortlaufenden Refactoring. Das testgetriebene Vorgehen hat auch einen großen Einfluss auf das Design. Dies liegt u.a. an folgenden Aspekten:

  • Das Schreiben der Tests versetzt uns als Designer und Entwickler einer Schnittstelle in die Perspektive des Konsumenten der konstruierten API. HĂ€ufig fĂŒhrt dies zur Konsolidierung des Vertrags der Schnittstelle.
  • Durch die Tests entstehen mehrere Clients fĂŒr die Schnittstelle. Dies fĂŒhrt zu einer flexiblen und modularen Architektur.
  • Um Tests fĂŒr sinnvolle Einheiten schneiden zu können, mĂŒssen Teile, die nicht zum Testgegenstand gehören, austauschbar sein. Dies fördert eine lose Kopplung von Komponenten durch die Verwendung von Interfaces und Dependency Injection.

Der zuletzt genannte Aspekt ermöglicht erst das Schreiben von Unit-Tests. Sind Komponenten nicht austauschbar, kann die Software immer nur mit allen technischen AbhÀngigkeiten getestet werden. Dies ist nicht praktikabel, wenn diese AbhÀngigkeiten I/O, eine Datenbank oder das Netzwerk einbeziehen.

Was sind Unit-Tests?

Auf die Frage, was eigentlich die zu testende Einheit einer Software fĂŒr einen Unit-Test ist, gibt es unterschiedliche Antworten. Fowler unterscheidet in seinem Beitrag „Mocks Aren't Stubs“ zwischen „Mockist and Classical Testing“. Beim Mockist-Style werden sĂ€mtliche AbhĂ€ngigkeiten der Testeinheit durch Doubles ausgetauscht, wĂ€hrend beim Classical-Style nur die schwierig zu kontrollierenden AbhĂ€ngigkeiten ersetzt werden.

Interessant ist auch der Sinneswandel des Autors des Buches „The Art of Unit Testing“, Roy Osherove. War er zunĂ€chst der Ansicht, die Unit sei der kleinst-mögliche testbare Teil einer Software, also eine Methode einer Klasse, vertritt er heute die Meinung, eine Unit könne eine Methode, eine Klasse oder mehrere Klassen einbeziehen. Entscheidend ist, dass die Unit einen einzelnen Use Case abbildet (http://artofunittesting.com/definition-of-a-unit-test).

Aufwand der Testentwicklung und Bindung an Implementierung

Die Definition von Osherove ist nach meinen Erfahrungen praktikabel. Jede AbhĂ€ngigkeit durch einen Mock zu ersetzen, schafft aus meiner Sicht nur einen geringen Mehrwert. Der besteht hauptsĂ€chlich darin, dass ein fehlgeschlagener Test vermeintlich unmittelbar auf den defekten Teil der Software verweist. Oder passt die Konfiguration eines Mocks nicht mehr zur vorgenommenen Code-Änderungen? Aus meiner Erfahrung ist die Recherche nach einem Defekt aber auch in grĂ¶ĂŸeren Test-Einheiten kein Problem und erfordert wenig Zeit.

Dem gegenĂŒber steht der enorme Wartungsaufwand der Tests die im Mockist-Style entwickelt wurden. Ändern sich durch Doubles ersetzte AbhĂ€ngigkeiten im Verhalten, dann ist hĂ€ufig die Konfiguration aller Mocks diesbezĂŒglich anzupassen. Verzichtet man hingegen auf Mocks an geeigneten Stellen, dann sind meist „nur“ die Assertions des Tests zu Ă€ndern.

Besonders aufwendig wird die Test-Wartung, wenn das Verhalten der Software mit „verify“ ĂŒberprĂŒft wird. Hierbei wird eine dem Entwickler bekannte Aufrufsequenz von AbhĂ€ngigkeiten ĂŒberprĂŒft. So entsteht eine besonders intensive Bindung des Tests an die Implementierung. Der Test muss ebenfalls mit Änderung einer Aufrufsequenz angepasst werden.

Ich bevorzuge daher das Testen eine Schnittstelle und nicht deren Implementierung. Als Ausnahme hiervon wird hĂ€ufig das Beispiel „Cache“ herangezogen. Dass z.B. nach Aufruf einer DAO-Methode der Cache entweder mit „put“ bzw. „get“ gerufen wurde, lĂ€sst sich in der Tat komfortable ĂŒber ein „verify“ eines Mocks feststellen.

VerfĂŒhrerisch einfaches Mocken

Der hohe Wartungsaufwand, der durch das Mocken sÀmtlicher AbhÀngigkeiten entsteht, ist unabhÀngig davon, ob ein Mocking-Framework verwendet wird oder nicht. Die vereinfachte Konfiguration von Mock-Objekten durch entsprechende Frameworks kann aber einen negativen Einfluss auf den kontinuierlichen Design-Prozess der Software haben.

Ist das Mocken von AbhĂ€ngigkeiten zu leicht, bietet das Schreiben der Tests keinen Anlass mehr, das Software Design ggf. zu ĂŒberdenken. NĂ€mlich genau dann, wenn man bei der Implementierung des Testfalls feststellt dass sich der gewĂŒnschte Test mit dem vorhandenen Design der Test-Einheit nicht herstellen lĂ€sst. Hier zwei Beispiele, bei denen dies besonders deutlich wird.

  • In einer zu testenden Klasse wird eine AbhĂ€ngigkeit nicht per Dependency Injection verfĂŒgbar gemacht. Das fĂ€llt bei der Verwendung eines Mocking-Frameworks womöglich nicht auf, da hiermit ein privates Feld einer Klasse leicht fĂŒr den Test zur Laufzeit manipuliert werden kann. MĂŒsste man hierfĂŒr selber die Reflection-API bemĂŒhen, wĂŒrde einem vermutlich auffallen, dass hier ein Design-Fehler vorliegt und die AbhĂ€ngigkeit besser in die Test-Klasse injiziert wird.
  • Eine AbhĂ€ngigkeit im Test-Kandidaten liegt als Aufruf einer statischen Methode einer weiteren Klasse vor. Auch das Verhalten der statischen Methode lĂ€sst sich z.B. mit PowerMock manipulieren. WĂ€re dieses Tool nicht verfĂŒgbar, könnte auffallen, dass die harte Bindung durch Verwendung statischer AbhĂ€ngigkeiten besser durch einen objektorientierten Ansatz ersetzt wird.

Da die Herstellung eines Doubles mit Mocking-Frameworks sehr wenig Aufwand erzeugt, fĂŒhrt es auch nicht dazu, dass AbhĂ€ngigkeiten generell minimiert werden. So fördern sie nicht das Schneiden kleiner Komponenten im Sinne des Single Responsibility Principles und können so eine schwer zu wartende KomplexitĂ€t schaffen.

NĂŒtzliche Helfer

Die Verwendung von Mocking-Frameworks vereinfacht die Herstellung eines Mocks enorm. Es ist deutlich angenehmer, z.B. genau die eine benötigte Methode eines Data Access Objects mit einem Mocking-Framework fĂŒr den Test zu konfigurieren als dafĂŒr ein gesamtes Interface implementieren zu mĂŒssen. Daher verwende auch ich gerne Mocking-Frameworks. Allerdings ist darauf zu achten, dass die Leichtigkeit der Anwendung nicht das kontinuierliche Hinterfragen des eigenen Software Designs hemmt.

Muss in einen Test Legacy Code einbezogen werden, können auch Tools wie z.B. PowerMock, die u.a. das Mocken statischer Methoden erlauben, nĂŒtzlich sein. Der Einsatz sollte sich aber auf die nicht Ă€nderbaren Code-Bestandteile beschrĂ€nken.

FĂŒr mich haben sich folgende Leitlinien bei der Entwicklung von Tests bewĂ€hrt:

  • Die zu testende Unit ergbit sich aus dem zu testenden Use Case. Eine Definition entlang von Code-Strukturen (Methode, Klasse, Modul etc.) ist nicht praktikabel.
  • AbhĂ€ngigkeiten werden durch Mocks ersetzt, wenn diese schwer zu kontrollieren sind. Ansonsten können AbhĂ€ngigkeit mit „echten“ Implementierungen verwendet werden, auch wenn dadurch „Mini-Integrationstests“ entstehen.
  • Mocks werden je nach Aufwand mit oder ohne Mockking-Framework hergestellt.
  • Mocking-Frameworks werden vorwiegend zur Konfiguration des Verhaltens eines Mocks verwendet. Weitergehende FunktionalitĂ€ten, wie z.B. „verify“, sind mit Bedacht zu verwenden. Hier gibt z.B. die Dokumentation von Mockito hilfreiche Hinweise, indem an einigen Stellen auf die „Risiken“ der Verwendung hingewiesen wird; z.B hier bezĂŒglich Spy http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#13 und hier zum VerhĂ€ltnis von „stub“ und „verify“ http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#2.


 

Wieviel Service Dynamik soll es denn sein?

Service Dynamik in OSGi handhaben

FĂŒhrt man sich die einschlĂ€gige OSGi-Literatur zu GemĂŒte, gelangt man schnell zur Thematik Service Dynamik. In diesem Kontext werden meist verschiedene Optionen prĂ€sentiert, wie ein brauchbarer Zustand von Service-Referenzen aus Client-Sicht sichergestellt wird. In den meisten FĂ€llen ist ein ServiceTracker eine gute Lösung zur Behandlung der Service Dynamik. Mit dem folgenden Code Snippet kann z.B. auf die VerfĂŒgbarkeit eines Services gewartet werden:

try {

     Filter filter = bundleContext.createFilter("(" + "objectClass" + "=" + serviceClass.getName() + ")");   

     ServiceTracker tracker = new ServiceTracker(bundleContext, filter, null);

     tracker.open();

     T service = tracker.waitForService(timeout);

     if (service == null) {

             throw new ServiceNotAvailableException();

     }

     return service;

} catch (InvalidSyntaxException e) {

     throw new RuntimeException("Invalid Syntax for Filter " + serviceFilter + ".");

} finally {

     if (tracker != null) {

             tracker.close();

     }

}

Bei intensiver Verwendung von Services, können solche Tests beim Coden wirklich lĂ€stig werden. GlĂŒcklicherweise gibt es die deklarativen AnsĂ€tze zur Bindung von Services, die hier Abhilfe leisten (Declarative Services und Blueprint).

Um zu entscheiden, wie die Service Dynamik behandelt wird, muss aus meiner Sicht auch die Frage beantwortet werden: „Ist der verwendete Service optional oder ist er basaler Kernbestandteil meiner Anwendung?“ Im ersten Fall ist die Behandlung in der Regel aufwendiger. Im zweiten Fall ist die Anwendung bei nicht VerfĂŒgbarkeit des betreffenden Services defekt und die Dynamik muss ggf. nicht aufwendig behandelt werden. Dieser Standpunkt wird im Folgenden erlĂ€utert.

Lose Kopplung von Modulen ĂŒber Services

Die Interaktion zwischen Modulen (Bundles) in einem OSGi-Framework wird idealerweise ĂŒber Services organisiert. Dadurch beschrĂ€nken sich AbhĂ€ngigkeiten zwischen Bundles auf Definitionen von Schnittstellen in Form von Java-Interfaces. Deren Implementierungen sind vollstĂ€ndig entkoppelt und werden erst zur Laufzeit als Service bereitgestellt. Ein Client eines Services kann unter dem Interface-Namen einen Service aus der OSGi-Service-Registry beziehen.

Anwendern von IoC-Containern (Inversion of Control resp. Dependency Injection) ist ein solches Vorgehen vertraut. Innerhalb einer Klasse wird die AbhĂ€ngigkeit zu einer Komponente ĂŒber ein Interface referenziert und zur Laufzeit wird die Bindung an eine Implementierung per Injektion hergestellt.

Service Dynamik mit der Low Level API handhaben

In unserem ersten mit OSGi realisierten Projekt haben wir die Low Level API fĂŒr einen Lookup-Service  verwendet. Services werden also nicht ĂŒber einen deklarativen Ansatz in die Clients injeziert sondern per Aufruf der Service Registry geholt. Dies hat nicht nur den unschönen Nebeneffekt, dass eine AbhĂ€ngigkeit zur OSGi-API im Code existiert und daher fĂŒr Business-Klassen Proxies gebaut werden sollten, die die OSGi-API bedienen. Vielmehr muss mit der Tatsache umgegangen werden, dass Services in einer OSGi-Laufzeitumgebung dynamisch sind, d.h. der Client kann nicht mit einer garantierten VerfĂŒgbarkeit rechnen.

Um diesen Umstand in den Griff zu bekommen ist einiges an Aufwand zu betreiben. In den meisten FĂ€llen ist hierfĂŒr ein ServiceTracker (s.o.) und ggf. ein dazu passender ServiceTrackerCustomzier geeignet. Mittels des ServiceTracker kann ĂŒber die Call-Back-Methoden des ServiceTrackerCustomizers auf Änderungen bzgl. des Service-Status reagiert werden.

Aufgrund dieses Aufwands und der damit verbundenen Frage „Was soll passieren, wenn der Service mal nicht da ist?“, stellt sich die Frage, in welchen Situationen sinnvoll auf das Nichtvorhandensein eines Services reagiert werden kann.

Wie dynamisch darf ein Service sein?

Bei n-design entwickeln wir fĂŒr unsere Kunden Unternehmenssoftware. Solche Anwendungen werden schnell komplex und daher von uns in der Regel in Module gegliedert, deren Implementierungen ĂŒber Services abstrahiert werden. In der Folge haben wir es mit einer großen Anzahl von Services zu tun. Die allermeisten davon sind aber ausgenommen von der Start-p Phase nicht dynamisch sondern zwingend fĂŒr die AusfĂŒhrung der Applikation erforderlich. Sie werden daher zur Laufzeit nicht beendet oder ausgetauscht.

Die meisten als Service registrierten Instanzen besitzen in unseren Anwendungen nicht einen typischen Service-Character im Sinne einer Schnittstelle zu einem externen Modul der Anwendung. D.h. der Großteil der Services entspricht dem Service-Begriff, wie er in der Domain-Driven Design Community verwendet wird. Beispielsweise wird bei uns jedes Data Access Object (DAO) als Service bereitgestellt. Diese Services sind innerhalb ihres Moduls zwingend erforderliche Bestandteile, so dass ihre Nicht-VerfĂŒgbarkeit zum kompletten Ausfall des betreffenden Moduls fĂŒhrt.

Beim Design einer Anwendung, die in OSGi realisiert wird, muss also berĂŒcksichtigt werden, welche Module bzw. Services optional sind. Dies können z.B. Plug-In-artige Bestandteile sein, wie etwas die Apps in unserer Software n-pat. Man kann dann auf einer grob-granularen Ebene in der Anwendung auf die Servicedynamik reagieren, in dem z.B. ein Plug-In abgeschaltet wird, wenn einer seiner nicht optionalen Services nicht mehr verfĂŒgbar ist.

Declarative Services und Dependency Injection

Die meisten der Services werden in unseren Anwendungen also als vorhanden vorausgesetzt. GlĂŒcklicherweise kann der Aufwand zur PrĂŒfung der VerfĂŒgbarkeit von Services deutlich reduziert werden, in dem die deklarativen Möglichkeiten zur Service-Bindung von OSGi verwendet werden. DafĂŒr stehen Declartive Services oder Blueprint zur VerfĂŒgung.

Bei n-design bevorzugen wir Blueprint. Blueprint ist aus dem Spring-Kontext entstanden und entspricht in seiner Verwendung im Wesentlichen den Mustern, die vom SpringContext bekannt sind. ZusĂ€tzlich können hier noch OSGi-Services registriert und referenziert werden, so dass diese als Dependencies einem Bean injeziert werden können. Dabei wartet Blueprint mit der Bereitstellung eines Services oder Beans, bis alles referenzierten Services verfĂŒgbar sind.

Ein weiterer großer Vorteil ist, dass Blueprint nicht den Service direkt sondern einen dynamisch generierten Proxy injeziert. Das macht die Clients eines Services robust gegen einen Austausch des Services zur Laufzeit. Sollte zur Laufzeit ein Service lĂ€ngere Zeit nicht mehr verfĂŒgbar sein, quittiert Blueprint dies mit einer entsprechenden Exception. Dies macht die Behandlung der Service Dynamik sehr einfach.

Blueprint besticht durch seine einfache Verwendung. Declarative Services (DS) bieten aber mehr Kontrolle ĂŒber die Art der AbhĂ€ngigkeit von Services untereinander. Wird z.B. eine Service-Referenz A innerhalb eines Services B als static (muss verfĂŒgbar sein) deklariert, dann wird bei deregistrieren des Services A zuerst der Services B deregistriert. So kann kontrolliert eine Kette von Services deaktiviert werden.

Fazit

FĂŒr uns ist die Verwendung von Blueprint eine enorme Erleichterung, um die Service Dynamik in unseren Produkten zu behandeln. Fehlen nicht optionale Kern-Services, können wir durch entsprechendes ExceptionHandling die Anwendung oder ein Plug-in z.B. kontrolliert beenden. Wir beantworten fĂŒr jeden einzelnen Service die Frage, ob dieser optional oder obligatorisch ist. Nur im Falle eines optionalen Services ist ggf. eine aufwendigere Behandlung der Service Dynamik notwendig z.B. durch Ausblenden von entsprechenden GUI-Elementen.

 

Docbook - Integration in der Praxis

Nach einer kurzen Vorstellung von Docbook in diesem Artikel, möchte ich an dieser Stelle einmal darstellen, wie sich das System bei uns in der Praxis bewÀhrt hat. Hierbei werde ich nicht auf das Erstellen eines Dokumentes mit Docbook eingehen. Im Netz gibt es zahlreiche HowTos, die dies bereits sehr gut erklÀren. Hier soll die Integration in ein bestehendes Entwicklungssystem aufgezeigt werden.

Docbook ist kein Programm im eigentlichen Sinne, sondern ein System bestehend aus DTDs (Document Type Definitions), den XSLs (XML-Stylesheets), einem Editor sowie einem XSLT-Prozessor zur Transformation der XML-Dokumente. Der Build-Prozess ist plattformunabhĂ€ngig und kann beispielsweise durch ein Shellskript, mit Hilfe von Ant oder auch Maven definiert werden. Die Werkzeugkette bei n-design beinhaltet unter anderem Tools wie Eclipse, Maven, SVN und GIT sowie Jenkins zur UnterstĂŒtzung des kontinuierlichen Entwicklungsprozesses. Jeder unserer Mitarbeiter kennt diese Werkzeuge und die meisten arbeiten tĂ€glich mit ihnen. Was liegt also nĂ€her, als auch Docbook in diesen Arbeitsablauf zu integrieren.

Eclipse

Eclipse wĂŒrde bereits ohne weitere Plugins mit Docbook umgehen können, da wir uns alles Nötige ĂŒber Maven-Plugins bereitstellen lassen. Jedoch können wir uns viel Arbeit ersparen, indem wir das Eclipse-Plugin DEP4E (Docbook Editing and Processing for Eclipse) installieren. DEP4E verwendet eigentlich Ant fĂŒr den Build-Prozess, stellt aber zusĂ€tzlich fĂŒr unsere Zwecke nĂŒtzliche Ansichten, den Docbook XML Markup Editor und eine PrĂŒfung der XML-Struktur im Dokument gegen die entsprechenden XSL-Dateien zur VerfĂŒgung. Alles Punkte, die wir nicht mehr manuell in Eclipse einstellen mĂŒssen.

Maven

Zur Erstellung von Docbook-Dokumenten verwenden wir in einem ĂŒblichen Maven-Projekt das Maven-Plugin docbkx-maven-plugin aus der Gruppe com.agilejava.docbkx. Um einigen Fehlern im aktuellen Release aus dem Wege zu gehen, verwenden wir direkt das Snapshot-Repository. Docbkx bringt Saxon als XSLT-Prozessor sowie Apache's FOP zur Erzeugung von PDF- und RTF-Dokumenten bereits mit.

Die AusfĂŒhrungsebene (<execution>) muss entsprechende goals enthalten, um Ausgabeformate wie HTML, RTF, EPUB3 und PDF generieren zu können. In der Konfigurationsebene des Plugins (<configuration>) können dann diverse Einstellungen zu den einzelnen Formaten definiert werden. Die Dokumentation von Docbkx ist hier sehr ausfĂŒhrlich und beinhaltet viele nĂŒtzliche Beispiele. Eine der wichtigsten Einstellungen in diesem Bereich ist der Verweis auf die benutzerdefinierten XSL-Dateien fĂŒr unsere Dokumente. Die von Docbook mitgelieferten Stylesheets sollten nicht verĂ€ndert werden. Anpassungen, beispielsweise fĂŒr Kopf- und Fußzeilen in einer PDF-Ausgabe, mĂŒssen in einer eigenen XSL-Datei vorgenommen werden. Diese beinhaltet außer den individuellen Anpassungen auch immer einen Verweis auf das eigentliche Stylesheet, erbt sozusagen davon. Ein Start des Maven-Builds mit den goals clean und pre-site erstellt alle definierten Ausgabeformate.

Jenkins

Abschließend erstellen wir auf unserem Jenkins-Server noch einen Job, der uns unser Dokument bei jeder Änderung und einmal nachts (als nightly-build) erstellt. Wie bei jedem anderen Job, werden die Entwickler bei FehlschlĂ€gen benachrichtigt, können durch die Log-Ausgaben und dem im Versionskontrollsystem hinterlegten Code die Änderungen verfolgen und anpassen.

Fazit

Die Integration von Docbook bei n-design hat sich vom technischen Aspekt her als relativ problemlos erwiesen. An einigen Stellen musste noch einmal gefeilt werden. So erwies sich Apache's FO-Prozessor in der Version 1.1 als noch nicht ganz ausgereift fĂŒr die RTF-Erstellung. Eine Umstellung auf die Version 1.0 war nötig. Auch die Formatierung der XML-Dateien im Editor ist nicht ganz wie erwartet: Block-Elemente (z.B. <para>) werden anders behandelt als sog. inline tags (z.B. <emphasis>), nach denen immer wieder ein Zeilenumbruch eingefĂŒgt wird. Dies ergibt auch im erstellten Dokument unschöne Leerzeichen bzw. UmbrĂŒche. Hier sollten alternative Editoren unter Eclipse in Betracht gezogen werden.

Aus Sicht der Mitarbeiter, die sich in Docbook einfinden durften, konnten wir feststellen, dass sich die Einarbeitungszeit in Grenzen gehalten hat, da die meisten bereits vorher mit unserer Werkzeugkette gearbeitet haben. Allerdings erwies es sich als gewöhnungsbedĂŒrftig, die nicht immer ganz aussagekrĂ€ftigen Konsolenausgaben des XSLT- oder FO-Prozessors zu interpretieren. Der grĂ¶ĂŸte Vorteil ist jedoch die Tatsache, dass diese Form des Dokumentierens den meisten Mitarbeitern sehr viel nĂ€her liegt, als in der ursprĂŒnglichen Form (mit Hilfe von WYSIWYG-Editoren). Das hat zur Folge, dass die Dokumentation weniger stiefmĂŒtterlich behandelt wird und als stĂ€ndiger Teil unseres kontinuierlichen und iterativen Entwicklungsprozesses akzeptiert wird. Durch die Möglichkeit der schnellen Anpassung, Wartung und der Nachvollziehbarkeit von Änderungen stellt die EinfĂŒhrung von Docbook fĂŒr n-design einen klaren Mehrwert dar.

 


Seite 3 von 5