Testgetriebene Entwicklung mit JUnit, EasyMock und Spring

Veröffentlicht in: Java, test-driven development | 0

Die testgetriebene Entwicklung – test-driven development (TDD) – wird häufig in der agilen Softwareentwicklung eingesetzt. Hierbei werden zunächst die Testfälle konzeptioniert und erst anschließend die gewünschte Funktionalität umgesetzt. Aufgrund fehlender Umsetzung werden daher die Testfälle zunächst zwangsläufig fehlschlagen. Erst mit sukzessiver Funktions-Implementierung werden die Testfälle nach und nach erfolgreich durchlaufen.

Am einfachsten zu Testen sind Methoden, die für unterschiedliche Übergabe-Parameter entsprechende Rückgabe-Werte liefern wie z.B. statische Utility-Methoden. Um diese Methoden zu testen wird JUnit verwendet, welches unter http://www.junit.org/ in der aktuellen Version 4.10 zum Download bereit liegt. Alternativ kann man sich JUnit auch als Maven Dependency laden:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>

Das Testen einer Utility-Methode mit JUnit sieht dann folgendermaßen aus:

assertTrue(MyUtility.returnTrue(„parameter“));

Sofern die zu testende Methode Aufgaben an weitere Klassen delegiert, ist es nicht mehr möglich ausschließlich JUnit Tests zu verwenden, ohne eine vollständige Anwendungs-Umgebung zu initialisieren. Stattdessen müssen zusätzlich alle verwendeten Klassen mit initialisiert und getestet werden.

Hierbei handelt es sich dann nicht mehr um Modultests, sondern um Integrationstests, die definitiv wichtig sind und ihre Berechtigung haben, bei der testgetriebenen Entwicklung aber nicht im Vordergrund stehen. Hier soll nur das zu entwickelnde Modul getestet werden.

Daher werden bei der testgetriebenen Entwicklung Mocking-Frameworks verwendet, damit alle Klassen die in der zu testenden Methode verwendet werden, durch sogenannte Mock-Objekte ersetzt werden können. Diese Mocks bieten die Möglichkeit zu testen, ob die erwarteten Methode-Aufrufe mit den definierten Übergabe-Parametern erfolgen. Findet der Aufruf nicht statt oder ein nicht erwarteter Aufruf, so schlägt der Testfall fehl. Die Funktionalität der gemockten Objekte wird also nicht mit getestet, sondern kann in separaten Testfällen geprüft werden.

Als Mocking-Framework wird im folgenden Beispiel EasyMock verwendet, welches unter http://easymock.org/ in der aktuellen Version 3.1 verfügbar ist. Auch EasyMock ist als Maven Dependency erhältlich:

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>

Die erste Library wird zum Mocken von Interfaces benötigt, was als best practice anzusehen ist. Ist es dennoch mal nötig eine Klasse zu mocken, weil man z.B. bestehenden Code nachträglich testen möchte, kann die EasyMock-Classextension verwendet werden.

Kommen wir nun zu einem kleinen Beispiel. Es wird ein UserService getestet der einen User erhält und diesen mittels eines UserDAOs speichern soll. Hierbei wird der UserDAO gemockt und es soll überprüft werden, ob die save-Methode des DAOs auch wirklich aufgerufen wird.

Auch wenn bei der testgetriebenen Entwicklung zuerst der Testfall erstellt werden sollte, folgt zum besseren Verständnis zunächst der UserService und erst anschließend die Testfälle:

public class UserService {

private UserDAO userDAO;

public User save(User user) {
return userDAO.save(user);
}

public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}

Zuallererst wird ein Setup initialisiert, in welchem der DAO gemockt und die erwarteten Methoden-Aufrufe definiert werden. Die Annotation @Before besagt hierbei, dass das Setup vor der Ausführung jedes einzelnen Testfalls in dieser Test-Klasse ausgeführt werden soll.

@Before
public void setUp() {
name = „userName“;
user = new User(name);
userDAO = createMock(UserDAO.class);
expect(userDAO.save(user)).andReturn(user); // (1)
replay(userDAO);

userService = new UserService();
userService.setUserDAO(userDAO);
}

Wie bereits erwähnt wird der UserDAO gemockt und die Erwartung ausgesprochen, dass die save-Methode mit dem gegebenen User aufgerufen wird. Bei der expect-Methode handelt es sich um eine statische Methode von EasyMock, die verwendet wird, wenn der erwartete Methode-Aufruf einen Return-Value besitzt. Über die andReturn-Methode kann anschließend der Rückgabe-Wert definiert werden, den das Mock-Objekt zurückgeben soll.

Hätte die save-Methode keinen Rückgabe-Wert, sähe die Stelle (1) folgendermaßen aus:

userDAO.save(user);

Der expect-Aufruf würde also einfach weggelassen. Der replay-Aufruf in der darauf folgenden Zeile besagt, dass keine weiteren Aufrufe auf dem UserDAO-Mock erwartet werden und nun nur noch die Aufrufe folgen, die bei Test-Ausführung getätigt werden.

Nun muss der UserService noch den UserDAO-Mock gesetzt bekommen und wir können zum eigentlich Test kommen. Hierbei sollte man sich nicht wundern, das Setup ist meist deutlich länger als der eigentliche Test.

@Test
public void shouldSaveUser() {
User save = userService.save(user);
verify(userDAO);
assertNotNull(save);
assertEquals(„userName“, save.getName());
}

Hier wird die save-Methode des UserService aufgerufen und anschließend die verify-Methode von EasyMock aufgerufen. Damit wird überprüft ob die zuvor definierten Mock-Aufrufe auch wirklich getätigt werden oder nicht. Anschließend kann der Rückgabe-Wert wie gehabt mit JUnit überprüft werden.

Um EasyMock etwas genauer zu verstehen modifizieren wir nun die Zeile (1) der Setup-Methode:

expect(userDAO.save(new User(„userName“))).andReturn(user);

In der Setup-Methode wird dem UserDAO-Mock also ein anderer User übergeben, als dem UserService im Test, lediglich der Namen ist gleich. Da nur der Name auf Gleichheit überprüft wird, läuft der Testfall wie gehabt durch. Fügen wir nun dem Testfall noch folgende Zeile hinzu:

assertEquals(save, user);

Hierauf hin wird der Testfall fehlschlagen. Grund dafür ist, dass EasyMock die equals-Methode der User-Klasse überprüft, um zu Testen ob der Übergabe-Parameter gleich ist. Sofern die equals-Methode nicht überschrieben wurde, wird die equals-Methode von Object verwendet, die auf gleiche Referenz testet. Da hier nun zwei unterschiedliche Objekte mit lediglich den gleichen Property-Werten verwendet werden, schlägt der Testfall fehl. Sobald allerdings die equals-Methode von der User-Klasse überschrieben wird und hierbei auf Namens-Gleichheit getestet wird, ist der Test-Durchlauf wieder erfolgreich.

Dies Verhalten ist auch der Grund, warum ich auf einen weiteren wichtigen Bestandteil von EasyMock kommen möchte, das Capture.

Stellen wir uns vor, der UserService reicht das User-Objekt nicht direkt zum UserDAO durch, sondern erstellt aufgrund des Users ein UserEntity, welches vom UserDAO konsumiert wird. Die Properties des Users sind hierbei identisch mit denen des UserEntities. Die Implementierung sieht also so aus:

public User save(User user) {
UserEntity userEntity = new UserEntity(user.getName());
UserEntity entity = userDAO.save(userEntity);
User result = new User(entity.getName());
return result;
}

Sofern das UserEntity viele Properties beinhaltet, müßte für den Testfall also ein exakt gleiches UserEntity erstellt werden, damit das UserEntity im Testfall und das im UserService erzeugte von EasyMock als gleich anerkannt wird. Sofern die equals-Methode nicht überschrieben werden dürfte, würde der Testfall sogar immer fehlschlagen, da es sich zwangsläufig um zwei unterschiedliche Referenzen handeln würde. Aber auch so ein Fall lässt sich problemlos mit EasyMock abbilden.

Hierzu muss lediglich der übergebene User durch ein Capture ersetzt werden:

captureUser = new Capture<UserEntity>();
userDAO = createMock(UserDAO.class);
expect(userDAO.save(capture(captureUser))).andReturn(userEntity);
replay(userDAO);

Es wird ein Capture Objekt erzeugt, welches nicht direkt in die save-Methode des UserDAOs gegeben wird. Stattdessen wird hier die EasyMock-Methode capture aufgerufen. Nun kann im Test über das Capture auf das dem Mock übergebene Objekt zugegriffen und die Werte überprüft werden:

assertNotNull(captureUser.getValue());
UserEntity userEntity = captureUser.getValue();
assertEquals(„userName“, userEntity.getName());

Mit diesem Funktionsumfang von EasyMock lassen sich bereits die meisten Test-Szenarien abdecken. Ab und an wird man in spezial Fällen Lösungen in der EasyMock-Dokumentation suchen müssen, für die meisten Anwendungsfälle dürfte es aber genügen.

Nun noch ein kleiner Exkurs über die Modultest hinaus zu den Integrationstests. Sofern man Spring verwendet kann es sehr nützlich sein, für Testfälle einen Spring-Context hochzuziehen. Folgende Spring-Library ermöglicht dies:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.0.6.RELEASE</version>
<scope>test</scope>
</dependency>

Nun legt man den zu testenden Service an und definiert diesen in der Spring-XML Konfiguration, welche sich im Classpath befinden sollte:

<version=“1.0″ encoding=“UTF-8″?>
<beans>
<context:annotation-config/>
<bean id=“myService“ class=“MyService/>
</beans>

Nun kann man die XML-Konfiguration über die Annotation ContextConfiguration angeben und muss anschließend die Runner Klasse definieren. Da in der Spring-Konfiguration Annotations aktiviert wurden, kann nun der Service direkt in den Testfall per Autowire-Annotation injected werden:

@ContextConfiguration(locations = { „/test.xml“ })
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringContextTest {

@Autowired
private MyService myService;

@Test
public void shouldExecuteMyService() {
assertNotNull(myService);
assertTrue(myService.doWhatYouHaveTodo());
}
}

Über die testgetriebene Entwicklung wird immer wieder gesagt, dass sie zu langsam und damit zu teuer ist. Dies konnte ich bislang nicht feststellen. Sicherlich ist es zunächst mehr Aufwand testgetrieben zu Entwickeln, aber die Einarbeitung in jedes neue Framework kostet zunächst einmal Zeit und bringt es mit sich, sich bei neuen Gegebenheiten erneut mit der API und deren Dokumentation zu beschäftigen. Der Zeitaufwand der beim Erstellen der Testfälle selber benötigt wird, ist überschaubar und wird durch die direkt bemerkten Flüchtigkeitsfehler wieder wett gemacht. Es muss nicht umständlich ein Server gestartet werden, nur um dann doch wieder ein Fehlverhalten festzustellen. Es ist grundsätzlich schneller einen Testfall auszuführen, als bei einer Software manuell einen ganz bestimmten Zustand zu testen.

OpenOffice-Dokumente mit NOA Verarbeiten

Veröffentlicht in: Java | 0

Der folgende Artikel widmet sich der automatisierten Verarbeitung von OpenOffice Dokumenten. So kann mit Hilfe des Frameworks Nice Office Access (NOA) recht einfach ein OpenOffice Template eingelesen, definierte Platzhalter ersetzt und das Dokument als PDF gespeichert werden.

Die NOA-Library ist unter dem Link http://ubion.ion.ag/solutions/004niceofficeaccess in der aktuellen Version 2.2.3 erhältlich und beinhalten zusätzlich zu NOA auch die benötigten OpenOffice Jar-Archive.

Bevor es zur eigentlichen Entwicklung geht, wird noch ein OpenOffice Template mit OpenOffice-Writer erstellt. Neben selbst zu definierendem Text, muss das Dokument mindestens einen Platzhalter erhalten, der von der Java Anwendung ersetzt werden soll. Zu den hierfür benötigten Feldbefehle-Editor gelangt man über die Menüpunkte Einfügen, Feldbefehl, Andere. Unter Variablen kann man nun eine neue Variable setzten. Hierbei muss sowohl ein Name, als auch ein Wert gesetzt werden. Als Format ist Text auszuwählen.

Um die OpenOffice Verarbeitung nutzen zu können, muss zunächst eine Connection zum OpenOffice-Service hergestellt werden. Im folgenden Beispiel befindet sich dieser auf dem gleichen Rechner wie die Java Anwendung. Sofern für die OpenOffice Installation der Standard-Installationspfad genommen wurde, erhält die Konstante OO_PATH im folgenden Code-Fragment den Wert: „C:Program Files (x86)OpenOffice.org 3“:

Map configuration = new HashMap();
configuration.put(IOfficeApplication.APPLICATION_HOME_KEY, OO_PATH);
configuration.put(IOfficeApplication.APPLICATION_TYPE_KEY, IOfficeApplication.LOCAL_APPLICATION);
IOfficeApplication officeAplication = OfficeApplicationRuntime.getApplication(configuration);
officeAplication.setConfiguration(configuration);
officeAplication.activate();

Im nächsten Schritt wird das anfangs erstellte Template mit dem NOA eigenen DocumentService geladen, bei der Konstante TEMPLATE handelt es sich um den Dateinamen inklusive vollständigem Pfad:

IDocumentService documentService = officeAplication.getDocumentService();
IDocument document = documentService.loadDocument(TEMPLATE);

Nun kommt der wirklich interessante Teil, in dem die Platzhalter des Templates ersetzt werden. Über den TextFieldService erhält man anhand der Methode getUserFields die im Template definierten Platzhalter. Über diese kann nun iteriert werden und über das PropertySet des jeweiligen TextFields auf das zugehörige Property Content, der Text des Platzhalters beliebig gesetzt werden. An den Namen des Platzhalters kann man über die Methode getDisplayText gelangen.

ITextFieldService textFieldService = ((ITextDocument) document).getTextFieldService();
ITextField[] placeholders = textFieldService.getUserTextFields();
for (ITextField field : placeholders) {
XPropertySet propertySet = (XPropertySet)     UnoRuntime.queryInterface(XPropertySet.class, field.getXTextContent());
String placeholder= placeholders.get(field.getDisplayText());
if („myplaceholder“.equals(placeholder)) {
propertySet.setPropertyValue(„Content“, „Platzhalter ist ersetzt“);
}
}
textFieldService.refresh();

Um die Änderungen im Dokument wirksam zu machen, muss der TextFieldService noch aktualisiert werden.
Ein wichtiger Bestandteil der OpenOffice-API ist der Zugriff auf Interfaces, für den die UnoRuntime zur Verfügung steht. Hier wird die Methode queryInterface aufgerufen, deren Übergabe-Parameter zum einen das gewünschte Interface ist – Interfaces der OpenOffice API beginnen immer mit X – zum anderen das Objekt, welches das Interface implementiert. Sofern das Objekt das Interface gar nicht implementiert, wird hier keine Exception geworfen sondern null zurückgegeben, andernfalls kann man den Return-Value auf das erwartete Interface casten und hat somit Zugriff auf die gewünschten Methoden.

XPropertySet propertySet = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, field.getXTextContent());

Will man nun das neue Dokument als PDF speichern, wird ein PDFFilter und ein OutputStream benötigt. Der PerstistanceService, der über das NOA-Document verfügbar ist, kann nun mittels der export-Methode den Dokumenten-Inhalt in den übergebenen OutputStream schreiben. Dieser wird anschließend in eine Datei gespeichert und nach dem schließen der Input- und OutputStreams findet man das gewünschte PDF an an der Stelle, die in der Konstanten DOCUMENT definiert ist.

ByteArrayOutputStream out = new ByteArrayOutputStream();
PDFFilter pdfFilter = new PDFFilter();
document.getPersistenceService().export(out, pdfFilter);

byte[] byteArray = out.toByteArray();
ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);

FileOutputStream outputStream = new FileOutputStream(new File(DOCUMENT));
IOUtils.copy(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);

Sobald das PDF generiert wurde, kann das OpenOffice Dokument geschlossen und die OpenOffice Connection deaktiviert werden.

document.close();
officeAplication.deactivate();

Die Verwendung von NOA erleichtert den Zugriff auf OpenOffice enorm, da man sich viele Zwischenschritte sparen kann. Ganz ohne Kenntnisse der OpenOffice-API kommt man allerdings nicht aus. Den dazugehörigen Development Guid findet man unter folgendem Link: http://wiki.services.openoffice.org/wiki/Documentation/DevGuide

Verwendung einer SQLite Datenbank in einer Android App

Veröffentlicht in: Android, Java | 0

Will man in einer Android Anwendung Daten speichern, kann hierzu bequem die in das Android SDK integrierte SQLite Datenbank verwendet werden. Diese ist besonders für den mobilen Anwendungsbereich konzipiert, da sie mit gerade mal 250 KByte extrem genügsam im Bezug auf Speicherplatz ist.

Im folgenden Beispiel sollen die Einstellungen einer Anwendung in der Datenbank gespeichert werden. Hierzu wird eine SettingsActivity erstellt – nicht vergessen, die Activity im Android Manifest zu definieren –  wobei das Layout in einer XML-Datei definiert ist. Als variables Feld erhält die Activity einen Benutzer-Namen, welcher vom Benutzer editiert werden kann. Außerdem gibt es den obligatorischen Speichern-Button, damit die Änderungen vom Benutzer auch gespeichert werden können:

<EditText
android:id=“@+id/name“
android:layout_width=“fill_parent“
android:layout_height=“wrap_content“  android:background=“@android:drawable/editbox_background“
android:text=““
android:padding=“3dip“ />

<Button
android:layout_column=“1″
android:id=“@+id/save“
android:layout_width=“wrap_content“
android:layout_height=“wrap_content“
android:layout_alignParentRight=“true“
android:layout_marginLeft=“10dip“
android:text=“Speichern“ />

Um in den Activities der Anwendung direkten Zugriff auf die UI-Komponenten zu haben, wird das Dependency Injection Framework roboguice verwendet, welches zur Zeit in Version 1.2.2 unter folgendem Link zum Download bereit liegt: http://code.google.com/p/roboguice/wiki/Downloads

Zusätzlich zu roboguice wird noch die Google Library guice benötigt, die man unter folgendem Link erhält: http://code.google.com/p/google-guice/downloads/list

Hierbei ist zu beachten, dass nicht die aktuelle Version 3.0, sondern guice-2.0-no_aop.jar benötigt wird. Beide Libraries werden dem Classpath hinzugefügt. Nun wird im Android Manifest unter Application der Name roboguice.application.RoboApplication gesetzt. Dies ist nötig, da die Activities nun nicht mehr von Activity oder ListActivity direkt erben, sondern von RoboActivity bzw. RoboListActivity.

In der folgenden SettingsActivity wird über die Annotation @InjectView per Dependency Injection auf die im XML definierten UI-Elemente per Id zugegriffen:

public class SettingActivity extends RoboActivity {

private DatabaseManager databaseManager;
@InjectView(R.id.name)
private EditText nameText;
@InjectView(R.id.save)
private Button saveButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.setting);
databaseManager = new DatabaseManager();
final Settings settings = databaseManager.fetchSetting();
nameText.setText(settings.getName());
saveButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View view) {
settings.setName(nameText.getText().toString());
if (settings.getId() == null) {
databaseManager.createSettings(settings);
} else {
databaseManager.updateSettings(settings);
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
databaseManager.close();
}
}

In der onCreate-Methode wird ein DatabaseManager erzeugt, der sich um die Datenbank Connection kümmert, aber hierzu später noch mehr. Ebenfalls über den DatabaseManager werden die Settings bezogen, die in der UI dargestellt werden. Hierzu wird dem nameText, der Name aus den Settings zugewiesen. Nun wird noch dem Save-Button ein onClickListener hinzugefügt, der die Änderungen der UI dem Settings Objekt übergibt und dieses anschließend in der Datenbank speichert.

Wichtig ist, dass in der onDestroy Methode der Activity, die Datenbank-Connection wieder geschlossen wird – verlässt der Benutzer also die Einstellungen wird automatisch die Datenbank Verbindung zurück gesetzt.

Nun zur eigentlichen Interaktion mit der Datenbank über den DatabaseManager:

public class DatabaseManager {

private static final String DB_FILE = „/data/data/myapp.db“;
private SQLiteDatabase database;

public DatabaseManager() {
File file = new File(DB_FILE);
if (!file.exists()) {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
database.execSQL(„create table Settings (id integer primary key autoincrement not null, name text);“);
} else {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
}
}

public void close() {
database.close();
}

public Settings createSettings(Settings settings) {
ContentValues initialValues = createContentValues(settings);
long id = database.insert(„Settings“, null, initialValues);
settings.setId((int) id);
return settings;
}

public long updateSettings(Settings settings) {
ContentValues updateValues = createContentValues(settings);
return database.update(„Settings“, updateValues, „id=“ + settings.getId(), null);
}

public Settings fetchSetting() {
try {
Cursor cursor = database.query(„Settings“,
new String[] {
„id“, „name“
}, null, null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
Integer id = cursor.getInt(cursor.getColumnIndexOrThrow(„id“));
String name = cursor.getString(cursor.getColumnIndexOrThrow(„name“);
Settings settings = new Settings(id, name);
return settings;
} else {
return new Settings();
}
} catch (SQLiteException e) {
return new Settings();
}
}

private ContentValues createContentValues(Settings settings) {
ContentValues values = new ContentValues();
values.put(„name“, settings.getName());
return values;
}
}

Im Konstruktor, der in der onCreate-Methode der SettingsActivity aufgerufen wurde, wird zunächst überprüft, ob die Datenbank-Datei bereits angelegt wurde. Ist dies nicht der Fall, werden die Settings der Anwendung Initial gespeichert und zusätzlich zum Öffnen der Datenbank muss die Settings Tabelle mittels SQL-Statement generiert werden.

Der nächste Aufruf der SettingsActivity lädt die bereits gespeicherten Settings über die fetchSettings-Methode. Hier wird auf der Datenbank eine Query gefeuert, der die gewünschten Properties mitgegeben werden und die sonst keinerlei Einschränkung enthält, es werden also wirklich alle Settings geladen. Das Ergebnis ist über einen Cursor zugreifbar, der zunächst über die moveToFirst-Methode auf die erste Position gesetzt werden muss. Die über den Cursor zugreifbaren Properties werden in einem Settings Objekt gespeichert, bzw. falls die Datenbank noch keinen Eintrag enthält wird ein neues Settings Objekt erzeugt und der SettingsActivity geliefert.

Werden die Einstellungen vom Benutzer geändert und der Speicher-Button betätigt, wird das Settings-Objekt entweder Initial in der Datenbank gespeichert oder aktualisiert. Dies hängt davon ab, ob die Settings bereits eine eindeutige Id enthalten. Bei dem Update-Statement wird eine Where Clause benötigt, die eine Einschränkung auf die gegebene Id – also den Primär Schlüssel – enthält. Andernfalls wird das Objekt neu erzeugt und die von der Datenbank vergebene Id in dem Settings-Objekt gespeichert.

Beim Insert bzw. Update wird nicht das Settings-Objekt selber verwendet sondern das generische Objekt ContentValues, dem per put-Befehl die einzelnen Properties zugewiesen werden können.

Sobald man nun noch die Permissions im Android-Manifest richtig gesetzt hat, steht dem ersten Probelauf jetzt nichts mehr im Wege.

<uses-permission android:name= „android.permission.WRITE_EXTERNAL_STORAGE“/>
<permission-group android:name=“android.permission-group.STORAGE“/>

Insgesamt lässt sich sagen, dass der Zugriff auf die SQLite Datenbank bei Android Anwendungen recht komfortable gestaltet ist, gerade weil hier alle benötigten Libraries bereits gegeben sind. Nur Schade, dass man nicht direkt mit Entities arbeiten kann, sondern dass diese auf die Datenbank Inhalte gemappt werden müssen. Es wäre angenehmer auf die Cursor zu verzichten und besser direkt über die Entity an die gewünschten Properties zu gelangen – ebenso wie beim Mappen der Properties auf die ContentValues ein zusätzlicher Schritt benötigt wird.

Der Event Admin Service

Veröffentlicht in: Eclipse, Java, RCP | 0

Bei der Entwicklung von Software-Architekturen bekommt die Verwendung von Events und EventHandlern eine immer größere Bedeutung. Betrachtet man CQRS – Axon Framework – oder die CDI Events von JEE 6, so ist hier in letzter Zeit ein klarer Trend zu erkennen.

Umso erstaunlicher, dass das Konzept der Events schon seit Oktober 2005 im OSGI Standart – OSGI Service Compendium – existiert. So wird es vom Event Admin Service ermöglicht, Events zu versenden. Diese werden von EventHandlern, die als OSGI Service registriert seien müssen, behandelt. Da es sich bei dem Event Admin Service um einen OSGI Service handelt, kann dieser z.B. über die ServiceRegistry abgefragt werden:

ServiceReference serviceReference = context.getServiceReference(EventAdmin.class.getName());
EventAdmin eventAdmin = (EventAdmin) context.getService(serviceReference);

Um nun ein Event zu versenden, wird wie bei JMS (Java Message Service) ein Topic definiert, aufgrundlage dessen sich entscheidet, welche EventHandler auf das Event reagieren. Im Vergleich hierzu haben sich die CDI- und CQRS-Events deutlich weiterentwickelt, da es sich bei diesen um POJOs handelt, die bestimmen welche EventHandler zuständig sind. Die CDI-Events können zusätzlich noch Annotations als Qualifier verwendet werden, falls zu einem Event verschiedene Handler existieren, die aber nicht alle ausgeführt werden sollen. Sowohl die CDI- als auch die CQRS-Events beinhalten die Informationen, die vom Handler zur weiteren Verarbeitung benötigt werden. Beim Event Admin Service muss stattdessen ein Dictonary erstellt werden, welches diese Informationen in Form einer Map beinhaltet:

Dictionary eventProperties = new Hashtable();
eventProperties.put(„message“, „Hello World“);
Event event = new Event(„my/topic“, eventProperties);
eventAdmin.sendEvent(event);

Der EventHandler selbst muss das gleichnamige Interface implementieren und als OSGI Service mit dem entsprechenden Topic registriert werden. Hier die recht simple Implementierung des EventHandlers:

public class MyEventHandler implements EventHandler {
@Override
public void handleEvent(Event event) {
// handle the event
}
}

Folgendermassen wird nun der EventHandler als OSGI-Service registriert:

Dictionaryproperties = new Hashtable();
properties.put(EventConstants.EVENT_TOPIC, „my/topic“);
context.registerService(MyEventHandler.class.getName(), new MyEventHandler(), properties);

Um den Event Admin Service verwenden zu können, werden folgende Bundles benötigt:

  • org.eclipse.osgi
  • org.eclipse.osgi.services
  • org.eclipse.equinox.event
  • org.eclipse.equinox.ds

Events sind ein sehr gutes Mittel um Softwarearchitekturen zu entkoppeln. So ist es recht verwunderlich, dass es so lange gedauert hat, bis sich dieses Architektur-Pattern auch in anderen Frameworks wiedergefunden hat.

Restlet – REST im OSGi Kontext

Veröffentlicht in: Eclipse, Java, RCP | 2

Der nachfolgende Artikel beschäftigt sich mit der Erstellung einer Client-Server Architektur, die mittels REST-Schnittstelle kommuniziert und auf dem OSGi-Framework aufbaut. Als REST-Framework wird http://www.restlet.org/ verwendet.

Zur Zeit liegt Restlet in der aktuellen Version 2.0.10 unter http://www.restlet.org/downloads/ zum Download bereit.

Zunächst wird für die Eclipse IDE eine neue Target-Platform erstellt, der als Location die Software Site http://download.eclipse.org/releases/indigo – die Indigo Update Site – hinzugefügt und hier die Equinox Target Components ausgewählt werden. Weiterhin wird als zweite Location auf das lib-Directory des entpackten Restlet-Archives verwiesen.

Kommen wir nun zur eigentlichen Programmierung. Insgesamt werden drei Plug-in Projects benötigt, jeweils ein Client/Server-Projekt und ein Projekt, welches die POJOs beinhaltet, die sowohl auf dem Client, als auch auf dem Server benötigt werden. In Letzteres wird eine User-Klasse erstellt, die zunächst nur aus Vor- und Nachname besteht.

Auf der Server-Seite wird eine eigene Implementierung der Klasse ServerResource benötigt, die eine Methode bereitstellt, um einen beliebigen User zu erhalten:

public class UserResource extends ServerResource {

@Get
public User getUser() {
return new User(„Max“, „Mustermann“);
}
}

Um beim Starten bzw. Stoppen des Server-Bundles den von Restlet bereitgestellten Jetty-Server hoch bzw. herunterzufahren, sind folgende zusätzliche Einträge im Server-Activator nötig:

    private Server server;
private int port = 8182;

public void start(BundleContext bC) throws Exception {
// … do what you have to do, while starting
server = new Server(Protocol.HTTP, port, UserResource.class);
server.start();
}

public void stop(BundleContext bC) throws Exception {
// … do what you have to do, while stopping …
server.stop();
}

Um den Server starten zu können, wird eine OSGI-Framework Run-Configuration benötigt, die zusätzlich zu den Equinox Target Components die Restlet-Bundles beinhaltet. Sollten einige der Restlet-Extensions Probleme bereiten, können diese getrost weg geklickt werden. Nun kann die soeben erstellte Run-Configuration ausgeführt und unter der URL http://localhost:8182/ die UserResource aufgerufen werden.

Natürlich sollte die UserResource nicht nur im Browser abrufbar sein, sondern von einem OSGi-Client konsumiert werden. Daher wird noch ein weiteres Bundle benötigt, wobei die Kommunikation mit dem Server denkbar einfach einfällt:

ClientResource clientResource = new ClientResource(„http://localhost:8182“);
User user = clientResource.get(User.class);

Wie man sieht, verwendet der Client ebenfalls das User-POJO, wobei die Serialisierung bzw. Deseriablisierung des User-Objekts vom Restlet-Framework umgesetzt wird. Dadurch lässt sich schnell und einfach eine REST-Kommunikation im OSGI Kontext realisieren. Zusätzlich zum OSGI Framework (Edition for Java SE) steht das Restlet-Framework noch für die Google AppEngine, GWT und die Android Entwicklung zur Verfügung. Schön das man nicht für jede Plattform ein unterschiedliches REST-Framework verwenden muss.

Eclipse Libra

Veröffentlicht in: Eclipse, Java, RCP | 0

Bei Eclipse Libra handelt es sich um ein Unter-Projekt vom Web-Tools-Platform-Projekt (WTP), welches den Funktionsumfang von WTP und dem Plug-in Development Environment (PDE) vereint. So können in einem WTP-Projekt auch die Werkzeuge eines PDE-Projektes genutzt werden.

Für die Installation von Libra unter der aktuellen Eclipse Version Indigo reicht es, auf der Indigo Update-Site die Bundles OSGI Bundle Facet und WAR Products zu installieren.

Erstellt man ein Dynamic Web Project können auf der ersten Seite des Wizards unter Configuration die Project Facets konfiguriert werden. Nach der Installation der oben genannten Pakete ist hier der Menüpunkt OSGI Bundle hinzugekommen, den man auswählt. Dies führt dazu, dass im Wizard eine weitere Seite verfügbar ist, auf der Details für das Manifest angegeben werden können. Das Manifest selbst befindet sich im META-INF Verzeichnis des Web-Ordners, also eine Hierarchiestufe tiefer als in herkömmlichen OSGI Bundles.

Um das Web-Projekt testen zu können, ist zumindest ein Servlet nötig, welches in der web.xml definiert wird:

  <servlet>
<servlet-name>hello</servlet-name>
<servlet-class>test.TestServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

Das Servlet selber erbt von der Klasse javax.servlet.http.HttpServlet und überschreibt die doGet-Methode.

Sofern nicht bereits eine passende Target-Platform definiert wurde, wird es spätestens bei der Implementierung des Servlet zu einem Compile-Fehler kommen, da javax.servlet nicht aufgelöst werden kann. Zur Ausführung des Web-Projektes mit dem OSGI-Framework kann die Target-Platform von http://wiki.eclipse.org/Gemini/Web verwendet werden.

Bei Gemini selbst, handelt es sich um modulare Enterprise-Technologien, welche speziell für das OSGI-Framework konzipiert wurden.

Für die Installation werden folgende Archive benötigt: http://www.eclipse.org/downloads/download.php?file=/equinox/drops/R-3.7.1-201109091335/org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar und http://www.eclipse.org/downloads/download.php?file=/gemini.web/release/GW/2.0.1.RELEASE/gemini-web-2.0.1.RELEASE.zip.

Anschließend wird gemini-web in ein beliebiges Verzeichnis entpackt und org.eclipse.osgi.jar in eben dieses kopiert. Nun wird hier noch das Verzeichnis configuration erstellt und die folgenden beiden Konfigurationsdateien hinein kopiert:

http://wiki.eclipse.org/images/0/0b/Config.ini.2.0.1.zip

http://wiki.eclipse.org/images/3/32/Java6-server.profile.zip

Jetzt wird in Eclipse eine neue Target-Platform (Window / Preferences / Target Platform) erstellt, deren Location auf das gemini-web Verzeichnis verweist.

Als nächstes wird eine neue Run-Configuration unter dem Punkt OSGI Framework erstellt. Unter den VM-Arguments wird folgender Parameter hinzugefügt:

-Dosgi.java.profile=file:configuration/java6-server.profile

Außerdem muss als Working directory das gemini-web Verzeichnis angegeben werden.

Weitere Schritte sind nicht nötig, nun kann die Run-Configuration ausgeführt werden und die Web Applikation ist unter http:127.0.0.1:8080/<application-name>/hello verfügbar. Der Applikations-Name wird hier standardmäßig genommen, will man dies ändern, so ist der Parameter Web-ContextPath im Manifest anzupassen.

Die Facets von Eclipse Libra lassen sich nicht nur in WTP-Projekten nutzen. Es ist durchaus möglich, dieses Facet z.B. einem Maven-Projekt zuzuweisen, um hier die PDE Funktionalitäten nutzen zu können. Der Nachteil zu Apache-Felix ist hierbei allerdings, dass die pom.xml und das Manifest separat gepflegt werden müssen. Werden also Maven-Dependendies hinzugefügt, müssen diese auch im Manifest eingetragen werden, damit die Abhängigkeiten zur Laufzeit verwendbar sind.

Erstellung einer Android Anwendung

Veröffentlicht in: Android, Java | 0

Um ein Android-Projekt zu Erstellen benötigt man zunächst das Android SDK, welches zur Zeit in der aktuellen Version 3.2 zum Download unter http://developer.android.com/sdk/index.html bereit liegt.
Weiterhin wird das ADT-Plugin für Eclipse benötigt, dass die Erstellung eines Android Projektes extrem vereinfacht und das Debuggen ermöglicht. Zusätzlich kann die Anwendung zertifiziert und als apk-Datei exportiert werden, so dass diese auf einem Android Gerät installiert werden kann. Die aktuelle Version des ADT-Plugins ist 12.0.0 und kann unter folgender Update Site https://dl-ssl.google.com/android/eclipse/ installiert werden.
Anschließend stellt man nun in Eclipse unter Window, Preference, Android die SDK Location ein.

Im Anschluss kann das erste Android-Projekt erstellt werden. Hierzu reich ein Projekt- und Applikations-Name, ein Package und das gewünschte Android SDK. Bei letzterem Punkt sollte man beachten, dass für das eigene Android-Gerät meist noch nicht die neuste Firmware-Version zum Download bereit liegt. Für das Samsung Galaxy ist die Version 2.3.3 verfügbar, während man bereits das Android SDK 3.2 verwenden kann.
Sofern man sich eine Activity generieren lässt, kann nun das Projekt direkt im Emulator ausgeführt werden und man bekommt einen HelloWorld Text zu Gesicht.

Das Design der Anwendung, sofern man bei der HelloWorld Anwendung von Design reden kann, befindet sich unter /res/layout/main.xml. Es wird ein einfaches LinearLayout verwendet, welches vertikal ausgerichtet ist und die komplette Größe einnimmt. Das Layout wird einer TextView zugewiesen, die für die HelloWorld Ausgabe zuständig ist.

<?xml version=“1.0″ encoding=“utf-8″?>
<LinearLayout    xmlns:android=“http://schemas.android…“
android:orientation=“vertical“
android:layout_width=“fill_parent“
android:layout_height=“fill_parent“   >
<TextView
android:layout_width=“fill_parent“
android:layout_height=“wrap_content“
android:text=“@string/hello“
/>
</LinearLayout>

Der Text selbst ist externalisiert und befindet sich unter /res/values/strings.xml.

Schaut man sich den Erzeugten Code an, so befindet sich in der onCreate-Methode der erstellten Activity folgende Zeile:

setContentView(R.layout.main);

Die hier verwendete Klasse R wird vom ADT-Plugin angelegt und verwaltet – werden weitere grafische Komponenten im XML angelegt, werden diese hier gepflegt. Das Design kann zwar auch im Code verändert werden, aufgrund einer klaren Trennung sind die XML-Dateien aber vorzuziehen.

Will man die Anwendung nicht nur im Emulator testen, sondern auf seinem Android-Gerät, kann man diese bequem als apk-Datei exportieren, auf sein Handy einspielen und installieren.

Auch wenn es sich hierbei nur um eine HelloWorld Anwendung handelt, ist es doch erstaunlich, wie schnell man diese erstellt, auf sein Handy aufgespielt und getestet hat. Lediglich die Langsamkeit des Emulators treibt einen ab und an in die Verzweiflung. Aber der erste Start ist schnell gemacht und man kann sich um die erste richtige Anwendungen kümmern.

Veröffentlichung einer Fremdbibliothek als OSGI Plugin

Veröffentlicht in: Eclipse, Java, RCP | 0

Wer mit OSGI arbeitet kennt das. Man findet eine Library die genau das tut, was man benötigt, es liegt nur keine OSGI-Plugin-Variante zum Download bereit.

Eine Möglichkeit ist nun das Jar-Archive zu downloaden und in Eclipse ein neues Projekt Plug-in from Existing JAR Archive zu erstellen. Im nächsten Schritt können nun External JAR Archives hinzugefügt werden. Benötigt man verschiedene Libraries, bzw. hat die gewünschte Library weitere Dependencies, können diese hier direkt hinzugefügt werden. Selbst wenn die verschiedenen Archive eine gleiche package-Struktur aufweisen, ergibt dies keine Probleme, die class-Dateien werden in die entsprechenden Packages gemergt.

Theoretisch ist man nun fertig und man kann die gewünschte Library verwenden. Allerdings werden sich dann die Kollegen bedanken, da sie nun ein weiteres Projekt auschecken müssen. Also exportiert man das soeben erstellte Projekt noch als Deployable plug-ins and fragments, legt das erstellte OSGI-Plugin in die Target-Plattform und kann nun das Projekt wieder schließen.

Sofern die Library nicht in einem Maven-Kontext benötigt wird, ist man nun tatsächlich fertig, andernfalls muss man sie noch manuell in ein Maven-Repository legen.

Eine zweite Möglichkeit, um aus einer Fremdbibliothek ein OSGI-Plugin zu erstellen, ist die Verwendung des Maven Plugins Felix von Apache. Hier wird eine pom.xml der folgenden Art benötigt:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>de.example</groupId>
<artifactId>de.example.project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>de.example.project</name>
<packaging>bundle</packaging>

<dependencies>
<dependency>
<groupId>de.example</groupId>
<artifactId>de.example.dependency</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/xxx.jar</systemPath>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

Wichtig ist hierbei, dass packaging den Wert bundle erhält, wofür allerdings das Plugin org.apache.felix korrekt eingebunden seien muss. Zu beachten ist hier der Wert true für Extensions.

Als dependency wird im obigen Beispiel die Library de.example.dependency benötigt, die sich in dem aktuellen Maven-Projekt in dem Jar-Archive xxx.jar befindet. Der Ort des Jar-Archives wird im systemPath definiert.

Die class-Dateien der auf diese Weise angegebenen Dependencies werden beim Bauen des Maven-Projektes mittels package mit ins target gelegt. Weiterhin erstellt bei obiger Definition das Plugin Felix ein Manifest in dem alle packages der eingebundenen Libriries exportiert werden. Möchte man diese Einschränken, so können die zu exportierenden packages explizit in der pom-Datei unter Export-Package definiert werden.

Als Fazit lässt sich sagen, dass beide Varianten Arbeit bedeuten und man stehts hoffen sollte, dass die benötigten Libraries bereits als OSGI-Plugin zur Verfügung stehen. So lohnt sich immer zuerst ein Blick auf das SpringSource Enterprise Bundle Repository, da hier bereits viele Libraries als OSGI-Bundle vorliegen: http://ebr.springsource.com/repository/app/

Internationalisierung in Java

Veröffentlicht in: Java | 0

Anders als vielleicht vermutet, geht es in diesem Beitrag nicht um die obligatorischen länderspezifischen Property-Dateien, sondern um die Verwendung von Ländern, Landesvorwahlen, Währungen und  landestypischen Formattern in Java-Anwendungen.

Benötigt man innerhalb eines Java  Programmes eine vollständige Liste von Ländern, stößt man schnell auf die Klasse java.util.Locale. Die hier definierten Länder-Konstanten sind zwar recht begrenzt, es gibt hier aber die statische Methode:

Locale.getAvailableLocales()

mit der man sich – denkt man – eine vollständige Liste von allen Locales liefern lassen kann. Tatsächlich werden hier aber nur 152 Locales zurückgegeben. Verwendet man stattdessen folgenden Aufruf:

for (String country : Locale.getISOCountries()) {
Locale lc = new Locale(„“, country);
}

kommt man auf insgesamt 247 Locales. Interessant hierbei ist, dass man sich sowohl den kompletten Namen des Landes, als auch den ISO 3166-2 bzw. ISO 3166-3 Code über die Locale liefern lassen kann.

locale.getDisplayCountry() //Ländername
locale.getCountry() // ISO 3166-2 Code
locale.getISO3Country() // ISO 3166-3 Code

Außer der Klasse Locale ist auch noch die Google Library libphonenumber.jar interessant, die zur Zeit in der Version 3.8 vorliegt. Hier gibt es die PhoneNumerUtil, die über den ISO 3166-2 Ländercode die Landesvorwahl liefert:

int countryCode = PhoneNumberUtil.getInstance().getCountryCodeForRegion(locale.getCountry());

Diese Library kann über maven verwendet werden:

<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>3.8</version>
</dependency>

Möchte man Zahlen landestypisch darstellen, kann die Klasse java.text.DecimalFormat verwenden werden, in dem man die entsprechende Locale übergibt. Als Beispiel wird hier die deutschsprachige Schweiz verwendet, da hier der Separator der 1000er Stelle abweichend zur deutschen Schreibweise ist:

DecimalFormat.getInstance(new Locale(„de“, „CH“).format(1234.123); //liefert: 1’234.123

Genauso einfach ist es, sich die passend formatierte Währung inklusive Währungssymbol an der korrekten Stelle ausgeben zu lassen:

NumberFormat.getCurrencyInstance(new Locale(„de“, „CH“).format(1234.123); // SFr. 1’234.12

In der deutschen Variante befindet sich das Euro-Symbol am Ende und die 1000er Stelle wird wie erwartet mit einem Punkt separiert:

NumberFormat.getCurrencyInstance(new Locale(„de“, „DE“).format(1234.123); // 1.234,12 €

Möchte man statt der lokalen die internationale Schreibweise verwenden, läßt man bei der Erstellung der Locale den Parameter für die Sprache weg. So ergibt sich für obige Beispiele folgende Ausgabe:

DecimalFormat.getInstance(new Locale(„“, „CH“).format(1234.123); //liefert: 1,234.123
NumberFormat.getCurrencyInstance(new Locale(„“, „CH“).format(1234.123); // CHF 1,234.12

Die in diesem Beitrag erwähnten Klassen bieten noch weitere Möglichkeiten für landesspezifische Darstellungen. Bevor man sich die Arbeit macht und hier eigene Komponenten entwickelt, lohnt sich ein Blick auf gegebenen Java Funktionalitäten.

Leider gibt es aber auch hier Grenzen. So treten grade für die Schweiz Probleme auf, da auf Schweizer Portalen eine Mischung aus Internationaler und Lokaler Darstellung gewählt wird. Aufgrund der Unterscheidung zwischen französischer, italienischer und deutscher Schweiz wird hier das internationale Währungssymbol CHF mit dem Hochkomma als Separator der 1000er Stellen kombiniert. Hierfür konnte ich leider keine fertige Java-Lösung finden.

Aufsetzten eines CQRS Projektes mit dem Axon-Framework

Veröffentlicht in: Java | 0

Das Axon-Framework ist eine Implementierung des CQRS – Command Query Responsiblity Segregation – Pattern, welches zur Erstellung von skalierbaren und erweiterbaren Software-Architekturen dient. Zum Einsatz kommen hier Commands, Events und Querys, die in verschiedenen Bereichen der Software-Architektur verwendet werden.

Will der Benutzer über die UI mit dem System interagieren, stehen zunächst nur Commands zur Verfügung, die mit Hilfe von einem CommandBus ausgeführt werden. Für die Darstellung von Informationen werden wiederum Querys verwendet, die unveränderbare DTOs – Data Transfer Objects – liefern. Für diesen Teilbereich sind im Axon-Framework keine Implementierungen verfügbar, diese müssen aufgrund verschiedenster Einsatzbereiche selbst erstellt werden.

Die Commands werden in den CommandHandlern behandelt, die sich in einem von der UI getrennten Bereich der Software-Architektur befinden.

Hier können nun entweder direkt Events über einen EventBus verbreitet werden, wobei im Gegensatz zu Commands mehrere EventHandler auf ein Event reagieren können. Die Alternative zur direkten Verwendung von Events ist die Benutzung von Aggregates. Diese können entweder neu erzeugt und anschließend in einem Repository gespeichert werden oder es wird ein bestehendes Aggregate aus einem Repository geladen. Erst nachdem alle Operationen auf einem Aggregate ausgeführt wurden, werden die dazugehörigen Events veröffentlicht und von den EventHandlern verarbeitet. Dies passiert automatisch, sofern das Aggregate aus einem Repository geladen wurde, bzw. sobald ein neu erzeugtes Aggregate gespeichert wurde.

Die EventHandler sind unter anderem für die Speicherung von Daten in einer Datenbank zuständig. Dem Benutzer werden über diesen Weg allerdings keine Daten angezeigt, sondern lediglich manipuliert. Zum Anzeigen von Datenbank-Inhalten werden die anfangs erwähnten Querys und DTOs verwendet.

Kommen wir nun zu einem Beispiel, bei dem zunächst ein Benutzer angelegt und in einem zweiten Schritt manipuliert werden. Es wird hierbei ein UserAggregate eingesetzt, d.h. die entsprechenden CommandHandler Arbeiten nicht direkt mit Events, sondern verwenden stattdessen das Domain Model.

Zunächst einmal wird folgende Dependency benötigt:

<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>1.0</version>
</dependency>

Für die Integration des Axon-Frameworks in die Anwendung wird in diesem Beispiel Spring verwendet, wobei außer den Spring Bibliotheken keine weiteren Abhängigkeiten benötigt werden.

Hierfür wird der AnnotationCommandHandlerBeanPostProcessor zur Erkennung von Java-Beans mit der CommandHandler-Annotation, der CommandBus und zwei CommandHandler im Spring Kontext registriert. Letztere dienen zum Anlegen bzw. Aktualisieren des Benutzers.

<bean class=“org…AnnotationCommandHandlerBeanPostProcessor“ />

<bean id=“commandBus“ class=“org…SimpleCommandBus“/>
<bean id=“createHandler“ class=“..CreateUserHandler“ />
<bean id=“updateHandler“ class=“..UpdateUserHandler“ />

Der CreateUserHandler erzeugt zunächst ein neues UserAggregate, führt auf diesem die save-Operation aus und speichert es im UserRepository. Events die beim Erzeugen und innerhalb der save-Operation generiert werden, werden erst publiziert wenn das UserAggregate dem Repository mittels add hinzugefügt wird. Es folgt die Implementierung des CreateUserHandlers, der auf das CreateUserCommand reagiert:

@Component
public class CreateUserHandler {

@Autowired
private UserRepository userRepository;

@CommandHandler
public void handle(CreateUserCommand command) {
UserAggregate userAggregate = new UserAggregate(command.getUser());
userAggregate.save(command.getUser());
userRepository.add(userAggregate);
}
}

Das CreateUserCommand wird auf dem CommandBus ausgeführt, welcher mit Hilfe der Inject-Annotation in die gewünschte Klasse injiziert wird. Das Axon-Framework sorgt dafür, dass der gewünschte CommandHandler mit dem Command  als Übergabe-Parameter aufgerufen wird. Zum Ausführen des Commands wird folgende Zeile benötigt:

commandBus.dispatch(new CreateUserCommand(new User()));

Nun wird das UserAggregate erzeugt, dass von der Klasse AbstractEventSourcedAggregateRoot erbt. Wird ein UserAggregate erzeugt, wird gleichzeitig ein AggregateIdentifier angelegt, anhand dessen das Aggregate beim Speichern im UserRepository identifiziert wird. Jede Änderung des Aggregates die im Repository gespeichert wird, erhält eine neue Version, die von der eigenen Repository Implementierung gespeichert werden kann. Neben der Erzeugung des Identifiers wird im Konstruktor ein Event angelegt, dass in der zu implementierenden handle-Methode behandelt wird. Die Events, die in diesem Beispiel verwendet werden, erben alle vom DomainEvent, welches hier als Übergabe-Parameter dient.

Im CommandHandler wurde weiterhin die save-Methode aufgerufen, auch diese wird hier implementiert, wobei ein weiteres Event registriert wird. Hier nun der Beispiel-Code:

public class UserAggregate extends AbstractEventSourcedAggregateRoot {

public UserAggregate(User user) {
super(new UserAggregateIdentifier(user.getDsn()));
apply(new UserCreatedEvent(user));
}

@Override
protected void handle(DomainEvent event) {
// do what you have to do with the created user
}

public void save(User user) {
registerEvent(new UserSaveEvent(user));
}
}

Wie anfangs beschrieben, werden die Events erst ausgeführt, sobald das Aggregate im Repository gespeichert wird.

Folgendermaßen kann nun das UserRepositorys aussehen:

public class UserRepository extends AbstractRepository{

@Override
protected UserAggregate doLoad(AggregateIdentifier aggregateIdentifier, Long version) {
// load aggregate from a map for example
}

@Override
protected void doSave(UserAggregate aggregate) {

// save aggregate in a map for example
}
}

Im CommandHandler wird beim Aufruf der add-Methode des Repositorys die abstrakte doSave-Methode ausgeführt und das Aggregate gespeichert. Anschließend werden die registrierten Events ausgeführt. Hierbei wird das UserCreateEvent im Aggregate selber behandelt, dass UserSaveEvent aber im folgenden SaveEventListener, welcher lediglich zum Speichern des im Event befindlichen Users in der Datenbank zuständig ist:

@Component
public class SaveEventListener {

@Autowired
private HibernateTemplate hibernateTemplate;

@EventHandler
public void handle(UserSaveEvent event) {
hibernateTemplate.saveOrUpdate(event.getUser());
}
}

Für die Behandlung von Events sind folgende Spring-Konfigurationen vorzunehmen:

<bean class=“class=“org..AnnotationEventListenerBeanPostProcessor“ />
<bean id=“eventBus“ class=“org..SimpleEventBus“ />
<bean id=“updateListener“ class=“..SaveEventListener“ />

Interessant dürfte noch die Umsetzung eines UpdateUserHandler sein, da hierfür ein bestehendes Aggregate aus dem UserRepository geladen wird:

@Component
public class UpdateUserHandler {

  @Autowired
  private UserRepository userRepository;

  @CommandHandler
  public void handle(UpdateUserCommand command) {
    UserAggregate userAggregate = userRepository.load(new UserAggregateIdentifier(command.getIdentifier()), command.getVersion());
    userAggregate.save(command.getUser());
  }
}

Nachdem die save-Operation mit aktualisierten Benutzer-Daten aufgerufen wurde, muss das Aggregate nicht erneut im Repository gespeichert werden. Das Axon-Framework erkennt automatisch, dass es sich hierbei um eine neue Version des geladenen UserAggregates handelt und führt nach vollständigem Durchlauf des CommandHandlers alle registrierten Events aus.

In diesem Fall wird erneut das UserSaveEvent aufgerufen, der entsprechende Listener ausgeführt und damit die Änderung des Benutzers in der Datenbank gespeichert.

Dieses Beispiel soll einen Einblick in die Funktionsweise von CQRS und das Axon-Framework liefern, wobei einige Teilbereiche, wie z.B. der EventStore, ungenannt blieben. Ein Blick in den Reference-Guide sei allemal empfohlen.