Posts Tagged: ‘xpages’

HCL, Domino & node.js

23. Juni 2018 Posted by Sven Hasselbach

I am very happy to hear that HCL invests in Domino and improves the existing technology stack. But as a German, I have to be sceptical (it’s in our genes), because I can not see any advantage in the integration of node.js applications on top of Domino. I have written a demo two years ago, just to prove that it would be possible.

One of the main reasons is that I have switched my application architecture, which means that Domino is nothing more than a great NoSQL-Datacontainer. While the existing REST APIs were absolutly not fitting my requirements (too slow, painfull output and not expandable), I have pursued „my own way“ by using Spring Boot as my preferred technology. This made me independent from IBMs release cycles, and since the Java 8 upgrade I am happy, because I was able to add the missing parts which where never delivered by IBM.

Token authentication? Solved by creating my own solution. Performance? Boosted with Memcache. Memory limitations? Also solved with Memcache. Delay of agent execution? Solved with Spring Boot. I have dropped the Designer and using Eclipse directly, especially development/maintenance of legacy Java agents makes a lot of more fun. Code analysis / quality? Maven, JUnit & SonarQube are your friends. SSL encryption? Nginx. And the list grows and grows…

My point is that beeing independet from IBMs releases allows me to be extremly flexible – which IBM is not. Just have a look at Bootstrap and XPages: I have created my own renderers, and I can switch to the latest version with a few clicks (as long as there is no fundamental change in the structure). I am not dependent that – maybe – in the future someone will adopt the version to the XPages Extension library. If one of my customers wants to use it, OK, no problem.

That‘s what my customers love: The sky (aka budget) is the limit.

And here comes the problem I see with the node.js integration: The release cycles are extremely short. Just have a look at the release list:

https://nodejs.org/en/download/releases/

In the last 8 monthes there where 10(!) new versions for Carbon (V8, LTS). 26 versions since 2016 for Boron (V6, LTS). And that’s only node.js – the core of the whole thing. Don’t forget the packages and their dependencies. Let’s skip the fundamental problems with the NPM ecosystem: If it is required to get the latest updates, „npm update -g“ and everything is fine.

But waiting for Big Blue for a hot fix? If the „Domino NPM Package“ is not updated, but depends on an older version, you maybe cannot update the whole application. Ever had problems withthe old Jar files of the Domino JVM? Or was it required to downgrade of the Eclipse Maven Plugin to run with Domino’s JRE 6? Just think about it…

Don‘t get me wrong: This is not against the technology. I am using JavaScript for more than 15 years and have build some node.js applications and React Native apps in the last years, but I am not a fan of JavaScript because of the chaotical language concept, and the pain when trying to find syntax errors in scripting languages in general, or the missing type safety (something which is not required in compiler languages). But you can build great and high performant applications, and ES6 was a big step forward.

In my eyes there is no reason for tying node.js with Domino. My advice is to build REST interfaces on top of Domino (and reuse the existing business logic), and access it with a separate application based on [enter your preferred technologie here] with a backend connector. The frontend can be realised by a web development team / company. This takes a lot pressure off the existing Domino environment (from the management perspective): You can build new applications with the current hipster technology, can find developers and administrators, and the costs for moderinzation are not as high as a migration. After taking this path, some customers who abandoned Domino years ago, are investing again in the product.

So far I am still open for a big surprise and hopefully HCL can convince me of the contrary.

P.S.

I am still developing XPages applications, in my eyes a great technologiy, but it was never adopted by the developers as it should. With node.js, you have a new learning curve: Dojo/jQuery is NOT JavaScript.

Quick-n-Dirty: Hotfix for DateTimeHelper

12. Juni 2017 Posted by Sven Hasselbach

This weekend I stumbled over a bug of the DateTimeHelper: If the value of the field is empty, no actions and/or action listeners connected with a managed bean will be executed anymore.

Here is an example of a small XPage to illustrate the problem:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
 
    <xp:label 
        value="#{javascript:java.lang.System.currentTimeMillis()}" id="labelNow" />

     <xp:inputText id="inputTextDT" value="#{myBean.valueDT}">
         <xp:this.converter>
             <xp:convertDateTime type="date" />
         </xp:this.converter>
         <xp:dateTimeHelper />
     </xp:inputText>

    <xp:button id="button" value="OK">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="labelNow"
            actionListener="#{myBean.action}" />
     </xp:button>

</xp:view>

It does not matter if you set the disableValidators property for the text field to true, even an immediate=true won’t help here. The reason for the problem is that the renderer of the dateTimeHelper always uses the attached converter and fails with a null pointer exception if the value is empty (this infringes the JSF specification, but IBM has implemented it this way).

The workaround for this problem is to overwrite the existing renderer class and handle the NPE by yourself:

package ch.hasselba.xpages.renderer;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
public class DateTimeHelperRenderer
    extends com.ibm.xsp.renderkit.dojo.DateTimeHelperRenderer{

    public Object getConvertedValue(FacesContext fc, UIComponent uiComponent, Object obj)
        throws ConverterException  {

          Object result = super.getConvertedValue(fc, uiComponent, obj);

          if( result == null )
            return new Object();

          return result;
    }
}

The renderer must now be registered in faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <render-kit>
    <renderer>
      <component-family>javax.faces.Input</component-family>
      <renderer-type>com.ibm.xsp.DateTimeHelper</renderer-type>
      <renderer-class>ch.hasselba.xpages.renderer.DateTimeHelperRenderer</renderer-class>
    </renderer>
  </render-kit>
</faces-config>

Now the problem is solved, the managed bean’s action get executed even if the value is empty.

DNUG-Fachgruppentag: Infrastruktur- und Development-News von der Connect [Fachgruppe Development]

22. Februar 2017 Posted by DNUG

Folgende Themen stellen wir Euch auf unserem Fachgruppentag vor:

Themenblock 1 – vormittags
Unter dem Codenamen PINK arbeitet IBM an einer Modernisierung aller Bestandteile von IBM Connections. In diesem Zusammenhang werden auch zentrale Infrastrukturkomponenten wie z.B. WebSphere Application Server ausgetauscht und durch komplett neue Technologien ersetzt. Hierdurch wird sich nicht nur das Deployment zukünftiger IBM Connections Versionen grundlegend ändern. Auch für Entwickler bieten sich neue Möglichkeiten. Erfahrt aus erster Hand, wie die neue technologische Basis aussehen wird, welche Skills Du zukünftig benötigst und wie Du dich heute bereits auf diese Änderungen vorbereiten kannst.

Themenblock 2 – nachmittags
Bei Domino Webanwendungen denken viele zunächst an XPages. Die Programmierung von Applikationen für und mit IBM Domino umfasst jedoch weitaus mehr Facetten. Die Neuigkeiten von der IBM Connect zum Thema Domino Anwendungsmodernisierung werden vorgestellt und gemeinsam mit den Spezialisten von IBM diskutiert. Darüberhinaus konnten wir einen der Entwickler von IBM Verse On-Premises als Sprecher gewinnen. Freue Dich auf spannende Vorträge und Diskussionen.

Die Veranstaltung richtet sich hauptsächlich an Architekten und Entwickler.
Moderation: Erik Schwalb (Technical Sales IBM Collaboration Solutions, IBM Deutschland GmbH)

Datum: 16.03.2017
Zeit: 10:00 – 16:00 Uhr
Ort: IBM Deutschland GmbH, Düsseldorf

Kosten: Für DNUG-Mitglieder kostenlos, andere Interessierte zahlen 149,00 EUR.

Weitere Informationen zur Veranstaltung und Link zur Anmeldung hier.
Die Teilnehmerzahl ist auf 50 Personen begrenzt.

Der Beitrag DNUG-Fachgruppentag: Infrastruktur- und Development-News von der Connect [Fachgruppe Development] erschien zuerst auf DNUG.

Entwicklercamp 2017: Meine Vorträge & Hands-On

19. Januar 2017 Posted by Sven Hasselbach

Dieses Jahr spreche ich wieder auf dem Entwicklercamp, und halte neben den Vorträgen auch noch eine Hands-On Session:

Im Laufe des Jahres 2016 kamen einige Themen hinzu, und dank FP8 wird es wohl auch einige Neuerungen geben, über die man mal reden müsste…

Hochperformante REST Schnittstellen entwickeln, die auf dem Domino Server laufen? Der Vortrag gibt Antworten auf die essentiellen Fragen des „Warum“ und des „Wieso“, und beleuchtet an praktischen Beispielen vor allem ausführlich die Frage des „Wie“.

Die praktische Umsetzung des Vortrages. Learning by doing.

Zur Anmeldung geht es hier entlang. Bis morgen (20.01.2017) gibt es auch noch den Frühbucherrabatt.

Verse / Notes / Domino forever!

18. November 2016 Posted by Peter Schütt, IBM

index

IBM investiert seit Jahren nachhaltig in die IBM Notes Domino Plattform, auch wenn die Marktbegleiter das natürlich gern anders darstellen. Es waren in den letzten 2 Jahren viele große und kleine Dinge: Unter anderem der äußerst gelungene Verse Mail Client im Browser und als App, der Zugriff auch von Outlook auf Mail auf Domino, der Zugriff auf Domino Anwendungen in einer Browser Umgebung und deren Nutzung in der IBM Bluemix Cloud, dann auch zusammen mit anderen Cloud-Lösungen, wie Watson Analytics.

IBM Verse / Domino

Damit sicher gestellt ist, dass IBM Verse bei Kunden von Anfang an problemlos läuft, hat IBM den Selbsttest gemacht und Verse gleich nach Verfügbarkeit für über 430.000 Nutzer ausgerollt und sie dazu in die Cloud geschoben. Waren es anfänglich 7500 Nutzer pro Woche, so konnte diese Zahl am Ende durch Prozessoptimierungen auf über 25.000 gesteigert werden. Das Projekt wurde vor der geplanten Zeit fertig und hatte eine Fehlerrate von unter 0,1 Prozent. Das sollen andere Anbieter erst einmal nachmachen!

Nun kommt man also als IBM-Mitarbeiter in den Genuss – das meine ich jetzt wörtlich – von IBM Verse. Designt, sodass man maximal zwei Klicks benötigt um Aktionen auszuführen und (optional) tief mit Connections integriert, bietet es eine unvergleichlich produktive Arbeitsumgebung. Und was man als Benutzer merkt, ist was Cloud und agile Programmierung bedeuten. Fast jeden Montag gibt es neue Funktionen. Nicht hunderte, wie bei einem Release, die die Nutzer Tsunami-artig treffen und ein aufwendiges Change Management erfordern, sondern in kleinen Schritten, die zudem nett erklärt werden. Es gibt zwar noch einzelne Funktionen, die in Verse im Moment fehlen oder durch Workarounds gelöst sind, aber das werden jede Woche weniger.

Was beim Nutzer hängen bleibt, ist, dass jede Woche ein paar Erweiterungen und neue Funktionen seinen Charme hat, ähnlich wie bei Apps auf mobilen Geräten. Immer aktuell sein nennt man ja auch “evergreen”. Und das hat was!

Mehr “evergreen” – auch on Premises!

IBM hat herausgefunden, dass die IT Abteilungen, die ihre Lösungen im eigenen Rechenzentrum betreiben oder betreiben lassen (“on Premises”) neue Releases typisch erst nach 3 Jahren installieren und dann noch einmal bis zu 3 Jahre brauchen, um die Notes Clients flächendeckend auszurollen. Das ist heute, in Zeiten in denen die IBM Connections Cloud eine “Evergreen”-Umgebung bietet, also regelmäßige, inkrementelle Updates, und damit dem Nutzer /der Nutzerin immer das Neueste, einfach nicht mehr zeitgemäß. Unser Offering Management hat sich deshalb überlegt, wie man auch für Kunden, die Domino weiter im eigenen Rechenzentrum betreiben und betreiben wollen, ein ähnliches Feeling wie “evergreen” bieten kann.

Die erste Erkenntnis war: wir müssen weg von dicken Desktop Clients, deren Rollout Packages größer als 1GB geworden sind. Vielleicht nicht ganz weg, aber zumindest als die absolut zentrale Lösung. Änderungen, die heute auf dem Server installiert werden, sollten auch heute beim Nutzer ankommen. Darum haben wir die Prioritäten geändert: Mobile Apps zuerst, dann Browser-“Clients” und dann Desktop Clients. Und deshalb haben wir den Entwicklungsschwerpunkt auf IBM Verse als Client im Browser verschoben.

IBM Verse auch on Premises

Goethe und Schiller haben sich selbst Kurznachrichten immer in Versform zugesandt. Wir wollten den Schritt zu etwas Neuem, das so kein Wettbewerber bietet, auch mit einem neuen Namen belegen. Wir hätten es auch iNotes.next nennen können, denn das ist, was es im Prinzip ist: Ein ganz neuer Mail Client, entwickelt nach neusten Design Thinking Methoden, auf der Basis modernster Technologien, der übrigens auch “offline” kann – und das sogar verschlüsselt! Gibt es im Sinne von Goethe und Schiller einen besseren Namen als das poetische „Verse“? Auf der Serverseite bleibt es aber beim besten Mail Server am Markt, ähnlich wie bei iNotes: Domino. In der Cloud und noch diesem Jahr (2016) auch on Premises.

Nachdem wir zunächst komplexere Lösung verfolgten, die in der Cloud mithilfe von Apache Solr umgesetzte, facettierte Suche auch on Premises anbieten zu können – was einiges an Zeit gekostet hat – ist den Spezialisten im Labor doch noch eine geniale Alternative eingefallen, die es jetzt ganz einfach macht: Der Schritt zu der modernsten E-Mail Lösung am Markt ist insbesondere für Bestandskunden, die heute schon iNotes nutzen, extrem einfach. Denn Verse on Premises (VoP) ist denkbar einfach zu installieren: Auf Basis Domino 9.0.1 FP7 wird es das Einspielen eines HotFixes sein. Fertig. Fast zumindest: Wenn es für die Nutzer besonders gut werden soll, bindet man es noch an Connections 5.5 Profile und Files an, die lizenzseitig ja auch bereits im Domino Paket mit drin sind. Für IBM ein großes Investment in die Plattform, für die Kunden softwareseitig ohne neue Kosten.

Outlook auf Domino

Domino benötigt, so zeigen es Umsteigerprojekte, nur etwa 1/3 der Anzahl der Server, die ein Microsoft Exchange typisch benötigt. Domino läuft auch deutlich stabiler (Ausfallsicherheit) und ist, unter anderem wegen der besonders effizienten Speichernutzung (DAOS) kostengünstiger. Deshalb ist und bleibt Domino der Server der Wahl – auch in der Cloud.

Für Outlook-Freunde mag da der Wunsch aufkommen ihr Outlook ebenfalls mit Domino zu nutzen. Auch hier hat IBM investiert und ermöglicht seit kurzem auch Outlook 2010, 2013 und 2016 sogar gemischt mit Verse und/oder Notes zu betreiben. Die Lösung nennt sich “IBM Mail Support for Microsoft Outlook – kurz IMSMO und nutzt SyncML als wesentliche und von Seiten Microsoft aus stabile Schnittstelle. Es sei an dieser Stelle nicht verschwiegen, dass es einige, wenige Funktionen unter IMSMO nicht gibt, dafür kommen andere hinzu, die Outlook normalerweise nicht kann.

Die Wahl des Clients für E-Mail mit Domino liegt – so es der Administrator zulässt – beim Nutzer. Und das gilt für den Betrieb aus der Cloud und für on Premises und durchaus auch parallel: Man kann sogar mehrere verschiedene Clients gleichzeitig nutzen, wenn man denn möchte.

Domino Anwendungen in einer Browser-Umgebung

Nutzt man den modernen Verse Browser Zugriff auf E-Mail, dann stellt sich die Frage nach Domino Anwendungen. Hier hatten wir zunächst eine Lösung als Browser Plug-in vorgesehen. Aufgrund von nachvollziehbaren Security Anforderungen mussten die Browser-Anbieter ihre Plug-in Frameworks allerdings so weit ändern, dass das nun keine Option mehr ist. Dafür gibt es jetzt für Bestandskunden kostenfrei runterladbar das IBM Client Application Access (ICAA) Tool, das zwar einmal installiert werden muss, sich dann aber bezüglich Updates benimmt wie ein klassisches Browser Plug-in. Es ermöglicht mit einem schlanken Client auf fast alle Domino-Anwendungen zuzugreifen und ist damit der perfekte Bruder zu Verse für E-Mail. Die wichtigste Ausnahme sind Eclipse-Anwendungen, die mit Version 8.0 kamen und nur von einigen Kunden genutzt werden. Für sie benötigt man weiterhin zwingend den Notes Standard Client.

Die Notes Client Roadmap

Auch wenn Verse und ICAA heute und in Zukunft die Speerspitze der Entwicklung im Domino-Umfeld darstellen und wir strategische Neuerungen damit zuerst und vielleicht auch alleinig dort bringen werden, wird auch der Notes Desktop Client weiterentwickelt. Das bedeutet im Moment nicht, dass wir jetzt planen würden zum Beispiel die Verse Oberfläche auch im Desktop Client anzubieten. Das würde auch wenig Sinn machen, denn abgesehen von wenigen Lücken, die wir schließen werden, bildet die Kombination aus Verse mit ICAA schon heute eine vollwertige Lösung. Deren klarer Vorteil ist eben nicht mehr mit jedem Update neu ausgerollt werden zu müssen. Und Updates wird es geben: Wir planen nämlich die Neuerungen von Verse in der Cloud weitgehend auch für Verse on Premises jeweils nachzuziehen.

Es gibt eine ganze Reihe von anderen Dingen, wie die Unterstützung neuer Technologien und Schnittstellen, die wir auch im Notes Desktop Client wieder dynamischer Unterstützen wollen. Auf der IBM Connect Hauskonferenz war einstens laut über eine Version 9.0.2 nachgedacht worden und dazu auch eine Folie mit möglichen Neuerungen aufgelegt worden. Ein erheblicher Teil ist mittlerweile – von vielen leider fast unbemerkt – mit den Feature Packs verfügbar geworden. Und die noch fehlenden, sowie weitere, neue Dinge werden in den nächsten Feature Packs, die zukünftig häufiger im Jahr ausgeliefert werden werden, kommen. Auch hier schimmert das Stichwort “evergreen” durch. Hierzu planen wir als Daumenregel zu jedem zweiten Featurepack ein Update des Notes Templates.

Das Supportfenster oder wie lange wird Verse / Notes / Domino unterstützt?

Üblich ist seit jeher, dass mit neuen Releases das garantierte Supportfenster wieder auf 5+3 Jahre hochgesetzt wird: fünf Jahre normaler Support und dann noch drei weitere Jahre gegen Geld. Das läuft seit mindestens 20 Jahren so. Da wir jetzt aus genannten Gründen (evergreen, agile Entwicklung) zukünftig von neuen Releases absehen wollen – wie es auch andere Hersteller tun – und stattdessen auf mehr kleinere Feature Packs setzen, war eine Anpassung notwendig. Das haben wir parallel zum Feature Pack 7 im September gemacht und das garantierte Supportfenster von bisher 2018 auf wieder 5+3 Jahre bis 2021, bzw. 2024 erweitert. Ein ganz normaler Vorgang und keinesfalls eine Aussage, dass 2021 Schluss wäre. So etwas können nur Wettbewerber streuen – geplant ist es auf jeden Fall nicht. Ganz im Gegenteil und dazu nur ein Beispiel: Im nächsten Template sind bereits die Feiertage für 2027 eingepflegt. Das würde ansonsten wenig Sinn machen.

Domino Anwendungen

Domino Anwendungen sind schnell, einfach und damit günstig zu erstellen und insbesondere mit der xPages Technologie auf dem Stand moderner Techniken. Dass sie in vielen Unternehmen ungemanagt und damit bei Infrastrukturverantwortlichen nicht sehr beliebt sind, teilen sie mit Excel Makros. Nichtsdestotrotz setzen viele Unternehmen Domino Anwendungen nicht nur für Abteilungslösungen ein, sondern auch strategische Anwendungen und bauen damit immer noch wunderbare Lösungen. Dadurch, dass IBM fortlaufend in diese eben auch sehr sichere Umgebung investiert, gibt es bei den meisten Kunden mittlerweile eine breite Palette davon. IBM Notes Domino ist eines der beständigsten IT Tools am Markt. Investitionsschutz pur. Domino Anwendungen aus den 1980-ern laufen heute noch unverändert. Es gab bis heute kein Rip-and-Replace oder irgendwas anderes, das größere Folgekosten nach sich gezogen hätte. Ein nahezu genialer Return-on-Investment.

Die Beständigkeit der Domino Plattform hat auch einen Nachteil – nämlich die Beständigkeit. Die Anwendungen, einmal geschrieben, mussten nie wieder angefasst werden. Sahen sie zum Erstellungszeitpunkt modern und Top of the Art aus, so ist über die Jahre in der IT doch viel passiert. Heute sehen 20 Jahre alte Anwendungen nicht mehr besonders cool aus, selbst wenn sie in ihren Prozessen immer noch brav ihren Dienst tun. Darüber ist die Plattform bei manchem in Verruf gekommen, als nicht mehr zeitgemäß und ähnliches. Wäre man gezwungen worden fortlaufend in die Anwendungserneuerung zu investieren, also etwa von klassischem Lotus Script auf xPages zu modernisieren, dann wären heute viele Nutzer glücklicher. Die Domino Technologie erlaubt mit xPages schon länger die Unterstützung von Browsern und mobilen Geräten. Nachdem IBM in jüngerer Zeit auch hier weiter investiert hat, werden xPages Anwendungen nun auch in der IBM Bluemix Anwendungs-Cloud unterstützt und können hier ganz einfach mit anderen Cloud Services, zum Beispiel Watson Analytics, angereichert werden. Für mache Lösungen, etwa Personalanwendungen, gibt es heute aber zugegebenermaßen auch standardisierte Lösungen, wie etwa von IBM Kenexa, sodass die Bedeutung von Domino Anwendungen in Teilbereichen rückläufig ist. Dennoch bleibt Domino eine der wichtigsten Anwendungsumgebungen.

Und für das Problem der veralteten Optik gibt es gelungene Partnerlösungen, um Domino Anwendungen zu modernisieren – auch hier in Deutschland. Was uns als Hersteller aber gerade beschäftigt ist vielmehr die Frage, was passiert, wenn demnächst Kunden doch massiv auf Cloud setzen wollen und sich dann fragen, wie sie auch die alten und neuen Domino-Anwendungen in die Cloud bekommen. Das ist nicht ganz trivial, da die Anwendungen nicht direkt mandantenfähig sein können und damit nicht dem Ideal einer Public Cloud Lösung entsprechen. An einer Lösung hierzu arbeiten unser Offering Management und Labor gerade intensiv. Und spätestens zur IBM Connect 2017 (20-23.2.2017, Moscone West, San Francisco) wird es hierzu Ankündigungen geben. Und dann ahne ich, dass sie noch mindestens ein weiteres Ass im Ärmel haben werden …

Cognitive Collaboration

Auf der Connect 2017 werden wir auch viel mehr dazu hören (und sehen), wie Watson-Funktionen das Arbeiten mit E-Mail, aber auch allen anderen Bereichen unseres Angebots, vereinfachen wird. Einfach ausgedrückt bekommen wir alle virtuelle Assistenten, die Hinweise geben, was besonders wichtig sein könnte und dazu Aktionen vorbereiten – wobei der Nutzer bestimmt. Was wir dort für 2017 planen, ist wirklich extrem spannend und IBM Verse ist auf jeden Fall ganz vorn als Teil der Story – und damit auch Domino, der beste Mail Server den es je gab. Domino forever!

Der Beitrag Verse / Notes / Domino forever! erschien zuerst auf DNUG.

XPages & Domino JNA

24. Juni 2016 Posted by Sven Hasselbach

Karsten Lehmann has published a very promising project named „Domino JNA„, which allows access to the underlying IBM Domino/Notes C API from Java.

If you want to use the project in a XPages, you have to add some Java permissions to the java.pol file on your server:

grant {
    permission java.util.PropertyPermission "jnidispatch.path", "write";
    permission java.util.PropertyPermission "jna.loaded", "write";
}

Additionally, you have to import the following JARS from „domino-jnatargetlib“ (after a successfull Maven build) to a directory on the server (jvm/lib/ext) or import it into an NSF:

  • commons-collections4-4.0.jar
  • jna-4.2.2.jar
  • joda-time-2.9.2.jar

Vaadin In XPages: A UIComponent for embedding Vaadin applications

3. Mai 2016 Posted by Sven Hasselbach

I have created an UIComponent to embed Vaadin applications into XPages. It allows to run both technologies on the same site by adding it to your XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:vaadin="http://vaadin.com/xsp/control"
    createForm="false">
    

    <!-- XPages Application -->
    <xp:form>
        <xp:div id="refreshMe">
            <h1>XPage</h1>
            <xp:label
                value="#{javascript:java.lang.System.nanoTime()}"
                id="label1">
            </xp:label>
        </xp:div>
        <xp:button
            value="refresh"
            id="button1">
            <xp:eventHandler
                event="onclick"
                submit="true"
                refreshMode="partial"
                refreshId="refreshMe">
            </xp:eventHandler>
        </xp:button>
    </xp:form>
    
    <!--  VAADIN Application -->
    <vaadin:Vaadin
        id="Vaadin1"
        url = "/vaadin/"
        divId="addressbook"
        version="7.3.8"
        forceResize="true" />
        
</xp:view>

This little example shows the XPage above including the the Adressbook application:

2016-05-03 12_31_17-VaadinInXPages

The Vaadin application is reachable at „http://localhost/vaadin/“ (hosted by Domino), that’s why the property url is set to „/vaadin/„. The property divId contains the name of the application which is used as id in the DOM tree.

It is required that the VaadinComponent is not inside a xp:form element. If it detects a surrounding form, a runtime exception is thrown. That’s why you have to set createForm to false and add your own xp:form to your application, as shown in the example.

2016-05-03 12_50_11-Runtime Error

If the parameter forceResize is set to true, a CSS style sheet is added to the XPage which resizes the height of html and the body node to 100%. Otherwise the Vaadin application will not be displayed correctly.

2016-05-03 12_54_35-view-source_localhost_VaadinInXPages.nsf_index.xsp

Resource Aggregation must be disabled, otherwise it won’t work.

The sources can be found at https://github.com/hasselbach/VaadinInXPages

EntwicklerCamp 2016: XPages erweitern und ausbauen

15. April 2016 Posted by Sven Hasselbach

Die PDF Version meiner Entwickler Camp 2016 Session „XPages erweitern und ausbauen“ gibt es hier: ec2016-Votrag-XPagesErweiternUndAusbauen.pdf

Die verwendeten Sources finden sich in diesem Repository auf GitHub: ec2016-xpages-erweitern

Bei Fragen/Problemen einfach eine kurze Info an mich.

EntwicklerCamp 2016: REST Services in Domino

14. April 2016 Posted by Bernd Hort

EntwicklerCamp

Nachdem Thomas Bahn gestern schon mit guten Beispiel voran gegegangen ist und seine Folien vom EntwicklerCamp veröffentlicht hat, will ich dies auch tun.

Anfangen möchte ich mit meinem Vortrag zum Thema "REST Services in Domino". In 90 Minuten habe ich ausführlich beschrieben, wie RESTful Webservices in Domino zur Verfügung gestellt werden können.

Wie immer können die Folien und die Beispiel-Anwendung heruntergeladen werden. Basis für diesen Vortrag war mein Vortrag auf der IBM Connect 2016: REST Services in Domino - Key to modern Web Applications. Insofern ist die Beispiel-Anwendung die gleiche wie beim IBM Connect-Vortrag.

 

Beim Öffnen der Beispiel-Anwendung werden alle in der Anwendung implementierten REST Services noch einmal vorgestellt.


EntwicklerCamp 2016: REST Services in Domino

14. April 2016 Posted by Bernd Hort

EntwicklerCamp

Nachdem Thomas Bahn gestern schon mit guten Beispiel voran gegegangen ist und seine Folien vom EntwicklerCamps veröffentlicht hat, will ich dies auch tun.

Anfangen möchte ich mit meinem Vortrag zum Thema "REST Services in Domino". In 90 Minuten habe ich ausführlich beschrieben, wie RESTful Webservices in Domino zur Verfügung gestellt werden können.

Wie immer können die Folien und die Beispiel-Anwendung heruntergeladen werden. Basis für diesen Vortrag war mein Vortrag auf der IBM Connect 2016: REST Services in Domino - Key to modern Web Applications. Insofern ist die Beispiel-Anwendung die gleiche wie beim IBM Connect-Vortrag.

 

Beim Öffnen der Beispiel-Anwendung werden alle in der Anwendung implementierten REST Services noch einmal vorgestellt.


IBM Connect 2016: REST Services in Domino – Key to modern Web Applications

2. Februar 2016 Posted by Bernd Hort

IBM Connect 2016

As promised the slides and the sample database from my session at IBM Connect 2016 REST Services in Domino - Key to modern Web Applications.

Even if you didn't had the chance to make it to my session, with the slides and specially with the sample database you have a good starting point.

.

Things I never blogged about: XPages & Google’s EventBus

1. Dezember 2015 Posted by Sven Hasselbach

This is another topic I wanted to blog about for a long time: The use of Google’s EventBus in XPages applications. EventBus is a replacement for the Java in-process event distribution. It makes life a lot easier.

My first plan was to add the guava.jar into an OSGi plugin because of security reasons (otherwise you have to use the „grant{ permission java.security.AllPermission;}„) but I never had the time. That’s why I created a simple example which uses the jar in the lib/ext folder instead.

To use the EventBus in an application, you first have to define an event. This simple example has a message only, but you can add any properties you want.

package ch.hasselba.xpages;

public class FrontendEvent {

    private final String message;

    public FrontendEvent(final String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

}

Then we need a subscriber. For this we have to add the annotation @Subscribe to a method of a managed bean which should handle an event. The class type of the parameter tells the EventBus what kind of event our method should handle.

This is a managed bean which stores the last message sent over the EventBus:

package ch.hasselba.xpages;

import com.google.common.eventbus.Subscribe;

public class EventSubscriber {

    private String message;

    @Subscribe
    public void handleEvent(FrontendEvent event) {
        this.message = event.getMessage();
    }

    public void getMessage(String msg) {
        this.message = msg;
    }

    public String getMessage() {
        return this.message;
    }
}

Then we have to create a managed bean which has an instance of EventBus, the FrontendEventBus. The bus has a managed property subscribers. As soon the bean is instantiated, the managed property is called and the list of objects is registered to the EventBus instance.

package ch.hasselba.xpages;

import java.io.Serializable;
import java.util.ArrayList;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;

@SuppressWarnings("serial")
public class FrontendEventBus implements SubscriberExceptionHandler,
        Serializable {

    final EventBus eventBus = new EventBus();
    ArrayList<Object> subscribers;

    /**
     * registers all subscribing managed beans from faces-config.xml
     * 
     * @param subscribers
     *            ArrayList with references to managed beans
     */
    public void setSubscribers(ArrayList<Object> subscribers) {
        this.subscribers = subscribers;
        for (Object subscriber : subscribers) {
            register(subscriber);
        }
    }

    public void post(final Object event) {
        eventBus.post(event);
    }

    public void register(final Object object) {
        eventBus.register(object);
    }

    public void unregister(final Object object) {
        eventBus.unregister(object);
    }

    public final void handleException(Throwable exception,
            SubscriberExceptionContext context) {
        exception.printStackTrace();

    }

}

There is only one problem: If a bean is in a lower scope, the initialization will fail. In this case we have to do the opposite to attach a subscriber to the bus. Instead of telling the bus which subscriber we want to register, we are telling the subscriber which bus to attach to.

package ch.hasselba.xpages;

public class EventSubscriberRequestScope extends EventSubscriber {

    /**
     * registers the current instance to the event bus
     * 
     * @param eventBus
     *            the frontend event bus
     */
    public void setEventBus(FrontendEventBus eventBus) {
        eventBus.register(this);
    }

}

As soon the bean is instantiated, the managed property will have an instance of our FrontendEventBus and the bean can register itself.

The faces-config.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>

    <managed-bean>
        <managed-bean-name>frontendEventBus</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.FrontendEventBus</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
        <managed-property>
            <property-name>subscribers</property-name>
            <list-entries>
                <value-class>java.lang.Object</value-class>
                <value>#{eventSubscriber}</value>
            </list-entries>
        </managed-property>
    </managed-bean>
    
    
    <managed-bean>
        <managed-bean-name>eventSubscriber</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.EventSubscriber</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    
    
    <managed-bean>
        <managed-bean-name>eventSubscriberRequestScope</managed-bean-name>
        <managed-bean-class>ch.hasselba.xpages.EventSubscriberRequestScope</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>eventBus</property-name>
            <value>#{frontendEventBus}</value>
        </managed-property>
    </managed-bean>
    
</faces-config>

The first subscribing bean is in the same scope as the FrontendEventBus, but the second one is a request scoped bean.

Now it’s time to send an event. This can be realized by creating a new instance of our FrontendEvent we have created earlier and posting it to the bus.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button
        id="button1" value="Click me">
        
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:importPackage( ch.hasselba.xpages );
                    var msg = "Timestamp: " + java.lang.System.currentTimeMillis();
                    var se = new ch.hasselba.xpages.FrontendEvent( msg );
                    frontendEventBus.post( se );
                }]]>
            </xp:this.action>
        </xp:eventHandler>
    
    </xp:button>
    
    <br />
    <br />
    
    <xp:label
        value="#{eventSubscriber.message}"
        id="labelSubscriberMessage">
    </xp:label>
    
    <br />
    
    <xp:label
        value="#{eventSubscriberRequestScope.message}"
        id="labelSubscriberRequestScope">
    </xp:label>
    
</xp:view>

As soon the button is clicked, both beans are receiving the event and updating their message.

2015-12-01 14_04_04-Mozilla Firefox

Hope someone finds it usefull.

Things I never blogged about: The XPagesExecutor Service

29. November 2015 Posted by Sven Hasselbach

The XPages engine has its own executor service to run jobs concurrently in another thread: the XPagesExecutor service. Under the hood the service uses a ThreadPoolExecutor for executing tasks, so it allows to use Runnables or Callables/Futures for asynchronous computation. For fun, you can even run a server instance, the started threads can run as long as you want (e.g. they are not bound to the XPages lifecycle).

Here is a small XPage with a button to start a Runnable via the Executor:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    
    <xp:button
        value="run the job"
        id="buttonDoIt">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:
                importPackage(ch.hasselba.xpages);
                new ch.hasselba.xpages.DemoExecutor().doIt();}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

To use the Service, you have to aquire it first, then you can execute a Runnable like in this code snippet:

package ch.hasselba.xpages;

import java.util.concurrent.ExecutorService;

import lotus.domino.NotesException;
import lotus.domino.Session;

import com.ibm.domino.xsp.module.nsf.NSFComponentModule;
import com.ibm.domino.xsp.module.nsf.NotesContext;
import com.ibm.domino.xsp.module.nsf.SessionCloner;
import com.ibm.xsp.application.XPagesExecutor;

public class DemoExecutor {
    private final NSFComponentModule module;
    private final SessionCloner sessionCloner = SessionCloner
            .getSessionCloner();

    public DemoExecutor() {
        // get the current NSFComponentModule
        this.module = NotesContext.getCurrent().getModule();
    }

    public void doIt() {
        // aquire the Executor
        ExecutorService exService = XPagesExecutor.acquire();
        
        // run the Runnable
        exService.execute(new DemoRunnable(this.module));
    }

    class DemoRunnable implements Runnable {
        private final NSFComponentModule module;

        public DemoRunnable(NSFComponentModule module) {
            this.module = module;
        }

        public void run() {
            try {

                // init the NotesThread
                NotesContext context = new NotesContext(this.module);
                NotesContext.initThread(context);

                // now do the job...
                System.out.println("Starting the job...");
                Session session = null;
                try {
                    // grab the session
                    session = sessionCloner.getSession();
                    System.out.println("Running as "
                            + session.getEffectiveUserName());
                    Thread.sleep(10000);
                } catch (NotesException e) {
                    e.printStackTrace();
                } finally {
                    DominoUtils.recycle( session );
                }
                System.out.println("Done!");

            } catch (InterruptedException ie) {
                // handle interruption here
            } finally {
                // kill the Notes thread
                NotesContext.termThread();
            }
        }

    }

}

2015-11-29 08_54_13-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator

Callables must be submitted instead:

   public long doItWithCallable() {

        ExecutorService exService = XPagesExecutor.acquire();
        Future<Long> result = exService.submit(new MyCallable(this.module));
        long returnValue = (-1);

        try {
            returnValue = result.get();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } catch (ExecutionException ee) {
            ee.printStackTrace();
        }

        return returnValue;
    }

    class DemoCallable implements Callable {
        private final NSFComponentModule module;

        public DemoCallable(NSFComponentModule module) {
            this.module = module;
        }

        public Long call() throws Exception {
            try {

                // init the NotesThread
                NotesContext context = new NotesContext(this.module);
                NotesContext.initThread(context);

                // now do the job...
                System.out.println("Starting the job...");
                Session session = null;
                try {
                    // grab the session
                    session = sessionCloner.getSession();
                    System.out.println("Running as "
                            + session.getEffectiveUserName());
                    Thread.sleep(10000);
                } catch (NotesException e) {
                    e.printStackTrace();
                } finally {
                    DominoUtils.recycle( session );
                }
                System.out.println("Done!");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // kill the Notes thread
                NotesContext.termThread();
            }

            return System.currentTimeMillis();
        }

    }

Hope someone finds it usefull.

[Discontinued] Testing XPages(3): Testing the Business Logic

28. November 2015 Posted by Sven Hasselbach

I will not develop my planned XPages Testing Framework further because for me it has no business case anymore. While there never was enough time to make it to a „real project“ for the daily development (full JUnit integration, deployed as an OSGi plugin etc) and this code is only an example, I used this technique with a similar code base in some projects. It was a big help during development, because it allows to „look inside“ the XPages engine while testing.

How does it work?

In a JUnit test, a local proxy is started which fetches all request / responses between the Selenium WebDriver and the Domino server. Only the first request is relevant, the other requests are the resources like Javascript files, Images and CSS.

During the test, a special HTTP Header is added to the request (X-XPAGES-TEST). It contains EL code and the JSF phase when to execute it. A phase listener (in the application, better in an OSGi plugin) intercepts the HTTP header and executes the code. The result it posted back to the client (encapsulated in another HTTP header). It can then be tested with an assert.

Because of the use of special headers instead of injecting the results into the HTML markup, this can be used with Diffy or similar products to verify the application while refactoring the code base.

Here is the JUnit Test application:


package ch.hasselba.xpages.test.XPagesTester;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.proxy.ProxyServer;
import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest;
import net.lightbody.bmp.proxy.http.BrowserMobHttpResponse;
import net.lightbody.bmp.proxy.http.RequestInterceptor;
import net.lightbody.bmp.proxy.http.ResponseInterceptor;

import org.apache.http.HttpResponse;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriver.Options;
import org.openqa.selenium.WebDriver.Timeouts;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class App
{
  private ProxyServer server;
  private LinkedList httpResponses;
  private LinkedList httpRequests;
  private WebDriver driver;
  private String baseUrl = "http://localhost/Demo.nsf/Test.xsp";
  private StringBuffer verificationErrors = new StringBuffer();
  private final String HTTP_HEADER_NAME = "X-XPAGES-TESTS";
	
  @Before
  public void setUp() throws Exception {
    this.server = new ProxyServer(4444);
    this.server.start();

    Proxy proxy = this.server.seleniumProxy();

    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability("proxy", proxy);

    ResponseInterceptor interceptor = new ResponseInterceptor() {
      public void process(BrowserMobHttpResponse response, Har har) {
    	  httpResponses.add( response );
      }
    };
    RequestInterceptor requestInterceptor = new RequestInterceptor() {
      public void process(BrowserMobHttpRequest request, Har har) {
    	  httpRequests.add( request );
      }
    };
    this.server.addRequestInterceptor(requestInterceptor);

    this.server.addResponseInterceptor(interceptor);
    this.driver = new FirefoxDriver(capabilities);
    this.driver.manage().timeouts().implicitlyWait(10L, TimeUnit.SECONDS);
  }
  @After
  public void tearDown() throws Exception {
    this.driver.quit();
    String verificationErrorString = this.verificationErrors.toString();
    if (!"".equals(verificationErrorString))
      Assert.fail(verificationErrorString);
  }

  @Test
  public void testDemo() throws Exception
  {
    StringBuffer strBuffer = new StringBuffer();
    strBuffer.append("[");
    strBuffer.append("{");
    strBuffer.append(""code": "#{javascript:java.lang.System.currentTimeMillis()}"}]");
    this.server.addHeader(HTTP_HEADER_NAME, strBuffer.toString() );
    
    reloadPage();

    System.out.println( "X-PAGES-TESTS: " + getXPageResponse().getHeader(HTTP_HEADER_NAME) );
    
    HttpResponse httpRawResponse = getXPageResponse().getRawResponse();
    Assert.assertTrue("HTTP/1.1 200 OK".equals(httpRawResponse.getStatusLine()
      .toString()));

    Assert.assertTrue("Lotus-Domino".equals(getXPageResponse().getHeader("Server")));

  }

  public void reloadPage()
  {
    this.httpRequests = new LinkedList();
    this.httpResponses = new LinkedList();
    this.driver.get(this.baseUrl);
  }
  
  public BrowserMobHttpResponse getXPageResponse(){
    return this.httpResponses.get(0);
  }
}

And here comes the phase listener:


package ch.hasselba;

import java.util.HashSet;
import java.util.Iterator;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

import com.ibm.commons.util.io.json.JsonEmptyFactory;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonParser;
import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.context.ExternalContextEx;
import com.ibm.xsp.context.FacesContextExImpl;

@SuppressWarnings("serial")
public class TestPhaseListener implements PhaseListener {

  private final String HTTP_HEADER_NAME = "X-XPAGES-TESTS";
	
  public void afterPhase(PhaseEvent event) {}

  public void beforePhase(PhaseEvent event) {
    FacesContextExImpl fc = (FacesContextExImpl) event.getFacesContext();
    ExternalContextEx ec = (ExternalContextEx) fc.getExternalContext();
    HttpServletRequest request = (HttpServletRequest) ec.getRequest();
    ApplicationEx app = fc.getApplicationEx();
    HashSet results = new HashSet();
    try {
      String jsonData = request.getHeader(HTTP_HEADER_NAME);
      // Create the JAVA json factory
      JsonJavaFactory factory = JsonJavaFactory.instanceEx;
      // Turn the sort parameter string into an array of JSON objects
      Object jsonTests;

      jsonTests = JsonParser.fromJson(factory, jsonData);
      // Loop through the JSON objects and do something
      for (Iterator<Object> oSort = factory.iterateArrayValues(jsonTests); oSort
					.hasNext();) {
         JsonJavaObject o = (JsonJavaObject) oSort.next();
	 //String phaseId  o.getJsonProperty("phaseId") );
	 String code = (String) o.getJsonProperty("code");

         try {
           Object result = app.createMethodBinding(code, null).invoke(fc,
						null);
           results.add( result.toString() );
         }catch( Exception e) {
	   results.add( e.getMessage() );
         }
      }

      StringBuffer buffer = new StringBuffer();
      buffer.append( "[" );
      int pos = 0;
      for( String res : results ) {
         buffer.append( "{ "result" : "" + res + ""}");
         pos++;
	 if( pos < results.size() )
	   buffer.append( "," );
      }
      buffer.append( "]");
			
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME, buffer.toString() );
			
    } catch (JsonException e) {
      e.printStackTrace();
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME,
					e.getLocalizedMessage());
    } catch (Exception e) {
      e.printStackTrace();
      fc.getXspResponse().addHeader(HTTP_HEADER_NAME,
					e.getLocalizedMessage());
    }

  }

  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }

}

Hope someone finds it usefull.

XPages: Java Interfaces Java and Test Driven Development

15. Oktober 2015 Posted by Bernd Hort

A word of warning: the following article is quite lenghty and abstract. It basically says that using Java Interface in your XPages projects leads to better (that means more maintanable) code and that with the help of the OpenNTF project org.openntf.junit.xsp a developer can utilize Java Interfaces to write better test code for their XPages projects.


I have to confess that when I started with Java Development some time ago I didn't like Java Interfaces. While in some cases I could see that they are useful most of the time they gave me a hard time trying to understand the code of someone else. My usual approach for reading someone else code is to figure out how to invoke the part I'm interested in and then take a close look at the parameters that are passed to the method and the return value. Interfaces due to their nature shield the implementation.. But that the part I was interested in. Searching for the current implementation made it more complicated.

Since working with XPages my preferences had completely changed. Like most of the Notes developer which start with Java my code used to be small. If you write some Notes Agent to invoke a Web Service or generate a PDF document there is normally not much need for an elaborated design of your class hierarchy.

But nowadays as we developed our own XPages framework totally Java based the rules and approaches must cope with the arising complexity. One aspect were Java Interfaces helps us is the Open/closed principle. Simply speaking the Open/close principal state that code should be open for extension but closed for modification. With modification are those meant that leads to changes in calling methods and therefore are costly and error-prone. If you find a better way to implement an algorithm inside your class no one will complain. But if there is change in your API resulting into the necessity to change code in a lot of calling methods things might get messy.

By using Interfaces as parameters and return values it is like having a contract. The caller of your method can rely on whatever will be passed to the method and returned from it will work as long as it implements the Interface. If you write a new class which is way faster than your old code you can use it instead without worrying that you break something. This is true as long as the new code implements the same Interface. If you find a cool new OpenSource library which you would like to use instead and it does not implement the Interface, you can simply write a wrapper for it.

To believe nothing breaks by your change is one thing. To know it, is another thing. This is were Test Driven Development and JUnit Testing comes in. The idea to walk away from any change you made and to be absolute confident that everything works is amazing.

My first steps using JUnit were a little bit frustrating because there is no way to run JUnit test in Domino Designer directly for your XPages Java code due to the virtual file system a NSF is from Eclipse point of view. So I had to export my Java code and then run JUnit tests in a stand alone Eclipse.

I really was delighted when I read about the OpenNTF project org.openntf.junit.xsp. This project allows to execute the JUnit Tests from within an XPage.

One of the challenges with writing test code is that with systems running in a special environment like XPages you have to find a way to have living objects from this environment. A way around this issue is to use a mock object. And again with Java Interfaces this is easy. Instead of basing an existing Notes document to your test code you could pass a mock up object since lotus.domino.Document is a Java Interface. So there is no need to have a Notes Session, a Notes Database and a existing Notes Document that meets your test requirements for now and all the future to pass your JUnit test.

Christian Güdemann did a great job integrating EasyMock into org.openntf.junit.xsp. Read more in his blog post "org.openntf.junit.xsp – now with EasyMock support".

Have a closer look into using Java Interfaces today. They really help you.

Happy coding!