Posts Tagged: ‘Extenisbility API’

Quick-n-Dirty: Use your own Factory classes in XPages

18. Oktober 2012 Posted by Sven Hasselbach

Here is a easy way to use your own factory classes in XPages:

1. Create a file named “com.ibm.xsp.factories.properties” in the WEB-INF-Folder of your NSF

 

 

2. In this file, define the factory classes you want to use in the format<NAME>=<Java Class>

HelloWorldFactory=ch.hasselba.factory.HelloWorld

3. Create a java class, in this example ch.hasselba.factory.HelloWorld

package ch.hasselba.factory;

public class HelloWorld 
{
   public HelloWorld(){
      System.out.println("Hello World Factory alive!");
   }

   public String getMessage(){
      return "Hello World!";
   }

}

The factory classes you are adding require a constructor. They will be instantiated during runtime.

4. After saving your java class and opening a XPage you should see the message on the server console (perhaps you have to do a clean first).

This means that the Factory is now accessible and is ready to use. The class will only be instantiated once during runtime.

5. To use your Factory, you have to do a lookup first, before you can use it. This can be done using the com.ibm.xsp.factory.FactoryLookup class which can be accessed via the current application (Please read the notice in the ExtAPI documentation!).

Here is a SSJS code snippet:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:label id="labelHelloWorld">
      <xp:this.value>
         <![CDATA[#{javascript:
            var app = facesContext.getApplication();
            var facLookUp = app.getFactoryLookup();
            var hwFac = facLookUp.getFactory("HelloWorldFactory");
            hwFac.getMessage();
         }]]>
      </xp:this.value>
   </xp:label>
</xp:view>

The main difference between a managed bean and this approach is, that the class is instantiated as soon the JSF application starts running, not if you access the bean in your code for the first time. The factory class can do what you want, f.e. run a thread or whatever. The result of the operations can be accessed via the  factory lookup.

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

XPages: Run your own Servlets

9. Juli 2012 Posted by Sven Hasselbach

A really interesting article about running your own servlets on domino server can be found here: http://www.ibm.com/developerworks/cn/lotus/xpage-servlet/index.html

It’s chinese, but you can translate f.e. with Google’s Translator.

With 8.5.3 I had have some problems because the required interface IServletFactory could not be resolved in DDE.

To get the example running, you have to add a JAR to your build path. Open Project properties, select “Java Build Path” and click “Add External JARs“:

Now you have to add the file “lwpd.domino.adapter.jar“. This can be found in the following folder:

<NOTES>\framework\shared\eclipse\plugins\com.ibm.domino.xsp.adapter_<VERSION>

The <VERSION> string depends on your current installation and Server-Version.

After adding this JAR to the build path, the compilation problems should be resolved.

XPages application events: Create your own ApplicationListener (2)

7. Juli 2012 Posted by Sven Hasselbach

There is another interface available which provides the additional method applicationRefreshed. This event is always raised if the method refresh() of the Application-Object is fired.

Instead of implement the interface described in the previous posting, you have to use the interface named com.ibm.xsp.application.events.ApplicationListener2.

package ch.hasselba.jsf.debug;

import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.application.events.ApplicationListener2;

public class MyApplicationListener implements ApplicationListener2 {

    public void applicationCreated(ApplicationEx arg0) {
        System.out.println("applicationCreated()");
        System.out.println("ID: " + arg0.getApplicationId());
     }

    public void applicationDestroyed(ApplicationEx arg0) {
        System.out.println("applicationDestroyed()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

    public void applicationRefreshed(ApplicationEx arg0) {
        System.out.println("applicationRefreshed()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

}

You can fire the refresh of the application manually. Here is a simple example how to do this:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
   <xp:button value="Label" id="button1">
      <xp:eventHandler event="onclick" submit="true"
         refreshMode="complete">
            <xp:this.action>
               <![CDATA[#{javascript:
                  facesContext.getApplication().refresh()
               }]]>
            </xp:this.action>
        </xp:eventHandler>
   </xp:button>
</xp:view>

By clicking the button, the refresh is fired:

XPages application events: Create your own ApplicationListener

7. Juli 2012 Posted by Sven Hasselbach

If you want to get a handle to application events and want to know if a XPages application is created or destroyed (which means the application was destroyed because of a time out), you can implement this by creating your own own application listener.

For this you have to do the following steps:

  1. Create a class which implements the ApplicationListener interface
  2. Activate the class in XPages runtime

To realise the first part, you have to create a class that implements the interface com.ibm.xsp.application.events.ApplicationListener. This interface has two methods: applicationCreated and applicationDestroyed (the method names should be self-describing enough).

Here is an example:

package ch.hasselba.jsf.debug;

import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.application.events.ApplicationListener;

public class MyApplicationListener implements ApplicationListener {

    public void applicationCreated(ApplicationEx arg0) {
        System.out.println("applicationCreated()");
        System.out.println("ID: " + arg0.getApplicationId());
    }

    public void applicationDestroyed(ApplicationEx arg0) {
        System.out.println("applicationDestroyed()");
        System.out.println("ID: " + arg0.getApplicationId());  
    }

}

Now to step two, the activation of the class. To do this, you have to add a special configuration file to your application:

  1. Switch to “Java” perspective
  2. Create a new folder “META-INF” in the “Code/Java” section
  3. Create a sub folder “services
  4. Create a file named “com.ibm.xsp.core.events.ApplicationListener
  5. In this file you add the full name of your MyApplicationListener class (including the package name):

The file structure should now look like this in Java perspective:

If you switch back to Domino perspective, the structure in the “Code/Java” looks like this:

You can now verify that all works correctly by opening a XPage and have a look on your server console:

[The application timeout was set to one minute.]

LotusScript in XPages (3): Quick-n-Dirty-Aktivierung

10. April 2012 Posted by Sven Hasselbach

LotusScript in XPages

Dies ist der dritte Teil des Artikels “LotusScript in XPages”. Der erste Teil befindet sich hier, der zweite Teil hier.

 

Die Quick-n-Dirty-Aktivierung

Damit die BindingFactory verwendet werden kann, müsste eigentlich ein Plugin erstellt werden, doch es gibt auch eine “Abkürzung”, denn die Factory kann auch über einen angepassten ViewHandler in XPages verwendet werden. Dies ist beim Testen / Entwickeln eine sehr praktische Angelegenheit, da sich dadurch der Aufwand deutlich verringern lässt (Vielen Dank an dieser Stelle an Jesse Gallagher für seine Idee).

Der ViewHandler ist einfach aufgebaut und sieht wie folgt aus:

package ch.hasselba.xpages.jsf.el;

import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.factory.FactoryLookup;

public class LSViewHandler extends
   com.ibm.xsp.application.ViewHandlerExImpl {
    public LSViewHandler(ViewHandler paramHandler) {
        super(paramHandler);
    }

    @SuppressWarnings({ "deprecation" })
    @Override
    public UIViewRoot createView(FacesContext context,
       String paramString) {
        ApplicationEx app = (ApplicationEx) context.getApplication();
        FactoryLookup facts = app.getFactoryLookup();

        LSBindingFactory lsfac = new LSBindingFactory();
        if(facts.getFactory(lsfac.getPrefix()) == null) {
            facts.setFactory(lsfac.getPrefix(), lsfac);
        }

        return super.createView(context, paramString);
    }

}

Der aktuellen Application-Instanz wird hierbei die BindingFactory im createView() hinzugefügt, wenn diese noch nicht vorhanden ist, danach wird die ursprüngliche createView()-Methode der erweiterten Klasse com.ibm.xsp.application.ViewHandlerExImpl aufgerufen.

Dann muss nur noch die faces-config.xml modifiziert und der neue ViewHandler eingetragen werden:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <application>
    <view-handler>ch.hasselba.xpages.jsf.el.LSViewHandler</view-handler>
  </application>
</faces-config>

Nun kann in der XPage mit dem neuen “lotusscript”-Tag experimentiert werden. Da der Designer den neuen Tag nicht kennt, wird immer eine Warnung ausgegeben werden bzw. die Syntax mit einem Ausufezeichen markiert:

Dies stellt aber (zumindest unter 8.5.3) kein Problem dar, ist aber erst durch eine “saubere” Plugin-Lösung behebbar.

In den nächsten Teilen wird die Plugin-Variante vorgestellt, und ein paar Erweiterungsmöglichkeiten für den LotusScript-Wrapper gezeigt.

LotusScript in XPages (2): LotusScript-Wrapper

8. April 2012 Posted by Sven Hasselbach

LotusScript in XPages

Dies ist der zweite Teil des Artikels “LotusScript in XPages”. Der erste Teil befindet sich hier.

 

Der LotusScript-Wrapper

Um dynamischen LotusScript-Code auszuführen, bietet sich die Execute()-Funktion an: Mit der Funktion lässt sich fast der gesamte Umfang der LotusScript-eigenen Backendfunktionalität nutzen, also auch Scriptlibraries einbinden uvm.

Leider steht diese Methode jedoch nicht  in Java direkt zur Verfügung (im Gegensatz zur Session.evaluate()-Methode für @Formelsprache), so dass nur der Umweg bleibt, die Funktion durch Aufruf eines LotusScript-Agenten zu verwenden, und das Ergebnis an die XPage zurück zu liefern. Dabei wird der auszuführende LotusScript-Code und das Ergebnis der Operation über ein temporäres NotesDokument via DocumentContext hin- und hergereicht.

Hier die Klasse “LSExceutor”, die die nötigen Hilfsfunktionen bereitstellt:

package ch.hasselba.xpages.jsf.el;

import javax.faces.context.FacesContext;
import java.util.Vector;
import lotus.domino.Agent;
import lotus.domino.Database;
import lotus.domino.Document;

public class LSExecutor {
    private final static String agentName  = "(LSExecutor)";
    private final static String fieldLSResult = "LSResult";
    private final static String fieldLSCode = "LSCode";

    public static Vector execLotusScriptCode( final String lscode ){
        try{
            Database curDB =  (Database) getVariableValue("database");
            Document doc = curDB.createDocument();
            String hlp = lscode.replace( "\n", "\t" );
            doc.appendItemValue( fieldLSCode, hlp );
            Agent agent = curDB.getAgent(  agentName );
            agent.runWithDocumentContext( doc );
            return doc.getItemValue( fieldLSResult );

        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

     public static Object getVariableValue(String varName) {
         FacesContext context = FacesContext.getCurrentInstance();
         return context.getApplication().getVariableResolver().
          resolveVariable(context, varName);
     }
}

Die statische Methode execLotusScriptCode() wird in den Bindings verwendet, die in Teil 1 beschrieben wurden. Durch den Umstand, dass die runWithDocumentContext()-Methode auf das Ende der Agentenausführung wartet, ist eine sequentielle Verarbeitung gewährleistet.

Der Agent ist ebenfalls recht einfach aufgebaut:

%REM
    Agent LSExecutor
    Created Apr 6, 2012 by Sven Hasselbach/Sven Hasselbach
    Description: LSExecutor agent is a mapper for executing
                 LotusScript code from XPages
%END REM
Option Public
Option Declare

Const fieldLSCode = "LSCode"
Const fieldLSResult = "LSResult"

Dim returnValue
Sub Initialize
    Dim session As New NotesSession
    Dim doc As NotesDocument
    Dim lsCode, tmp , ret

    Set doc = session.Documentcontext
    lsCode = doc.Getitemvalue( fieldLSCode )
    tmp = Evaluate( | @ReplaceSubstring( "| & _
        lsCode(0) & |"; @Char(9) ; @Char(10) ) | )

    ret = Execute( tmp(0) )
    doc.Replaceitemvalue fieldLSResult , returnValue

End Sub

Um auf das Ergebnis der Berechnung zurückgreifen zu können, muss eine globale Variable verwendet werden, da die Execute()-Funktion selbst keine Rückgabemöglichkeit bietet (siehe Domino Designer Hilfe). In diesem Fall ist es “returnValue”, dessen Wert in das via DocumentContext übergebene Dokument geschrieben wird. Entsprechend muss der LotusScript-Code angepasst sein, siehe dazu auch die Beispiele am Anfang des 1. Teils des Artikels. Hier zeigt sich eine Schwachstelle: Es können keine Objekte zwischen Java und LotusScript übergeben werden; ein Zugriff auf z.B. den FacesContext ist in LotusScript nicht möglich. Soll eine DocumentCollection zurück geliefert werden, muss dieses als Array von DocumentUniversalIds geschehen usw.

Der Agent muss im Namen des Benutzers laufen, daher muss der “Run as Web user”-Haken gesetzt sein:

Im dritten Teil wird eine Quick-n-Dirty-Variante gezeigt, die BindingFactory bekannt zu machen.

 

Anmerkung:

An dieser Stelle sei nocheinmal ausdrücklich darauf hingewiesen, das der hier gezeigte Code sich maximal im “Alpha”-Status befindet.

LotusScript in XPages (1): Basics

7. April 2012 Posted by Sven Hasselbach

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.