Montag, 21. März 2011

Mapping mit Dozer

Für das Mapping eines Objekts der Logikschicht auf eines der Persistenzschicht benötige ich eine Art Mapper. Zunächst habe ich mir dafür eine Mapperklasse geschrieben, in der ich per Hand ein Objekt eines Typs A in ein Objekt des Typs B umgewandelt habe. Dafür habe ich mir ein sehr einfaches Schema entwickelt und daraus meine Klassen mit JAXB generiert. Für jede Modelklasse musste außerdem eine äquivalente JPA-Klasse erstellt werden. Diese Modelklassen wollte ich nun mit Hilfe der Mapperklasse in eine Klasse mit JPA-Annotationen überführen. Der folgende kurze Codeausschnitt aus der Mapperklasse zeigt das Verfahren:


public class EntityMapper {   
    public static JpaEntity getJpaEntity(Model model) {
        JpaEntity entity = null;
       
        if (model instanceof Adresse) {
            entity = new AdresseJpaEntity();
            ((AdresseJpaEntity) entity).setId(((Adresse)  model).getId());
            ((AdresseJpaEntity) entity).setStrasse(((Adresse)   model).getStrasse());
            ((AdresseJpaEntity) entity).setNummer(((Adresse) model).getNummer());
            ((AdresseJpaEntity) entity).setOrt(((Adresse) model).getOrt());
            ((AdresseJpaEntity) entity).setPlz(((Adresse) model).getPlz());
            return entity;
        }
        else if (model instanceof Artikel) {
            entity = new ArtikelJpaEntity();
            ((ArtikelJpaEntity) entity).setId(((Artikel) model).getId());
            ((ArtikelJpaEntity) entity).setBezeichnung(((Artikel) model).getBezeichnung());
            ((ArtikelJpaEntity) entity).setHersteller(((Artikel)  model).getHersteller());
            ((ArtikelJpaEntity) entity).setPreis(((Artikel) model).getPreis());
        }

        ...
   }
}


Bereits bei diesen wenigen einfachen Modelklassen hat sich gezeigt, dass das Mappen einen enormen zusätzlichen Aufwand bedeutet. In dem Beispiel ist das Mapping nur für zwei Klassen und nur von Model zu JPA-Entity dargestellt.
Nach einigen Gesprächen mit anderen Entwicklern wurde mir "Dozer" (http://dozer.sourceforge.net/documentation/about.html) empfohlen. Dozer nimmt genau diese Arbeit ab. Das Framework ist sehr einfach einzubinden. Bereits die folgenden Zeilen genügen um ein Mapping zubewirken:


AdresseJpaEntity adresseJpaEntity;
Adresse adresse = new Adresse("Musterstraße","1a","02826","Görlitz");
Mapper mapper = new DozerBeanMapper(); 
adresseJpaEntity = mapper.map(adresse, AdresseJpaEntity.class); 

Dozer erkennt anhand des Names der Klassenattribute welche Attribute aufeinander zu mappen sind. Falls man sich nicht an diese Konvention halten will, man die Mappingregeln in einer XML-Datei spezifizieren.

Eine neue Mapping-Schicht kommt hinzu

Der letzte Post zu dem bekannten Problem (XsdToClass) beschreibt eine Lösung, die die Transformation der Schema-Datei in eine Konfigurationsdatei (persistence.xml) für das Persistenzframework (JPA). Dieser Ansatz hat durchaus Vorteile:
  • Man bleibt beim Contract-First-Prinzip.
  • Bei Änderungen, muss lediglich das Schema und die Transformationsdatei (XSLT) angepasst werden.
Dem stehen allerdings auch einige Nachteile gegenüber:
  • Die JPA-Konfiguration in einer XML-Datei ist unbequem und schlechter lesbar, als die Annotierung in den Klassen.
  • Die Konfiguration müsste in dem Schema mit einem zusätzlichen Namespace vorgenommen werden. Es entsteht zusätzlich ein hoher Aufwand für die vollständige und korrekte Umsetzung der JPA-Konfiguration in diesem Namespace.
Schluss endlich bedeutet dies, dass ich einen weiteren Ansatz gefunden habe, der einfacher umzusetzen ist als die Variente mit der Tranformation mit XSLT. Darum nehme ich von diesem Ansatz vorerst Abstand.

Mapping von Objekten verschiedener Schichten...
... heißt mein neuer Ansatz

die folgende Grafik zeigt den groben Ablauf des Mappings

Die Modelklassen werden nach wie vor per JAXB aus einem Schema generiert. Die generierten Klassen stellen das Domainmodel dar und werden in der Logikschicht verwendet. Soll ein Objekt der Logikschicht persistiert werden, wird dieses auf ein äquivalentes JPA-Objekt gemappt. Dieses kann durch JPA gehandhabt werden. Ebenso muss in umgekehrter Richtung ein Mapping von JPA-Entities auf das Domain-Model stattfinden.

Vorteilhaft an diesem Ansatz ist:
  • Eine Trennung von Schichten und die lose Kopplung werden unterstützt.
  • Das Mapping ist beliebig erweiterbar.
Der Nachteil an dieser Verfahrensweise ist jedoch:
  • Wenn das Model im Schema geändert wird, muss die entsprechende JPA-Entity sowie das Mapping per Hand ergänzt werden

Mittwoch, 16. März 2011

Recherche zum Problem mit JAXB und JPA

Im Post "XSD to Class" habe ich das aktuelle Problem bereits beschreiben. An dieser Stelle möchte ich kurz aufführen was ich bei meiner Recherche zur Problemlösung bereits gefunden habe.

Zuerst war ich auf HyperJaxb3 gestoßen. Zu diesem Projekt findet man die eine oder andere Seite, auf der man einige dazu erklärt bekommt. (z.B. http://xircles.codehaus.org/projects/hyperjaxb3) Dies schaut zwar nach einer Lösung aus, jedoch ist das Projekt schlecht dokumentiert. Eine eigene Homepage konnte ich nicht finden.
Der nächste hoffungsvolle Treffer war "Fluid" (http://people.apache.org/~ppoddar/fluid/site/welcome.html). Dies ist schon besser dokumentiert und hat eine richtige Projektseite. Eine kurze Einführung ins Thema und der Download der Ressourcen später habe ich bereits damit herumexperimentiert. So wie man es im Usermanual lesen konnte, ließen sich die Ressourcen aber nicht benutzen. Nachdem ich und ein Kollege damit herumprobiert haben, sind wir zu dem Schluss gekommen die Suche nach einer anderen Lösung fortzusetzen.

Aktuell gibt es noch die Idee, anstatt von JPA-Annotationen alles in einer XML-Konfigurationsdatei zu schreiben. Die Persistenzinformationen müssten dann jedoch durch einen zusätzlichen Namespace irgendwie in das Schema zusätzlich hineingebracht werden. Aus dem Schema könnte man dann per XSLT eine XML-Konfigurationsdatei für das Persistenzframework erzeugt werden. Dies ist jedoch nur ein zusätzlicher Gedanke.

Für den Fall, dass man auf die Annotationen verzichtet und die Konfiguration in einer XML-Datei formuliert, wäre dies eine Mögliche Lösung des Problems. Jedoch besteht immer noch der Gedanke, dass es doch auch irgendwie mit JPA-Annotationen funktionieren muss!

Freitag, 11. März 2011

Allgemeine Architekturbeschreibung

Der Human Task Service besteht im Prinzip aus 3 Komponenten, dem HTP, der HTI und der PK. Diese werden hier kurz grob beschrieben.

1. HTP - Human Task Process
Der HTP wird vom Business-Process angesprochen. Er stellt also die Schnittstelle nach Aussen dar.
Vom Business-Prozess bekommt der HTP ein fachliches Dokument, welches die Parameter des Tasks enthält.

2. HTI - Human Task Infrastructure
Die HTI enthält die eigentliche Funktionalität. Sie verwaltet die Task-Instanzen sowie die Task-Beschreibungen. Ausserdem stellt sie Schnittstellen für die Präsentationskomponente und für den HTP bereit.

3. PK - Präsentationskomponente
Die Präsentationskomponente stellt die Verbindung zu den Bearbeitern der Tasks bereit. Mitarbeiter authentifizieren sich und können anschließend die ihnen zugeordneten Tasks bearbeiten.

System-Ablauf
Die folgende Skizze beschreibt den grundsätzlichen Aufbau und den Ablauf des Systems.



1. Der Business-Prozess löst einen asynchronen Webservice-Aufruf aus. Er übergibt ein Fachliches Dokument, welches Informationen zum Taks enthält

2. Es kann eine Vielzahl an unterschiedlichen Task-Arten geben, für die es jeweils Task-Beschreibungen gibt, welche von der HTI verwaltet werden. Der HTP beinhaltet eine Referenz auf eine Task-Beschreibung (die in der HTI verwaltet werden) und reichert diese mit den Informationen aus dem fachlichen Dokument an. Die angereicherte Task-Beschreibung gibt er an die HTI, welche daraus eine Task-Instance erzeugt und sich um die weitere Verwaltung kümmert. Auch dieser Aufruf ist asynchron.

3. Zwischen HTI und PK gibt es verschiedene Interaktionsmöglichkeiten, die in der Regel synchron erfolgen können. Beispielsweise kann die PK eine Liste aller Tasks eines Nutzers verlangen oder einen einzelnen Task anfordern. Die HTI kümmert sich um die Verwaltung und stellt entsprechende Schnittstellen bereit.

4. Ein Bearbeiter kann sich an der PK anmelden und authentifizieren. Nur wenn er die nötigen Berechtigungen hat, kann er Tasks bearbeiten.

5. Zunächst lässt er sich eine Liste aller Tasks geben, auf die er Zugriff hat bzw. die er bearbeiten kann.

6. Aus der Liste der Tasks wählt er den zu bearbeitenden Task aus. Darauf hin erhält er eine Detail-Ansicht des Tasks. Diesen Task kann er nun bearbeiten und abschließen.

7. Sobald ein Task abgearbeitet wurde, meldet die HTI dies dem HTP. Dies ist die Antwort auf den asynchronen Aufruf.

8. Der HTP leitet diese Antwort an den entsprechenden Business-Prozess weiter.

Donnerstag, 10. März 2011

XSD to Class

Die Komponenten des Human task Service, die nach Außen erreichbar sein müssen, sind per Webservice ansprechbar. Die Beschreibung der Schnitstellen der Webservices erfolgt über ein XML-Schema. Aus diesem werden die Webservices und die Modelklassen (mit JAXB) erzeugt. Jedoch hat diese Verfahrensweise scheinbar auch einen Nachteil: Da die Persistierung der Models per JPA geschieht, müssen die entsprechenden Annotationen auch gesetzt werden. Dies muss momentan noch per Hand gemacht werden. Momentan ist mir noch nicht bekannt, wie dieses Problem gelöst werden kann.