OpenOffice-Dokumente mit NOA Verarbeiten

Eingetragen bei: 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

Eingetragen bei: 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

Eingetragen bei: 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

Eingetragen bei: 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.