Teammitglieder von Splendit IT-Consulting vor dem Start des vienna night run 2015

Splendit-Team beim vienna night run 2015

Laufen für die gute Sache

Auch in diesem Jahr drehte ein 7-köpfiges Team von Splendit IT-Consulting im Rahmen des vienna night run zugunsten der Hilfsorganisation Licht für die Welt eine Runde um den Wiener Ring. Die Gruppe absolvierte locker die 5 Kilometer lange stimmungsvolle Strecke durch die nächtliche Innenstadt und ließ den Abend anschließend gemeinsam gemütlich ausklingen.

 

Teammitglieder von Splendit IT-Consulting vor dem Start des vienna night run 2015

Das Splendit-Team beim vienna night run 2015

Blogbeitrag über den Stream Editor sed, eingesetzt zum Beispiel zur Analyse von altem Cobol-Code

Praktischer Nutzen von sed

Einleitung

Bei der Analyse von (alten) Quellcode-Dateien können Programme wie sed oder grep von großem Nutzen sein. Beispielsweise kann ein Ordner mit Cobol Quellcode-Dateien auf fehlende „Includes“ geprüft werden (ohne Compiler). Ebenso können externe Aufrufe ohne vorhandenen Quellcode ausfindig gemacht werden.
Die Syntax von sed ist zudem in anderen Programmen wieder zu finden, zum Beispiel in vi oder vim.

sed ist ein Unix-Werkzeug, ein sogenannter „Stream Editor“ und wird verwendet, um Text zu filtern. Oft wird sed für ad hoc Manipulation von Ausgaben anderer Befehle verwendet, um ungewollte Information zu entfernen oder Information zu ergänzen.

Als ein frühes Unix-Standardprogramm (Version 7 Unix, 1979) zeichnet sich sed durch Schnelligkeit und geringen Speicherbedarf aus.
Auch wenn sed auf unterschiedlichen unixoiden Betriebssystemen verfügbar ist, gibt es kleine Versionsunterschiede. So unterscheiden sich die GNU Version und BSD Version in gewissen Bereichen. Die GNU Version unterstützt beispielsweise erweiterte „Reguläre Ausdrücke“.

Blogbeitrag über den Stream Editor sed, eingesetzt zum Beispiel zur Analyse von altem Cobol-Code

Programme wie sed erleichtern die Analyse von Cobol-Dokumenten und anderen alten Quellcode-Dateien

Funktionsweise und Beispiele

Die Beispiele in diesem Abschnitt basieren auf GNU sed (4.2.2).

Eingaben werden mit sed zeilenweise eingelesen und bearbeitet, wobei pro Zeile eine oder mehrere Operationen ausgeführt werden und ein modifizierter Text zurückgegeben wird. Dabei können beispielsweise Teile ersetzt, hinzugefügt oder gelöscht werden – einmalig oder mehrfach.

Die vermutlich grundlegendste Anwendung ist eine einfache Ersetzung beziehungsweise „Substitution“.

Beispiel 1

echo "Hallo Splendit! Hallo Splendit!" | sed s/Splendit/Welt/

In diesem Beispiel wird mittels echo-Befehl eine Eingabe an sed weitergeleitet („Hallo Splendit! Hallo Splendit!“). Auf diese Eingabe wendet sed eine einmalige Substitution von „Splendit“ durch „Welt“ an. Die Ausgabe ist demnach: „Hallo Welt! Hallo Splendit!“

Eine Substitution hat das Muster „s/Suchmuster/Ersetzung/“. Nach dem letzten Schrägstrich wir spezifiziert, auf welche Vorkommnisse des Suchmusters die Ersetzung angewandt werden soll. Steht hier nichts, wird nur das erste Vorkommnis pro Zeile ersetzt.

Beispiel 2

# Substitution aller Vorkommnisse von "a" durch "b" (global):
sed s/a/b/g

# Substitution des 3. Vorkommnisses:
sed s/a/b/3

# Substitution aller Vorkommnisse nach dem 3. Vorkommnis:
sed s/a/b/3g

Interessant wird es dann, wenn „Reguläre Ausdrücke“ dazu kommen.

Beispiel: Ersetzung aller Abkürzungen

Dieser Abschnitt soll einige interessante Konzepte von sed anhand eines Beispiels näher bringen.

Ein Eingabetext beinhaltet diverse (zum Teil unverständliche) Abkürzungen. Alle Abkürzungen sollen anhand einer Liste durch die entsprechenden ausgeschriebenen Wörter ersetzt werden.

Eingabetext

Der Eingabetext wird für dieses Beispiel in der Textdatei „eingabe.txt“ gespeichert. Der beispielhafte Inhalt ist wie folgt:

Sg. Damen und Herren!

Bei der Wohnungssuche gibt es ggf. sehr viele Abkürzungen, die nicht unbedingt verständlich sind, z.B. KM, NK, HZK oder KT.

Wäre es nicht besser, wenn diese Abkürzungen ausgeschrieben wären?

Mfg,

Splendit

Liste der Abkürzungen

Die Abkürzungen werden in diesem Beispiel in einer Textdatei namens „abkuerzungen.txt“ mit dem folgenden Inhalt gespeichert:

Sg.=Sehr geehrte
ggf.=gegebenenfalls
z.B.=zum Beispiel
KM=Kaltmiete
NK=Nebenkosten
HZK=Heizkosten
KT=Kaution
Mfg=Mit freundlichen Grüßen

Schritt 1

Die Liste der Abkürzungen muss in ein für sed übliches Format gebracht werden, also in das bekannte „s/a/b/“-Format. Dies wird natürlich auch mit sed bewerkstelligt.

Es muss vorne, in der Mitte und hinten ein Schrägstrich („/“) hinzufügt werden.

Hier gibt es mehrere Dinge anzumerken:

  • Der Schrägstrich hat standardmäßig eine besondere Bedeutung, weil er ein Steuerzeichen ist. Diese Bedeutung kann mit einem vorgestellten Backslash aufgehoben („escaped“) werden: „\/
    Das Verhalten kann jedoch vereinfacht werden, indem wir „/“ als Steuerzeichen durch ein anderes ersetzen, denn sed erlaubt das auf einfache Weise. So kann einfach „sed s_a_b_“ statt „sed s/a/b/“ geschrieben werden. Das erste Steuerzeichen im Muster bestimmt hier das gültige Steuerzeichen für den Rest des Musters. Es kann aber nicht innerhalb eines Musters gewechselt werden. 
Typische alternative Steuerzeichen sind hier: „_“, „#“ oder „,
  • Auf den Anfang und das Ende einer Zeichenkette kann – regex konform – mit „^“ und „$“ zugegriffen werden.
  • Mehrere Suchmuster können mittels „(a|b|c)“ auf einmal angewandt werden. Jedoch müssen standardmäßig sowohl Klammern („(“ und „)“), als auch das Verkettungszeichen („|“) escaped werden. 
Auch das kann vereinfacht werden, indem der Parameter „-r“ übergeben wird (erfordert GNU sed). Dadurch wird erweiterte regex-Unterstützung aktiviert, was dazu führt, dass die Zeichen nicht mehr escaped werden müssen.

Diese Punkte kombiniert ergeben den folgenden Befehl:

sed -r 's_(^|=|$)_/_g' abkuerzungen.txt

Die Ausgabe davon:

/Sg./Sehr geehrte/
/ggf./gegebenenfalls/
/z.B./zum Beispiel/
/KM/Kaltmiete/
/NK/Nebenkosten/
/HZK/Heizkosten/
/KT/Kaution/
/MfG/Mit freundlichen Grüßen/

Die einfachen Anführungszeichen müssen in diesem Fall vorhanden sein, um das Muster zu begrenzen.

Dieser sed Befehl ersetzt den Anfang (vor dem ersten Zeichen), das „=“ und das Ende (nach dem letzten Zeichen) durch einen Schrägstrich. Das „g“ ist hier natürlich notwendig, sonst würde nur der erste Treffer ersetzt werden – in diesem Fall wäre das also immer nur der Anfang.

Schritt 2

Weiters ist es notwendig, ein „s“ an den Anfang und ein „g“ an das Ende zu setzen.

sed erlaubt es, mehrere Muster hintereinander auszuführen. Die einzelnen Muster müssen hierbei durch einen Strichpunkt getrennt werden.

Beispiel

sed 's/a/b/g; s/c/d/g'

Somit kann der sed Befehl von Schritt 1 einfach erweitert werden:

sed -r 's_(^|=|$)_/_g;s/^/s/;  s#$#g#' abkuerzungen.txt

Wie hier zu sehen ist, kann das Steuerzeichen für jedes Muster gewechselt werden, falls erwünscht. Außerdem spielt es keine Rolle, wie viele Leerzeichen vor oder nach dem Strichpunkt sind.

Hier die Ausgabe:

s/Sg./Sehr geehrte/g
s/ggf./gegebenenfalls/g
s/z.B./zum Beispiel/g
s/KM/Kaltmiete/g
s/NK/Nebenkosten/g
s/HZK/Heizkosten/g
s/KT/Kaution/g
s/Mfg/Mit freundlichen Grüßen/g

Schritt 3

Der nächste Schritt besteht darin, alle vorher erstellten Muster auf den Eingabetext anzuwenden.

sed bietet hier zwei interessante Möglichkeiten:

1. Muster können aus einer Datei ausgelesen werden.
2. Muster können aus einem Script ausgelesen werden.

Beide Varianten sind sehr ähnlich, sollen jedoch hier separat erklärt werden.

Variante 1: Muster wird aus einer Datei ausgelesen

Hierfür wird die Ausgabe von Schritt 2 in einer Datei benötigt. Selbstverständlich können wir einerseits einfach eine gewöhnliche Weiterleitung der Ausgabe vornehmen:

sed -r 's_(^|=|$)_/_g; s/^/s/; s/$/g/' abkuerzungen.txt > abkuerzungen.sed

Andererseits ist es mit sed auch möglich, gleich „in-place“ zu editieren, also in der Eingabedatei Änderungen vorzunehmen:

sed -i.bak -r 's_(^|=|$)_/_g; s/^/s/; s/$/g/' abkuerzungen.txt

Anmerkung

-i.bak erstellt eine Sicherheitskopie der Originaldatei mit der (frei wählbaren) Endung „.bak“. Wird .bak weggelassen und nur -i geschrieben, wird diese Sicherheitskopie nicht erstellt.

Heißt die Muster-Datei nun „abkuerzungen.sed“, sieht der komplette Befehl wie folgt aus:

sed -f abkuerzungen.sed eingabe.txt

-f bewirkt hier, dass sed eine Datei mit Mustern als Eingabe verwendet.

Variante 2: Muster wird als Script angegeben

Der grundlegende Unterschied ist hier, dass keine Muster-Datei erstellt werden muss, sondern, dass die Ausgabe des sed Befehls von Schritt 2 direkt als Muster verwendet wird. Es werden also zwei geschachtelte sed Befehle ausgeführt.

Um den inneren sed Befehl zu begrenzen, muss sich der Befehl zwischen "$( und )" befinden. Zudem muss der äußere sed Befehl den Parameter -e verwenden, der ein Script als Eingabe spezifiziert.

Ergebnis von Variante 2:

sed -e "$(sed 's_\(^\|=\|$\)_/_g; s/^/s/; s/$/g/' abkuerzungen.txt)" eingabe.txt

Die Ausgabe von Variante 1 und Variante 2 ist jeweils wie folgt:

Sehr geehrte Damen und Herren!

Bei der Wohnungssuche gibt es gegebenenfalls sehr viele Abkürzungen, die nicht unbedingt verständlich sind, zum Beispiel: Kaltmiete, Nebenkosten, Heizkosten oder Kaution.

Wäre es nicht besser, wenn diese Abkürzungen ausgeschrieben wären?

Mit freundlichen Grüßen,
Splendit

Schlusswort

sed ist ein mächtiges Tool, um Streams zu manipulieren. Das gezeigte Beispiel sollte hier einen kleinen Einblick in die Möglichkeiten von sed vermittelt haben.
Natürlich könnten bei diesem Beispiel noch diverse Verbesserungen gemacht werden. So wird beispielsweise nicht nur nach ganzen Worten gesucht, Klein- und Großschreibung wird nicht beachtet und die korrekte Adjektivdeklination wird schon gar nicht verwendet (Beispiel: „Sg. Herr Müller“ wird zu „Sehr geehrte Herr Müller“).
Es sollte sich dennoch anhand des Beispiels erahnen lassen, wie hilfreich sed sein kann. Vor allem in Anbetracht der Erweiterbarkeit des gezeigten Beispiels.

Alle Beispiele sind auf GitHub zu finden:
https://github.com/Splendit/sed-example

Für Interessierte empfiehlt sich zudem die folgende Seite:
http://www.grymoire.com/Unix/Sed.html

Außerdem sind die Wikipedia Einträge sowohl auf Deutsch, als auch auf Englisch sehr empfehlenswert:
https://de.wikipedia.org/wiki/Sed_(Unix)
https://en.wikipedia.org/wiki/Sed

Abbildung 2: MVC in JSF

MVC Pattern und die Umsetzung in verschiedenen Frameworks

In diesem Artikel wird die Umsetzung des MVC Patterns (Model View Controller) – eines der bekanntesten Architekturmuster – in zwei unterschiedlichen Frameworks erläutert. Dabei werden folgende Frameworks betrachtet:

  • JSF (JavaServer Faces), ein Framework zur Entwicklung von grafischen Benutzeroberflächen für Webapplikationen
  • AngularJS + Java Enterprise Edition (Java EE), ein Open Source-Framework von Google, mit dem Entwickler in HTML und JavaScript Single Page Webanwendungen nach einem MVC (Model View Controller)-Muster erstellen können.

Im Internet gibt es unzählige Beiträge, die diese Technologien miteinander vergleichen. Dieser Artikel konzentriert sich hauptsächlich auf die Umsetzung des MVC Patterns. Im nächsten Unterkapitel wird das MVC Pattern erklärt. In Kapitel 2 und 3 wird auf die Umsetzung des MVC Patterns in den verschiedenen Technologien eingegangen und die Nachteile bei der Umsetzung werden aufgezeigt.

1. MVC Pattern

Das MVC Pattern ist ein Architekturmuster zur Strukturierung von Applikationen in drei Schichten: Daten (Model), deren Darstellung (View) und die Interaktion mit dem Benutzer (Controller). Es gibt verschiedene visuelle Darstellungen des MVC Pattern. Die Darstellungen unterscheiden sich zumeist minimal, aber doch entscheidend für die Interaktion zwischen den verschiedenen Schichten. Abbildung 1 zeigt eine mögliche Darstellung, die in diesem Artikel als Referenz herangezogen wird.

 

Abbildung 1: Interaktion zwischen den Schichten

Abbildung 1: Interaktion zwischen den Schichten

Model

Im Model sind die darzustellenden Daten enthalten. Es nimmt Informationen vom Controller entgegen und speichert/manipuliert diese. Das Model enthält zumeist die Businesslogik oder Teile davon.

View

Ist für die Darstellung der Daten aus dem Model zuständig und leitet Benutzerinteraktionen an den Controller weiter.

Controller

Der Controller nimmt Informationen von der View entgegen und bearbeitet diese entsprechend weiter. Zumeist werden die Informationen an die Model-Schicht weitergereicht. Zu Ausnahmen kann es zum Beispiel kommen, wenn Teile der Businesslogik in der Controller-Schicht liegen (zum Beispiel die Validierung von Benutzereingaben).

2. Umsetzung des MVC Patterns in JavaServer Faces (JSF)

In JavaServer Faces (JSF), Teil des JavaEE Frameworks, sind die Schichten auf den ersten Blick nicht klar erkennbar. Die Abgrenzung zwischen den Schichten ist nicht immer eindeutig und liegt auch im Auge des Betrachters. Abbildung 2 zeigt die Umsetzung des MVC Patterns in JSF.

Abbildung 2: MVC in JSF

Abbildung 2: MVC in JSF

Model

Den Einstiegspunkt in die Model-Schicht stellen die Backing-Beans dar. In der Model-Schicht ist die Businesslogik mit Services, Data Access Objects (DAOs) und Entities.

View

Die View-Schicht besteht ausschließlich aus den in HTML eingebetteten JSF-Tags. Die JSF-Tags werden in normale HTML Tags umgewandelt und dem Aufrufer übermittelt.

Controller

Der Controller ist im Framework eingebettet. Fälschlicherweise werden die Backing-Beans oft als das Steuerelement bezeichnet. Das liegt vermutlich daran, dass die Beans zumeist für den Entwickler als der serverseitige Einstiegspunkt gesehen werden. Den eigentlichen Controller bildet aber das FacesServlet. Das FacesServlet nimmt Anfragen entgegen und leitet den JSF-Lebenszyklus für die einzelnen Anfragen.

MVC Schwächen in JSF

  • Der Controller ist im Framework verankert.
  • Die View-Schicht und die Model-Schicht sind über die Backing-Beans eng verzahnt, sodass die View nicht durch eine andere Komponente ersetzt werden kann, ohne dabei die Model-Schicht zu ändern.

3. Umsetzung des MVC Patterns in AngularJS + JavaEE

Als Alternative zu JSF existieren zahlreiche Technologien, die auf eine lose Kopplung zwischen der View- und der Controller-Schicht basieren. In diesem Abschnitt wird die Umsetzung des MVC Patterns in AngularJS beschrieben. AngularJS ist eine JavaScript Bibliothek, die für SPAs (Single Page Applications) verwendet wird. In Verbindung mit JavaEE können lose gekoppelte Applikationen entwickelt werden.

Hier ist zu beachten, dass MVC in zwei verschiedenen Ebenen betrachtet werden muss, da alleine mit AngularJS in Verbindung mit HTML nach dem MVVM-Pattern (Model View ViewModel) Single Page Applications erstellt werden können. MVVM ist eine Variation des MVC-Patterns zur Trennung der View von der Logik der Benutzerschnittstelle. Abbildung 3 zeigt die Interaktionen. Die ViewModel-Schicht ist das Bindeglied zwischen der View und dem Model (data binding). Sie tauscht Daten aus und ruft JavaScript Methoden auf.

Im Folgenden wird auf die MVC Komponenten eingegangen. Die ViewModel-Schicht des MVVM-Patterns wird dabei außer Acht gelassen, da sie für die Implementierung des AngularJS-Frameworks dient.

Abbildung 3: MVC in AngularJS mit JavaEE

Abbildung 3: MVC in AngularJS mit JavaEE

Model

Die Businesslogik wird (fast nur) mit JavaEE realisiert. Die Benutzereingaben werden wie schon bereits erwähnt an JavaScript-Variablen gebunden. Diese können clientseitig validiert oder ohne bearbeitet zu werden an den JavaEE-Server geschickt werden. Es kann damit zwischen zwei Model-Schichten unterschieden werden. Wobei die eine Schicht hauptsächlich für die Entgegennahme der Benutzereingaben zuständig ist, die über HTTP an den JavaEE-Server geschickt werden, wohingegen die zweite Schicht vom Controller angesprochen wird und die erwünschten Manipulationen/Operationen durchführt.

View

Die View besteht aus ganz normalem HTML, das mit AngularJS-Direktiven erweitert wird. Zu der View müssen die Aufrufe in JavaScript programmiert werden. Durch Data-Binding werden die Benutzereingaben an JavaScript-Variablen gebunden. Die Ergebnisse der Anfragen werden in Callbacks aufgearbeitet und an die View über die ViewModel-Schicht übermittelt. In Abbildung 4 wird mit JSON.stringify($scope.data) das vom Backend gelieferte Ergebnis in ein Array ($scope.products) geschrieben, über das durch AngularJS-Direktiven im HTML Code durchiteriert werden kann (Abbildungen 4 und 5).

Abbildung 4: REST Call

Abbildung 4: REST Call

Abbildung 5: Produkt-Iteration

Abbildung 5: Produkt-Iteration

Controller

Die Controller-Schicht wird über eine REST Schnittstelle angesprochen. Der Controller kann als ein Servlet implementiert werden. Daneben gibt es zahlreiche andere Frameworks für die Implementierung von RESTful Web Services in Java, zum Beispiel JAX-RS. Für die Übertragung der Daten zwischen dem Backend und der View können verschiedene Datenformate (XML, JSON, CSV, HTML) verwendet werden. Da AngularJS eine JavaScript Bibliothek ist, ist es naheliegend JSON zu verwenden. Der Controller übernimmt die Validierung von Benutzereingaben, wertet diese aus und leitet sie an die Model-Schicht weiter.

Nachdem eine Anfrage in der Businesslogik abgearbeitet wurde, wird das Ergebnis über den Controller an die View zurückgegeben. Das Format für die Rückgabewerte kann frei gewählt werden, aber es ist zu empfehlen das gleiche Format zu verwenden wie für die eingehenden Anfragen.

4. MVC-Schwächen in AngularJS + JavaEE

Die Trennung der Schichten ist auf Grund der Tatsache, dass AngularJS ohne JavaEE bereits nach dem MVVM-Pattern entwickelt wurde, nicht ganz eindeutig.

5. Fazit

Über die Vor- und Nachteile der beiden Technologien (bzw. Architekturen) gibt es unzählige Berichte und Gegenüberstellungen im Internet. Beide Technologien weisen Schwächen in der Umsetzung des MVC-Patterns auf. JSF zeigt deutliche Schwächen bezüglich loser Kopplung zwischen den Schichten. Oberflächen-Entwickler müssen sich gut mit den Backend-Entwicklern absprechen, um nicht aneinander vorbei zu entwickeln. Da liegt der große Vorteil von AngularJS. Die REST Schnittstelle unterstützt eine lose Kopplung zwischen der View- und Controller Schicht.

Entscheidend für die Auswahl eines Frameworks ist natürlich nicht nur die Unterstützung von erwünschten Architekturmustern. Viel entscheidender sind die Funktionen, die die Frameworks anbieten und welche für die zu entwickelnde Software notwendig sind.

 

Abstraktion von Datenbankoperationen mit Spring und Hibernate erhöht die Wartbarkeit und vereinfacht die weitere Codeerstellung

CRUD mit Spring 3.2 und Hibernate 4.2

Abstraktion von Datenbankoperationen mit Spring und Hibernate erhöht die Wartbarkeit und vereinfacht die weitere Codeerstellung

Die Java-Frameworks Spring und Hibernate werden bei der Applikationsentwicklung oft gemeinsam verwendet. Spring bietet mittlerweile zwar eine gute Hibernate-Anbindung, das Zusammenspiel ist aber nicht immer einfach und klar; nicht jede Version von Spring funktioniert mit jeder Version von Hibernate. Dennoch bietet die Kombination der beiden Frameworks interessante Optionen. In diesem Beitrag zeigen wir eine neue Möglichkeit, den Datenbankzugriff zu abstrahieren, wozu wir Spring 3.2 und Hibernate 4.2 kombiniert haben.

Datenbankzugriffe werden in Java über die Datenbankschnittstelle JDBC (Java Database Connectivity) durchgeführt. Diese Methode ist einfach, jedoch fällt eine Abstraktion beziehungsweise das Zusammenfassen von ähnlichen Methoden schwer. Auch muss die Verbindung zur Datenbank manuell verwaltet werden.

Eine einfachere Methode bietet Spring mit dem JDBC-Template und der automatischen Verwaltung der Verbindung zur Datenbank. Das Framework kümmert sich selbstständig um die Verbindung zur Datenbank, man muss nur die SQL-Statements selber schreiben. Spring bringt außerdem noch eine Verwaltung von Beans und damit auch Dependency-Injection mit.

Die Verwendung von Hibernate führt eine weitere Abstraktionsebene ein, welche ein Mapping von Modell-Klassen direkt auf Tabellen erlaubt. Ein weiterer Effekt dieses Mappings ist, dass Datenbank-Constraints auf Modell-Klassen abgebildet werden können, dadurch ist eine Prüfung der Constraints zur Laufzeit, noch vor eines Commits zur Datenbank, möglich.

Durch Verwendung von Generics, Interfaces und abstrakten Klassen im Zusammenspiel mit Spring und Hibernate lassen sich CRUD-Zugriffe auf die Datenbank sehr gut abstrahieren, sodass nur eine allgemeine Klasse diese erledigt und lediglich spezielle Aggregationen eindeutig zu implementieren sind.

Im folgenden Beispiel soll Spring die Verwaltung der Datenzugriffsschicht übernehmen und Hibernate den eigentlichen Datenzugriff. Für Hibernate wird ein Primary Key benötigt, um Tupel eindeutig identifizieren zu können. Dieser Key wird im folgenden Beispiel als ID in den Klassen abgebildet. Diese ID wird in der Basis-Modell-Klasse über Typ-Parameter für alle weiteren Modell-Klassen definiert
public abstract class AbstractEntityObject<T extends Serializable>
Die Modell-Klasse Employee muss diese ID nicht mehr implementieren, sondern nur noch AbstractEntityObject erweitern.
public class Employee extends AbstractEntityObject<Long>
Mit der Abstraktion der ID ergibt sich, dass auch ein Basis-DAO erstellt werden kann.
public interface InterfaceBasicDaoSupport<E extends AbstractEntityObject<I>, I extends Serializable> {
  public E findById(I id);
  public void update(E e);
  public List<E> findAll();
  public void saveOrUpdate(E e);
  public void delete(E e);
  public void merge(E e);
  public List<E> findByCriteria(Criterion criterion);
}

public abstract class AbstractBasicDaoSupport<E extends AbstractEntityObject<I>, I extends Serializable> implements InterfaceBasicDaoSupport<E, I> {
  protected Class<E> entityClass;
  protected AbstractBasicDaoSupport(Class<E> entityClass) {
    this.entityClass = entityClass;
  }
  public E findById(I id) {...}
  public void update(E e) {...}
  public List<E> findAll() {...}
  public void saveOrUpdate(E e) {...}
  public void delete(E e) {...}
  public void merge(E e) {...}
  public List<E> findByCriteria(Criterion criterion) {...}
}

Es werden auch alle CRUD-Methoden im AbstractBasicDaoSupport definiert. Dadurch ergibt sich ein fast leere Implementierung für den Zugriff auf Employee.
public interface InterfaceEmployeeDao extends InterfaceBasicDaoSupport<Employee, Long> {
}

public class EmployeeDao extends AbstractBasicDaoSupport<Employee, Long> implements InterfaceEmployeeDao {
  public EmployeeDao() {
    super(Employee.class);
  }
}

Folglich kann auch ein Basis-Service erstellt werden.
public interface InterfaceBasicService<E extends AbstractEntityObject<I>, I extends Serializable, D extends InterfaceBasicDaoSupport<E, I>> {
  public E findById(I id);
  public void update(E e);
  public List<E> findAll();
  public void save(E e);
  public void delete(E e);
}

public abstract class AbstractBasicService<E extends AbstractEntityObject<I>, I extends Serializable, D extends InterfaceBasicDaoSupport<E, I>> {
  public E findById(I id) {...}
  public void update(E e) {...}
  public List<E> findAll() {...}
  public void save(E e) {...}
  public void delete(E e) {...}
}

Das Service für Employee wird durch die Abstraktion auch zu einer sehr kleinen Klasse.
public interface InterfaceEmployeeService extends InterfaceBasicService<Employee, Long, InterfaceEmployeeDao> {
}

public class EmployeeService extends AbstractBasicService<Employee, Long, InterfaceEmployeeDao>{
}

Im DAO und Service müssen nur noch spezielle Methoden implementiert werden, die CRUD-Funktionalität ist schon durch AbstractBasicDaoSupport und AbstractBasicService abgedeckt.

Durch die Kombination von Spring und Hibernate ist der Code besser wartbar, und man spart sich bei der Code-Erstellung Zeit, da die CRUD-Funktionen nur einmal definiert werden. Man sollte jedoch beachten, dass im Fall eines Fehlers bei der Implementierung die Fehlersuche doch um einiges schwieriger ist.

Das komplette Beispiel findet man bei GitHub unter:
https://github.com/Splendit/hibernate-example

Versionierungssoftware im Einsatz 2014: Subversion 48%, Git 38%, CVS 9%, Mercurial 3%, Bazaar 2%

Teamarbeit unterstützen mit Versionierung

Versionierung als Mittel zur Qualitätsverbesserung in der Softwareentwicklung

Versionierung ist mittlerweile ein zentraler Begriff, wenn es um verteiltes Arbeiten an Software-Code, kooperatives Entwickeln und effektives Programmieren geht. Da die Thematik „Was ist Versionsverwaltung?“ schon zur Genüge abgehandelt wurde, gehe ich gleich zum Kern dieses Beitrags weiter: Auswahl und Einsatz eines geeigneten Versionierungs-Tools.

Git und SVN teilen sich den Großteil des Markts

Die Wahl des richtigen Werkzeugs zur Versionierung des Software- Entwicklungsprozesses ist sehr wichtig, da ein geeignetes Tool die Kommunikation im Team, die Distribution von Code und die Beurteilung von neuem Code verbessern kann. Wenn ein Team entsprechend eingespielt ist, kann es mit Hilfe der Versionierung viele Team-dynamische Prozesse entschlacken, um Zeit für andere Herausforderungen frei zu legen.
Die Hauptvertreter sind die Open Source-Tools Git und Apache Subversion (SVN) (siehe Abbildung 1).

Versionierungssoftware im Einsatz 2014: Subversion 48%, Git 38%, CVS 9%, Mercurial 3%, Bazaar 2%

Abbildung 1: Die Open Source-Tools Git und Subversion (SVN) sind die Hauptvertreter im Markt für Versionierungs-Software

Andere Software-Tools kommen da nicht heran: Bazaar und Mercurial haben mit Markanteilen von 2 beziehungsweise 3 Prozent kaum eine Bedeutung. Von Concurrent Versions System (CVS), das 2014 einen Anteil von 9 Prozent hatte, wurde die Entwicklung mittlerweile komplett eingestellt.

Git und SVN teilen sich den Markt im Wesentlichen untereinander auf, wobei SVN derzeit die Oberhand hat, Git in den letzten Jahren aber an Boden gewonnen hat (siehe Abbildung 2).

 

Git und Subversion (SVN) sind die populärsten Versionierungstools. Versionierungs-Software hilft, die Qualität in der Software-Entwicklung zu steigern

Abbildung 2: Die populärsten Versionierungs-Tools im direkten Vergleich: SVN führt, Git holt auf

SVN ist sehr gut geeignet für Projekte, bei denen die Anzahl an mitwirkenden Personen minimal ist – optimal, wenn ein Entwickler alleine an einem Projekt arbeitet. Git zeigt seine Stärken beim Zusammenarbeiten vieler Parteien und kann durch das geschickte Nutzen der Strukturen wie Branches (Erstellen neuer Entwicklungszweige) das Projektmanagement wesentlich erleichtern. Jedoch bringt Git dadurch eine erhöhte Einstiegshürde mit sich: Es ist schlicht und ergreifend mehr Lernaufwand, bis man die Software effektiv einsetzen kann. Dafür erhält man ein mächtiges Tool, welches die aktuellen Anforderungen an eine Versionierungssoftware umsetzt.

Tipps für den Einsatz im Team

Auf die Art wie Git und SVN technisch einzusetzen sind, gehe ich hier nicht weiter ein, da es für diesen Aspekt mehr als genug Beschreibungen gibt, gute Quellen sind etwa https://www.atlassian.com/git/ oder http://git-scm.com/. Vielmehr steht in diesem Beitrag der Einsatz von Versionierungssoftware im Team im Fokus. Der wichtigste Punkt, über den sich alle Beteiligten einig sein müssen, ist, dass man lernen muss, mit der Software umzugehen. Entsprechend sollte man genügend Zeit einplanen, um sich mit den Funktionen auseinander zu setzen. Nur dann ist der effektive Einsatz gewährleistet.
Empfehlenswert ist es auch, einen Git- beziehungsweise SVN-Master zu ernennen – eine Person, die sich weit über die Grundlagen mit der Versionsverwaltungssoftware auseinander setzt, um anderen im Team mit Rat zur Seite zu stehen, falls etwas nicht entsprechend funktioniert.
Als letzten Punkt möchte ich noch ansprechen, dass in Betracht gezogen werden sollte, Guidelines für die Benutzung einer Versionsverwaltungssoftware zu erstellen. Damit wird eine unternehmensweit einheitliche Benutzung gewährleistet, und neuen Projektmitarbeitern fällt die Verwendung der Versionierungssoftware leichter.

Fazit

Der richtige Einsatz von Versionierungssoftware bringt einige Vorteile. Er:
• fördert Kooperation im Team
• erhöht die Code-Verfügbarkeit
• erleichtert Code Reviews
• verbessert die Dokumentation der Evolution des Codes

Quellennachweis

Abbildung 1:
https://www.openhub.net/repositories/compare
Abbildung 2:
http://programmers.stackexchange.com/questions/136079/are-there-any-statistics-that-show-the-popularity-of-git-versus-svn

Influence on execution time when omitting auto-boxing

Parallel Stream Processing in Java 8

When does the use of parallel stream processing make sense

Although the implementation of parallel data processing isn’t a difficult task anymore, the use of it is not always profitable. The main reason is that the processing time isn’t always lower when implementing data processing parallelly. According to small examples in [4], [3] and experiments in the context of this work, many situation can lead even to a much higher execution time when using parallel streams, than using them in a sequential way. It has to be noted that parallel streams have a much higher overhead compared to sequential ones. Coordinating, splitting and combining are mandatory and time consuming jobs when dealing with parallel streams. But these are not the only reasons why parallel stream processing could behave worse than sequential ones. So the question is: when to use parallel data processing, and when to use streams with sequential access or even use external iteration? The following list should give some indicators which influence the execution time when using parallel stream [3] [1].

1. Number of CPUs respectively cores

2. Amount of data (N – Number of items)

3. Complexity of the task (Q – Cost): A higher value of N as well as a higher value of Q leads to a higher chance of a better performance when using a parallel stream.

4. Kind of operation: Some methods will naturally perform worse in parallel mode than on a sequential stream. Such operations like findFirst() or limit often rely on the order of the stream and so they are expensive in parallel mode. Others like findAny() or map() will perform much better with parallel streams because it isn’t constrained to operate in the encountered order.

5. Underlying data structure: For example ArrayList can be split with much less effort than a LinkedList because the ArrayList can be divided without traversing the whole collection, as it is necessary for linked data structures (see performance experiment in the next section).

6. Boxing and unboxing: Boxing (or Autoboxing) is the automatic conversion that the Java compiler accomplished between a primitive type and the corresponding object wrapper class, for example between the primitive type int and its corresponding class Integer. This operation can influence the costs of processing of streams in a very bad way. The solution for this problem when working with primitive values is to use also primitive streams which comes with Java 8. These primitive streams are represented in Java 8 by the classes IntStreamDoubleStream and LongStream. By using these classes instead of using for example Stream, the additional and expensive step of boxing can be omitted.

The above enumeration outlines how many aspects can influence the performance of parallel stream processing. They all have to be considered when making decisions between sequential and parallel processing. Although they give good indicators only more experience in dealing with streams and parallel streams can prevent the programmer of picking the wrong one. So in case of having doubt about the best solution it is advisable to measure the time-effort of the two approaches and compare them. Since the change between the sequential and the parallel version of stream processing is an easy task by only substitute a method name like stream() to parallelStream() the effort of testing which processing method behaves faster, is low.

Test scenarios for finding out when to use parallel data processing and when to use streams with sequential access in Java 8

Table 1.1 Overview of the following test scenarios

Quantitative Comparison between external iteration, sequential and parallel stream processing

This section should give an idea of how the difference factors (compare with the enumeration of the previous section) like amount of data, complexity of task/operation, kind of operation, Boxing and the the underlying data structure influence the execution time. Therefore four different test scenarios should measure how the execution time changes by influencing these factors. So the execution time in milliseconds acts as the benchmark. Table 1.1 summarizes the different test scenarios and emphasizes the test settings roughly.

Test Description

Test Settings: According to table 1.1, the first test scenario Test A demonstrates an example from the article ’Iterating over collections in Java 8’ [2] executed on a 64 Bit Windows system with a dual-core Pentium processor and 4 GB RAM, while the following three test scenarios (Test B – Test D) are own designed tests executed on a 64 Bit Mac OSX system with a Intel i5 – 2,5 GHz processor (two cores) and 8 GB RAM.

Measuring Approach: To measure the execution time, one and the same test scenario was executed 100 times. The duration of each iteration was measured in nanoseconds, converted to milliseconds and averaged over all 100 iterations (see listing 1.1). This is how the values in the result tables were calculated. Since for every single test scenario (like an external iteration of an ArrayList or a parallel stream processing of an ArrayList), a new application was launched, the JVM warm up phase was not taken into account, because the prerequisites were the same for all of them. This fact ensures the comparability of the results. To demonstrate also the impact of the underlying data structure when traversing collections, the tests ’A’, ’B’ and ’C’ were executed for the most common ones, that are LinkedListArrayListHashSetLinkedHashSet and TreeSet.

One and the same software test scenario was executed 100 times

Listing 1.1 General Measurement Approach

Result Representation: The results of the test scenario are represented by a table with the following columns: 

Collection type: Underlying type of data structure on which the test scenario is executed. 
External iteration (A): Execution time in milliseconds for processing the collection with an external iteration approach. 
Sequential stream (B): Execution time in ms for processing the collection with a sequential stream approach. 
Parallel stream (C): Execution time in ms for processing the collection with a parallel stream approach.
Relative change (C/A): Shows the relative change of execution time between external iteration and parallel stream processing. A positive percentage value indicates a higher execution time for parallel processing in contrast to external iteration, while a negative one indicates a faster execution time. 
Relative change (C/B): Similar to the previous point, but this column demonstrates the relative change between sequential stream and parallel stream processing.
Trend: Possible values are increasing, indicating a slower execution time of parallel execution, and decreasing to indicate a faster execution time parallel execution.

Test A

This test scenario is taken from article [2]. The benchmark code counts the numbers of names in a collection with 1 million elements (N = 1.000.000) that begin with letter ’A’. The costs for each element (Q) are very low, since there is only one method call to check if the element starts with the letter ’A’.

The test scenarios were run with five different collection types and by using the three different approaches that are external iteration, sequential and parallel stream processing (see table 1.2). The average results of several runs are shown in table 1.2.

The test scenarios were run with five different collection types and by using three different approaches

Table 1.2 Test A – Iteration benchmark results

Test B

To verify the data taken out of the literature in the last section, a similar test should be performed where the number of elements of the collection (N = 20 Mill.) is much higher. To examine also the influence of boxing (autoboxing), the test scenario has been changed slightly. The test now deals with integers and checks a modulo-condition. This fulfills the requirements needed to emphasize the impact of autoboxing. The last method in table 1.3 shows the same test scenario, but by using an IntStream in parallel mode to avoid auto boxing, to emphasize also the impact of boxing.

Table 1.3 shows the measurement results. The trends of these results are the same like in the previous test case (compare with table 1.2), but in contrast to them, the influence on the execution time is more distinct, since the number of elements (N) is much higher.

The additional last row in table 1.2 shows the influence on execution time when omitting autoboxing, by using an IntStream instead of Stream<Integer>. As a result the execution time decreased dramatically, no matter which iteration approach was used (external, internal or parallel internal approach).

Influence on execution time when omitting auto-boxing

Table 1.3 Test B – Iteration benchmark results

Test C

In contrast to the last test scenario, the number of elements is now very small (N = 10) while the cost (Q = 2 Mill.) of processing each element is much higher by iterating over a loop 2 million times.

The measurement results are represented in table 1.4. The finding clearly shows that the influence of the underlying data structure is negligible in this case. That is because the number of elements which has to be processed is with only 10 elements very low, and so the influence of a worse decomposability of some data structure is marginal. That leads to an improvement of execution time when using parallel stream processing, no matter which type of data structure the underlying collection has.

Test scenario run with five different collection types and using three different approaches

Table 1.4 Test C – Iteration benchmark results

Test D

The last test should demonstrate the influence of blocking or synchronized functions. For that test scenario, only the collection type ArrayList is used, which is a perfect decomposable data structure and consists of 100.000 elements (N). The cost for processing each element (Q) is low since every element is only outputted to console without doing further operations. As the values in table 1.5 show, this task is not suitable for parallelism, because the output function is a blocking function. Because of the additional overhead, which has to be performed when using streams and parallel streams, the execution time is even higher when parallel stream processing is used.

Execution time is higher when parallel stream processing is used

Table 1.5 Test D – Iteration benchmark results

Bibliography

[1] D. Lea, B. Goetz, P. Sandoz, A. Shipilev, H. Kabutz, and J. Bowbeer. When to use parallel streams. Online-Draft: http://gee.cs.oswego.edu/dl/html/ StreamParallelGuidance.html, Sept 2014.

[2] J. I. Moore. Iterating over collections in java 8. Java World, pages 1–36, Aug. 2014.

[3] R.G. Urma, M. Fuso, and A. Mycroft. Java 8 in Action. Manning, 2014.

[4] Richard Warburton. Java 8 Lambdas : Pragmatic Functional Programming. O’Reilly Media, Inc., 2014.