Archiv der Kategorie: Projects

Fake It Easy – WithArgumentsForConstructor

Dieser Tage stand ich vor dem Problem, dass ich bei einem Test der zu testenden Klasse prüfen musst, ob ein Aufruf innerhalb der Klasse stattgefunden hat.

Beispiel: Die Klasse Vb6Process enthält die 2 Methoden Monitor() und Kill(). Die Monitormethode ruft unter bestimmten Umständen, welche ich in meinem Arrange bzw. Act des Unit Tests provoziert habe, die Kill() Methode auf. Und genau das wollte ich prüfen. Nun ist es natürlich sinnfrei das zu mocken, was man testen will.

Fake It Easy bietet uns dafür eine Lösung, nämlich die Methode WithArgumentsForConstructor. Bei meinem Code sieht das wie folgt aus:

   1: _vb6Process = A.Fake<Vb6Process>(

   2:     x => x.WithArgumentsForConstructor(

   3:         () => new Vb6Process(

   4:               jobController,

   5:               token,

   6:               printServiceConfig)

   7:           )

   8:       );

 

Folgendes ist zu beachten:

  • In Zeile 1 wird nicht wie üblich ein Interface, sondern die konkrete Klasse angegeben
  • Über die Methode WithArgumentsForConstructor wird dann die konkrete zu testende Instanz reingereicht. Wichtig ist hierbei, dass eine Expression verwendet wird. Ein Delegate ist nicht möglich!

Damit kriegt Fake It Easy alle Aufrufe innerhalb einer Klasse mit, sodass ich im Assert über

   1: () => A.CallTo(() => _vb6Process.Kill()).MustHaveHappened()

 

prüfen kann, ob die Kill-Methode aufgerufen wurde.

Wichtig: Folgender Aufruf hätte mich nicht zum Ziel geführt

   1: _vb6Process = A.Fake<IVb6Process>(

   2:     x => x.Wrapping(

   3:         new Vb6Process(

   4:             jobController,

   5:             token,

   6:             printServiceConfig

   7:         )

   8:     )

   9: );

 

In diesem Fall hätte ich nur einen Wrapper um die Klasse herum, d.h. ich würde nur Aufrufe von außerhalb auf die Instanz mitbekommen, nicht aber Aufrufe innerhalb der Klasse! Ein riesiges Dankeschön an dieser Stelle an Alex Groß für seine Hilfe dabei.

Social Media: Das diffizile Thema mit dem großen Potential

In diesem Eintrag will ich auf meinen Post Twitter/Facebook gehört ins ERP-System Bezug nehmen. Genauer gesagt will ich den Beitrag erweitern:

Twitter und Facebook sollte man idealerweise in das System einbinden, zu welchem die Mehrheit der Mitarbeiter Zugang hat! Logisch, denn wer den Kommunikationsfluss optimieren will, der muss – neben der Optimierung der Nachricht an sich – mehr Menschen schneller erreichen. Ich will an dieser Stelle nicht auf das Thema Nachrichten eingehen. Ich empfehle jedem, den dies interessiert, sich über das Vier-Seiten-Modell zu informieren. Das Modell dient als Grundlage für alle Arten von Nachrichten, egal ob sie nun verbal in einem Mitarbeitergespräch geäußert oder digital in einer E-Mail übermittelt werden.

Mir geht es darum, dass am Anfang eine Analyse stehen sollte, die aufdeckt, von wo die Mitarbeiter ihre Informationen überwiegend beziehen bzw. beziehen können (je nach Berechtigungsstufe) – und v.a. auch beziehen wollen. Welches System wird intuitiv mit dem Thema Information assoziiert? Die Firma heco gmbh beispielsweise setzt SharePoint als Intranet Plattform ein. Zu dieser Seite haben alle Anwender mit einem eigenen Konto Zugriff. Besonders hervorzuheben ist, dass zur Intranetseite auch alle Infoterminals / öffentliche PCs, welche beispielsweise im Lager und in unserer Fertigung aufgestellt sind, Zugang haben. Außerdem kann man sowohl über das Internet von Zuhause, als auch über das eigene Smartphone (wenngleich diese Funktionalität nur selten genutzt wird) darauf zugreifen. Zwei wesentliche Punkte, die nicht für unser ERP System gelten! Das fällt umso mehr ins Gewicht, je mehr Mitarbeiter im Home Office oder im Außendienst arbeiten. Hinzu kommt, dass es natürlich auch Personen gibt, die krankheitsbedingt oder wegen Elternzeit längere Zeit fernab vom Kommunikationsfluss des Unternehmens sind.

In Gesprächen und Rückmeldungen hat sich klar gezeigt, dass ausnahmslos alle Mitarbeiter typischerweise das Thema Information mit dem Intranet verbinden, was nicht verwundert, wenn man bedenkt, dass hier direkt auf der Startseite seit Jahren alle wesentlichen Ankündigungen stehen. Das freut mich natürlich auf der einen Seite, schließlich bedeutet das, dass wir SharePoint als ein zentrales Medium etablieren konnten.  Einige Tipps, wie man dies erreichen kann, findet ihr hier. Dementsprechend stellt sich die Frage, warum der Status Quo nicht beibehalten werden sollte: Informationen werden weiterhin auf der Startseite im Intranet veröffentlicht.

An dieser Stelle möchte ich wieder auf die Absicht hinter Twitter & Co. in der Business Domäne kommen: Kommunikation optimieren, d.h. Nachrichten schneller an mehr Menschen bringen. Das geht ohne Frage sehr leicht mit Portalen wie Facebook und Twitter von der Hand. Viele meiner Kollegen – und damit sind nicht nur die Jüngeren gemeint – haben bereits einen Facebook Account. Twitter hingegen ist kaum verbreitet. Offensichtlich gibt es also zwei konkurrierende Informationskanäle und –ansätze: Social Media und Intranet. Das sahen zumindest viele der Tester so, die wir Twitter ausprobieren ließen. Die Angst, dass das bekannte und bewährte Intranet abgeschafft werden soll, war dementsprechend hoch. Grund genug für uns den Ansatz nochmal in Frage zu stellen und mit Kollegen aus allen Abteilungen nochmal konstruktiv zu diskutieren! Dementsprechend fokussierten wir die Fragen nach dem Wo, dem Was und dem Wie.

Das Wo: Bei uns ist dies ganz klar das Intranet. Diskussion beendet. Aber: Es wäre durchaus denkbar, dass man zusätzlich die Web 2.0 Kanäle in weitere Systeme, die von vielen Mitarbeitern benutzt werden, einbindet, wie z.B. das ERP System. Das macht vor allem dann Sinn, wenn es keine Plattform gibt, auf die das Groß der Leute Zugriff hat. Aber auch in allen anderen Fällen sollte die Option erörtert werden, da es nicht viel Aufwand ist Twitter in das eigene ERP System zu implementierten (Link s.o.). Und noch ein wesentlicher Punkt, für welchen wir uns schließlich entschieden haben: Man bindet Twitter einfach in SharePoint als Webpart ein. Wie das geht, könnt ihr in einem Forenbeitrag von mir nachlesen. To make a long story short: Wir wählten den Weg der Integration, sodass Twitter und SharePoint zusammen zentral koexistieren. Weitere Optionen wie das Einbinden des Firmen Tweet ins ERP oder auf unserer Homepage evaluieren wir zu einem späteren Zeitpunkt erneut. Die Antwort auf die Frage der Abteilungen, nämlich „verwenden wir dann Twitter statt SharePoint“, beantworteten wir mit „Wir verwenden SharePoint, als auch Twitter“! Alte Kommunikationsstrukturen werden erweitert.

Das Was: Wenn der zuvor erwähnte Status Quo optimal ist, dann muss natürlich nichts geändert werden. Aber bisher habe ich noch nie jemanden getroffen, der der Überzeugung war, dass die Unternehmenskommunikation ausnahmslos gut ist und nicht optimiert werden könnte. In der heco gmbh ist es so, dass Nachrichten z.B. über Neuzugänge oder Strukturänderungen erst zu dem Zeitpunkt veröffentlicht werden, wenn diese kurz bevorstehen. Schließlich macht ein Eintrag dazu 2-3 Monate vorher selten Sinn, insbesondere dann nicht, wenn noch nicht alle Informationen dazu zur Verfügung stehen (z.B. ein Bild des neuen Mitarbeiters). Außerdem gibt es viele kleine, wissenswerte Nachrichten (da passt die Bezeichnung Tweet hervorragend), die zwar keine größere Ankündigungen rechtfertigen, aber trotzdem verbreitet werden sollten. Genau diese Abgrenzung hatten wir unterlassen, weil es sich lediglich um einen Testlauf handelte. Wie es aber so ist, machte die Nachricht über unseren Tweet unter den restlichen Kollegen schnell die Runde – also auf dem herkömmlichen analogen Weg. Töricht zu glauben, dass man einen Testlauf umsetzen kann, ohne dass die Existenz eines neuen (neu ist zunächst einmal böse!) Werkzeugs die Arbeit der restlichen Kollegen tangiert. Deshalb haben wir genau definiert, was wo einzustellen ist. Als Grundlage klassifizierten wir dazu bei uns im Intranet über 400 Nachrichten, um ein Gespür dafür zu bekommen, was alles an Informationen aufläuft. Auf dieser Basis legen wir die Definition für Ankündigung, die ins Intranet gehören, wie folgt fest:

  • Ausformulierte, vollständige und ggf. um Anhang erweitere Nachrichten
  • Informationen, die primär zeitnah veröffentlicht werden sollten
  • Sehr wichtige Ankündigungen, die lange zu sehen sein sollen
  • Firmeninterna

Wohlgemerkt ist dies unsere Definition! In jedem Fall muss hierzu nochmal die Datenschutzerklärung und die Social Media Guidelines zur Ratifizierung herangezogen werden. Eine gute Verallgemeinerung, die für das Web 2.0 getroffen werden kann, stellt der Inhalt der Firmenwebsite dar, denn was hier steht, ist bereits öffentlich zugänglich und kann somit auch z.B. über Twitter verbreitet werden. Gerade der Umgang mit Multimediainhalten, bei denen man sonst von jedem Beteiligten die Einwilligung einholen muss, kann damit einfach geregelt werden. Der wesentliche Punkt ist hier aber genau wie bei der Frage nach dem Wo, dass hier nichts ersetzt wird, sondern dass die bisherigen Lücken gestopft und ein Wissensvorsprung erreicht wird.

Das Wie: Wie gepostet wird und wer im Namen der Firma posten darf, ist eine längere Evaluierung, die ich in einem späteren Beitrag aufgreifen will. Speziell dieser Punkt ist aber sehr unternehmensspezifisch auszuarbeiten.

Summa summarum: Am Anfang steht die Analyse der bisherigen Kommunikation mit den einzelnen Abteilungen. Idealerweise kann man hier schon auf eine breite Datenbasis zurückgreifen. Das Ergebnis gibt Aufschluss über das Wo und das Was. Diese zwei Punkte hatten für uns eine starke Kohäsion und waren besonders dringlich. Außerdem ließen sie sich schnell evaluieren und implementieren. Das Wie hingegen definierten wir für uns als separaten Prozess.

SharePoint Calculated Fields – Wissenswertes über Formulas

Wer wie ich in einem eigenen Feature berechnete Spalten definieren muss, wird sicherlich auf ähnliche Probleme stoßen. Im Gegensatz zu Formeln, die man über die Oberfläche eingeben kann, befindet man sich bei Spaltendefinitionen innerhalb von XML, sodass Sonderzeichen immer ein leidiges Thema sind. Hier zwei Beispiele:

   1: <Field ID="{FC13F3E2-ED5A-41B6-99A7-FEA6A2605D9E}"

   2:    Name="Heco_ITAM_MachineGroup"

   3:    StaticName="Heco_ITAM_MachineGroup"

   4:    DisplayName="Machine Group"

   5:    Type="Calculated"

   6:    ShowInNewForm="TRUE"

   7:    ShowInEditForm="TRUE"

   8:    Group=" heco ITAM"

   9:    ReadOnly="TRUE"

  10:    Indexed="TRUE"

  11:    Hidden="FALSE"

  12:    Description="Dient zum Gruppieren von zusammengebundener Soft- und Hardware"

  13:    ResultType="Text"

  14:    SourceID="http://schemas.microsoft.com/sharepoint/v3" >

  15:   <FieldRefs>

  16:     <FieldRef Name="Heco_ITAM_AssetNo"/>

  17:     <FieldRef Name="Heco_ITAM_MachineRefId"/>

  18:   </FieldRefs>

  19:   <Formula>=IF(Heco_ITAM_MachineRefId=&quot;&quot;,Heco_ITAM_AssetNo,
Heco_ITAM_MachineRefId)</Formula>

  20: </Field>


  
  1: <Field ID="{9379E5FF-0DDA-4772-91AB-61CAFB9F88E9}"

   2:    Name="Heco_ITAM_OperatingSystem"

   3:    StaticName="Heco_ITAM_OperatingSystem"

   4:    DisplayName="Operating System"

   5:    Type="Calculated"

   6:    ShowInNewForm="TRUE"

   7:    ShowInEditForm="TRUE"

   8:    Group=" heco ITAM"

   9:    ReadOnly="TRUE"

  10:    Indexed="TRUE"

  11:    Description="Dient zum Gruppieren von zusammengebundener Soft- und Hardware"

  12:    ResultType="Text"

  13:    SourceID="http://schemas.microsoft.com/sharepoint/v3" >

  14:   <FieldRefs>

  15:     <FieldRef Name="Heco_ITAM_WindowsVersion"/>

  16:     <FieldRef Name="Heco_ITAM_Architecture"/>

  17:     <FieldRef Name="Heco_ITAM_ServicePack"/>

  18:   </FieldRefs>

  19:   <Formula>=Heco_ITAM_WindowsVersion &amp;&quot; &quot; &amp;
Heco_ITAM_ServicePack &amp;&quot; &quot;&amp; Heco_ITAM_Architecture</Formula>

  20: </Field>

Auf Folgendes sollte man achten:

  • Die FieldRefs benötigen lediglich den Spaltennamen, auf den verweisen wird. Die GUID muss nicht (und darf nicht?) hinterlegt werden
  • Bei den Formeln werden keine Klammern verwendet
  • Um Hochkommata und kaufmännische & abzubilden, müssen die entsprechenden HTML Entities verwendet werden, d.h. &quot; und &amp;. Diese benötigt man z.B. zum Zusammenfügen der Zeichenketten oder zum Prüfen von leeren Eingaben. Eine Tabelle findet ihr hier.
  • Statt Semikolons müssen Kommas verwendet werden, um Funktionsparameter bzw. um Funktionen zu trennen

Projekt: Urlaubspflege mit SharePoint und Export in GDI

 

Seit langem können bei uns die Mitarbeiter ihren Urlaub in SharePoint verwalten. Verwalten bedeutet, dass sie Urlaub eintragen, löschen und übersichtlich anschauen können. Außerdem sehen die Kollegen auf der Startseite übersichtlich, wann wer nicht da ist. Die Eingabemaske sieht wie folgt aus:

image

Und meine eigene Urlaubsübersicht sieht so aus (37,0 sind die Anzahl Tage, die mir dieses Jahr zur Verfügung stehen):

image

Und die rechte Leiste unserer Startseite sieht beispielsweise so aus (Ausschnitt):

image

 

Kurz zum technischen Hintergrund:

Wir verwenden dafür einen eigenen Inhaltstyp Urlaub. Daneben gibt es in unserem Kalender noch die Inhaltstypen ‘Krank’, ‘Außer Haus’ und ‘Im Haus’. Zum Erzeugen der Ausgabe (Spalte Titel), wie man sie auf der Startseite sieht, verwenden wir inzwischen einen Eventhandler, der diese zusammenbaut. Früher war dies ein berechnetes Feld. Wer also nicht programmieren will, muss dies nicht notwendigerweise.

 

Den Geschäftsprozess optimieren und Kosten senken:

Bisher war es so, dass die Buchhaltung am Ende des Monats die Daten in unser Lohnsystem manuell übernommen haben. Das benötigte ca. 1 Manntag. Wenn ihr euch jetzt fragt, ob das dann überhaupt Sinn macht bzw. sich lohnt, dann ist die Antwort ja, da

  • Die Daten müssten so oder so im System erfasst werden
  • Es entfallen die Anfragen der Mitarbeiter nach ihren verbleibenden Urlaubstagen
  • Die Informationen auf der Startseite sind wichtig. Darüber hinaus gibt es noch einen Urlaubskalender, sodass sich z.B. in- und außerhalb der Abteilungen besser abgestimmt werden kann. Kurz: Der Informationsgewinn ist wichtig!

Um den Geschäftsprozess nun vollständig zu automatisieren, benötigten wir noch die Möglichkeit die Daten in unser Lohnsystem von GDI zu exportieren.

Dazu habe ich die Liste der Mitarbeiter, welche gefüllt ist mit Einträgen vom Inhaltstyp ‘heco Mitarbeiter’ um zwei Einträge erweitert:

  • Export zu GDI (interner Feldname: ExportToGDI)
  • Urlaub in Tagen (interner Feldname: HolidayInDays)

Beide Spalten sind vom Typ Ja/Nein. Das sieht dann so aus:

image 

Die Spaltennamen sind selbsterklärend. Nun habe ich mit freundlicher Unterstützung von Olaf Didszun eine Kommandozeilenanwendung geschrieben, die anhand der gesetzten Eigenschaften in der Mitarbeiterliste die Daten aus dem Kalender ausliest und als CSV Datei exportiert (vgl. meinen Artikel dazu hier).

In dem ganzen Prozess gab es zwei besondere Herausforderungen:

  • Monatsübergreifende Einträge
  • Logik für Änderungen nach dem Export

Bei Punkt 1 wäre eine Option gewesen über Gültigkeitsprüfungen (ab SharePoint 2010 für Listen und Spalten out of the box möglich) solche Einträge zu unterbinden. Wir haben uns dagegen entschieden und haben die Logik so programmiert, dass das Programm solche Einträge erkennt und entsprechend monatsgenau abrechnet.

Zu Punkt 2: Das Problem ist, dass es möglich wäre, dass Mitarbeiter im Nachhinein Änderungen vornehmen, die im Vormonat liegen. Das stellt insofern ein Problem dar, als dass wir immer nur am letzten des Monats den Export vornehmen. Das Programm hat und sollte keine Logik dafür enthalten. Deswegen sind wir so Vorgegangen, dass wir eine SharePoint Gruppe angelegt haben, die sich “CalendarAdmins” nennt. Hier sind die Mitarbeiter der Buchhaltung enthalten. Nachdem ein Urlaubseintrag exportiert wurde, wird die Berechtigungsvererbung für das Element gestoppt und es werden alle Berechtigungsgruppen außer die ‘Besucher’ (die haben nur Lesezugriff) und die ‚CalendarAdmins’ entfernt. So ist gewährleistet, dass keine Änderungen nach einem Export mehr durchgeführt werden können, ohne über die die entsprechenden Stellen zu gehen. Der Code hierfür sieht so aus:

   1: urlaubItem.BreakRoleInheritance(true);

   2: SPGroup groupToRemove = urlaubItem.Web.AssociatedOwnerGroup;

   3: urlaubItem.RoleAssignments.Remove(groupToRemove);

   4: groupToRemove = urlaubItem.Web.AssociatedMemberGroup;

   5: urlaubItem.RoleAssignments.Remove(groupToRemove);

   6: urlaubItem.Update();

Typische SharePoint Probleme

Im Rahmen meines Besuchs des Shared Solutions Day haben sich einmal mehr die typischen Schwierigkeiten, die ich auch in früheren Gesprächen auf UserGroups und Konferenzen identifizieren konnte, bestätigt.

Hier die meiner Meinung nach verbreitetsten Probleme:

  • Die Anwender nehmen das neue System nicht an
  • Daraus resultiert auch die Problematik, dass die Systemlandschaft trotz teilweise erheblichen Aufwands und erheblicher Kosten brach liegt… Bis das System eingestampft wird
  • Ein anderer Grund ist sogar in der IT selbst zu finden: Es bleibt bei wenigen Spielereien wie leichtes Anpassen einer Liste und das Erstellen einer Dokumentenbibliothek. Danach wird das System links liegen gelassen.
  • Kein Feature wie es in SharePoint mitgeliefert wird, kann eigentlich genau so out of the box verwendet werden
  • Da jedes Unternehmen typische Geschäftsprozesse wie ‘Besprechung  vereinbaren’ immer in irgendeiner Form um eigene, unternehmensspezifische Modalitäten erweitert hat, lassen sich die vermeintlich einfachen Implementierung dann doch nicht so einfach umsetzen wie geplant. Das sogenannte Eichhörnchen… Demzufolge hat man die Wahl einen größeren Aufwand (meist Programmierung) zu betreiben oder sich mit Einschränkungen abzufinden. Beides ist in der Regel nicht akzeptabel und hinterlässt einen bitteren Beigeschmack, der den anfänglichen Optimismus stark reduziert.
  • Die eigentlich wichtigen Features, um Geschäftsprozesse dann wirklich vollständig (und nicht nur isolierte Teilprozesse) und gewinnbringend umsetzen zu können, stecken in der kostenpflichten Version. Über diesen Tellerrand wollen und können aber gerade kleine und mittelständische Unternehmen nicht hinausschauen, v.a. nicht, wenn man sich gerade erst mit SharePoint auseinandersetzt.

Im folgenden will ich ein paar Lösungsansätze vorschlagen:

Der eigentliche Knackpunkt, bevor man überhaupt zu SharePoint greift, ist, dass man sich klar macht, dass man wie bei jedem anderen System alleine durch die Anschaffung und Einarbeitung Zeit- und Kostenaufwand hat. Lediglich weil die SharePoint Foundation bzw. die Windows SharePoint Services kostenfrei erhältlich sind und weil es gerade in aller Munde ist, heißt dies gewiss nicht, dass nicht wie bei jeder anderen Produkteinführung auch eine entsprechende Planung vorausgehen muss.

Drei wichtige Schritte gehören in jedem Fall zu dieser Planung:

Schritt 1: Holen Sie sich von Anfang an die Fachabteilungen ins Boot. Greifen Sie sich hier v.a. die Abteilungsleiter und IT-affine Mitarbeiter heraus. In der Regel sind diese Personen entscheidend allgemeinen Annahme des neuen Systems.

Schritt 2: Identifizieren Sie gemeinsam einen ausgewählten Satz an einfachen Geschäftsprozessen. In der Regel gibt es fast immer solche, deren Ablauf nicht wirklich störend aber dafür hinderlich sind. Meistens finden sie solche Prozesse, indem sie prüfen, was schon lange auf einer ToDo-Liste steht und immer wieder wegen niedriger Priorität verschoben wird (sogenannte nice-to-have Feature). Idealerweise sollte es auch ein Prozess sein, der viele Mitarbeiter betrifft. Ein Beispiel könnte das Ausdrucken und das manuelle Ausfüllen von Anträgen (z.B. Urlaub) sein, was jeder Verkäufer 1-2 Mal pro Monat machen muss.

Schritt 3: Holen Sie sich einen Mann vom Fach dazu, der längere praktische Erfahrung in diesem Bereich hat. Es reicht keinesfalls aus auf Vorträge zu gehen oder sich durch Online Material zu arbeiten. Vergessen Sie ähnliche Beispiele von Konferenzen und UserGroups, da wie schon erwähnt der Teufel im Detail steckt. In der Regel reicht es völlig aus, wenn man sich einen Tag mit dem SharePoint Consultant zusammensetzt. Stellen Sie klar, dass sie für die Lösungen nur auf die mitgelieferten Hilfsmittel in SharePoint zurückgreifen möchten (Anmerkung: Hin und wieder können vor allem auch kostenfreie Lösungen im Netz ganz hilfreich sein!). Programmieren sollte in diesem frühen Stadium kein Thema sein. Wichtig: Es geht in diesem Gespräch noch nicht um einen detaillierten Plan zur Umsetzung. Lediglich die Machbarkeit und den benötigen Zeitraum gilt es zu evaluieren. Bedenken Sie: Die Kosten für diesen einen Tag sind in jedem Fall geringer als SharePoint aus dem Gedanken heraus aufzusetzen, dass man ja einfach mal das Ganze testen und ein wenig herumspielen will.

Wenn sich nach diesen drei Schritten herausstellt, dass Ihre Anliegen durchaus machbar sind, dann können Sie loslegen. Andernfalls werden Sie mit hoher Wahrscheinlichkeit keinen großen Erfolg bei der SharePoint Integration haben. Halten Sie sich bei der Realisierung an folgende zwei wichtige Regeln:

  • Führen sie immer eine Änderung nach der anderen durch und fangen sie mit den kleinsten davon an
  • Setzen Sie Lösungen für die alltäglichen Geschäftsprozesse um, die das Gros der Mitarbeiter betrifft

Wohlgemerkt halte ich diese zwei Regeln nur für sinnvoll bis eine allgemeine Akzeptanz erreicht bzw. SharePoint eingeführt wurde! Um Ihnen ein paar Beispiele zu nennen, was wir uns dafür herausgegriffen haben:

  • Wir haben die Möglichkeit geschaffen, dass zentral auf der Startseite jeder Mitarbeiter für den heutigen und morgigen Tag einsehen kann, wer an- und wer abwesend ist. Dazu gehört auch, wenn jemand Urlaub hat oder lediglich für ein paar Stunden nicht verfügbar ist.
  • Wichtige Firmennachrichten werden ebenfalls auf der Startseite veröffentlicht. Idealerweise kommen hier mindestens 1-2 neue pro Woche hinzu. Meistens werden die Neuigkeiten durch die Abteilungsleiter eingepflegt. In einem Handelshaus könnten das auch Bekanntmachungen bzgl. der Preisanpassungen sein.
  • Für uns im Handel war ein zentrales Werkzeug der Tourenplan unserer LKWs. Ein Gespräch (vgl. Schritt 1 weiter oben) ergab, dass dieser von allen Verkäufern täglich eingesehen wurde.
  • Tipp-Spiel: Ja, auch solche kleine Dinge können sehr zuträglich sein. Bisher konnte ich für jede EM und WM ein entsprechendes Feature online finden, das ich kostenlos bei uns einbinden konnte. Die Beteiligung an den Tippsielen ist seit Jahren extrem hoch!

Diese 4 Features bewirkten, dass jeder Mitarbeiter mindestens 1x täglich – nämlich morgens – zum Intranet navigierte, um sich einen Überblick für den Tag zu verschaffen. Mit der erwähnten Option die Infos auch für den morgigen Tag einsehen zu können, wurde nach kurzer Zeit dann jeden Abend nochmal hineingeschaut. Nach relativ kurzer Zeit hatten wir so eine vollständige Akzeptanz innerhalb der Belegschaft erreicht. Darauf ließen sich dann weitere Projekte aufsetzen. Hier kann ich vor allem zwei Kernpunkte identifizieren:

  • Dokumentenmanagement, um dem Chaos in der Dateiablage und den öffentlichen Ordnern Herr zu werden
  • Projektmanagement, da es immer und überall Projekte gibt und das Thema an sich seit jeher schwierig und komplex ist

Meines Erachtens nach gibt es jetzt zwei Möglichkeiten wie sie fortfahren können. Das ist vor allem von Ihren Ressourcen und Ihrer IT abhängig. Entweder Sie setzen eines der Themen gleich für Ihren Betrieb um oder – und das ist der bessere Weg – Sie lassen die IT fachinterne Geschäftsprozesse für sich umsetzen, quasi als Prototyp. In der Regel werden Sie hier schon auf eines der oben genannten Themen stoßen. Bei uns war es eine Dokumentationsseite mit allen IT Dokus und eine Inventarliste unserer Soft- und Hardware. Mit diesen Erfahrungen gingen wir die dann die oben genannten Punkte an.

Speziell auf Rückmeldungen zu diesem Artikel würde ich mich besonders freuen. Ich möchte nochmal darauf hinweisen, dass die oben genannten Punkte meine subjektive Wahrnehmung und Meinung wiedergeben. Vergessen Sie auch nicht, dass sich diese Ergebnisse im Rahmen meiner Tätigkeit in einem mittelständischen Handelsbetrieb aus der Edelstahlbranche ergeben haben.

Legacy Code robust machen – Teil 2

Aufbauend auf dem EPK Diagramm meines ersten Beitrags zu dem Thema war der nächste Schritt das Aufsetzen der Architektur. Gemäß dem CCD Prinzip „Entwurf und Implementation überlappen nicht“ (siehe hier) habe ich dazu als erstes die folgenden Kontrakte definiert:

Das Interface IPrintServiceController stellt den Einstiegspunkt für die eigentliche Anwendungslogik des Dienstes dar. Mit StartPrintJobs wird eine dynamische Anzahl (in der Konfiguration definiert) an Prozessen (Stichwort Threads) zum Verarbeiten der Druckjobs gestartet. Die Methode StopPrintJobs ist dafür zuständig diese wieder korrekt zu beenden.

clip_image001[6]

Die den (Arbeiter-)Prozessen zu Grunde liegenden Objekte implementieren das Interface IPrintJobWorker, welches lediglich die öffentliche Methode Print und das Property WorkerName vorgibt. Letzteres ist nur fürs Logging von Interesse, wohingegen die Print-Methode genau das tut, was ihr Name schon sagt: Sie kümmert sich darum einen Druckjob zu verarbeiten, d.h. zu drucken (Anmerkung: Hier wird der VB6 Prozess aufgerufen).

clip_image002[4]

Die Verwaltung der Druckjobs übernimmt die Implementierung des Interface IPrintJobController. Das Interface deklariert folgende Methoden:

  • GetNextJobId: Holt den nächsten Druckauftrag aus der Warteschlange (=> Datenbank)
  • MarkJobState: Die Methode setzt den Zustand eines Druckauftrags, d.h. ist er gerade in der Bearbeitung oder wurde er erfolgreich verarbeitet oder …
  • ProcessIsStillWorking: Da wir unseren alten instabilen VB6 Legacy Code in einem separaten Prozess laufen lassen, muss stetig geprüft werden, ob dieser abgestürzt ist.
  • StopWorking: Über diese Schnittstelle lässt sich der PrintJobController beenden, sodass die Methode GetNextJobId keine Druckjobs mehr zurückgibt

clip_image003[4]

Wichtig: Da es sich um eine parallelisierte Verarbeitung der Druckjobs handelt, muss die konkrete Implementierung des IPrintJobController ein Singleton sein, da sonst z.B. mehrfach der gleiche Druckjob für die Verarbeitung zurückgeliefert werden könnte.

 

Um den gesamten Kontext nochmal formal aufzuzeigen:

Der PrintServiceController startet einen oder mehrere Druckprozesse. Jeder Druckprozess läuft so lange bis dieser wieder vom PrintServiceController beendet wird (siehe Anmerkung unten). Außerdem kümmert sich jeder Prozess selbstständig darum, dass er vom PrintJobController neue Druckaufträge erhält. Ebenso liegt auch die Verantwortung für die Jobverarbeitung, d.h. der Aufruf und die Kontrolle des externen VB6 Prozesses, bei ihm. Der PrintServiceController kümmert sich nur darum den PrintJobWorker zu starten und zu stoppen und der PrintJobController dient allein der prozesssicheren Kommunikation mit der Datenbank. Demzufolge sind die Verantwortlichkeiten klar definiert und sauber getrennt, was der Einhaltung des „Single Responsibility Principle“ Prinzips (siehe hier) entspricht. Außerdem ist die Kohäsion meines Erachtens sehr stark ausgeprägt, was man auch an den Interfaces sehen kann (im Prinzip existieren keine Properties). Dementsprechend ist das CCD Prinzip „tell don’t ask“ (siehe hier) auch eingehalten.

Anmerkung: Eine Unschönheit ist hier, dass aus der Architektur nicht hervorgeht geht wie das Beenden realisiert wird. Das liegt daran, dass dies auf Grund der Implementierung von Threads nicht nötig ist (die Task Parallel Library arbeitet mit CancellationToken). Hierauf will ich nicht weiter eingehen, jedoch wäre das durchaus ein Diskussionspunkt, den man erörtern könnte. Auf der anderen Seite kann ich entgegen halten, dass das Stoppen des PrintJobController völlig ausreichend ist. Und dies wiederum spiegelt sich in der Architektur wieder.

Legacy Code robust machen – Teil 1

Wie in vielen Unternehmen existiert auch bei uns noch jede Menge Legacy Code, der an manchen Stellen nicht weiter stört, in anderen Bereichen aber die Geschäftsprozesse erheblich verlangsamt oder gar lahmlegt. Bei uns sind das v.a. die Druckdienste unseres ERP Systems. Vor etlichen Jahren wurden diese in VB6 geschrieben und es fehlt an so manchem essentiellen Feature, wie z.B. einer adäquaten Fehlerbehandlung oder einem Logging-System. Das fällt besonders ins Gewicht, da der Dienst sehr instabil läuft.

Deswegen haben wir uns jetzt intern entschieden genau diesen Störfaktor auszuräumen. Das Problem ist nur, dass zu viel Code und zu viele Abhängigkeiten darin verstrickt sind. Deshalb gehen wir mit einer anderen Methodik heran: Wir beheben das Symptom, aber nicht die Ursache. Konkret bedeutet das die Implementierung eines auf .NET basierenden Windows Dienstes, welcher den alten Legacy Code steuert. Dazu haben wir die reine Anwendungslogik aus dem alten Dienst herausgelöst und sauber in einer EXE-Datei verpackt, welche sich per Kommandozeile aufrufen lässt.

Der neue .NET Dienst ist nun dafür zuständig die Druckjobs zu verwalten, diese an den alten VB6 Prozess zu übergeben und die Verarbeitung zu überwachen. Das folgende EPK Diagramm gibt den groben Ablauf wieder (von oben nach unten lesen…). Es ist nicht ganz vollständig, enthält aber alle wichtigen Informationen.

 

Druckdienst Prozesskette

 

Wie man sieht, setzen wir auf Parallelisierung. Durch das Starten des Service werden mehrere Threads erzeugt, die das Verarbeiten der Jobs übernehmen. Im weiteren Verlauf des EPKs zeige ich den Prozess allerdings nur anhand eines Threads.

Wird ein Thread gestartet, so versucht er sich zunächst einen Druckjob aus der Datenbank zu holen. Wenn er keinen findet, dann legt sich der Thread für eine beliebig definierte Zeit X (die Information kommt aus der Konfiguration) schlafen und beginnt danach erneut.

Wenn er dann einen Job zum Abarbeiten findet, startet er den VB6 Prozess, indem er die ID des gefunden Druckauftrags an diesen übergibt, woraufhin dieser zu arbeiten anfängt. Nun können 3 Ereignisse eintreten:

  • Der Job wird erfolgreich verarbeitet: In diesem Fall muss der .NET Service nur noch den Job in der Datenbank als erledigt kennzeichnen. Danach kann er von vorne beginnen
  • Der Prozess antwortet nicht, d.h. das Programm „hängt“
  • Der Job wurde vom Prozess nicht erfolgreich verarbeitet: In diesem Fall wurde der Prozess korrekt beendet, allerdings war der Job fehlerhaft

Die beiden letzten Ereignisse drücken zwar unterschiedliche Ergebnisse aus, führen jedoch zum selben Resultat: Der Druckjob wurde nicht erfolgreich verarbeitet. In diesem Fall wird geprüft, wie oft schon versucht wurde diesen Job zu drucken. Ist eine definierte Anzahl x (die Information kommt aus der Konfiguration) an Wiederholungen erreicht, so wird der Job als fehlerhaft in der Datenbank markiert, sodass er nicht erneut von einem Thread abgeholt werden kann. Ist das nicht der Fall, so wird der Druck durch den VB6 Prozess ein weiteres Mal angestoßen.

%d Bloggern gefällt das: