Posts Tagged: ‘extension library’

OpenNTF XPages Extension Library und Domino 9.0.1 FP3 / FP4

26. August 2015 Posted by Bernd Hort

Auf der Download Seite für die OpenNTF XPages Extension Library befindet sich ein kleiner Hinweis bei den letzten beiden Releases 901v00_12.20150311-1316 and 901v00_13.20150611-0803:

“Please be aware of technote SWG21696682 as it affects the installation of the Extension Library if the UpdateSite.nsf method is used.”

Mit diesem Hinweis gemeint ist, dass wenn Sie eine UpdateSite.nsf auf einem Domino 9.0.1 Server mit FP3 oder FP4 verwenden, um eine Extension Library zu installieren, Sie in Schwierigkeiten sind.

Bei beiden Fix Packs verhindert eine Änderung in den Sicherheitseinstellungen für die Verwendung des Befehls java.classforName in der JVM das eine Extension Library geladen wird. Auf der Serverkonsole gibt es keine Fehlermeldung oder sonsts einen Hinweis. Die Extension Library steht einfach nicht zur Verfügung.

Für das FP4 ist die Lösung sehr einfach: Installieren Sie den JVM Patch SR16FP7 so wie in der IBM technote “Interim Fixes & JVM patches for 9.0.1.x versions of IBM Notes, Domino, iNotes & Notes Browser Plug-in” beschrieben ist.

Nach der Installation des JVM Patch SR16FP7 werden Sie auch wieder die geliebte Meldung auf der Konsole sehen: “...NSF Based plugins are being installed in the OSGi runtime...”


Quick-n-Dirty: Non Closable Dialog for ExtLib

19. Oktober 2012 Posted by Sven Hasselbach

I have created a dirty hack for dialogs which prevents users to close the dialog by pressing the escape key or the close button. In my first approach  (StackOverflow.com)the hack has overwritten all dialogs for the whole XPage. This version allows to enable / disable it per dialog.

To use this hack you have to add the CSJS library to your XPage and call the dialog with an additional parameter ‘nonclosable‘:

XSP.openDialog("#{id:dialogNonClosable}", {'nonClosable': true } );

The hack was tested with IE 9 and FF 16, ExtLib 8.5.3.20120605-0921. Here is the CSJS library you have to attach to your XPage:

/**
 * ExtLib Dialog Hack to enable NonClosable Dialogs
 * 
 * This function overrides the exsiting openDialog method of the XSP object. It
 * adds a new option named 'nonclosable'
 * 
 * To open a non closable dialog, you have to use it like this
 * 
 * XSP.dialog('#{id:dialog}', {'nonclosable': true } );
 * 
 * @category CSJS
 * @category ExtLib
 * @author Sven Hasselbach
 */

function injectExtLibHack() {
    XSP.openDialog = function xe_od(dialogId, options, params) {
        dojo.addOnLoad( function() {
            var created = false;
            var dlg = dijit.byId(dialogId);
            if (!dlg) {
                var type = XSP._dialog_type;
                try {
                    if (options['nonClosable'])
                        type = XSP._dialog_type_nonClosable;
                } catch (e) {
                }

                options = dojo.mixin( {
                    dojoType : type || "extlib.dijit.Dialog"
                }, options);

                dojo.parser.instantiate( [ dojo.byId(dialogId) ], options);
                dlg = dijit.byId(dialogId);
                created = true;
            } else {
                if (dlg.keepComponents) {
                    dlg.show();
                    return;
                }
            }

            var onComplete = function() {
                dlg.show();
            };
            var axOptions = {
                params : dojo.mixin( {
                    $$showdialog : true,
                    $$created : created
                }, params),
                onComplete : onComplete,
                formId : dialogId
            };
            dlg.attr("content", "<div id='" + dialogId + ":_content'></div>");
            XSP.partialRefreshGet(dialogId + ":_content", axOptions);
            if (dojo.isIE < 8) {
                dojo.query(".lotusDialogBorder").style("width", "500px");
            }
        });
    }

    dojo.provide("extlib.dijit.OneUIDialogNonCloseableDialog");
    dojo.require("extlib.dijit.Dialog");
    dojo.declare(
       "extlib.dijit.OneUIDialogNonCloseableDialog",
       extlib.dijit.Dialog,
       {
          baseClass: "",
          templateString: dojo.cache("extlib.dijit", "templates/OneUIDialog.html"),
          disableCloseButton: true,
          _onKey: function(evt){
          if(this.disableCloseButton &&
             evt.charOrCode == dojo.keys.ESCAPE) return;
             this.inherited(arguments);
          },
          _updateCloseButtonState: function(){
             dojo.style(this.closeButtonNode,
             "display",this.disableCloseButton ? "none" : "block");
          },
          postCreate: function(){
             this.inherited(arguments);
             this._updateCloseButtonState();
             dojo.query('form', dojo.body())[0].appendChild(this.domNode);
          },
          _setup: function() {
             this.inherited(arguments);
             if (this.domNode.parentNode.nodeName.toLowerCase() == 'body')
                dojo.query('form', dojo.body())[0].appendChild(this.domNode);               
          }        
       }
    );

    // This is used by the picker dialog to grab the correct UI
    XSP._dialog_type_nonClosable="extlib.dijit.OneUIDialogNonCloseableDialog";

}

XSP.addOnLoad( injectExtLibHack );

And here is an example XPage which demonstrates how to use the hack:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xe="http://www.ibm.com/xsp/coreex">
    <xp:this.resources>
        <xp:script src="/dialogHack.js" clientSide="true"></xp:script>
    </xp:this.resources>

    <xp:button value="Show Dialog Closable" id="button1">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script><![CDATA[
            XSP.openDialog("#{id:dialogClosable}");]]></xp:this.script>
        </xp:eventHandler>
    </xp:button>
    <xp:button value="Show Dialog Non Closable" id="button2">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script>
                <![CDATA[
                    XSP.openDialog("#{id:dialogNonClosable}", {'nonClosable': true } );
                ]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>

    <xe:dialog id="dialogClosable" title="This is a closable dialog">
        <xe:dialogContent>
            You can close me via Escape or the close button.
        </xe:dialogContent>
        <xe:dialogButtonBar>
            <xp:button value="Ok" id="button3"
                styleClass="lotusFormButton">
                <xp:eventHandler event="onclick" submit="false">
                    <xp:this.script><![CDATA[XSP.closeDialog('#{id:dialogClosable}')]]></xp:this.script>
                </xp:eventHandler>
            </xp:button>
        </xe:dialogButtonBar>
    </xe:dialog>

    <xe:dialog id="dialogNonClosable" title="This is a non-closable dialog">
        <xe:dialogContent>
            Try to close me via Escape or the close button ;-) 
        </xe:dialogContent>
        <xe:dialogButtonBar>
            <xp:button value="Ok" id="button4"
                styleClass="lotusFormButton">
                <xp:eventHandler event="onclick" submit="false">
                    <xp:this.script><![CDATA[XSP.closeDialog('#{id:dialogNonClosable}')]]></xp:this.script>
                </xp:eventHandler>
            </xp:button>
        </xe:dialogButtonBar>
    </xe:dialog>

</xp:view>

This is the “standard” ExtLib dialog:

And this is the “non-closable” version:

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Quick-n-Dirty: Control Dojo generation for individual XPages

17. Oktober 2012 Posted by Sven Hasselbach

Another interesting question has been asked on stackoverflow.com: How to enable or disable the Dojo libraries for individual XPages, not the whole application?

Just adding the parameter as shown below to the XPage won’t work:

<xp:this.properties> 
 <xp:parameter name="xsp.client.script.libraries" value="none" />
</xp:this.properties>

The workaround is to add a single line to beforePageLoad or beforeRenderResponse event:

facesContext.getRequestParameters().setJsLibrary(0);

The parameter can be set as the following

  • “0″ disables Dojo Framework
  • “1″ enables Dojo Framwork
  • “2″ enables Dojo Lite Framework

If you are using the ExtLib, there is still a problem with the automatically added CSJS libraries. These headers will be added automatically (it is the same result if you are disabling dojo application wide):

<script type="text/javascript">dojo.registerModulePath('extlib', '/xsp/.ibmxspres/.extlib');</script>
<script type="text/javascript" src="/xsp/.ibmxspres/.mini/dojo/@Eya.js"></script>

Edit:

This can only be removed by disabling the ressource aggregation or disabling the Extension Library for the whole application.

You can disable it f.e. by adding this to your XPage beforeRenderResponse event:

facesContext.getRequestParameters()
   .setProperty("xsp.resources.aggregate", "true")

Erweiterung der XPages Extension Library REST Services

5. September 2012 Posted by Thomas Ladehoff

Domino-Designer-Logo-small.png
Mit den vorhandenen REST-Funktionen der Extension Library kommt man bereits sehr weit und kann unter anderem die Standard CRUD-Operationen (Create, Read, Update, Delete) abdecken. Es gibt jedoch Fälle, in denen man noch weitere Anforderungen hat, die eine Anpassung bzw. Erweiterung der bestehenden Funktionalität wünschenswert machen.

Nachdem im letzten Blogeintrag zum Thema REST die Grundlagen und die Verwendung der entsprechenden XPages Controls behandelt wurden, geht es in diesem Beitrag um eine Erweiterungsmöglichkeit mit Hilfe eines Custom Database Servlets.

Die REST-Funktionalität (Representational State Transfer) der Extension Library bietet verschiedene Arten der Erweiterung, um an die eigenen Bedürfnisse angepasst zu werden. Unter anderem stehen folgende Möglichkeiten zur Verfügung:
  • XPages Control "Custom REST Service" (xe:restService) aus der Extension Library.
Dieses Control bietet große Freiheit, weil keinerlei Vorgaben zur Ausgabe gemacht werden. Zur Verarbeitung von Anfragen kann Server Side JavaScript (SSJS) für die jeweiligen HTTP-Methoden hinterlegt werden (Control-Eigenschaften: "doGet", "doPost", "doPut", "doDelete").
  • Erstellen eines "Custom Database Servlets" in Java
Über ein Custom Database Servlet lässt sich sowohl Verarbeitung als auch die generierte Ausgabe komplett steuern, ohne dabei vordefinierten Mustern zu folgen. Dieses Vorgehen eignet sich auch, um die bestehenden Services in der Extension Library anzupassen.
  • Erstellen eines Custom Wink Servlet
Ein mit dem Apache Wink Projekt erstellter RESTful Service wird direkt in das OSGI Framework vom Domino Server installiert und hat Datenbank-übergreifenden Zugriff (siehe auch OpenNTF).

In den folgenden Abschnitten wird die Erstellung eines Custom Database Servlets anhand eines konkreten Beispiels erklärt, das auch als Ansatz für eigene Umsetzungen herangezogen werden kann.




Ein wenig Theorie


Die eigentliche Verarbeitung der REST-Anfragen wird von einer Klasse übernommen, die das Interface "com.ibm.domino.services.ServiceEngine" aus der Extension Library implementiert.
Eine solche Klasse könnte man z.B. selbst erstellen und dann der Laufzeitumgebung zur Abarbeitung von Anfragen zur Verfügung stellen (wie Letzteres geht wird weiter unten erklärt).
Alternativ kann man eine bestehende Klasse aus der Extension Library benutzen und deren Funktion durch Ableitung entsprechend erweitern oder abändern.

Die Klasse zur Operation auf Dokumenten (die auch im folgenden Beispiel verwendet wird) heißt "RestDocumentJsonService".
Wenn Sie etwas tiefer einsteigen möchten: Die Klasse ist im Package "com.ibm.domino.services.rest.das.document" im Plugin "com.ibm.domino.services" enthalten (den Quellcode gibt's im extension library download).
Es existieren unter anderem Methoden zum Lesen und Erstellen von Dokumenten ("renderServiceJSONGet" bzw. "createDocument"). Doch der zentrale Einstiegspunkt zum Verarbeitungsbeginn wird durch das genannte ServiceEngine-Interface vorgegeben und zwar durch die Methode "processRequest".

Die Klasse RestDocumentJsonService implementiert allerdings nicht direkt das Interface ServiceEngine sondern erbt noch von diversen weiteren Klassen.
Die Vererbungshierachie sieht dann etwas verkürzt wie folgt aus (inkl. eigener Klasse).

A picture named M2


Der REST Service wird über eine URL in der folgenden Form erreichbar sein:
http://{host}/{database}/xsp/services/{pathInfo}/unid/{unid}

Die Extension Library sorgt dafür, dass Anfragen vom Client mit entsprechender URL an das dafür vorgesehene ServiceEngine-Objekt zur Verarbeitung weitergereicht werden.
Die Zuordnung erfolgt hierbei über den URL-Teil "pathInfo".
Der nächste Schritt besteht also darin, der Laufzeitumgebung mitzuteilen, welcher pathInfo-String von welchem Objekt verarbeitet werden soll.

Dies geschieht über eine Factory-Klasse, die für die Erzeugung von Instanzen der richtigen Klasse zuständig ist.

Das Schema der Factory-Klasse sieht wie folgt aus:



(Neben dem folgenden Beispiel kann ein weiteres übrigens auch in der Extension Library Demo Datenbank gefunden werden.)

Kurze Erklärung:
Die Erstellung eines ServiceEngine-Objekts (z.B. ein RestDocumentJsonService-Objekt) erfolgt in der Methode "createEngine" (Zeile 9), welche Teil des ServiceFactory-Interface ist.
Ein entsprechendes ServiceFactory-Object wird hier an eine übergeordnete "DefaultServiceFactory" übergeben (Methode "addFactory, Zeile 7).
An dieser Stelle wird auch festgelegt, welcher Service letztendlich die Verarbeitung für welchen pathInfo-String bzw. URL übernimmt.
Die DefaultServiceFactory wird wiederum von einer übergeordneten ServletFactory-Klasse verwendet.


Was jetzt noch fehlt, ist das Registrieren der ServletFactory-Klasse, damit die Laufzeitumgebung sie finden und verwenden kann.
Dies geschieht über eine Textdatei mit dem Namen "com.ibm.xsp.adapter.servletFactory", die in einem Verzeichnis "META-INF/services" abgelegt werden muss.
In die Datei muss der (voll qualifizierte) Name der ServletFactory-Klasse eingetragen werden.

Klassen und Textdatei werden in einem Verzeichnis abgelegt, welches in den Build-Pfad des Projekts integriert werden muss.
Im Domino Designer in der Java-Perspektive könnte das im Ergebnis dann z.B. so aussehen, wie im folgenden Screenshot gezeigt (bereits ein Vorgriff auf das Beispiel):


A picture named M3



Erstellen eines Custom Database Servlets


Als Beispiel soll die Klasse RestDocumentJsonService erweitert werden, und zwar soll vor dem Speichern eines Dokuments eine Validierung stattfinden.
Bei nicht erfolgreicher Validierung sollen entsprechende Meldungen zum Client geschickt werden, die dieser ggf. direkt dem Benutzer anzeigen kann.
Prinzipiell ließe sich eine Validierung auch über das Konfigurieren des RestDocumentJsonService-Objekts mit der Eigenschaft "computeWithForm" erreichen, in Zusammenhang mit entsprechenden Validierungsformeln in der jeweiligen Maske.
Die im Folgenden vorgestellte Lösung bietet eine Alternative hierzu und hat auch verschiedene Vorteile gegenüber der "computeWithForm"-Variante.

Zur Umsetzung eines Services, der die Eingaben validiert müssen nur zwei Methoden der Basisklasse RestDocumentJsonService überschrieben werden:
  • Methode querySaveDocument
Diese Methode dient zur Ausführung von Code vor dem Speichern des Dokuments (analog zu dem entsprechenden XPages-Datenquellen-Ereignis). Auch für die anderen Dokumentenereignisse (open, new, delete) existieren Methoden.
Die querySaveDocument-Methode wird in diesem Beispiel dazu benutzt die Validierung auszuführen. Wenn die Validierung fehlschlägt, wird eine "ServiceValidationException" geworfen (siehe Codeausschnitt weiter unten).
  • Methode displayError
Diese Methode dient zur Generierung einer Fehlermeldung für den Client, wenn generell während der Verarbeitung etwas schief läuft und eine Exception auftritt.
Das Überschreiben der Methode erlaubt das Generieren einer benutzerdefinierten Nachricht für den Client, für den speziellen Fall einer fehlgeschlagenen Validierung.


Insgesamt sind für dieses Beispiel drei Klassen nötig:
  • ValidatingDocumentService-Klasse zur Implementierung des REST Services
  • ServiceValidationException zur Anzeige von Validierungsfehlern
  • ServletFactory-Klasse zur Erzeugung der Services


Die beiden Methoden "querySaveDocument" und "displayError" sind im Folgenden dargestellt. Das Beispiel zeigt die Validierung von Dokumenten vom Typ "company", also Firmendokumente (gleiches Szenario, wie im letzten Artikel).
Der gesamte Quellcode kann am Ende dieses Artikels heruntergeladen werden.




Die Validierung besteht der Einfachheit halber in diesem Fall nur aus einer einzelnen Prüfung des Feldes "CompanyName" auf Inhalt (Zeile 18).
Sollte die Prüfung nicht erfolgreich sein, wird eine entsprechende Exception geworfen, die auch als Container für die Fehlernachrichten der Validierung fungiert (Zeile 25).
In der Methode "displayError" wird dann geprüft, ob der Fehler von einer fehlgeschlagenen Validierung verursacht wurde, indem der Exception-Typ geprüft wird (Zeile 39).
Falls dies der Fall ist, werden die Fehlernachrichten zum Client geschickt (Zeile 53; Methode"writeJSONValidationException" siehe Download). Andernfalls wird der Standard-Mechanismus zur Fehlerausgabe benutzt (Zeile 61).




Test der API


Zum Testen des Services kann das Firefox Add-on RESTClient verwendet werden (siehe auch voriger Blogeintrag).

Das Ergebnis sieht dann wie folgt aus:
A picture named M4



Clients können einfach die Antwort analysieren, indem Sie bei einem Fehler (Code 500) die Eigenschaft "type" auswerten und die Nachrichten anzeigen.




Weitere Bemerkungen


Bei meinen Tests trat beim ersten Aufruf des Services eine "java.security.AccessControlException" auf. Dies ist auf die unterschiedlichen Security-Kontexte der Extension Library Laufzeitumgebung auf der einen Seite und der NSF-basierten Klassen auf der anderen Seite zurückzuführen (ohne die Ableitung der Klasse RestDocumentJsonService gibt es keine Exception). Trotz Exception scheint keine Einschränkung in der Funktion zu bestehen (zumindest so weit meine Tests reichten).




Download


REST Services mit der XPages Extension Library

13. August 2012 Posted by Thomas Ladehoff

Domino-Designer-Logo-small.png
Die Benutzung von REST-basierten Services ist eine sehr schöne Möglichkeit andere Systeme und Anwendungen mit dem Lotus Domino Server zu integrieren.

Dieser Blogeintrag fasst die Bedeutung und Vorzüge von REST im Allgemeinen zusammen und zeigt die Grundlagen der Benutzung der REST Controls innerhalb der XPages Extension Library.

REST-basierte (oder RESTful) Webservices sind sowohl für Integrationsszenarios nützlich als auch zur Vorhaltung von Daten für externe Anwendungen.
Ich benutzte die REST Services zum Beispiel kürzlich, um eine Schnittstelle für eine mobile Anwendung zu realisieren.

RESTful Webservices (Falls Sie sie noch nicht kannten..)


Representational State Transfer (REST) bezeichnet einen Software-Architekturstil, der von Roy Thomas Fielding in seiner Dissertation beschrieben wurde und der Architektur des Internets entspricht. Genauer gesagt ist das im Internet und durch RESTful Webservices verwendete HTTP-Protokoll eine Implementierung des REST-Architekturstils.

Wenn Sie also die Prinzipien des HTTP-Protokolls kennen, wissen Sie auch schon viel über REST. Um die wichtigsten Punkte zu nennen:
  • Informationen werden mittels sogenannter Ressourcen zu Verfügung gestellt. Jede Ressource kan eindeutig über eine ID angesprochen werden (die URL).
  • "Respresentational" meint das Übertragen der Informationen in verschiedenen Repräsentationen, also Formaten. Das heißt sowohl im technischen Sinne (z.B. JSON, XML) als auch im inhaltlichen Sinne (z.B. Text, Bilder).
  • Zustandslosigkeit. Der Server behält keinen (Session-) Status über eine Folge von Requests.
  • Ressourcen können Verknüpfungen zu anderen Ressourcen enthalten, wodurch eine Navigation ermöglicht wird.
  • Die Operationen, die auf einer Ressource ausgeführt werden können, sind generischer Form. Typischerweise werden folgende vier verwendet:
    • GET - lesender Zugriff auf eine Ressource, frei von Seiteneffekten
    • POST - Erstellen einer neuen Ressource
    • PUT - Erstellen oder Aktualisieren einer bestimmten Ressource (bestimmte ID)
    • DELETE - Löschen einer Ressource

RESTful Webservices stellen einen auf HTTP basierenden Service bereit, der den genannten Prinzipien folgt.


Einige generelle Vorteile von RESTful Webservices sind:
  • Loose Kopplung von Anwendungen und Systemen (führt zu hoher Interoperabilität)
  • Einfaches Ressourcen-orientiertes Konzept mit wenigen generischen Methoden
  • Gute Skalierbarkeit
  • Einfaches Caching

Im Wesentlichen sind dies auch die Vorzüge des HTTP-Protokolls.



REST-basierte Services zum Zugriff auf Domino-Daten


Eine gute Übersicht über die REST-Funktionen in der Extension Library bietet das Video "REST Services for Domino and XPages" auf der Extension Library Homepage.
An dieser Stelle werde ich insbesondere die Benutzung der XPages Controls für REST vorstellen. Es gibt noch zwei alternative Wege, die außerhalb des XPages Kontext funktionieren (Benutzung des Domino Data Service und Erstellen eines Custom Servlets).

Es gibt verschiedene XPages Controls, die man zum Zugriff auf Domino-Daten benutzen kann. Zum Beispiel:
  • Database Collection Service (xe:databaseCollectionJsonService): Abrufen einer Datenbankliste auf dem Server.
  • View Collection ervice (xe:viewCollectionJsonService): Abrufen einer Liste von Ansichten und Ordnern in einer Datenbank.
  • View Service (xe:viewJsonService): Lesen von Ansichts- und Ordnerdaten (mit Filterung), Erstellen, Aktualisieren und Löschen von Dokumenten (begrenzt).
  • Document Service (xe:documentJsonService): Operationen auf Dokumenten.

Wie Sie vielleicht erahnen, steht das "Json" innerhalb der Tags der Controls für das verwendete Übertragungsformat JSON.

Beispiele der Controls können auch in der Beispieldatenbank innerhalb des Extension Library Downloads gefunden werden.

Für die XPages REST Controls ist es nicht erforderlich etwas auf dem Domino Server zu konfigurieren (aber es bringt eine kleine Vereinfachung, wie wir später sehen werden).
Es genügt eine XPage zu erstellen und die gewünschten REST Controls hinzuzufügen, um Zugriff auf die gewünschten Daten zu ermöglichen.

Um dies detaillierter zu zeigen und die wichtigsten Paramter zu erklären, werden wir ein einfachen Beispielszenario mit einer Maske "company" und einer Ansicht "companiesByName" verwenden.

Die Services, die wir benötigen, um in bequemer Weise mit den Firmendokumenten zu arbeiten sind:
  • xe:viewJsonService (Auflistung bestehender Firmen)
  • xe:documentJsonService (Erstellen, Lesen, Aktualisieren, Löschen bestimmter Firmendokumente)

Es ließe sich auch der View JSON Service zum Ändern von Dokumenten benutzen. In diesem Fall ist man jedoch auf die Felder im Dokument beschränkt, die direkt einer Ansichtsspalte zugeordnet sind.
Mit dem Document JSON Service lassen sich sogar Rich Text Felder und Anhänge lesen und bearbeiten.



Zugriff auf die Ansichtsdaten


Wir starten mit einer leeren XPage und dem Hinzufügen eines REST Service Controls (siehe Screenshot)
A picture named M2


Einige Eigenschaften dieses Controls:
  • ignoreRequestParams: Parameter in der URL werden für den REST Service ignoriert (analog zu der gleichnamigen Eigenschaft der XPages Datenquellen)
  • pathInfo: Dieser String identifiziert den Service auf der XPage. Für den View JSON Service in diesem Beispiel ist diese Eigenschaft "companies".
  • service: Hier wird der konkrete REST Service angegeben, einer der Services aus der Liste (siehe Screenshot). Im Beipiel der "xe:viewJsonService".


Die Eigenschaften des View JSON Service sind über die hinterlegten Beschreibungen schon fast selbsterklärend. Trotzdem hier ein paar wichtige:
  • columns: Für diese Eigenschaft werden Elemente vom Typ "xe:restViewColumn" angegeben, welche wiederum eine Eigenschaft "columnName" (Referenz zur Ansichtsspalte) und "name" (Eigenschaftsname des JSON Objekts in der Ausgabe). Letzteres Mapping wird nur bei GET Requests angewendet. Außerdem gibt es hier noch eine Eigenschaft "value", die zur Berechnung einer Wertes benutzt werden kann.
  • defaultColumns: Wenn "true", werden alle Spalten der Ansicht in die Ausgabe einbezogen.
  • systemColumns: Spezielle System Spalten, die nicht notwendigerweise als benutzerdefinierte Spalten angegeben sein müssen (z.B. UNID, Maskenname, Anwortdokument (ja/nein)).
  • viewName: Name der Ansicht, die für diesen REST Service benutzt werden soll.

Außerdem gibt es einige Eigenschaften zur Filterung der zurückgegebenen Ergebnisliste, wie z.B. "start" und "count" zur Realisierung von Paging, oder "search" zur Volltextsuche.
Auch Ereignisse zur Reaktion auf Dokumentenereignisse, wie "querysaveDocument" stehen zur Verfügung. Diese werden weiter unten erklärt.

Der folgende Screenshot zeigt die View Service Eigenschaften, die in diesem Beispiel benutzt wurden:
A picture named M3




Testen der API


Um auf den Service zuzugreifen, muss der für die Eigenschaft "pathInfo" angegebene String verwendet werden. Das generelle URL Schema im XPages Kontext ist:
http://{Host}/{Datenbank}/{XPage Name}/{pathInfo}/unid/{unid}?{Parameter}

Für den View Service in diesem Beipiel ist es nicht erfoderlich auf einzelne Dokumente zuzugreifen, daher kann der UNID-Teil weggelassen werden.
Angenommen, die Datenbank heißt "Test1.nsf" und die XPage "data.xsp", so wäre die URL:
http://localhost/Test1.nsf/data.xsp/companies

Die URL kann direkt über den Browser aufgerufen werden (impliziert Request-Methode GET) und die Einträge in der Ansicht sollten im JSON Format angezeigt werden.

Für diesen Zweck und zum weiteren schnellen Testen der API gibt es ein schönes Firefox Add-on mit dem Namen RESTClient. Damit lassen sich benutzerdefinierte Requests erstellen durch Angabe von HTTP-Methode, URL, Request Header und Request Body. Nach dem Senden der Anfrage wird die Antwort vom Server angezeigt.

Der folgende Screenshot zeigt die Antwort für den View Service "companies" im RESTClient:
A picture named M4



In der Antwort sind JSON-Eigenschaften mit einem '@' am Anfang enthalten. Diese markieren Systemspalten (nicht-benutzerdefinierte Spalten).
Die verschiedenen Datentypen werden in verschiedenen Formaten dargestellt, z.B. werden Strings in Anführungszeichen eingeschlossen, Zahlen nicht.
Eine detaillierte Beschreibung über diese und weitere Formate ist in der Dokumentation zu finden (Datei "DominoDataServiceDoc.zip" im Extension Library Download).


Arbeiten mit Dokumenten


Um ein API für einzelne Dokumente zur Verfügung zu stellen, muss ein zweites REST Service Control zu der bestehenden XPage hinzugefügt werden. Diesmal wird die "pathInfo"-Eigenschaft entsprechend dem Zugriff auf ein einzelnen Firmendokument benannt, z.B. "company". Der konkrete Service ist der Document Service (xe:documentJsonService):

A picture named M5



Die folgenden Eigenschaften sind gesetzt:
  • compact: Bei "true" enthält die Ausgabe keine nicht sichtbaren Zeichen.
  • defaultItems: Bei "true" werden alle Items sowie einige Metainformationen in die Ausgabe aufgenommen.
  • formName: Name der Maske, die zum Erstellen von Dokumenten verwendet werden soll.

Um den neuen Service zu Testen lässt sich wieder der RESTClient oder direkt der Browser verwenden (die UNID kann aus der View Service Antwort kopiert werden):
http://localhost/Test1.nsf/data.xsp/company/unid/56A63CD8312606F8C1257A54004009DD

Das Ergebnis sollte nun alle Felder des Dokuments enthalten, sowie einige Metainformationen (z.B. @unid, @form, $UpdatedBy).

Der nächste Schritt besteht im Aktualisieren eines bestehenden Dokuments. Aktualisieren wird für gewöhnlich über die HTTP-Methode PUT durchgeführt. Die Extension Library Implementierung macht hier noch eine weitere Unterscheidung: Die PUT-Methode wird zum Ersetzen des bestehenden Dokumenteninhalts mit den Daten im Request verwendet, wobei vorheriger Inhalt verworfen wird. Das Dokument wird anschließend genau die Daten enthalten, die im Request gesendet wurden.
Aber in den meisten Fällen wird man vermutlich nur einige Items ändern wollen und den Rest des Inhalts unberührt lassen. Dies wird in der Extension Library über eine weitere HTTP-Methode "PATCH" realisiert.

An diesem Punkt ist es erwähnenswert, dass der Domino Server in der Voreinstellung nur die Mehtoden GET und POST unterstützt. Die weiteren Methoden zu erlauben ist eine Einstellungssache in einem Internet Site Dokument, allerdings gibt es noch einen alternativen Ansatz, der auch dann sehr nützlich ist, wenn auf Client-Seite keine PATCH Requests unterstützt werden:
Die HTTP-Methoden PUT, PATCH und DELETE können alternativ auch durch die POST-Methode ersetzt werden und ein zusätzlicher HTTP Header "X-HTTP-Method-Override" kann gesetzt werden, der dann die zu verwendende Methode spezifiziert.

Wie lässt sich also das Ändern eines Items in einem bestimmten Dokument mit Hilfe des RESTClient testen?

1. Setzen des "Method-Override" Header
Im RESTClient von der Titelleiste folgenden Punkt wählen: Headers -> Custom Header
Der folgende Dialog sollte wie folgt befüllt werden:
A picture named M6


2. Es gibt noch einen weiteren Header, der gesetzt werden muss. Ansonsten wird die Anfrage mit einem Fehler quittiert:
Es geht um den "Content Type" der Anfrage, der das JSON-Format angeben muss.
Header Name: Content-Type
Header Wert: application/json

3. Ein bestehendes Dokument per GET abrufen (nur um Daten zum Ändern zu haben).

4. Die Request-Methode auf POST ändern und die korrekte Angabe der Header prüfen (siehe auch folgender Screenshot).

5. Im Request Body die zu ändernden Daten angeben (im JSON Format).
A picture named M7



6. Per "Send" die Anfrage abschicken. Wenn es keine Fehlermeldung gibt, kann das Ergebnis mit einem erneutem GET überprüft werden.


Andere Operationen:
  • Erstellen eines neuen Dokuments: Setzen der HTTP-Methode auf POST (ohne X-HTTP-Method-Override Header)
  • Löschen eines Dokuments: Setzen der HTTP-Methode auf DELETE (oder Methode POST und X-HTTP-Method-Override Header auf DELETE)



Programmatisch die Funktion des Document Service erweitern


Wenn Sie einen Ansatzpunkt zur programmatischen Erweiterung der Request-Verarbeitung benötigen, besteht eine Möglichkeit darin die Dokumentenereignisse, wie "querySaveDocument" zu verwenden.
Auf diesem Weg können zusätzliche Aufgaben durchgeführt werden und Dokumenteninhalt geändert oder ergänzt werden.

Die folgende Liste bietet eine Übersicht über die Parameter der verschiedenen Methoden:
  • queryNewDocument: Keine Parameter
  • queryOpenDocument: Parameter 'id' (Typ String)
  • querySaveDocument: Parameter 'document' (Typ lotus.domino.Document)
  • queryDeleteDocument: Paramter 'id' (Typ String)
  • postNewDocument: Parameter 'document' (Typ lotus.domino.Document)
  • postOpenDocument: Parameter 'document' (Typ lotus.domino.Document)
  • postSaveDocument: Parameter 'document' (Typ lotus.domino.Document)
  • postDeleteDocument: Paramter 'id' (Typ String)

Wenn die Request-Verarbeitung abgebrochen werden soll, bieten alle Methoden, deren Name mit "query" beginnt, einen boolesche Rückgabewert. Wird 'false' zurückgegeben, so wird eine Exception geworfen und eine entsprechende Fehlernachricht wird zum Client gesendet. Wenn 'true' zurückgegeben wird, so wird die Verarbeitung fortgesetzt.

Für noch mehr Kontrolle über die Verarbeitung und die generierte Ausgabe gibt es außerdem die Möglichkeit, ein Custom Servlet zu schreiben. Wie das funktioniert wird Thema eines zukünftigen Blogeintrags sein.

ObjectDataSource: Kleines “How To”

1. April 2012 Posted by Sven Hasselbach

Mit der Extension Library bzw. dem Upgrade Pack 1 ist für XPages eine neue Datasource-Komponente hinzugekommen, die ObjectDataSource. Diese Datasource kann wie die Standard-Datasources für View und Dokument ebenfalls an den verschiedensten Elementen einer XPage angehangen werden, d.h. sowohl an die UIViewRoot der XPage, als auch in Custom Controls, Repeat Controls oder sonstigen Elementen, die eine DataSource verwenden.

Grundlegend gilt für ObjectDataSources, das sie beliebige Java-Objekte beinhalten können, solange die Objekte selbst serialisierbar sind, also das Interface java.io.Serializable implementieren. Hier ein einfaches Beispiel eines Java-Objektes:

package ch.hasselba.extlib.demo;

import java.io.Serializable;

public class ObjectDataDemo implements Serializable {

    long timeStamp;

    public ObjectDataDemo(){
        this.update();
    }

    public void update(){
        this.timeStamp = java.lang.System.currentTimeMillis();
    }

    public long getTimeStamp() {
        return timeStamp;
    }

}

Die Java-Klasse ist einfach aufgebaut und liefert den Zeitstempel zurück, der beim letzten Aufruf der update()-Methode gesetzt wurde. Um die Klasse in der XPage verwenden zu können, muss sie nur in einem Source-Folder bereitgestellt sein, der dem Build-Path hinzugefügt wurde bzw. die Klasse im neuen “JavaCode”-Ordner  abgelegt sein – eine Modifikation der faces-config.xml ist nicht nötig.

Mittels der createObject-Eigenschaft der ObjectDataSource wird die XPage dazu veranlasst, eine Instanz der Klasse anzulegen, womit das instanzierte Objekte der ObjectDataSource gehört; um die Stabilität einer Applikation nicht zu gefährden und unerwartete Ergebnisse zu provozieren, sollten die Objekte daher nicht anderweitig gespeichert werden (z.B. in Managed Beans o.ä.).

<xp:this.data>
   <xe:objectData var="objectData1" scope="view"
      createObject="#{javascript:
         new ch.hasselba.extlib.demo.ObjectDataDemo();}">
   </xe:objectData>
</xp:this.data>

Eine ObjectDataSource hat neben dem zu verwendenden scope auch noch die Eigenschaft var, die (wie bei den anderen Datasources auch) den programmatischen Namen darstellt, mit dem die DataSource referenziert werden kann. In diesem Fall ist das Objekt und alle Eigenschaften/Methoden des Objektes über objectData1 zu erreichen:

<xp:label id="label1"
   value="#{javascript:objectData1.getTimeStamp()}" />

Da z.B. ein Partial Refresh die Datasources nicht jedesmal neuberechnet, muss ein eventuell benötigter Update-Mechanismus selbst implementiert werden. In diesem Beispiel dient dafür die Methode update(), die im Backend aufgerufen werden kann:

<xp:button value="Label" id="button1">
   <xp:eventHandler event="onclick" submit="true"
      refreshMode="partial" refreshId="label1">
      <xp:this.action>
         <![CDATA[#{javascript:objectData1.update()}]]>
      </xp:this.action>
   </xp:eventHandler>
</xp:button>

[Button ruft die Update-Funktion auf, label1 wird refresht und neuer Zeitstempel angezeigt]

Die ObjectDataSources besitzen eine weitere Eigenschaft namens saveObject. Diese Eigenschaft ist optional und nur erforderlich, wenn man beabsichtigt, auf ein Speichern der DataSources reagieren zu wollen (um z.B. Daten ins Backend zu speichern, o.ä.).

Achtung:

Einige Methodennamen dürfen in der Java-Klasse nicht verwendet werden, da dies sonst zu Problemen mit der bestehenden Architektur kommen kann. Namen wie save() oder load() sind zu vermeiden!

XPages trifft Excel – Cooles Projekt auf OpenNTF

9. August 2011 Posted by Lars Buntrock

Heute habe ich mir auf OpenNTF ein wirklich tolles und beindruckendes Projekt angeschaut:

ZK Spreadsheet for XPage

Dennis Chen zeigt anhand des Projektes wir man Java basierende Frameworks mit XPages zusammenbringt. In diesem Fall das ZK Spreadsheet. Es handelt sich dabei um ein Open Source webbasierendes Spreadsheet, welches Excel-Funktionalität im Browser zur Verfügung stellt. Dabei wird vollständig Java genutzt. Die Lauffähigkeit mit XPages setzt jedoch die Nutzung der Extension Library von OpenNTF voraus.

Hier finden Sie ein Video, welches die Funktionalität, meiner Meinung nach in beeindruckender Weise, zeigt.

Viel Spaß beim Anschauen…

REST Services für Domino und XPages

7. Juli 2011 Posted by schmhen

Lotus Notes/Domino 8.5.3 steht vor der Tür und die Entwicklung in Richtung Web Apps geht weiter. Das neueste Geschenk, welches wir erhalten, sind REST Services für Domino und XPages! Was bedeutet das?

Zukünftig können Daten aus Lotus Notes Datenbanken mit einfachsten Mitteln als REST Feed ausgelesen werden. Die Daten können sowohl als JSON als auch als XML formatiert abgefragt werden. Daten können aber hierüber nicht nur gelesen werden, es können Daten auch geschrieben werden.

Worauf kann mit den REST Services zugegriffen werden? Grundsätzlich werden Funktionen bereitgestellt, mit denen auf Datenbanken, Ansichten und Dokumente zugegriffen werden kann. Für den Zugriff werden vier Möglichkeiten geboten.

  1. Zugriff via DOJO direkt auf Notes-Datenbanken (Domino Data Service)
  2. Zugriff über XPages (XPages REST Services Control)
  3.  Zugriff per Java über ein Servlet (Custom Database Servlet)
  4. Zugriff über das WINK Servlet (Custom WINK Servlet)

Je nachdem, was entwickelt wird und welche Werkzeuge zum Einsatz kommen, kann man sich für einen (oder auch mehrere kombiniert) Weg entscheiden.

Voraussetzung für die Nutzung ist derzeit die aktuellste Version der Extension Library und Lotus Notes 8.5.3 Code Drop 5. Den Code Drop bekommt man leider nur, wenn man im Beta Programm für 8.5.3 ist.

Eine ausführliche Dokumentation über die Arbeitsweise und die Möglichkeiten der Services gibt es auf OpenNTF. Niklas Heidloff hat ein Video produziert, in welchem er die Services erklärt.