Donnerstag, 22. Dezember 2011

coopXarch wünscht Frohe Weihnachten!

Liebe Leser!

Ein spannendes und arbeitsreiches Jahr liegt hinter uns - angesichts des unmittelbar bevorstehenden Weihnachtsfestes ist es an der Zeit, 2011 Revue passieren zu lassen.

Das Thema im Jahr 2011 aus Sicht der coopXarch war "Cloud Computing". Und wir nahmen und nehmen uns dieses Themas mit viel Engagement und Enthusiasmus an. Nach einer Zeit der Informationsrecherche, Veranstaltungsbesuche und Diskussionen war es im Frühjahr an der Zeit, selbst aktiv zu werden.

Mit joinMe! haben wir im Oktober eine Referenzarchitektur und -implementierung für ein elastisch skalierbares location-based social service vorgelegt, das auf einer Platform-as-a-Service (PaaS) läuft. Dabei ist es irrelevant, ob ein public PaaS Angebot (wie z.B. VMware CloudFoundry) gewählt wird, oder eine private PaaS (z.B. VMware vFabric) aufgebaut wird, um das Service zu betreiben. Die Softwarekomponenten sind so beschaffen, dass sie ohne eine einzige Code-Änderung in einem lokalen Tomcat, dem vFabric tc Server oder in der CloudFoundry Laufzeitumgebung betrieben werden können! Das zeigt die unglaubliche Deployment-Flexibilität und Umgebungs-Agnostik heutiger Enterprise Frameworks (in diesem Falle Spring).

joinMe! zeigt auch, wie "application-level modularization" erreicht werden kann, die eine Grundlage für elastische Skalierbarkeit ist, aber auch andere Vorteile wie Fehlerlokalisation und "always-on" Deployments bietet. Insofern bietet Cloud Computing für die Software Engineering Praxis die Möglichkeit, auch im Business Software Bereich ein Umdenken weg von monolithischem Applikationsdesign hin zu "loosely coupled" Systemen herbei zu führen.

Schade finden wir es, dass es keinen österreichischen Platform-as-a-Service Anbieter gibt - egal ob für public hosting oder als Anbieter für private PaaS Clouds für Unternehmen. Das würde unseres Erachtens Software-as-a-Service Anbietern die Möglichkeit geben, zu österreichischen Qualitätskriterien und unter österreichischer Gesetzgebung SaaS Angebote zu lancieren. Ob wir uns darum kümmern sollten?

Schlussendlich haben wir - durch die Entwicklung von Apps für Smart Devices für drei unterschiedliche Betriebssysteme (Android, iOS und Windows Phone 7) - einiges an Know How in diesem Bereich hinzu gewonnen. Zentral ist nach wie vor die Frage: "Soll ich nativ für jedes OS entwickeln oder auf 'one-serves-all' Frameworks wie PhoneGap setzen?". Hier haben wir unsere Antwort gefunden.

Unbedingt muss auch der große Erfolg erwähnt werden, den unser coopXarch Architekt Jürgen und unser erfahrener WP7 Entwickler Robert mit der "Spritpreise AT" App für Windows Phone 7 eingefahren haben. Diese App war lange unter den Top 3 Apps im österreichischen Windows Phone Marketplace. Das macht uns stolz und wir freuen uns mit Euch!

Die coopXarch Architekten wünschen ein frohes und besinnliches Weihnachtsfest: Zeit, um inne zu halten und zu reflektieren, Ruhe, um Kraft und Zuversicht zu schöpfen und viel Motivation und Mut, um im neuen Jahr neue Herausforderungen anzugehen und innovative Ideen Realität werden zu lassen.

Euer
JWR@coopXarch.

Montag, 17. Oktober 2011

joinMe! - ein Cloud2Mobile Showcase von coopXarch und Xion IT Systems AG

Liebe Leser!


Unser Cloud- und Mobile-Computing Showcase "joinMe!" ist fertig! joinMe! präsentiert sich für den Benutzer als App für Android, iOS oder Windows Phone 7 Smartphones und Tablets, die es ermöglicht, Personen in der Nähe mit dem gleichen Interesse zu finden. Das Datenhandling, der Geo-Location und Interessens-Match und die Notifikationen an die mobilen Geräte werden dabei von einem losen Applikationsverbund in der Cloud - in unserem Fall CloudFoundry von SpringSource/VMWare - durchgeführt. Durch die lose Kopplung ist eine elastische Skalierbarkeit der Services - so wie man es von den großen Anbietern Facebook, Twitter und Co. gewohnt ist - ermöglicht.

Die Homepage zu joinMe! findet sich hier. Wir haben auch einen Produktflyer mit QR Codes zu den einzelnen Apps erstellt.

In Kürze werden wir in einer Reihe von Blogbeiträgen die technischen Grundlagen, Architekturentscheidungen und den Umsetzungsweg vorstellen und auf Fallstricke und Lessons Learned eingehen.

Cloud- und Mobile-Computing ermöglichen innovative und elastisch skalierbare Services. Unternehmen werden in Zukunft durch die Nutzung von Plattform-as-a-Service Clouds weitere Homogenisierung und Konsolidierung zusammen mit den entsprechenden Einsparungspotentialen erzielen können.

Unser Ziel ist es, durch die frühest mögliche technologische und strategische Erschließung der Grundlagentechnologien als Berater und Umsetzer auf diesem spannenden und innovativen Gebiet erfolgreich tätig zu sein.

Euer
JWR@coopXarch.

Dienstag, 11. Oktober 2011

"Sehnsucht nach Scrum"

Liebe Leser!

Ich halte am Donnerstag, 03. November 2011, 09:00 einen Vortrag zum Thema "Agile Softwareentwicklung" mit dem Titel "Sehnsucht nach Scrum" im Rahmen der Vortragsveranstaltung des IT-Clusters Wien "Agile Software-Entwicklung trifft auf agiles Qualitätsmanagement".

Hier der Link mit näheren Information und der Anmeldung!

Wenn auch Ihr von einer Sehnsucht nach agilen Vorgehensmodellen beschlichen seid (oder die Existenz derjenigen bezweifelt), würde ich mich freuen, Euch zu sehen!

Euer
JWR@coopXarch.

Mittwoch, 17. August 2011

"Elastische Skalierbarkeit in der Cloud" - spritpreisrechner.at zeigt, wie man es nicht macht...

Liebe Leser!

Zugegeben - es ist immer ungleich viel schwerer, Leistung zu erbringen, als Fehlleistung zu kritisieren (als Beispiel sei die österreichische Fußball-Nationalmannschaft angeführt) - aber "die Initiative des BMWFJ für mehr Preistransparenz und Wettbewerb" hat nicht gerade einen Blitzstart hingelegt. Siehe hierfür z.B. http://diepresse.com/home/meinung/marginalien/685968/BenzinpreisRechner-stuerzt-ab_Die-Roboter-sind-schuld, wo es - sehr sarkastisch - heißt:
"Die lange erwartete 'Spritpreis-Datenbank' hätte am Dienstag online gehen sollen. Doch die Homepage kollabierte. Mit so einem Ansturm konnte ja wirklich niemand rechnen."
Seit nunmehr ca. 2 Jahren ist "Cloud Computing" in aller Munde - und die Vorteile, die das neue Paradigma ins Rennen führt, und hier interessiert uns insbesonder das Modell "Platform as a Service (PaaS)" gemeinsam mit elastischer Skalierbarkeit, auch "Auto-Scaling" genannt. Der Anwendungsfall "Spritpreis-Datenbank" - ein paar Clients melden Daten ein (Anforderung 1), viele viele (ev. auch extrem viele - Entschuldigung, wieder sarkastisch) Clients holen Daten ab (Anforderung 2), und die Daten müssen nicht transaktional-aktuell sein (Anforderung 3: soll heißen - es ist ok, wenn ein Client noch kurz den alten Preis bekommt, auch wenn der neue Preis gerade eingemeldet wurde), man verzeihe den Terminus - schreit gerade nach dem Lösungsmuster "PaaS-Cloud mit Auto-Scaling".

Wie hätte coopXarch nun die Architektur ausgelegt:

  1. Web Role spritpreis-meldung@cloudanbieter.com
    1. Eine "Web Role" (wir folgen hier der Microsoft Windows Azure Nomenklatur) ist das Eingangstor für Web Requests und läuft normalerweise auf einer Cloud Instanz (i.e. früher "ein Server"). Diese Web Role ist für die Melder, egal ob es Menschen über ein Web Formular sind, oder ein Automatismus, der die Preise updated (wieder über ein HTTP-basiertes Protokoll, z.B. REST).
    2. Da es eine bekannte Anzahl an Meldern gibt, ist es leicht die Anzahl der Knoten zu dimensionieren - z.B. ein Knoten pro 150 potentielle Benutzer (Warum 150? weil z.B. der Apache Web Server Konfigurations-Wert "maxClients" auf 150 eingestellt ist oder der Tomcat maximal 150 worker threads bietet).
  2. Web Role spritpreis-auskunft@cloudanbieter.com
    1. Das ist nun die Web Role für die Auskunftsapplikation - wieder können hier Menschen vor einen Web Browser oder einer "mobilen App" sitzen bzw. Robots / Crawler, welche die Web Role für Preisauskünfte ansteuern. Hier ist die elastische Skalierbarkeit unumgänglich, da zum Design-Zeitpunkt nicht gesagt werden kann, wie viele Clients auf das Auskunftsservice zugreifen werden. Noch dazu ist mit Spitzen zu rechnen - eben (sic!) bei der Inbetriebnahme. Die Skalierung in Form des Hochstarts neuer Serverinstanzen, um die Last der Anfragen bewältigen zu können, übernimmt nun der Cloud Controller des PaaS Anbieters. Das Starten dieser zusätzlichen Instanzen muss dabei nicht länger als 10-30 Sekunden dauern - daher die Bezeichnung "elastisch", i.e. "quasi in Echtzeit je nach aktuellem Bedarf".
  3. NoSQL Datenbank
    1. Nachdem Konsistenz hier eine untergeordnete Rolle spielt (siehe Anforderung 3), wird eine NoSQL Datenbank verwendet, die hoch skalierbar ist und z.B. dokumentenorientiert arbeitet (Dokument "Tankstelle", Embedded Dokument "Produkt", Embedded Dokument "Preis") - ich würde hier die MongoDB als geeignet vorschlagen, da sie auch (Index-basierte) Geo-Abfragen bietet (z.B. für den Anwendungsfall "Zeige mir den billigsten Diesel im Umkreis von 5km"). Die VMWare CloudFoundry PaaS Cloud bietet etwa die MongoDB als persistenten Storage an.
  4. Worker Roles "Datenbank Cleanup", "Report Generator" etc.
    1. Eine "Worker Role" arbeitet "im Hintergrund" auf einer Datenbasis, um gewisse Aufgaben auszuführen. In diesem Falle könnten alle Spritpreise, die älter als die gehaltene Historie von 14 Tagen (Anforderung 4) sind, aus der Datenbank gelöscht werden. Oder es könnten Preisverlaufskurven generiert und als Bilder abgelegt werden, um Abfragen möglichst effizient mit den Verlaufskurven ausliefern zu können (Anforderung 5).
Zugegeben - viele PaaS Anbieter sind noch im Beta-Stadium bzw. bieten eventuell nur unzureichende Service Levels - ich als staatlich befugter und beeideter Ziviltechniker für Informatik finde es jedoch besonders schade, dass der Staat (in Gestalt des BMWFJ) diese unsere Kompetenz nicht zu Rate gezogen hat, um ein innovatives Service mittels innovativer Technik umzusetzen (und dieser somit ein Stückchen mehr zum Durchbruch zu verhelfen) und verbleibe, mit besten Grüßen,

Euer,
JWR@coopXarch.

Donnerstag, 30. Juni 2011

Architektur vs. Agile Software-Entwicklung

Liebe Leser,

angespornt durch einige Aussagen von Kollegen und Branchengrößen möchte ich meine Gedanken zur vermeintlichen Kontroverse von Software-Architektur und agiler Software-Entwicklung zum Besten geben.

Architektur
Mein generelles Verständnis zur Definition von Software Architektur habe ich bereits in einem früheren Blogpost beschrieben.

Agile Software-Entwicklung
Unter agiler Software-Entwicklung verstehe ich kein konkretes Vorgehensmodell. Ich bin der Überzeugung, dass man Agilität auf unterschiedlichste Art und Weise in der Praxis ausleben kann. Sei dies in Form von XP, Scrum, Kanban, einem weniger verbreiteten Modell oder einfach nur der individuellen Mixtur aus mehreren davon. Persönlich kann ich jedem der namentlich genannten Vorgehensmodelle etwas abgewinnen. Wie schon oft zitiert ist das Agile Manifesto die Wurzel all dieser Ausprägungen.

Die beiden Extreme
Zwei extreme Einstellungen sind in Bezug auf diese Kontroverse durchaus verbreitet:

1) Wir arbeiten agil und können daher keine Architektur vorhab festlegen.
2) Wir legen unsere Architektur vorab fest und können daher nicht agil arbeiten.

Diese Extreme sind meiner Ansicht nach schlicht weg falsch! Natürlich sind beide Themen unter einen Hut zu bringen. Eine einfach klingende Regel, die allerdings nicht so leicht in die Praxis umzusetzen ist, sollte lauten: So wenig Architektur wie möglich und so viel wie nötig.

Wie kommt es zu diesen Extremen?
Warum glauben dennoch viele Agilisten, dass Architektur nicht zur Agilen Software-Entwicklung passt? Ich denke, dies ist fälschlichen Interpretationen geschuldet. Kent Beck hat in seinem Buch "Extreme Programming Explained: Embrace Change" den Begriff "YAGNI" geprägt. YAGNI steht für "You aren't / ain't gonna need it" und bedeutet, dass man nicht alle Möglichkeiten und Varianten vorab berücksichten, sondern lediglich die vorhanden Anforderungen umsetzen soll. Dies vor dem Hintergrund, da nicht bekannt ist wie sich ein System - basierend auf ändernden Anforderungen - in Zukunft entwickeln wird. So wie auch viele das agile Manifest fälschlich interpretieren (aber das ist eine andere Geschichte), so wird auch dieser Begriff meiner Meinung nach oft zu hart ausgelegt. Das YAGNI-Prinzip hat in der Feature-Entwicklung seine Berechtigung, darf aber nicht auf den gesamten Software-Entwicklungsprozess umgelegt werden.

Meiner Ansicht nach sind die grundlegende System-Architektur und die notwendigen Design-Entscheidungen vorab festzulegen und können nicht laufend anhand von Anforderungen angepasst werden. Die Betonung liegt hier auf dem Wort "grundlegend". Ein gewisses Maß an Flexibilität ist durchaus wünschenswert und gerade durch eine vorab durchdachte Architektur gewährleistet. Ein einfaches Beispiel soll die Notwendigkeit von Architektur und Design-Entscheidungen veranschaulichen:

Bei der Umsetzung einer konkreten Anforderung wird festgestellt, dass gewisse Daten persistiert werden müssen. Ohne zuvor festgelegte Architektur bzw. grundlegende Design-Entscheidungen kann es nun sehr schnell passieren, dass ein momentan passend wirkender Datenspeicher - nehmen wir eine relationale Datenbank an - eingeführt wird. Die Persistenz wird, weil es gerade zur Anforderung passt, als Active Record Pattern umgesetzt. Im Zuge der späteren Entwicklung stellt das Team fest, dass die aktuellen Persistenzobjekte besser als Domain Entities konzipiert gewesen wären und für die Datenpersistenz ein Repository Pattern Ansatz auch brauchbarer wäre. Ich wage zu behaupten, dass man in diesem Fall vor einem nicht unwesentlichen und aufwändigen Refactoring stünde, welches die Grundfesten des gesamten Systems erschüttern würde.

Warum glauben Architektur Hardliner, dass agile Vorgehen nicht anwendbar sind? Einerseits vermute ich, dass dies einem Umkehrschluss aus der agilen Extremeinstellung zuzuschreiben ist (Architektur passt nicht zu einem agilen Vorgehen - s.o.). Andererseits kann am Beispiel von Scrum auch wieder eine Misinterpretation oder zu strikte Auslegung des Vorgehensmodells zu dieser Ansicht führen. Scrum gibt einen Rahmen vor, welcher natürlich absichtlich sehr offen gehalten ist. Es gibt defacto keine Design-Phase. Warum sollte man dennoch nicht in der ersten Iteration auch die Architektur gestalten? Auch wenn es in der Scrum-Community eine Diskussion zu "Sprint Zero" gibt, ist es in meinen Augen alternativ durchaus legitim eine Initialisierung vorab der Featureimplementierung in einer eigenen Iteration auf Basis des bekannten Product Backlogs durchzuführen. Man sollte ausserdem nicht vergessen, dass Scrum-Projekte mit Planning Meetings beginnen in denen nicht ausschließlich die Anforderungen besprochen werden, sondern auch die gesamte Vision im Mittelpunkt steht. Auf Basis der Anforderungen und der Produktvision kann man auch eine Architektur(vision) ableiten.

Mein Fazit
Die Ära der voll durchgeplanten und bis ins letzte Detail designten Software-Systeme ist vorbei. BDUF (Bug Design Up Front) sollte vermieden werden. Software-Architektur darf allerdings auch in einer zunehmend agilen Software-Entwicklungswelt dennoch nicht zu locker gesehen oder gar gänzlich vernachlässigt werden, da einem sonst die vermeintlich günstig erkaufte Flexibilität schnell sehr teuer kommen kann. Der Weg liegt wie so oft in der Mitte und diesen zu finden ist mitunter Aufgabe von agil denkenden Software-Architekten. Ob die Rolle des Architekten nun im Team gemeinschaftlich wahrgenommen wird, ein Teammitglied dazu auserkoren wird oder ein konsultierender Architekt dem Team zu Beginn zur Verfügung gestellt wird soll an dieser Stelle nicht weiter behandelt werden. Software-Architektur ist auf jeden Fall weiterhin ein wichtiger Bestandteil in der Software-Entwicklung, auch wenn sich die Umwelt durch agile Vorgehensmodelle geändert hat.

Euer,
JL@coopXarch

Montag, 6. Juni 2011

These zur mangelnden Investitionsbereitschaft in Software Qualität betrieblicher Informationssysteme

Liebe Leser!

Sperriger Titel, aber folgender Gedanke beschlich mich beim morgendlichen Zähneputzen:

These:

Investitionsbereitschaft in Software Qualität ist reziprok proportional zum zeitlichen Abstand des Eintritts eines - kausal auf ein Qualitätsproblem folgendes - negativen Ereignisses, dessen Eintrittswahrscheinlichkeit und dessen Auswirkung.

Argumentation anhand von Beispielen (die angeführten Zahlen sind meine persönlichen Annahmen):

Softwareversagen im Bereich Avionik:

Zeitlicher Abstand -> wenige Minuten
Negatives Ereignis -> Absturz
Eintrittswahrscheinlichkeit -> nahezu 100%
Auswirkung -> Multiple Todesfälle nahezu 100%

Softwareversagen im Bereich Automotive:

Zeitlicher Abstand -> wenige Sekunden
Negatives Ereignis -> Unfall
Eintrittswahrscheinlichkeit -> ca. 50%
Auswirkung -> Tod zu 40%, Verletzung zu 60%

Softwareversagen im Bereich betrieblicher Informationssysteme:

Zeitlicher Abstand -> Stunden, Tage, Monate, Jahre
Negatives Ereignis -> Fehler, verletzte Datenintegrität und Konsistenz, Datenverlust, Finanzieller Verlust, überproportional steigende Wartungskosten
Eintrittswahrscheinlichkeit -> ca. 99%
Auswirkung -> Imageschaden, finanzieller Schaden, Kundenunzufriedenheit, Haftungen (bis hin zur Haftstrafe)

Interpretation:

Trotz hoher Eintrittswahrscheinlichkeit eines negativen Ereignisses ist der zeitliche Abstand in vielen Fällen derart groß, dass eine Investitionsbereitschaft stark gebremst wird, noch dazu, wo die Auswirkungen nicht als lebensbedrohlich gewertet werden und Folgen wie mögliche Haftungen oft nicht transparent sind.

Ergo: 

Um die Investitionsbereitschaft zu steigern, müssen die möglichen Auswirkungen transparenter dargestellt werden, um Entscheidungsträger nach dem Kosten-Nutzen Prinzip geeignet aufzuklären. Eine Darstellung des Ablaufs im Zeitraffer ist anzuraten, um dem Gefühl der „Sicherheit aufgrund eines ausreichenden zeitlichen Puffers“ entgegen zu wirken. Geeignete Simulationsmodelle sind auszuarbeiten.

Einen sonnigen Juni wünscht
Euer 
JWR@coopXarch.

Montag, 9. Mai 2011

Der Fluxkompensator und seine Bedeutung für den Software Test

Liebe Leser!

Jeder, der den Film "Zurück in die Zukunft" mit einem großartigen Michael J. Fox in der Hauptrolle gesehen hat, kennt auch die Basiserfindung für die Durchführung jeglicher Zeitreise, den
Fluxkompensator.

Aus [wikipedia], 08.05.2011:


"Der Fluxkompensator ist das Kernstück der 
Zeitmaschine, der nach Browns Aussage „Reisen durch die Zeit erst möglich macht“. Er besteht aus einem grauen, rechteckigen Kasten mit drei länglichen, blinkenden Glasröhrchen, die hinter einer Glasscheibe eben im 120°-Winkel zueinander angeordnet sind. Der Fluxkompensator ist an der Rückwand zwischen den beiden Sitzen im Fahrzeuginneren angebracht. Brown hat ihn am 5. November 1955 erfunden, als er beim Aufhängen einer Uhr in seinem Badezimmer von der Schüssel seiner Toilette abrutschte und unsanft mit dem Kopf aufschlug."



Nun - obwohl der ein oder andere von uns sicher auch schon mal unsanft mit dem Kopf auf diverse Nassraumeinrichtungsbestandteile geprallt ist (nein?), hat das - wie ich vermute - keine revolutionären Ideen zum Thema Zeitreisen ausgelöst (bei mir jedenfalls nicht).

Das Problem

Als ich jedoch ein "Mehrrunden-Mehrgüter" Auktionssystem als Scrummaster und Backend Architekt verantworten durfte, kam ich in die Verlegenheit, folgendes Problem zu lösen:
  • Eine Auktion startet mit der Katalogphase, in dieser werden zu versteigernde Güter im Katalog von den Eigentümern registriert - diese Phase dauert 2 Tage.
  • Nach der Katalogphase beginnt die Auktion, wobei diese in maximal 20 Runden zu je 2 Minuten 40 Sekunden Bietzeit und 20 Sekunden Berechnungszeit gegliedert ist.
  • Gibt es in einer Runde kein neues Gebot auf zumindest ein Gut in der Versteigerung, ist die Auktion beendet und die Höchstbieter erwerben jeweils die Güter.

Die Entscheidung


Ich traf nun - entgegen einigen Widerstands aus dem Team (was ich nicht gerne mache, aber manchmal muss man sich eben durchsetzen) - folgende weitreichende Designentscheidung:

Die einzelnen Auktionsstati (Katalogphase, Rundenphase, Berechnungsphase) werden aufgrund der aktuellen Uhrzeit in Bezug auf den Start der Katalogphase bestimmt (z.B. Start Katalogphase + 2 Tage = Start Auktionsphase). Der Vorteil hierbei ist, dass der Status nicht persistent gespeichert werden muss. Somit kann der aktuelle Auktionsstatus - aufgrund fehlerhafter bzw. vergessener Statusübergänge (z.B. Systemabsturz während eines Übergangs) - nie falsch sein und man spart sich einen Scheduler, der zum richtigen Zeitpunkt den Status "eins weiter" setzt (z.B. auf die nächste Runde). Dieser Scheduler könnte auch nur schwer auf die Millisekunde genau arbeiten und damit könnte ein Bieter auch kurz nach Beendigung der Runde bieten - für einen Architektur-Ästheten eine unerträgliche Einschränkung.

Soweit - so gut. Wie testet man so eine Applikation aber in der Praxis? Wenn ich Güter registriere, will ich nicht zwei Tage warten, bis ich darauf bieten kann. Selbst wenn ich die Zeitdauer der Katalogphase per Konfiguration für den manuellen Test auf 20 Sekunden setze, muss ich mich beeilen, um alle Güter rechtzeitig zu registrieren, ganz zu schweigen davon, dass z.B. ein Telefonat mit dem Chef einen Neustart des Tests bedingt (außer man kann nebenbei Güter erfassen, was sicher auch auf die Stimmung des Chefs ankommt). Beim Unit Test stellt sich dasselbe Problem - wie kann ich deterministisch testen? Wenn z.B. der Build Server während eines Tests mit 100% CPU Load gesegnet ist, könnte es sein, dass in Runde x gar kein Gebot einlangt, und somit die Auktion beendet ist. Non-Determinismus ist aber hier ein absolutes No-Go.

Aufgrund dieser Problematik dachte das Team wohl, dass ich die Design-Entscheidung zurücknehmen würde. Ich aber habe meine grauen Zellen angestrengt und kam zu folgender Idee: Wir brauchen einen Fluxkompensator! (Team: staunende Augen, Kopfschütteln). Sowohl im manuellen als auch automatischen Test will ich - gleich nach der Durchführung einer Aktion wie z.B. einer Gebotsabgabe - in die Zukunft reisen können, um zur richtigen Zeit meine nächste Aktion zu setzen und ohne unnütz Zeit mit Warten verbringen zu müssen. Die explizite Kontrolle der Zeit bringt den gewünschten Determinismus! Soviel zur Idee - fehlt nur noch die Umsetzung.

Der Lösungsansatz

Wie sieht der Lösungsansatz nun konkret aus? Software-technisch beginnt alles (wie meistens) mit einem Interface:

package at.coopXarch.flux;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * This service simply provides timestamps.
 * @author jwr
 */
public interface ITimestampProvider {
    /**
     * This formatter is required to achieve the same representation as java.lang.String of java.util.Date and
     * java.sql.Timestamp
     */
    final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    /**
     * Gets a notion of "now".
     * @return Timestamp that represents "now".
     */
    Date getTimestamp();
    /**
     * Notion of a time increment. In real time we would <code>sleep()</code>, but note that in a test bed we can control the sampling of time.
     * @return Timestamp after increasing "current" time with sample value.
     */
    Date sample();
    /**
     * Set the time increment we desire when calling <code>sample()</code>.
     * @param sampleIncrement Increment of time we desire.
     */
    void setSampleIncrement(long sampleIncrement);
    /**
     * Set a specific time as a notion of "now". We cannot do this with real time but in a test bed we can control it.
     * @param sampleValue Value of time representing "now".
     */
    void setSampleValue(long sampleValue);
    /**
     * Reset "now" timestamp to a specific absolute "begin" value. We cannot do this with real time but in a test bed we can control it.
     */
    void reset();
}

Die Implementierung - Variante 1 "Echtzeit"

Die folgende Implementierung liefert Echtzeit - die Zeit wird also, wie üblich, mittels new Date() bestimmt. In der Applikation muss man nun dafür sorgen, dass überall statt new Date() nun getTimestamp() aufgerufen wird. In der Produktion ist also folgende Implementierung des Interfaces zu verwenden:

package at.coopXarch.flux;

import java.util.Date;

/**
 * This service simply provides timestamps.
 * @author jwr
 */
public class TimestampProviderImpl implements ITimestampProvider {
    @Override
    public Date getTimestamp() {
        return new Date();
    }
    @Override
    public Date sample() {
        throw new UnsupportedOperationException("You cannot do this in real time!");
    }
    @Override
    public void setSampleIncrement(long sampleIncrement) {
        throw new UnsupportedOperationException("You cannot do this in real time!");
    }
    @Override
    public void setSampleValue(long sampleValue) {
        throw new UnsupportedOperationException("You cannot do this in real time!");
    }
    @Override
    public void reset() {
        throw new UnsupportedOperationException("You cannot do this in real time!");
    }
}

Die Implementierung - Variante 2 "Fluxzeit"

Die folgende Implementierung des Interfaces steuert jetzt den Fluxkompensator bei - sie wird also im manuellen und automatisierten Test verwendet:

package at.coopXarch.flux;

import java.util.Calendar;
import java.util.Date;

/**
 * This class acts as a FluxKompensator. Do not tell Albert Einstein!
 * @author jwr
 */
public class FluxKompensator implements ITimestampProvider {
    private final Calendar cal;
    private Date initialTimestamp;
    private static final long DEFAULT_SAMPLE_INCREMENT = 500;
    private long sampleIncrement = DEFAULT_SAMPLE_INCREMENT;
    private Date dateHolder = null;

    public FluxKompensator() {
     synchronized (this.getClass()) {
      cal = Calendar.getInstance();
         // D-Day: e.g. 31.03.11 10:10
         cal.clear();
         cal.set(2011, 2, 31, 10, 10, 0);
         initialTimestamp = cal.getTime();
     }
    }
    @Override
    public synchronized Date getTimestamp() {
        if (dateHolder == null) {
            sample();
        }
        System.out.println("getSampledTimestamp()=" + dateFormat.format(dateHolder));
        return new Date(dateHolder.getTime());
    }
    @Override
    public synchronized  Date sample() {
        final Date previous = dateHolder;
        final Date now = (previous != null) ? new Date(previous.getTime() + sampleIncrement) : initialTimestamp;
        dateHolder = now;
        System.out.println("sample()=" + dateFormat.format(now));
        return new Date(now.getTime());
    }
    @Override
    public void setSampleIncrement(long sampleIncrement) {
        this.sampleIncrement = sampleIncrement;
        System.out.println("setSampleIncrement() to " + (sampleIncrement / 1000) + "s");
    }
    @Override
    public synchronized void setSampleValue(long sampleValue) {
        final Date now = new Date(sampleValue);
        System.out.println("setSampleValue()=" + dateFormat.format(now));
        dateHolder = now;
    }
    @Override
    public synchronized void reset() {
        cal.clear();
        cal.set(2011, 2, 31, 10, 10, 0);
        initialTimestamp = cal.getTime();
        sampleIncrement = DEFAULT_SAMPLE_INCREMENT;
        dateHolder = null;
    }
}

Voila! Nun kann man in der Testumgebung mittels setSampleValue() jeden beliebigen Zeitpunkt setzen - während des manuellen Tests sogar im User Interface (siehe unten)! Und beliebig lange auf einem konkreten Zeitpunkt verweilen (ist schön im Log zu sehen, alle Aktionen haben auf die Millisekunde denselben Timestamp). Auch im Unit Test haben wir die Zeit vollkommen unter Kontrolle und man kann sie beliebig vorwärts und auch rückwärts setzen (Achtung: rückwärts fließende Zeit kommt im realen Zeitfluss nur sehr SELTEN vor und kann zu Risiken und Nebenwirkungen führen!).

Im GUI sieht das folgendermaßen aus:


Besonders die Option "Zeit vergehen lassen" finde ich besonders nett - dahinter steht natürlich ein Aufruf von sample().

Fazit

Mit diesem Ansatz haben wir alle Tests im gegenständlichen Projekt ohne Probleme durchgeführt und das Produkt erfolgreich ausgeliefert. Ich finde den Lösungsansatz kreativ und leistungsfähig, bin aber natürlich auf Eure Meinung gespannt.

Und zum Schluss der Dialog zwischen Marty McFly und Dr. Emmett L. „dem Doc“ Brown nach meiner absoluten Lieblingsszene zu Beginn des Films:
Marty: "Hören sie, Doc. Die Geräte waren die ganze Woche eingeschaltet."
Doc:
"Meine Geräte? Da fällt mir ein Marty. Schalte lieber nicht den Verstärker ein. Es könnte die Möglichkeit bestehen, daß er zuviel Spannung kriegt."
Insider wissen um die Vorgeschichte (Gitarre, Box, Explosion) und schicken mir bitte einen YouTube Link dazu. Ich habe nur den, wo Marty "Johnny B. Goode" spielt (auch nicht zu verachten: http://www.youtube.com/watch?v=d4Cr7kxjSBs)

Mit diesen Ausführungen verbleibe ich
Euer
JWR@coopXarch.

Montag, 7. Februar 2011

Standard Software ist nicht gleich Standardisierung!

Liebe Leser!

Im Jänner besuchte ich ein Cloud Computing Event eines namhaften Beraters - eine durchaus interessante Mischung von Vorträgen zu diesem interessanten Hype-Thema. Ein Punkt hat mich dann aber doch sehr gestört. Ein Berater hat die Top 5 Punkte präsentiert, die in Zusammenhang mit einer Migration in die Cloud wichtig sind.

Der erste Punkt der Aufzählung war "Standardisierung". Und als Maßnahme, um diese zu erreichen, fand sich auf der Folie der Vorschlag, Standard Software einzusetzen, und der Berater riet ergo dazu, keine Eigenentwicklungen mehr durchzuführen.

"Schön!" denkt man sich nun reflexartig als IT Verantwortlicher - viele dieser Projekte sind ohnehin nicht reibungslos gelaufen, sparen wir uns das in Zukunft und geben Standardprodukten "aus der Schachtel" den Vorzug.

"Halt!" sage ich als Hersteller von Individualsoftware. Warum?

  1. Was bitte, hat Standard Software mit Standardisierung zu tun, außer vorerst einmal das gemeinsam benutzte Wort "Standard". Gerade große, monolithische Standard-Software-Produkte können es sich leisten, Standards eben nicht oder nicht sofort umzusetzen, und wenn, dann mit herstellerspezifischen, proprietären Ausnahmen, die der verbesserten Effizienz dienen sollen. Als Entwickler einer individuellen Lösung kann ich es mir oft gar nicht leisten, nicht konform zu den letzten Standards zu sein.
  2. Die Integration von Standardprodukten treibt vielen IT Verantwortlichen die Schweißperlen auf die Stirn - wo es bei vielen Eigenentwicklungen eine Sache von Stunden bzw. Tagen ist, eine neue Schnittstelle umzusetzen und in Produktion zu bringen, kann es bei einem Standardprodukt lauten "Kommt mit Release x.y Mitte nächsten Jahres".
  3. Wie kann ich mich, als Unternehmen, mit innovativen Prozessen und Produkten vom Markt unterscheiden, wenn ich nur Standardprodukte einsetze? Natürlich erlauben es moderne Prozesssteuerungs-Tools, individuelle Prozesse umzusetzen, jedoch liegt die Unterscheidung zum Mitbewerb immer auch in innovativer fachlicher Logik. Diese Innovation bedingt aber zu einem gewissen Grad, Business Logik in Technologie zu gießen - und zwar als "Eigenentwicklung".
Diese Punkte führe ich im Kampf um das Alleinstellungsmerkmal "Eigenentwicklung" an (und habe noch mehr in der Hinterhand). 

Scheinbar entbrennt ein Kampf Standardisierung versus Flexibilität. Am vorjährigen Oracle Day hat ein Top Berater eines anderen großen Beratungsunternehmens den anwesenden Entscheidern in selber Manier geraten, nur mehr Srandardprodukte einzusetzen und diese nicht einmal mehr zu groß anzupassen. Warum? Damit man Versionsupgrades reibungsfrei durchführen kann. Viele Kunden zahlen nämlich jährlich hohe Supportkosten, können dann aber nicht auf die neueste Produktversion umsteigen, weil die vielen individuellen Anpassungen genau das verhindern.

Standard Software ist ohne Zweifel Bestandteil jeder funktionierenden und kosteneffizienten IT. Aber Standardisierung bedeutet meines Erachtens, die Voraussetzungen für Integration und Flexibilität zu schaffen, um die innovativsten und produktivsten Prozesse unterstützen zu können, die zum Wohl des Unternehmens umsetzbar sind. Und in diesem Kontinuum hat Eigenentwicklung aus meiner Sicht nach wie vor seinen Platz.

Ob der - von anderer Seite vorgeschlagene - Verzicht auf jegliche Eigenentwicklung uns den entscheidenden Schritt in Richtung Service Orientierung und Prozessketten Chaining, Cloud, IT als Commodity und IT Industrialisierung voranschreiten lässt, überlasse ich, geschätzte Leser, Eurer werten Einschätzung. 

Ich habe meinen Standpunkt klar gemacht.

Euer 
JWR@coopXarch.

Montag, 31. Januar 2011

Asynchrone eventbasierte Programme mit Rx

Liebe Leser,

am 12. Oktober 2010 hatte ich das Vergnügen mich beim Mittagessen mit Bart de Smet (Microsoft) über Rx für .NET [1] zu unterhalten. Zuvor konnte ich seiner amüsant inszenierten hoch professionellen Keynote beim DevCamp 2010 [2] beiwohnen. Rx steht für Reactive Extensions for .NET und kann mit einem Satz folgender Maßen beschrieben werden:

Rx ist eine Bibliothek zur Erstellung von asynchronen und event-basierten Programmen, welche "observable collections" verwenden.

Das mag auf den ersten Blick nicht sonderlich spannend klingen. Denkt man allerdings ein wenig weiter, dann offenbaren sich tolle Möglichkeiten um kostengünstigere, wartungsfreundlichere und für den Benutzer bedienungsfreundlichere Software zu erstellen.

Viele sind vermutlich schon mindestens einmal vor dem Szenario gestanden, dass auf Grund einer Eingabe von Daten oder einer sonstigen Benutzeraktion eine Liste von Ergebnissen angefordert wird, welche dann in Form eines Listen- oder Gridsteuerelementes angezeigt werden soll. In der Regel muss man als Entwickler die Entscheidung treffen, ob man das Laden der Daten synchron oder asynchron umsetzt. Der einfachere Weg ist sicher die synchrone Verarbeitung, welche sich in einem nicht reaktionsfähigen GUI auswirkt, dafür aber viel einfacher und übersichtlicher programmierbar ist. Für kleine Datenmengen und schnelle Zugriffsmöglichkeiten auf Daten ist dies sicher ein gültiger Ansatz. Jedoch stößt man in der Praxis leider oft auf andere Gegebenheiten, wo man evtl. ein Service konsumieren muss, welches die Daten liefert. Für viele Applikationen ist es ein No-go, wenn das GUI in einen blockierenden Zustand gerät. Bleibt also noch die asynchrone Variante. Wer sich schon einmal dafür entschieden hat, dem ist bewusst, dass hierfür zwar Methoden in .NET zur Verfügung stehen, jedoch einiges an Mehraufwand notwendig ist und der produzierte Sourcecode auch nicht sonderlich wartungsfreundlich wirkt. Ich wage auch zu behaupten, dass manche Konstellationen sehr schwer damit umsetzbar sind.

Hier kommt Rx ins Spiel. Rx bietet elegante und einfach anzuwendende Möglichkeiten um eine asynchrone Verarbeitung von event-gesteuerten Aktionen zu realisieren. Es ermöglicht weitergehend die Kombination von asynchronen Verarbeitungen ohne unnötigen Verbindungscode an verteilten Stellen im Sourcecode schreiben zu müssen (Stichwort Wartbarkeit!). Die asynchronen Verarbeitungen werden als Datenquellen betrachtet, wodurch das LINQ Programmiermodell zur Anwendung kommt. Man kann sich beispielsweise Mausbewegungen einfach als Datenbank von Punkten vorstellen. Rx bietet ein ein push-based Modell im Gegensatz zu dem pull-based Ansatz, der bis dato häufig vorherrscht.


Wie verwendet man nun Rx und was ist als Voraussetzung notwendig? Die beiden wichtigsten Interfaces wurden bereits mit der .NET 4 Base Class Library (BCL) mitgeliefert. Dabei handelt es sich um IObservable<T> und IObserver<T>. Damit ist lediglich die Basis gelegt. Für den effizienten Einsatz muss man eine der Rx Bibliotheken herunterladen und einbinden. Rx steht aktuell für .NET 4, .NET 3.5 SP1, Silverlight 3, Silverlight 4 und JavaScript zur Verfügung. Des Weiteren ist Rx integraler Bestandteil von Windows Phone 7. Alle Ausprägungen sind auf der Rx Downloadseite verfügbar.

Die folgenden Codefragmente sollen den Unterschied zwischen synchroner und asynchroner Verarbeitung von Collections verdeutlichen.

Gewohnte Verarbeitung von Collections:

IEnumerable<BlogItem> items = _service.GetBlogItems();
foreach (BlogItem item in items)
{
    // Verarbeitung der BlogItem Objekte...
}

// Erst wenn alle BlogItems geliefert wurden, kommt man an diese Stelle.

Mittels Rx und den ObservableCollections würde der Code wie folgt aussehen:

IObservable<BlogItem> items = _service.GetBlogItems();
items.Subscribe(item =>
{
    // Verarbeitung der BlogItem Objekte...
});

// Sofort hier. Sobald Daten ankommen -> Lambda Expression aufgerufen.

Rx baut auf der LINQ Infrastruktur auf und erlaubt uns daher einen sehr komfortablen Umgang mit den asynchron zu liefernden Daten. Zum Beispiel können Einschränkungen sehr einfach mittels „where“ eingebaut werden.

Die bereits erwähnte Überlegung mit den Mousekoordinaten soll für ein weiteres Beispiel noch einmal aufgegriffen werden. Der MouseMove Event wird einer Form zugefügt und gleichzeitig mittels "observable collection" abgefragt. Folglich wird die Mouse lediglich als Datenquelle von Punkten betrachtet:

var form = new Form();
var mouseMoves = from mme in Observable.FromEvent<MouseEventArgs>(form, "MouseMove")
                 let point = mme.EventArgs.Location
                 where point.X == point.Y
                 select point;

using (mouseMoves.Subscribe(pt =>
       {
          Console.WriteLine(“Mouse Cursor bei {0}”, pt);
       })
{
   Application.Run(form);
}

Im obigen Beispiel wird mittels LINQ Query die Eventsequenz zu einer Sequenz aus Punkten (Point Objekt) projiziert und gleichzeitig nach Gleichheit von X- und Y-Koordinate gefiltert.

Rx soll nicht als Ersatz für bestehende Möglichkeiten der eventgesteuerten (asynchronen) Programmierung dienen. Durch die Verwendung von observable Collections entstehen jedoch einfacher und übersichtlicher zu entwickelnde Anwendungsszenarien, die bisher nur mit Mühe zu bewerkstelligen waren. Um die vielfältigen Möglichkeiten von Rx kennenzulernen empfiehlt sich ein Blick auf die Rx Beispielsammlung [3].

Euer
JL@coopXarch

[1] Rx Webseite (DevLabs) http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
[2] DevCamp http://www.devcamp.at
[3] 101 Rx Samples http://rxwiki.wikidot.com/101samples