Archiv des Autors: triplem

git und der Merge Conflict

TL;DR
Es werden Ansätze beschrieben, die Merge-Konflikte mit git verhindern bzw. handhabbarer machen. Die wohl wichtigeste Regel lautet „Tue nur das
nötigste“ und „Bleib am Ball“ (Tugenden aus der Clean Code Philosophie).

Derzeit arbeite ich in einem kleinen Team, das ein bereits produktives System weiterentwickelt. Die eigentliche Entwicklungsumgebung beinhaltet Eclipse und SVN als Versionskontroll-System. In unserem Team verwenden wir (in weiten Teilen) IntelliJ (meine bevorzugte IDE) und git als VCS. Ursprünglich wurde git eingeführt um Mergekonflikte zu vermeiden und dadurch das mergen zu vereinfachen. git als dezentrales VCS ist SVN in weiten Teilen überlegen, insbesondere lassen sich damit einfach lokale Branches bzw. Feature Branches erzeugen und teilen. Durch die Verwendung von SVN müssen wir immer sicherstellen, das eine „lineare“ Versionshistorie vorliegt. Dies ist bei git, im Gegensatz zu SVN, keine Selbstverständlichkeit, und lässt sich nur durch die Verwendung von rebase anstelle von merge hinbekommen. Ein Workaround dazu ist, das man die nicht-linearen Einträge schlicht ignoriert, wenn man den aktuellen Branch (nennen wir Ihn release) auf SVN mergen möchte. Dieser Workaround wird im Team derzeit eingesetzt.

Feature Branches werden extensiv eingesetzt. Durch leider nur sehr grob definierte User-Stories laufen die Feature Branches teilweise recht lange und es wird nicht nur eine User-Story darin abgearbeitet. Es gibt für jeden Hotfix einen eigenen Branch, der dann in den releasse-Branch gemerged wird. Diese Branches werden (leider nicht immer zeitnah) in den Development-Branch (develop) gemerged. Die Anzahl der Hotfixes ist derzeit recht hoch (bis zu 3 Stück pro Woche), was zu einem gestörten Ablauf des Sprints sorgt.

In der derzeitigen Verwendung werden die Vorteile von git zwar genutzt, jedoch erweist sich dies immer wieder als schwierig und führt zu langen Merge-„Orgien“. Dadurch entsteht immer wieder unnötiger Aufwand und auch Frust.

Nachfolgend möchte ich einen Vorschlag ausarbeiten und Lösungsansätze bieten, um das Konflikt-Potential der Merges zu verringern bzw. gar ganz zu vermeiden.

Reduktion der Anzahl der Hotfixes

Dies vereinfacht bzw verringert nicht nur die Anzahl der notwendigen Merges, sondern erhöht auch die Stabilität der Sprints. Um das zu erreichen muss die Qualität der Auslieferungen steigen. Dies muss das durch automatisierte, aber auch manuelle Tests sichergestellt werden.

Hotfixes sollten zeitnah/umgehend in den Integrations Branch (develop) integriert werden, um diese allen anderen Entwicklern zur Verfügung zu stellen.

Einhaltung der Code-Conventions

Durch die Verwendung von verschiedenen IDEs (IntelliJ und Eclipse), aber auch durch miss-konfigurationen auf verschiedenen Entwickler-Systemen kommte es zu Merge-Konflikten. Eine Code-Convention (Google Java Style Guide) ist eingeführt und muss entsprechend genutzt werden. Die Verwendung von UTF-8 ist für alle Dateien (mit Ausnahme von *.properties) verpflichtend. Eine deutliche Verringerung von Merge-Konflikten ist hier jedoch nicht zu erwarten, da diese durch git selbst bzw. durch Unterstützung der Merge Funktionalitäten der IDEs bereits abgepuffert werden (Stichwort: Zauberstab in IntelliJ).

Verringerung der Laufzeit von Feature-Branches

Je länger ein Branch existiert, desto mehr Änderungen ergeben sich sowohl in dem Feature-Branch aber auch in dem Eltern-Branch. Dies führt zu einem erhöhten Merge-Aufwand, insbesondere, wenn in allen betroffenen Branches dieselben Dateien angepasst werden. Dies kann durch eine kurze Laufzeit eines Feature-Branches eingegrenzt werden.
Hier ist jeder selbst dafür verantwortlich, neue Features in Teilen so zu programmieren, das man die Änderungen möglichst frühzeitig integrieren (in den develop-Branch mergen) kann.

Insbesondere wenn man ein neues Feature anfängt, sollte man sich überlegen, welchen Branch man nutzt und welche Änderungen man bereits auf dem develop-Branch durchführen kann.

Vermeiden von Anpassungen an vielen Dateien

Wie im Abschnitt vorher erwähnt, wird ein Merge schwieriger, sobald Dateien in mehreren Branches angepasst werden. Wenn Änderungen nur an wenigen Dateien erfolgen verringert sich also die Wahrscheinlichkeit, das es Überschneidungen gibt.

Dies lässt sich durch Absprachen zwischen den Entwicklern, aber auch durch eine bessere Kapselung von Klassen erreichen. Wenn eine Klasse richtig gekapselt ist, ist eine Änderung nur an dieser Klasse notwendig und eben nicht über den kompletten Classpath verteilt.

https://stackoverflow.com/a/13444663/1546453
https://stackoverflow.com/a/457988/1546453

Squashen von Commits

Um die Commits die vom Feature Branch auf den develop Branch gerebased werden und somit die Anzahl der Merges gering und übersichtlich zu halten, kann man die zu rebasenden Commits mit einem „squash“ zusammenfassen.

Diese Funktionalität ist im VCS-ChangeLog von IntelliJ über den Context-Menüpunkt „Interactively Rebase from Here“ verfügbar.

Hier gilt es zu beachten, das dies nur mit lokalen Änderungen, die noch nicht gepushed wurden, gemacht werden darf, da damit die Versionshistorie verändert wird (sprich: ein force push/pull wäre notwendig, und dies ist zu vermeiden).

http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html
https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
https://intellij-support.jetbrains.com/hc/en-us/community/posts/360001787220-Squash-and-Merge-Github

Verwendung von git rerere

Durch die Verwendung von rebase werden bei einem rebasen eines Branches auf einen anderen Branch (meist develop) immer alle Commits seit Abzweigung es Feature Branches erneut gemerged. Daher kommt es zu einem wiederholten Merge derselben Commits.

Dies kann durch die Verwendung von git rerere verbessert werden. Hiermit werden die bereits durchgeführten Merges aufgezeichnet und zu einem späteren Zeitpunkt wieder genutzt. Dies wird jedoch nur für den lokalen Nutzer gespeichert, so dass dies nur eine lokale Lösung ist. Wenn also ein Feature-Branch nur von einer Person genutzt bzw. gemerged wird, ist dies ein valides Vorgehen.

Siehe dazu insbesondere auch
https://git-scm.com/docs/git-rerere
https://medium.com/@techupbusiness/enable-git-rerere-for-easy-merging-303c6f2dacd3

Vermeiden von rebase (mit –force) und merge

Wenn ein Branch bereits gemerged wurde, führt dies bei einem rebase dazu, das alle Änderungen, die bereits in dem Merge Commit gemacht wurden nochmals durchgeführt werden und die Versionshistorie sich ändert. Dies gilt es zu vermeiden.

Daher sollte man am besten entweder nur Mergen bzw. nur Rebasen.

FAZIT

Your milage may vary. Im Prinzip sollte jeder das verwendete Toolset kennen und verstehen. Eigene Lösungswege gibt es immer und ich wäre daran interessiert, eure Lösungen bzw Best-Practices zu erfahren um auch mein Skillset zu verbessern.

Weiterführende Information (unbedingt lesenswert)

https://tobywf.com/2018/01/the-ultimate-git-merge-vs-rebase-tutorial/
https://stackoverflow.com/a/11219380/1546453

https://clean-code-developer.de/die-tugenden/

kotlin and gradle with integrationTests

In a small private project of mine, I am using kotlin. Since this project does apped on a database (cassandra in this specific case) and furthermore I want to keep several classes „internal“ (private to the corresponding module) I am in need to see those „internal“ classes in my integration-tests.
This sound like an easy task, doesn’t it? Well basically I needed to google quite some to find a solution for this one. Problem is, that kotlin does not recognize those „internal“ classes out-of-the-box in additional sourcesets. Classes marked as „internal“ are only to be seen inside the same „module“. A „module“ is defined as:

  • an IntelliJ IDEA module
  • a Maven project
  • a Gradle source set (with the exception that the test source set can access the internal declarations of main)
  • a set of files compiled with one invocation of the Ant task

So, basically I do define a new gradle sourceset inside my „module“ which makes the integrationTest then a different kotlin module.

The Reason

On compilation of a kotlin module, the compiler generates a file named module-name.kotlin_module inside the META-INF-folder of this module (you can usually find this in the build/classes folder of your project).
So, what happens here?
Well, basically kotlin uses name-mangeling to disallow access to classes and methods marked with the „internal“-modifier. This is roughly and shortly described in a blog-post here (search for Internal visibility and mangling).

The solution

Since this mangling uses the module-name (which is usually the name of the module inside IntelliJ exclusive the suffix .main) I thought it a good idea to provide the same module name to my integrationTests-Compilation-step. Unfortunately this was to no avail.
I found out about another Parameter in the kotlin compiler called „friend-paths“. I tried to set this one in gradle as well (you need to use the -X in front of this parameter), but this also didn’t work out quite as wanted.
What really worked in the end, was to „associate“ the compilation-unit in gradle with the main-compilation-unit.

kotlin.target.compilations.getByName("test") {
associateWith(target.compilations.getByName("main"))
}

There are already quite some issues in YouTrack (Issue Tracker of Jetbrains used for kotlin and its libraries), but you still need to look for the right keywords to be able to find this solution. Hope that this will help other people find the solution.
For detailed Issues see this as an example.

Struktur eines Java Moduls (oder Wie vermeide ich einen Big Ball of Mud)

Microservices sind in aller Munde, jedoch ergeben sich immer wieder Projekte, bei denen es darum geht, schnell und „einfach“ einen Erfolg abzuliefern. Wenn dazu der eingesetzte Technologiestack etwas betagt ist und das Projektteam recht klein, erscheint der Einsatz von Microservices als nur sehr schwer umsetzbar. Daher werden auch heute noch Monolithen gebaut, die jedoch, nach meinem Verständnis, so strukturiert werden müssen, das auch eine „Migration“ zu Microservices prinzipiell einfach möglich erscheint. Das macht dann den vermeintlichen Monotlithen zu einem „Modul Monolithen“ (siehe dazu Informatik Aktuell) und nicht zu einem „Big Ball of Mud“ (BBoM). Eine Beschreibung, was schlecht ist an einem BBoM und warum dieser entstehen kann findet sich unter Wikipedia und soll hier nicht näher betrachtet werden. Ein BBoM führt in Folge zu erhöhten Wartungskosten und Aufwand für die Umsetzung von neuen Features, die dann unangenehm auffallen, wenn niemand damit rechnet und in der Kommunikation gegenüber Kunden und Auftraggebern nur schwer zu argumentieren sind.

Wie kann man also einen solchen Big Ball of Mud verhindern? Daraus ergeben sich dann auch Schritte, wie man solche Systeme aufräumen kann bzw. was der Zielzustand eines solchen Monolithen ist.

Code-Conventions

Code-Conventions sind ein wichtiger Bestandteil um Source Code für alle lesbar und somit auch wartbar zu halten. Häufig werden die Google Code Stlye Java verwendet. Doch Vorsicht, unterschiedliche IDEs (hier: IntelliJ und Eclipse) interpretieren diese geringfügig anders. So sind Codezeilen bei IntelliJ max. 100 Zeichen lang, bei Eclipse 100, heißt also, das Eclipse nach dem 99 Zeichen anfängt, die Zeile umzubrechen, wohingegen IntelliJ nach dem 100sten Zeichen umbricht. Dies sehe ich aber eher als ein marginales Problem an, so dass jeder Entwickler „seine eigene“ IDE nutzen kann/darf/soll.

Qualitätsmetriken

Für die Einhaltung von Code-Conventions und damit zusammenhängenden Qualitätsmetriken können in den IDEs entsprechende Plugins sorgen (SonarLint), die die Entwickler aktiv dabei unterstützen. Wesentliche Code-Metriken (wie zB. lines-of-code in einer Klasse) können Hinweise darauf liefern, ob ein System sich zu einem BBoM entwickelt. Dies kann zB. durch die Verwendung von Sonarqube gemessen und nachgehalten werden.
Die Konventionen und –metriken können und sollten in einem Code-Review angesehen und besprochen werden. Dadurch wird die Tendenz der Entropie der Software zu einem BBoM verhindert bzw. zumindest verlangsamt.
Wikipedia – Code Review

Package Strukturen

In Java werden packages genutzt, um Quellcode voneinander zu separieren. Diese Separierung ist nur eine kleine Maßnahme für die Strukturierug des Codes und kann zB. zyklische Abhängigkeiten bzw. Domain-Übergreifende Zugriffe nicht verhindern, aber bereits sichtbar machen. Die Quintessenz für das Vorgehen ist die Strukturierung nach Features und Layers. Dieses Vorgehen ist sehr schön hier erklärt. In realen Projekten wird es trotz dieser Aufgliederung immer wieder Aufrufe von Klassen in einem Package in einem anderen Package geben. Diese aufgerufenen Klassen sollten durch Interfaces realisiert werden und deutlich dokumentiert werden. Dies stellt sicher, dass die aufrufende Klasse unabhängig von der konkreten Implementierung innerhalb einer spezifischen Domäne bleibt. Somit werden auch Zirkuläre Abhängigkeiten (sprich: Eine Klasse in Package x ruft eine Klasse in Package y auf, die wiederum eine Klasse in Package x aufruft) vermieden. Wenn ein Projekt re-strukturiert wird, können solche Abhängigkeiten sehr deutlich sichtbar werden, zB. wenn man alle packages in eigene Maven-Module packt und somit die Abhängigkeiten zwischen diesen Modulen auch explizit deklarieren muss. Abhängigkeiten zwischen Packages in jedweder Form sollten genau beleuchtet und ggf hinterfragt werden.
Dazu ein interessanter Artikel sind die Code Conventions der Carnegie Melon Universität.

Loose Kopplung

Wie bereits in der Package Struktur angesprochen, sollten die einzelnen Packages nur loose voneinander abhängen. Dies wird zB. durch die Verwendung von Interfaces unterstützt. Dabei ist darauf zu achten, dass die verwendeten Interfaces möglichst passgenau für die Verwendung geschnitten werden, also nicht ein Interface für einen Service, sondern verschiedene Interfaces, die wiederum von anderen Features genutzt werden können. Interfaces bieten zudem den Vorteil das man idR abstrakter programmiert und somit eine Wiederverwendung von Klassen und Methoden deutlich verbessert. Weiterhin verbessern Interfaces bei richtigem Einsatz die Testbarkeit der Applikation, da man ggf. nur ein Mock (eine konkrete Implementierung des Interfaces) an eine zu testende Methode übergibt. Als Beispiel sei hier mal eine Methode findById(Long id) erwähnt. Natürlich kann jede spezifische Klasse eine eigene Methode (a la findClubById(Long id), findPlayerById(Long id)) implementieren, jedoch ist die Wiederverwendbarkeit deutlich höher, wenn man diese Methoden abstrahiert und ein eigenes Interface ByIdFinder implementiert. Jedoch sollte im Umkehrschluß auch immer darauf geachtet werden, nicht unnötig viele Interfaces (also für jede Klasse) zu nutzen (dazu hat Martin Fowler einen interessanten Artikel geschrieben

Martin Fowler – Interface implementation Pair. Ein Schlüssel dazu ist vor allem „Interfaces should be designed around your clients‘ needs, often these don’t match the implementation“.
Zu Interfaces siehe auch: Stackexchange und
Martin Fowler – Minimal Interface

Atomarität von Modulen

Interfaces, Klassen und insbesondere Methoden sollten möglichst Atomar angelegt sein. Das heißt, das eine Methode einen (bzw. mehrere) Parameter bekommt, damit etwas tut, und dann ein Ergebnis zurückgibt, ohne auf dritte Resourcen (zB. die Datenbank) zurückzugreifen. Natürlich müssen Daten in der DB persistiert werden, jedoch sollte das, so möglich, in einer anderen Methode erfolgen, als die komplexe Berechnung von Details. Somit kann die Nutzbarkeit von Unit-Tests erhöht werden, wohingegen der Aufwand für die Erstellung und Ausführung von Integrations-Tests immer deutlich höher ist. Dadurch sinkt auch insbesondere die Zeit, die für einen Build mit allen Tests benötigt wird. Ziel ist hier auch, die Anzahl von Anpassungen für neue Features bzw. Feature-Änderungen möglichst auf eine kleine Anzahl von Klassen zu begrenzen (Clean Code Developer – Minimiere Abhängigkeiten).

Alle oben genannten und mehr Punkte finden sich in den Tugenden des Clean Code Developers. Diese Tugenden sollten in jedem Team bekannt und bestenfalls auch verinnerlicht werden. Dies verhindert Entropie und einen BBoM.

Ein interessanter und für mich inspirierender Artikel zum Thema BBoM, warum ein solcher entsteht und wie man damit umgeht, ist hier zu finden.

Warum ist mir OpenSource so wichtig?

Seitdem ich profesionell Software entwickle, treibe ich mich in OpenSource Projekten herum. Warum ist immer mal wieder eine Frage, die mir gestellt wird. Hier möchte ich kurz darauf eingehen, was OpenSource für mich bedeutet und warum ich mich immer wieder mit Projekten im OpenSource Umfeld beschäftige.

Der wohl wichtigste Grund für mich, liegt darin, das ich viel lernen kann. Man lernt nicht nur durch das lesen von Offenem Quellcode, sondern auch, wenn man eigene Code veröffentlicht und von anderen reviewen lässt.

Ein anderer wichtiger Grund ist die Kommunikation mit anderen Entwicklern. Andere Entwickler haben einen anderen Blickwinkel, durch den man einiges lernen kann. Hier hat sich Stackoverflow als das Hilfsmittel der Wahl etabliert. Dort kann man Fragen stellen, aber auch eigene Antworten geben. Die Fragen, aber auch die Antworten werden bewertet und man kann dadurch viel lernen, immer in der Kommunikation mit anderen. Eines der wohl interessantes Projekte an dem ich beteiligt war, hatte einen IRC-Channel, auf dem täglich (eher nächtlich) Meetings stattfanden und man sich ausgetauscht hat.

Jedoch lernt man nicht nur durch das lesen und schreiben von Code, auch das lesen von Dokumentation zu verschiedenen Projekten öffnet die Augen für neues. Bug-Reports sind ebenso immer wieder auch interessant, denn dort lernt man, Software mit anderen Augen zu sehen.

In den letzten Jahren sind andere Projekte aufgetaucht, bei denen es um Gesundheitliche belange geht. Hier geht es nicht nur um den Austausch von Code, sondern plötzlich auch um konkrete Gesundheitliche Fragen. Das bedeutet mir (als Diabetiker) eine ganze Menge.

Die Tätigkeiten in einem OpenSource Projekt sind vielfältig und jeder kann dazu beitragen, warum also nicht auch Du?

THESE: EJBs sind Tod/JEE ist Tod…

Meine recht provokant formulierte These, das EJBs tot sind, habe ich basierend auf einem wohlbegründeten Bauchgefühl meiner bescheidenen Erfahrung vor einiger Zeit getätigt. Nun hat mich eine vertiefende Betrachtung und eine durch Fakten belegte Begründung interessiert. Diese könnt Ihr hier lesen.

Schaut man sich die beiden Spezifikationen EJB und CDI an, so wird man feststellen, das diese beiden Technologien recht ähnlich aufgebaut sind. In einigen Artikeln wird gar davon gesprochen, das EBJ >= CDI ist [Freely Mix EJB and CDI]. So erscheint also eine Betrachtung der beiden Spezfikationen gegeneinander kaum sinnvoll. Ich versuche mich dennoch, um im Fazit dann auf den Grund dafür einzugehen.

Einen Quellennachweis habe ich versucht, an die einzelnen Punkte anzuhängen, erhebt jedoch keinen Anspruch auf Vollständigkeit. Auch hier gilt, das sich jeder eine eigene Meinung bilden sollte. Über Feedback würde ich mich sehr freuen.

EJB

EJBs werden seit 1997 spezifiziert und spätestens durch die Adoption von SUN 1999 auch genutzt [Wikipedia EJBs]. Mit den Versionen 1.0/1.1 und 2.0 wurde eine sehr strikte und Code-Intensive Umsetzung der Spezifikation angeboten. So war es z.B. zwingend erforderlich, das man Interfaces implementiert. Insbesondere in den Versionen 1.0/1.1 mussten auch lokale Beans immer über Corba aufgerufen werden, auch wenn man ein Distributed Computing nicht verwendete. Dies führte zu einem hohen Performance impact [Wikipedia EJBs History]. Dies änderte sich dann mit Version 2.0.

In der Version 3.0 der EJB-Spezifikation (2004) wandte man sich in Teilen von den bisherigen Konzepten ab, und übernahm Konzepte aus dem bereits damals schon aufkommenden und als leichtgewichtiger empfundenen Spring Framework. So wurde hier verstärkt auf Annotationen und nicht mehr auf die Implementierung von Interfaces gesetzt.[Wikipedia EJBs History].

Eine Versions-Historie bis zur Version 3.2 (2013) kann man unter [Wikipedia EJBs Versions Historie] einsehen.

CDI – Contexts and Dependency Intjection

Bereits mit der EJB Spezfikation 3.1 und dem JEE Stack 6 (2009) wurde CDI [Wikipedia CDI] eingeführt. Dieses Konzept entstammt in weiten Teilen dem Spring Framework und beruht auf dessen und Google Guice‘ Konzepten.

Im folgenden möchte ich auf einige Punkte in der JEE Spezifikation eingehen, die meist mit EJBs umgesetzt werden.

Remoting

Im Bereich des Remoting (sprich, die Method Invocation innerhalb einer anderen VM bzw. sogar über Hardware-Genzen hinweg ist ein Einsatzbereich der EJBs (@Remote). Dieses Paradigma wird in einigen Applikationen für die Kommunikation zwischen verschiedenen Applikatiionen genutzt. Dieses Programmier-Paradigma ist längst durch andere Paradigmen mit loser Kopplung (zB. REST [Adam Bien – Local and Remote EJB]) abgelöst worden.

Für die Asynchrone Kommunikation werden vielfach die Message Driven Beans (MDB) und JMS eingesetzt. Dies wird voraussichtlich mit der JEE 9.0 Spezifikation auch in CDI abgebildet, so dass eine Verwendung von MDBs in Zukunft nicht mehr notwendig erscheint [Eclipse JEE8 – Absatz CDI 2]. Derzeit erscheinen diese jedoch immer noch eine einfache Möglichkeit um asynchron Messages zwischen Systemen auszutauschen.

Security

Im Bereich der Security haben EJBs auch eine Stärke, die mit CDI1.1/1.2 leider noch nicht komplett aufgehoben wurde. Dennoch ist es bereits in dieser Version möglich, die von EJB bekannten Sicherheitskonzepte einfach „nachzubauen“[Wofür braucht man JEE eigentlich noch – Absatz Security].

Transaktionshandling

Mit CDI1.1 wurde das Transaktionshandling mit dem Handling von EJBs weitgehend angeglichen. Bei der Behandlung von Exceptions ist CDI sogar besser als das bisherige EJB Konzept. Eine Ausnahme bildet der Einsatz von sogenannten Extended Transactions, die die Objekte nach dem Ende einer Transaktion nicht von der DB detachen und so einen einfacheren Flow der Daten ermöglichen [Wofür braucht man JEE eigentlich noch – Absatz Transaktionsbehandlung und JPA].

Hier hat sich bereits jetzt in Projekten „eingebürgert“, das Objekte gemerged werden, was eine Extended Transaction nicht mehr notwendig macht. Dies geschieht häufig in einer zentralen DAO Klasse.

Scheduling

Mit JEE8 wurde leider nicht, wie ursprünglich geplant, die @Schedule-Annotation in CDI implementiert. Dies ist auch weiterhin nur über ein Singleton-EJB möglich. Bei Bedarf kann diese Zeitsteuerung aber über entsprechende Zusatzbibliotheken auch in CDI nachgerüstet werden [Schedule Jobs in CDI].

Startup

Häufig sollen beim Startup eines Containers einige Funktionen ausgeführt werden, zB. um spezifische Konfigurationsdaten zu lesen. Auch dies wurde mit CDI 2.0 noch nicht umgesetzt. Auch dies kann bereits in CDI 1.1 recht einfach nachgebildet werden [CDI and Startup].

FAZIT

Da immer noch ein paar (wenige) Features in der EJB Spec nicht in CDI abgebildet sind, ist es derzeit nicht möglich, die EJBs bereits aus den Projekten zu entfernen. Dies ist auch nicht notwendig, da die beiden Spezifikationen sehr gut miteinander kooperieren. Dennoch empfiehlt es sich, bei einer Implementierung eines Projektes darauf zu achten, das die EJBs in Zukunft durch CDI abgelöst werden (könnten). Um dann zusätzliche Aufwand zu vermeiden (wie zB. bei den in JEE8 deprecateden @ManagedBean und den daran hängenden Scopes) sollte somit vorwiegend CDI eingesetzt werden.

Die Fokussierung auf Microservices und schnell startende bzw. Self-Contained Container [Thorntail] und die damit zusammenhängenden Mirco-Profiles [Microprofile] ergibt sich, zumindest aus meiner Perspektive, ein klarer Trend in Richtung einer EJB-losen Welt.

Für die Zukunft gerüstet sein, heißt hier also ganz klar, auf EJBs möglichst verzichten [birdseye JEE8 – Absatz CDI 2, letzter Abschnitt].

Hibernate Entity and Revision Listeners

In a project I do work on currently, we do use Hibernate 4.3 and Envers 4.3. We wanted to store modification informations like creation date and modification date as well as userinformation on some domain entities as well.

This is done by using Entity Listeners in addition to Hibernate Envers. One problem we faced, was on how to retrieve the username in the Listeners, since this information is not Injectable in a JEE environment.

The following classes/interfaces are used to provide this functionalities. I do hope, that this helps others as well.

@Embeddable
public class ModificationInformation implements Serializable {

  @Type(type = "org.javafreedom.LocalDateUserType")
  @Column
  private LocalDate creationDate;

  @Type(type = "org.javafreedom.LocalDateUserType")
  @Column
  private LocalDate modificationDate;

  @NotNull
  @Column
  private String createdBy;

  @Column
  private String modifiedBy;

  public ModificationInformation() {
    this("SYSTEM");
  }

  public ModificationInformation(String createdBy) {
    super();
    this.createdBy = createdBy;
  }

  public String getCreatedBy() {
    return this.createdBy;
  }

  public void setCreatedBy(String createdBy) {
    this.createdBy = createdBy;
  }

  public String getModifiedBy() {
    return this.modifiedBy;
  }

  public void setModifiedBy(String modifiedBy) {
    this.modifiedBy = modifiedBy;
  }

  public LocalDate getCreationDate() {
    return this.creationDate;
  }

  public void setCreationDate(LocalDate creationDate) {
    this.creationDate = creationDate;
  }

  public LocalDate getModificationDate() {
    return this.modificationDate;
  }

  public void setModificationDate(LocalDate modificationDate) {
    this.modificationDate = modificationDate;
  }

}

 

The above class is marked as @Embeddable and therefor is used as an embedded object inside our domain objects. All classes using this embedded object, do need to implement the following interface.

public interface HasModificationInformation {

  ModificationInformation getModificationInformation();

}

The domain object then looks like the following. All properties not related to this short post are obviously skipped.

@Entity
@Audited
@EntityListeners({ModificationInformationListener.class})
@Table(name = "domain_object")
public class Domain extends implements HasModificationInformation {

  @Embedded
  private ModificationInformation modificationInformation = 
                                    new ModificationInformation();

  public ModificationInformation getModificationInformation() {
    return this.modificationInformation;
  }

This Entity is marked as @Audited, to be able to use Hibernate Envers and its functionality. Furthermore, we added a new Listener (ModificationInformationListener), to be able to act on certain events during the Entity Lifecycle.

We then needed to provide this Listener and furthermore to use Hibernate, the following classes are implemented.

public class ModificationInformationListener extends AbstractUserNameListener {

  @PrePersist
  public void prePersist(HasModificationInformation hmi) {
    if (hmi.getModificationInformation().getCreationDate() == null) {
      hmi.getModificationInformation().setCreationDate(LocalDate.now());
      hmi.getModificationInformation().setCreatedBy(this.getUserName());
    }

    this.preUpdate(hmi);
  }

  @PreUpdate
  public void preUpdate(HasModificationInformation hmi) {
    hmi.getModificationInformation().setModificationDate(LocalDate.now());
    hmi.getModificationInformation().setModifiedBy(this.getUserName());
  }

}
public class EnversRevisionListener extends AbstractUserNameListener implements RevisionListener {

  @Override
  public void newRevision(Object revisionEntity) {
    EnversRevisionEntity rev = (EnversRevisionEntity) revisionEntity;
    rev.setUsername(getUserName());
  }
}

As you do see, both Listeners do use an Abstract class (AbstractUserNameListener), which allows to access the Principal of the current Session. This is necessary, because both of theses Listeners are not managed by CDI and therefore cannot use @Inject.

public abstract class AbstractUserNameListener {

  // could be, that this will not work for SOAP/REST API calls
  protected String getUserName() {
    BeanManager beanManager = CDI.current().getBeanManager();
    Bean<Principal> principalBean =
      (Bean<Principal>) beanManager.getBeans(Principal.class).iterator().next();
    CreationalContext<Principal> context = beanManager.createCreationalContext(principalBean);
    Principal principal =
      (Principal) beanManager.getReference(principalBean, Principal.class, context);

    String userName = principal.getName();

    if (userName == null) {
      userName = "SYSTEM";
    }

    return userName;
  }

}

The above method is basically copy-pasted from Stackoverflow.

Now every Update and Persist the internal ModificationInformationListener and the corresponding method in there is called and the user and date information is stored in the domain entity. Furthermore, every Lifecycle Event calls Hibernate Envers, which stores the username in the corresponding EnversRevisionEntity.

@Entity
@RevisionEntity(EnversRevisionListener.class)
@Table(name = "envers_revision")
public class EnversRevisionEntity extends DefaultRevisionEntity {

 private String username;

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
}

Hope that this helps. Feedback would be greatly welcome.

 

 

 

 

Determine Musicbrainz Id from Discogs Release

I just figured out, how you are able to retrieve the Musicbrainz Id (MBID) from a specific Discogs Release. The following link can be used:

https://beta.musicbrainz.org/ws/2/url?inc=release-rels&fmt=json&resource=https://www.discogs.com/release/XXXX

Obviously for the XXXX you need to put the release-Id of the release in discogs.

The Discogstagger2 contains a small script (scripts/find_mb_ids.py) which calls the above mentioned url and puts the determined mb-id and the given discogs-id in a file. This script is based on an existing id.txt, which I do use to determine the discogs id for each releaase i own.

Install upmpdcli on Raspi 1 B+ with hifiberry dacplus using Archlinux

This could have been done using packages from the AUR, but I wanted to have the latest version. Furthermore some specifics do apply to my personal setup, because me is still using an rather old raspi 1 B+.

So, see the following steps to get this up and running on your own machine as well.

The first step is just for easier handling of users, so install sudo on the machine:


pacman -S sudo
visudo to add user alarm to sudoers

For details, on how to use sudo, please see the famous ArchWiki.

After this, several packages should get installed, to be able to compile all necessary packages:


pacman -S base-devel libupnp libmpdclient libmicrohttpd jsoncpp curl expat python2

As the usual user (alarm in my case), you can now download and compile and install libupnpp as well as upmpdcli. Note, that this could have been done using the AUR packages for these modules as well, but like I said, I wanted to do this all on my own and use the latest package versions.


curl -O http://www.lesbonscomptes.com/upmpdcli/downloads/libupnpp-0.16.0.tar.gz
tar xzf libupnpp-0.16.0.tar.gz

cd libupnpp-0.16.0
./configure --prefix=/usr
sudo make install

cd ..

curl -O http://www.lesbonscomptes.com/upmpdcli/downloads/upmpdcli-1.2.15.tar.gz
tar xzf upmpdcli-1.2.15.tar.gz
cd upmpdcli-1.2.15
./configure --sysconfdir=/etc --prefix=/usr
sudo make install

groupadd --system upmpdcli
useradd -g upmpdcli --system upmpdcli -s /bin/false -d /
chown upmpdcli:upmpdcli /etc/upmpdcli.conf
mkdir /var/log/upmpdcli
chown upmpdcli:upmpdcli /var/log/upmpdcli
mkdir /var/cache/upmpdcli
chown upmpdcli:upmpdcli /var/cache/upmpdcli
mkdir /usr/share/upmpdcli
chown upmpdcli:upmpdcli /usr/share/upmpdcli

install -Dm644 systemd/upmpdcli.service /usr/lib/systemd/system/upmpdcli.service
sed '/\[Service\]/a User=upmpdcli' -i /usr/lib/systemd/system/upmpdcli.service

Now, several options should be edited in the /etc/upmpdcli.conf file, but this is not strictly necessary:

  • edit /etc/upmpdcli.conf
  • adopt friendlyname (eg. Upnp Sleeping Room)
  • adopt logfile (mine is /var/log/upmpdcli/upmpdcli.log)
  • adopt cachedir (mine is /var/cache/upmpdcli)

Now enable the services installed previously:


systemctl enable upmpdcli

Since me is using a hifiberry dac, I needed to add an overlay to the boot process by editing /boot/config.txt and adding the following line:


dtoverlay=hifiberry-dacplus

Since we are going to use alsa for the sound handling, we do need to install several packages:


pacman -S alsa-tools alsa-utils

To now enable this dac (basically a soundcard) the /etc/asound.conf should get edited:


pcm.!default {
type hw
card sndrpihifiberry
}
ctl.!default {
type hw
card sndrpihifiberry
}

To be able to handle alsa using the user „alarm“, we do need to add this user to the audio group as well:


sudo usermod -aG audio alarm

Since upmpdcli uses mpd, we do need to install this one as well:


pacman -S mpd

Now we can add the above mentioned soundcard to the file /etc/mpd.conf as well.


user "mpd"
pid_file "/run/mpd/mpd.pid"
state_file "/var/lib/mpd/mpdstate"
playlist_directory "/var/lib/mpd/playlists"
log_file "/var/log/mpd/mpd.log"
#log_level "verbose"
replaygain "album"
replaygain_preamp "15"

audio_output {
type "alsa"
name "sndrpihifiberry"
mixer_type "software"
}

Now you should reboot the machine. Afterwards, you can test the soundcard by issueing the following commands:


cat /proc/asound/cards
aplay -l

You can furthermore test the soundcard by issueing „aplay A-WAV-FILE“, which plays the given file.

All should be set, and the Controlpoint should now show the Renderer and be able to play albums and songs on this one.

Use rtl8812AU on ArchlinuxArm

For my Raspberry Pi, I needed to support the rtl8821au driver for my wireless USB device. The following steps worked for me.

This is based on this article


pacman -S make dkms linux-raspberrypi-headers

git clone https://github.com/diederikdehaas/rtl8812AU.git
cd rtl8812AU

change Makefile:
CONFIG_PLATFORM_I386_PC from y to n
CONFIG_PLATFORM_ARM_RPI from n to y

make
make install

reboot

To use the driver and the device, several additional steps have to be followed, this is based on StackExchange.


pacman -S netctl dhclient

cd /etc/netctl
install -m640 examples/wireless-wpa wireless-home

Adopt above file to your needs

Add /etc/netctl/hooks/dhcp:
#!/bin/sh
DHCPClient='dhclient'

netctl start wireless-home

netctl enable wireless-home

After all those steps and another reboot, the wifi-device should work.

DevOpsKube – Redmine SSO with KeyCloak via OpenId Connect

Because we could not make the logout functionality work using the SAML Plugin of Redmine (see last post), we decided to use the OpenId Connect Plugin. Due to the fact, that some functionality (eg. SSL Validation was always enabled, which is not wanted in Development Environments) we have forked this plugin and implemented some fixtures (see here). Those fixtures are already commited to the original project as Pull Requests. As soon as those are merged, we are going to use the original version instead of our fork.

Obviously DevOpsKube is not interested in a manual configuration of this SSO connection, but some documentation is always helpful. Unfortunately we haven’t found any documentation about the Integration of Redmine and KeyCloak via the OpenId Connect Plugin, therefor we do provide some additional documentation about this integration on our DevOpsKube Documentation. The Integration is already commited into our Repositories, but there is still some work to do on the KeyCloak setup (we do need to implement keys, which are used in Redmine as well as KeyCloak). As soon as this is done, we have implemented the first full integration between two components of our DevOpsKube Stack.

Hope you do find this helpful. If you would like to support us, in building up a modern SDLC stack on Kubernetes, do not hesitate to join our effort.