Seiten

Mittwoch, 26. Juni 2013

CDI Commons – Dynamic Service Resolver

In meinem Blog über CDi und i18n (Artikel) hatte ich einen PropertyRegistryService vorgestellt.

Als Beispiel wurde der Producer für die Instanz eines SimpleDateFormat gezeigt.

image

Nicht schön war die harte Kopplung der Implementierung des PropertyRegistryService. Per Annotation wurde

die Implementierung deklariert. @CDIPropertyRegistryFileBased (im Blog noch @CDIFileBased)

Immer wieder muss aber zur Laufzeit entschieden werden welche Implementierung verwendet werden soll/muss.

Das kann verschiedene Gründe haben wie z.B. das derzeitige Lastverhalten, der angemeldete User und und und.

Wie also die Endscheidung vornehmen?

Zwei Dinge sind notwendig:

1) Das Interface der implementierenden Klasse, in unserem Fall PropertyRegistryService

2) und der Qualifier um den richtigen Producer zu selektieren.

image

 

Das Interface ist relativ leicht zu ermitteln. Mann kann es fest angeben, da man weiß das man genau dieses Interface benötigt.

Wenn auch das dynamisch ermittelt werden muss, kann man hier ähnlich wie bei den MessageRessourcen vorgehen. Es wird ein Key übergeben, der an anderer Stelle auf ein Interface gemappt wird. Hier einfach simuliert durch die Methode

Class mapp2Class(final String key)

Spannender wird es bei der Auswahl der richtigen Qualifiers. Das ist der Kern der Implementierung.  Simuliert wird es hier

durch die Methode resolveContextImpl(). Wie genau die Implementierung aussehen kann, hängt von der Umgebung ab und welche Parameter ausgewertet werden müssen.

Die Implementierung hier erzeugt ein AnnotationLiteral<CDIPropertyRegistryFileBased>. Das ist wieder eine harte Kopplung, kann jedoch wieder durch einen Mappingmechanismus zur Laufzeit aufgelöst werden.  Am einfachsten,  in dem man eine Klasse dynamisch instanziiert, die von AnnotationLitaral erbt.

z.B.

image

Donnerstag, 20. Juni 2013

CDI Commons – i18n

Immer wieder müssen Anwendungen mit i18n angereichert werden.
Der Std.Weg sieht vor, dass man mit Properties-Dateien die unterschiedlichen Sprachen unterstützt. Nicht nur Sprachen, auch verschiedene Mandanten können so getrennt werden.
Der von Oracle vorgeschlagene Weg sieht dabei wie folgt aus. (Internationalization)

Locale currentLocale; //… wo auch immer das herkommen mag..
ResourceBundle messages = ResourceBundle.getBundle("MessagesBundle", currentLocale);
System.out.println(messages.getString("greetings"));

Für jedes Locale muss eine Ressourcendatei vorhanden sein.
 
Hier nun der Ansatz per CDI. (Quelltext wie immer unter : Git-Repo )
Im Blog CDI Commons – Param per Annotation habe ich gezeigt wie man 
Parameter per Annotationen übergeben kann. Das reicht natürlich nicht, wenn man z.B. das Datum
unterschiedlich formatieren möchte in Abhängigkeit von Locale und Mandant.
Schön wäre eine Annotation die den RessourcenKey enthält, der dann zur Laufzeit aufgelöst wird.
image
 
Als zusätzliche Anforderung soll das Locale und dabei  verschiedene Level berücksichtigt werden.
Exemplarisch habe ich das hier mit den Leveln
  • Company – firmenweite Vorgaben
  • Application – applikationsweite Vorgaben
  • Modul
  • Class
partiell umgesetzt.
Sehen wir uns zu Beginn den Producer an.

image

 Hier gibt es zwei Besonderheiten, die Annotation @CDIRessourceKey und die Annotation @CDIFileBased

@CDIRessourceKey:
Mit dieser Annotation wird der RessourcenKey übergeben anhand die Auflösung erfolgen soll.
Das ist noch analog zu der normalen Verwendung messages.getString("greetings").  Innerhalb der Produces-Methode wird
auf die Annotation geprüft und der RessourcenKey extrahiert.

@CDIFileBased:
Diese Annotation deklariert die PropertyRegistryService Instanz. Das Interface selbst hat nur eine Methode.
public String getRessourceForKey(String ressourceKey);
Versucht der CDI-Container eine Instanz zu erzeugen, sucht er nach einer passenden Producer Methode.
Diese findet er in der Klasse  PropertyRegistryServiceProducer   die aus lediglich einer Methode besteht und eine Instanz von

FileBasedPropertyRegistryService erzeugt. Hier können umgebungsspezifische Parameter angewendet werden.
image

Innerhalb der Klasse FileBasedPropertyRegistryService befinden sich dann die einzelnen Registries für die jeweiligen Level die man zur Verfügung stellen möchte. Die Implemenntierung der Methode
public String getRessourceForKey(String ressourceKey)
gibt die Reihenfolge der Auflösung vor.  An dieser Stelle kann man auch ein Regelwerk hinterlegen, das sich an die derzeitige Systemumgebung anpasst…

image


Die Registries selbst sind sehr einfach gehalten. Es handelt sich hier um eine file-based – Implementierung.
Denkbar ist natürlich auch der Zugriff auf andere Medien wie Datenbank, Webservice,…

image

Zu erwähnen ist, dass diese Implementierung mindestens immer mit einer leeren dummy – Propertiesdatei
initialisiert wird, wenn keine konkrete gefunden wird.
Sieht man sich die Gesamtimplementierung an, handelt es sich um eine sehr überschaubare Anzahl Klassen.

image

Der Vollständigkeit halber noch die UML-Diagramme:
image



image


image

JetBrains TeamCity Blog » » TeamCity 8 — Welcome to The Light Side of Continuous Integration

JetBrains TeamCity Blog » » TeamCity 8 — Welcome to The Light Side of Continuous Integration:

Dienstag, 11. Juni 2013

CDI Commons – Param per Annotation

Heute geht es um die Möglichkeit in einer CDI Umgebung per Annotationen Parameter zu übergeben.
Als Beispiel nehme ich den SimpleDateFormatter. Typischerweise wird ein SimpleDateFormatter in der folgenden Form initialisiert.
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd hh:mm:ss");
Die erste Verbesserung hier ist die Verwendung einer zentralen Registry für die Definition der Pattern. ("yyyy:MM:dd hh:mm:ss"); Der Ausdruck selbst ist aber nicht kürzer und auch immer noch hart an die Registry gekoppelt. Der erste Ansatz per CDI ist dann ähnlich wie die Definition der Logger.
image
Die Annotation @CDICommons ist hier zur Abgrenzung zu den evtl. im ClassPath vorhandenen Implementierungen und Konstruktoren. Es empfhielt sich eine solche Annotation pro Modul zu verwenden. So können Mehrdeutigkeiten einfach gelöst werden. Die Annotation selbst ist schlicht aufgebaut.
image
Da die die Implementation SimpleDateFormat keinen Default-Konstruktor hat, ist es notwendig eine Erzeuger-Methode zu implementieren. Die Annotation @Produces ist hier das einzig auffällige.
image
Zu erkennen ist auch hier die Verwendung der konstanten Definition PATTERN. Zumindest im Quelltext ist nur noch an einer Stelle ein Verweis auf das Pattern selbst. Was aber, wenn man diese nun dynamisch zu Laufzeit ermitteln und verwenden möchte? Den Zugriff auf die jeweiligen Registries sollen nicht im gesamten Quelltext zu sehen sein.
Hier kann man den Weg über eine weitere Annotation gehen. Nennen wir sie @CDIRegEx.
image
Die Implementierung ist um die Definition eines Values angereichert worden. Das wichtige allerdings ist die darin verwendete Annotation @Nonbinding. Damit erst wird es möglich dieses Attribut zu verwenden, ohne das der CDI-Container versucht dieses Attribut für die Auflösung selbst zu verwenden. Wird diese Annotation nicht eingesetzt, kann der CDI-Container nicht mehr die vermeintliche Erzeuger-Methode finden.
 image
Die Erzeuger-Methode wird um die Annotation @CDIRegex in der Signatur erweitert. Innerhalb der Methode wird die Annotation selbst als Auslöser für die Initialisierung verwendet. In diesem Fall nur , indem das Pattern aus dem Attribut verwendet wird. An dieer Stelle kann dann allerdings der Zugriff auf die Registry erfolgen, damit zur Laufzeit dynamisch entschieden werden, welches Pattern zu Einsatz kommen soll.
Im Quelltext sind nun folgende Definitionen gültig, wobei die erste für eine Defaultinitialisierung verwendet wird.
image

Die Annotation CDIRegex kann auch in die Annotation CDICommons eingebettet werden. Das allerdings ist dann eher Geschmackssache, da der Code nicht reduziert wird.
image
image
Interessant ist allerdings, das nun alle drei vorgestellten Variablen-Definitionen gültig sind.
Das Beispiel ist wie üblich im Repository zu finden: Bitbucket - CDI-Commons

Freitag, 7. Juni 2013

Löschen von JPA-Entitäten (Blättern) in Baumstrukturen (und kleine, fiese Hibernate-Bugs...)



Ich weiß nicht ob es nur mir so geht, aber im Umgang mit JPA/Hibernate tat ich mich anfangs recht schwer. Das fing schon bei trivialen Sachen (sollte man meinen) wie dem Löschen von Entitäten an.. Wenn dann noch Baumstrukturen hinzukommen, tappt man schnell in die ein oder andere Falle. Hier mal ein Beispiel einer Umsetzung einer Entitäts-Baumstruktur (inkl. dem Löschen von Blättern im Baum).

Ich habe heute eine Entität "SearchPattern" erweitert. Zuvor stand diese für sich alleine, hatte also keine Verbindungen zu anderen Tabellen / Entitäten.
Hier stellte das Löschen natürlich noch kein Problem dar:

entityManager.remove(toDeleteSearchPattern);
entityManager.flush();


bumms..fertig.

Nun habe ich die Entität dahingehend erweitert, dass sie eine Baumstruktur darstellt.
Sprich: Ein SearchPattern kann ein Parent-SearchPattern und eine Liste von Kind-SearchPattern haben. Dies realisierte ich grob skizziert folgendermaßen:

@Entity
public class SearchPattern{

    @OneToOne
    private SearchPattern parent;

    @OneToMany
    private List<SearchPattern> children;
}


Lassen wir den Fall, dass ein Parent-SearchPattern (also eine Entität die Kinder hat) gelöscht werden soll , mal außen vor und betrachten den Fall, dass eine "Blatt"-Entität gelöscht werden soll. Also ein SearchPattern, dass keine Kinder aber einen Vater hat. In diesem Fall muss der Löschvorgang innerhalb einer Transaktion nun grob so aussehen:

parentSearchPattern.getChildren().remove(toDeleteSearchPattern);
entityManager.remove(toDeleteSearchPattern);
entityManager.flush(); 


Es wird also zunächst das zu löschende SearchPattern aus der Kindliste des Vaters entfernt. Anschließend kann dann das eigentliche SearchPattern entfernt werden, da dieses nun nicht mehr in anderen Entitäten referenziert wird. Anschließend können die Änderungen geflusht werden.

Übrigens: Wer in Hibernate-Entitäten mit Collections arbeitet, sollte zumindest meiner Einschätzung nach wenn möglich auf Listen (statt Sets) setzen. Zunächst hatte ich für die Kinder ein Set benutzt. Ich habe es jedoch nicht hinbekommen, das zu löschende SearchPattern aus der Kindliste des Vaters zu entfernen. Ich dachte zunächst ich hätte einfach Tomaten auf den Augen, aber nein..alles schien okay zu sein. Es wollte mir einfach nicht in den Kopf warum ich das gewünschte SearchPattern nicht aus dem Set removen konnte. Die equals- und hashcode-Methoden der Entität waren korrekt und die SearchPattern-Instanz die ich der .remove-Methode übergeben habe war definitiv in dem Set enthalten. Woran lags? Nun, offensichtlich hat Hibernate einen Bug (und das scheinar schon recht lange! http://yigitdarcin.com/2010/01/08/hibernate-persistentset-remove-not-working/ leider funktioniert der dort verlinkte Link zum entsprechenden Bugtracking-System bei mir zur Zeit nicht) , der es manchmal unmöglich macht etwas aus dem Set einer Entität zu löschen. Nachdem ich das Set in eine List umwandelte, funktionierte das Entfernen einwandfrei.

Mittwoch, 5. Juni 2013

CDI SE – registry for managed services

Auf der Serverseite erfreut man sich der Möglichkeit per CDI die Komponenten zu entkoppeln.
Das ist ja bekanntlich auch auf der SE Seite möglich. Typische Konstrukte sehen dann meist wie folgt aus.
image
1) Initialisiere den Weldcontainer
2) Hole eine erste managed instance
3) Arbeite auf den Referenzen.

Was hier nicht schön ist, ist die Referenz auf den initialen Weldcontainer. Die muss an den verschiedenen Stellen in der Applikation zur Verfügung gestellt werden.
Anbei eine Demo wie dieses für eine Registry von Services aussehen kann.
image

Es gibt eine ServieRegistry von der aus man die vom Weldcontainer verwalteten Servicees bekommen kann. Der Einfachheit halber als Liste von Services.
Die Services selber sind abgeleitet von dem Interface Service. Soweit alles wie bekannt. Jeder Service erhält die Annotation RegisteredService.
image
Über diese Annotation kann die ServiceRegistry die Services selbständig identifizieren. Dass ist hier durch ein Classloading realisiert.
Dieser Prozess wird initial durch die Annotation @PostContruct getriggert.
image
Das Laden der Klassen ist per Reflection gelöst, kann natürlich auch beliebig anders erfolgen.
image

Die Klassen werden in einer internen ArrayList gespeichert und erst instanziiert , wenn die Services benötigt werden.
image

Wie man unschwer erkennen kann, ist die Serviceregistry selbst schon eine vom WeldContainer verwaltete Instanz. (Siehe Annotationen)
Der WeldContainer selbst wird in der ServiceRegistryFactory initialisiert und gehalten.
 image
Damit hat man über diesen Umweg, die Möglichkeit eine vom WeldContainer verwaltete Serviceregistry zu erhalten.
Die Services selbst werden bei Bedarf erzeugt. Das passiert in der Methode getManagedService
image
Zu beachten ist, das man die Services über einen Producer erzeugen muss, damit die Services selber wieder vom WeldContainer verwaltet werden.
image

Die Verwendung selbst ist dann sehr einfach… 
image

Die Sourcen zu diesem Beispiel befinden sich im Repository: https://bitbucket.org/rapidpm/java-cdi-se-demo

Arquillian – real world usage

Since a  few days we are using Arquillian. The first tests are good, because we tried it in a separated project. This you can see from my blog entry http://www.rapidpm.org/2013/05/javaee-arquillian-first-version-to-play.html But.. I found some problems.

The demo project is a single module maven project. The real live project is a multi-maven project. So I started to fill the parent pom.xml with the dependencies, went to the child module and started with my first jUnit. A quite simple CDI.
As you can see, it is the LoggerProducer.
image

The jUnit test looks like this one:
image
As you can see, nothing complicated. The test is running with a weld-ee container. After this, I thought that we can start with the next jUnit test. A simple EJB test would be the first one using a jboss managed container.
First step is writing the deployment like this:
image
But what happened? The jUnit test failed with the error that to many Service-implementations are available.
After a little searching it shows..  not to many Services.. No Service was found.. But it was not able to run.
So I changed a few things.
1) If you have a multi module project you have to put in every pom.xml the dependencies incl the dep-management. Not nice, but the only way to get it running in the moment.
2) You have to read every pom.xml you need… only reading the parent pom.xml is not enough. I hope this will change in the future.
image
3) Creating from a ready war is more stable..
image

Finally the full create deployment class will look like this.
image

With this you can create the jUnit deployment in the way here shown..
image