LotusScript in XPages
Wäre es nicht schön, wenn man in XPages direkt mit Lotus Script arbeiten könnte? Wenn es einen Weg gäbe, mit der sich Lotus Script-Code direkt in der XPage einbetten liesse, und wie folgt zu verwenden wäre?

Prinzipiell ist das möglich, aber der hier dargestellte Weg ist eher als Workaround anzusehen und wird das Alpha-Stadium wohl eher nicht verlassen. Aber es lässt sich anhand dieser Anleitung zeigen, wie man XPages flexibel erweitern und weitere Interpreter-Sprachen der XPages-Engine hinzufügen kann.
Was mit der hier vorgestellten Lösung jedoch möglich ist, kann man in dieser kleinen Beispiel-XPage sehen:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:label id="label1">
<xp:this.value>
<![CDATA[#{lotusscript:returnValue=-1 }]]>
</xp:this.value>
</xp:label>
<xp:br/>
<xp:br/>
<xp:label id="label2">
<xp:this.value><![CDATA[${lotusscript:
Dim session as New NotesSession
Dim db as NotesDatabase
Set db = session.CurrentDatabase
returnValue = db.AllDocuments.getFirstDocument.UniversalID
}]]></xp:this.value>
</xp:label>
</xp:view>
In der XPage kann der neue Tag “lotusscript:” verwendet und beliebiger LotusScript-Code direkt in die XPage eingebettet werden. Das Ergebnis im Browser sieht dann wie folgt aus:

Soviel vorab, nun zu erst einmal zu den Basics…
Grundlagen: Method-Bindings & Value-Bindings
Um einen neuen Tag für XPages bereitzustellen, müssen zu aller erst Bindings für Methoden- und für Werte-Zuweisungen erstellt werden. Die beiden Binding-Arten unterscheiden sich – grob formuliert* – wie folgt: Value-Bindings werden überall dort verwendet, wo ein Wert einer Komponente zugewiesen wird und (wie der Name erahnen lässt) eine value-Eigenschaft exitsiert. Dies ist bei praktisch allen UIKomponenten der Fall. Method-Bindings hingegen kommen bei der Zuweisung bei samtlichen Events ins Spiel: Der SSJS-Code eines BeforePageLoad-Events wird mit einem Method-Binding gesetzt, oder der Code bei Button-Events usw.
Die Value-Binding-Klasse, die für die hier geschilderten Zwecke benötigt wird, sieht wie folgt aus:
package ch.hasselba.xpages.jsf.el;
import com.ibm.xsp.binding.ValueBindingEx;
import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.PropertyNotFoundException;
public class LSValueBinding extends ValueBindingEx {
private String data;
public LSValueBinding(String content) {
this.data = data;
}
@Override
public Object getValue(FacesContext context)
throws EvaluationException, PropertyNotFoundException {
return LSExecutor.execLotusScriptCode( data );
}
@Override
public void setValue(FacesContext context, Object obj)
throws EvaluationException, PropertyNotFoundException {}
@Override
public boolean isReadOnly(FacesContext context)
throws EvaluationException, PropertyNotFoundException {
return true;
}
@SuppressWarnings("unchecked")
@Override
public Class getType(FacesContext context)
throws EvaluationException, PropertyNotFoundException {
return Object.class;
}
}
Das Value-Binding erweitert die Klasse com.ibm.xsp.binding.ValueBindingEx und überschreibt deren Eigenschaften, wo es nötig ist. Im Konstruktor der Klasse findet die Wertezuweisung statt; es wird hier der Teil, der in der XPage nach dem “lotusscript:” folgt, übergeben. Auch mehrzeiliger Code in der XPage wird als einfacher String durchgereich, getrennt durch Zeilenumbrüche.
Um an den Wert des Bindings zu gelangen, wird während der Verarbeitung durch das JSF Framework die Methode getValue() aufgerufen; dies ist die Stelle, an der die Daten des Value-Bindings verarbeitet werden, und wie man hier sehen kann, findet der Aufruf des LotusScript-Codes genau an dieser Stelle statt.
Das notwendige Method-Binding ist so ähnlich aufgebaut:
package ch.hasselba.xpages.jsf.el;
import com.ibm.xsp.binding.MethodBindingEx;
import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.MethodNotFoundException;
public class LSMethodBinding extends MethodBindingEx {
private String data;
public LSMethodBinding () {
super();
}
public LSMethodBinding (String expr) {
super();
this.data = expr;
}
@Override
public Object invoke(FacesContext context, Object[] obj)
throws EvaluationException, MethodNotFoundException {
return LSExecutor.execLotusScriptCode( content );
}
@SuppressWarnings("unchecked")
@Override
public Class getType(FacesContext context)
throws MethodNotFoundException {
return null;
}
}
Hier wird wie Klasse com.ibm.xsp.binding.MethodBindingEx erweitert und wenn nötig überschrieben. Anders als bei Value-Binding erfolgt der “Abruf” der Daten eines Method-Bindings durch das JSF Framework nicht durch getValue(), sondern durch die Methode invoke(). Hierbei können theoretisch noch Parameter übergeben werden, die für die Verarbeitung relevant sein könnten. In diesem Fall kann dies aber getrost ignoriert werden.
Zu guter Letzt müssen die beiden Bindings in eine BindingFactory-Klasse zusammen geführt und mit dem “lotusscript“-Tag verbunden werden:
package ch.hasselba.xpages.jsf.el;
import com.ibm.xsp.util.ValueBindingUtil;
import com.ibm.xsp.binding.BindingFactory;
import javax.faces.application.Application;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
public class LSBindingFactory implements BindingFactory {
public String getPrefix() {
return "lotusscript";
}
@SuppressWarnings("unchecked")
public MethodBinding createMethodBinding(
Application app, String expr, Class[] obj) {
String script = ValueBindingUtil.parseSimpleExpression(expr);
return new LSMethodBinding(script);
}
public ValueBinding createValueBinding(Application app, String expr) {
String script = ValueBindingUtil.parseSimpleExpression( expr );
return new LSValueBinding(script);
}
}
Die Klasse erweitert com.ibm.xsp.binding.BindingFactory und ist die “Weiterleitung” innerhalb des JSF-Frameworks: Die Methode getPrefix() liefert den String zurück, für den diese BindingFactory zuständig ist; es ist praktisch jeder Bezeichner verwendbar, nur id und javascript sind bereits verwendet.
Während der Verarbeitung der Bindings such das JSF-Framework zur Laufzeit nach der passenden Factory. Dabei werden alle bekannten Factories nach dem passenden Prefix durchsucht, weshalb die BindingFactory dem Framework noch bekannt gemacht werden muss, um verwendet werden zu können.
Im zweiten Teil wird eine Quick-n-Dirty-Variante gezeigt, die BindingFactory bekannt zu machen und der LotusScript-Wrapper wird vorgestellt.
*: Anmerkung:
Aus Sicht der JSF-Spezifikation ist der Unterschied zwischen Value-Binding und Method-Binding etwas komplexer, als hier dargestellt.