Quick-n-Dirty: Die HTTP-Session des Domino-Servers

Um mit SSJS auf die HTTP-Session eines Domino-Servers zuzugreifen, ist nicht viel Code nötig:

var exCon = facesContext.getExternalContext();
var sess = exCon.getRequest().getSession();

Dadurch lassen sich folgende Informationen ermitteln:

  • getAttribute(Name:String)

Liefert gebundenes Objekt des Attributes „Name“ zurück

  • getAttributeNames()

Liefert die Namen aller Attribute als java.util.Enumeration zurück.

  • getCreationTime()

Zeit der Session-Erstellung, Millisekunden seit 1.1.1970 GMT, z.B. „1.323785660873E12“

  • getId()

Liefert die aktuelle SessionID zurück, z.B. „D18P2LFFB5“

  • getLastAccessedTime()

Zeit, an der die Session zuletzt aufgerufen wurde. In Millisekunden seit 1.1.1970 GMT, z.B. „1.323785660873E12“

  • getMaxInactiveInterval()

Länge der maximalen Gültigkeit einer Session (in Sekunden) , die vom Servlet ohne Clientaktivität abgewartet wird.

  • isNew()

True, wenn der Client die Sessio noch nicht kennt, false wenn der Client einer bestehende Session beitritt.

  • invalidate()

Setzt eine Session zurück und löscht alle Daten der Session

  • removeAttribute(Name:String)

Löscht das Attribut „Name“ und entfernt das gebundene Objekt

  • setAttribute(Name:String, obj:Object)

Setzt das Attribut „Name“ mit dem Objekt „obj„.

  • setMaxInactiveInterval(int interval)

Setzt maximale Länge der Session in Sekunden, bis das Servlet die Session ablaufen lässt.

 

Um die Attribute einer Session auszugeben, kann z.B. folgender Code verwendet werden:

var attr = sess.getAttributeNames();
while( attr.hasMoreElements() ){
   var elem = attr.nextElement();
   result += elem + " -> ";
   result += sess.getAttribute( elem );
   result += " [";
   result += typeof(sess.getAttribute( elem ));
   result += "]";
 }

result
Veröffentlicht unter Allgemein, Java, Server, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , | Schreib einen Kommentar

Quick-n-Dirty: Hijacking TypeAhead in CSJS

Matthias Nicklisch hat eine interessante Frage im XPages Forum gestellt, nachdem er festgestellt hat, dass im Designer zwar ein OnStart- / OnComplete-Event für die TypeAhead-Funktion angeboten wird, der Code aber als Deprecated angezeigt wird – und auf der XPage auch nicht funktioniert: Wie kann ein OnStart- / OnComplete-Event trotzdem verwendet werden?

Meine Idee dazu ist, den darunter liegenden dojo.xhr-Request zu hijacken, und auf diese Weise die Events zu erhalten. Dadurch lässt sich der Code bequem auf die jeweilige XPage einbetten, ohne das eine Manipulation der original Javascript-Dateien erfolgen muss.

Der folgender Code muß in einem CSJS-Scriptblock eingebettet werden. Dann erhält man für die TypeAhead-Funktion die Events, um zum Beispiel ein kleines „Loading“-Icon einzublenden, wenn die Daten vom Domino Server geladen werden.

var typeAheadLoad;

dojo.addOnLoad( function(){
   /*** hijacking xhr request ***/
   if( !dojo._xhr )
      dojo._xhr = dojo.xhr;

   dojo.xhr = function(){
      try{
         var args = arguments[1];
         if( args['content'] ){
            var content = args['content'];
               if( content['$$ajaxmode'] ){
                  if( content['$$ajaxmode'] == "typeahead" ){
                
                     /*** hook in load function ***/
                     typeAheadLoad = args["load"];

                     /*** overwrite error function ***/
                     args["error"] = function(){
                        alert('Error raised!') 
                     };
                    
                     /*** hijack load function ***/
                     args["load"] = function(arguments){
                  
                        /*** On Start ***/
                        alert("On Start!");
                     
                        /*** call original function ***/
                        typeAheadLoad(arguments);
                     
                        /*** On Complete ***/
                        alert("On Complete!")
                     };
                 }
             }
         }
      }catch(e){}
      dojo._xhr( arguments[0], arguments[1], arguments[2] );
   }
});
Veröffentlicht unter Allgemein, CSS, Dojo Toolkit, Errorhandling, Expression Language, Infrastruktur, Java Script, Server, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , , , , | 2 Kommentare

Performance-Tuning (6): Parallele Partial Refreshs

Multiple Partial Refreshs sind eine schöne Sache, um mehrere Elemente einer XPage zu aktualisieren. Doch da die AJAX-Requests generell asynchron verarbeitet werden, stellt sich die Frage, in wieweit es erforderlich ist, sie sequentiell seriell wie in dem verlinkten Beispiel abzuarbeiten: Denn je länger die Kette der Partial Refreshs ist, desto mehr Performance gewinnt man, wenn man stattdessen mit parallelen Aufrufen arbeitet.

Das verlinkte Beispiel sieht in der parallelen Variante wie folgt aus:

XSP.partialRefreshGet(id1);
XSP.allowSubmit();
XSP.partialRefreshGet(id2);
XSP.allowSubmit();
XSP.partialRefreshGet(id3);

Das Ergebnis unterscheidet sich nicht von von der seriellen Variante, ausser der Tatsache, das die Performance im Client deutlich höher ist. Zwischen den einzelnen Partial Refreshs muss nur die Funktion XSP.allowSubmit() aufgerufen werden, um die interne „Refresh-Sperre“ zurück zu setzen (Eine kurze Erläuterung hierzu findet sich in Matthias Blog).

Die Events der Parial Refreshs (OnComplete, OnError, OnStart) können natürlich wie sonst auch verwendet werden.

Wichtig ist nur, dass man eine „Feinabstimmung“ vornimmt, denn es läßt sich aufgrund der Asynchronität nicht voraussagen, in welcher Reihenfolge die Partial Refreshs verarbeitet werden: Sind z.B. die Elemente im DOM-Baum voneinander abhängig und ein Element wird durch ein Partial Refresh ausgeblendet, kann das natürlich zu ungewollten Fehlern führen – und ein serielles Aufrufen erforderlich machen. Doch wo es möglich ist, sollte aus Performancegründen der Einsatz von parallel ausgeführten Partial Refreshs erfolgen.

Veröffentlicht unter Dojo Toolkit, HTML, Java Script, Performance, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , , | 2 Kommentare

Quick-n-Dirty: Das xp:hidden-Element

Durch die Verwendung des <xp:hidden>-Elements lässt sich ein verstecktes Feld auf der XPage anlegen.

Hier ein Beispiel mit einem statischen Wert:

<xp:inputHidden id="inputHidden1" value="abc" />

Die XPages-Engine rendert daraus diesen HTML-Code:

<input type="hidden" id="view:_id1:inputHidden1"
   name="view:_id1:inputHidden1" value="abc">

Soweit so gut, doch wenn man den Wert dynamisch zuweisen will, rendert die XPages-Engine nicht mehr ein referenzierbares Feld,…

<xp:inputHidden id="inputHidden1">
   <xp:this.value>
      <![CDATA[#{javascript:"abc"}]]>
   </xp:this.value>
</xp:inputHidden>

… sondern einen <span>-Tag, der natürlich auch den Wert nicht enthält:

<span id:"view:_id1:inputHidden1"></span>

Will man trotzdem den Wert des Feldes berechnen, gibt es zwei Möglichkeiten:

1. Die Berechnung wird auf Compute on page load geändert:

<xp:inputHidden id="inputHidden1">
   <xp:this.value>
      <![CDATA[${javascript:"abc"}]]>
   </xp:this.value>
</xp:inputHidden>

2. Dem Feld wird eine Scope-Variable oder einem Dokumentenfeld via EL zugewiesen

<xp:inputHidden id="inputHidden1" value="#{viewScope.hiddenField}">
<xp:inputHidden id="inputHidden1" value="#{document1.hiddenField}">

Dann wird wie in der statischen Variante ein verstecktes Feld generiert, was sich sowohl mit CSJS als auch mit SSJS verarbeiten lässt.

Veröffentlicht unter Allgemein, Expression Language, HTML, Java Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , , , | Ein Kommentar

Der neue HTTP Header ‚X-XspRefreshId‘

Mit Domino 8.5.3 ist der neue HTTP Header ‚X-XspRefreshId‘ eingeführt worden, mit dem sich die refreshId eines Partial Refreshs vom Server aus verändern lässt. Dadurch ist es möglich, ein Element zu refreshen, dass Ergebnis dieser Operation jedoch auf ein anderes Element im Client anzuwenden.

Hier ein kleines Beispiel anhand einer XPage, die vor dem Partial Refresh wie folgt aussieht:

Screenshot: Vor dem Partial Refresh

Der Code der XPage ist ebenfalls simpel, ausser das bei einem Partial Refresh der XPage ein Header an den Request angefügt wird. Dazu wird das Event afterRestoreView genutzt.

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

    <xp:this.afterRestoreView><![CDATA[#{javascript:
        var exCon = facesContext.getExternalContext();
        var writer = facesContext.getResponseWriter();
        var response = exCon.getResponse();
        response.setHeader("X-XspRefreshId",  getClientId('label2') ); }]]>
    </xp:this.afterRestoreView>

    <xp:button value="Label" id="button1">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="partial" refreshId="label1">
        </xp:eventHandler>
    </xp:button>
    <xp:br></xp:br>
    <xp:br></xp:br>
    <xp:label value="Label1" id="label1"></xp:label>
    <xp:br></xp:br>
    <xp:br></xp:br>
    <xp:label value="Label2" id="label2"></xp:label>
</xp:view>

Der Button löst einen Partial Refresh auf das Label label1 aus, der dazugehörige Request, der an den Server gesendet wird liefert auch den zu erwartenden HTML-Code zurück:

<span id="view:_id1:label1" class="xspTextLabel">Label1</span>

Doch nun kommt der zusätzliche Header ins Spiel. Er bewirkt, dass nicht das label1 ersetzt wird, sondern das Element label2:

Durch den in der HTTP Header in der Antwort des Servers wurde das XSP-Objekt dazu veranlasst, den HTML-Code im DOM-Baum an einer anderen Stelle zu ersetzen.

In diesem kleinen Beispiel tritt ein kleiner Seiteneffekt auf: Das Label mit der id label2 verschwindet komplett aus dem DOM-Baum. Betätigt man den Button ein zweites Mal, funktioniert der Refresh nicht mehr, und folgende Fehlermeldung erscheint im Browser:

Veröffentlicht unter Dojo Toolkit, Java Script, Server, ServerSide JavaScript, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , , , | Ein Kommentar

Abbrechen eines Partial Refresh im Client

Leider bietet das XSP-Objekt keine Möglichkeit, einen Partial Refresh via CSJS vorzeitig zu beenden. Zwar basiert der Partial Refresh-Mechanismus auf dojo.xhr-Requests, die diese Funktionalität bieten, doch das XSP-Objekt stellt keine Möglichkeit zur Verfügung, auf die darunter liegenden Dojo-Objekte zuzugreifen.

Um dennoch Zugriff auf die Requests zu erhalten, müssen die Aufrufe von dojo.xhrGet und dojo.xhrPost daher direkt abgefangen und umgebogen werden. Dadurch kann auf das  zurück gelieferte dojo.Deferred-Objekt zugegriffen werden und es lassen sich dessen Methoden verwenden.

Hier ein kleines Beispielskript, dass diese Aufgabe verrichtet. Es muss in einen CSJS-Scriptblock eingebettet werden:

var xhrCall = null;

dojo.addOnLoad( function(){
   /*** hijack dojo's xhrRequest ***/
   dojo._xhrPost = dojo.xhrPost;
   dojo._xhrGet = dojo.xhrGet;

   dojo.xhrPost = function( args ){
      xhrCall = dojo._xhrPost( args );
   }

   dojo.xhrGet = function( args ){
      xhrCall = dojo._xhrGet( args );
   }
});

Will man nun im Client einen Partial Refresh abbrechen, muss nur die cancel-Methode des dojo.Deferred-Objektes aufgerufen werden, und der Partial Refresh wird beendet*:

xhrCall.cancel();

[Die Methode cancel ist unter Dojo 1.3 dokumentiert; in höheren Versionen ist sie aber aus Kompatibilitätsgründen weiterhin vorhanden.]

Das Abbrechen eines Requestes wird allerdings als Partial Refresh-Fehler betrachtet. Um die Popup-Meldung des XSP-Objekts zu unterbinden, muss dem Partial Refresh noch ein eigener Error-Handler mitgegeben werden. Dieser darf ruhig leer sein, er sorgt nur dafür, daß die Fehlermeldung unterdrückt wird:

XSP.partialRefreshGet('id', { onError: function(){} } );

*: Der eigentliche AJAX-Request wird nicht abgebrochen, sondern die weitere Verarbeitung im CSJS unterbunden.

Veröffentlicht unter Dojo Toolkit, Java Script, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , | Schreib einen Kommentar

Quick-n-Dirty: Leeres NotesDocumentCollection-Objekt instanzieren

EDIT:

Tja, da habe ich wohl nicht aufgepasst! Die Funktionalität gibt es schon:

database.createDocumentCollection()

Schade, daß man bei den XSnippets Code nicht mehr entfernen kann.

 

Domino bietet Out-of-the-Box leider keine Möglichkeit, ein leeres NotesDocumentCollection-Objekt zu instanzieren. Um trotzdem in den Genuss zu kommen, mit einer leeren NotesDocumentCollection arbeiten zu können, ist der schnellste Weg, einfach alle Dokumente der Datenbank zu verwenden und von sich selbst „abzuziehen“.

In SSJS sieht das wie folgt aus:

/*****
 *** getEmptyDocumentCollection()
  *** returns an empty NotesDocumentCollection-Object  
 *****/
 function getEmptyDocumentCollection(){
    var dc:NotesDocumentCollection = null;
    try{
       dc = database.getAllDocuments();
       dc.subtract( database.getAllDocuments() );
    }catch(e){}
    return dc; }

 /*** how to use ***/
 var hlpDC:NotesDocumentCollection = getEmptyDocumentCollection();
 var hlpDoc:NotesDocument =  database.getAllDocuments().getFirstDocument();
 hlpDC.addDocument ( hlpDoc )
 hlpDC.getCount();

Die gleiche Funktionalität sieht in LotusScript wie folgt aus:

%REM
 Function getEmptyDocumentCollection
     Description: Returns an empty document collection
     Created Nov 28, 2011 by Sven Hasselbach
 %END REM
 Function getEmptyDocumentCollection() As NotesDocumentCollection
          On Error GoTo errH
          Dim session As New NotesSession
          Dim db As NotesDatabase
          Dim dc As NotesDocumentCollection
          Set db = session.Currentdatabase
          Set dc = db.Alldocuments
          dc.Subtract db.Alldocuments
          Set getEmptyDocumentCollection = dc
 the_end:
          Exit Function
 errH:
          Print "getEmptyDocumentCollection() - Error #" & Err & _ ": '" & Error & "' @ Line " & Erl
 Resume the_end
 End Function

Und schon hat man eine leere NotesDocumentCollection, der man nach Belieben arbeiten kann!

Veröffentlicht unter Java Script, Lotus Script, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , | 3 Kommentare

OpenNTF XSnippets Beta gestartet

XSnippets – The next generation code bin, ist als Beta gestartet.

Das neue OpenNTF Projekt dient als Sammlung für kleine Code-Schnipsel, die von der Community für die Community zur Verfügung gestellt werden. Hier sind die ersten Schnipsel zu finden.

Veröffentlicht unter Allgemein, Dojo Toolkit, Java, Java Script, JSF, Lotus Script, ServerSide JavaScript, XPages, XSP | Verschlagwortet mit , , , , , , , | Schreib einen Kommentar

Performance-Tuning (5): Links und xsp.redirect

Ein Serverseitiger Redirect wird durch die XPages-Engine standardmäßig so durchgeführt, daß an den Client ein spezieller HTTP-Header gesendet wird, der via Javascript ausgewertet und dann durch CSJS geöffnet wird.

Der Hintergrund hierbei ist, daß sich dadurch die URL in der Browser-Addressleiste ändert, und so dem Nutzer die Seitenänderung aktiv angezeigt wird. Das Problem hierbei ist jedoch, daß dadurch ein zusätzlicher Request vom Server an den Client gesendet wird, der zum Einen natürlich die Last auf dem Server erhöht, zum Anderen den Vorgang unnötig verlangsamt.

Ein Clientseitiger Link…

<xp:link escape="true" text="LinkClient" id="link1"
value="/Page2.xsp" />

… ist daher besser als das gleiche Konstrukt über einen Serverseitigen Event-Handler …

<xp:link escape="true" text="LinkServer" id="link2" >
   <xp:eventHandler event="onclick" submit="true"
      refreshMode="complete">
      <xp:this.action>
         <xp:openPage name="/Page2.xsp"></xp:openPage>
      </xp:this.action>
   </xp:eventHandler>
</xp:link>

… auch wenn im ersten Moment daraus das gleiche Ergebnis resultiert.

Das erste Beispiel öffnet den Link direkt über den Client:

Screentshot: Firebug- Direktes Öffnen eines Links

[Beispiel 1 Firebugged: Direktes Öffnen von Page1.xsp via HTTP GET]

Das zweite Beispiel sorgt dafür, daß beim Klicken auf den Link zuerst ein HTTP POST-Request an den Server an die aktuelle Seite geschickt wird; die Antwort (versehen mit dem HTTP-StatusCode 302) löst dann einen zweiten Request mittels HTTP GET aus, durch den dann die Page2.xsp geöffnet wird.

Screenshot: Firebug - xsp.redirect=true

[Beispiel 2 Firebugged: POST-Request an Page1.xsp, dann GET-Request an Page2.xsp]

Daher sollte prinzipiell darauf geachten werden, daß z.B. Links in der XPage direkt eingebettet werden, und das Serverseitige Verarbeiten mittels OpenPage besser zu unterlassen.

Doch auch dieses Verhalten läßt sich noch durch eine Einstellung im xsp.properties-File modifizieren. Durch die Einstellung xsp.redirect = false wird kein Redirect mehr für Event-basierte Links ausgeführt, sondern die neue Seite wird direkt an den Client übergeben:

Screenshot: Firebug - xsp.redirect = false

[Firebugged: Ergebnis mit xsp.redirect=false]

Im Browser sieht das ganze dann wie folgt aus (in den Screenshots wird ein Button verwendet), man beachte die URL im Browser:

Screenshot: Page1 - Before

[Page1.xsp vor dem Klick auf den Button]

Screenshot: Page1 - After

[Page1.xsp nach dem Klick, jetzt mit dem Inhalt von Page2.xsp]

Sieht man von dem „unschönen“ Effekt ab, daß die URL im Browser sich nicht verändert hat, hat sich die Performance auch für den Client deutlich erhöht. Als Beispiel hier noch die Zeiten, die für den Seitenaufbau benötigt werden; da der DOM-Baum nur einmal im Browser geändert wird, ist auch dies deutlich schneller.

Screenshot: Zeit für Seitenaufbau mit xsp.redirect = true

[Zeit für Seitenaufbau mit xsp.redirect = true]

Screenshot: Zeit für Seitenaufbau mit xsp.redirect=false

[Zeit für Seitenaufbau mit xsp.redirect = false]

Wie sich unschwer erkennen läßt, ist mit xsp.redirect=false der Seitenaufbau in etwa der Hälfte der Zeit erledigt wie im anderen Fall. Das hierbei die ULR im Browser nicht aktualisiert wird, kann im Einzelfall natürlich zu den hier geschilderten Problemen führen, aber hier muß klar der Performance-Vorteil in den Vordergrund gestellt werden.

Weiterführende allgemeine Informationen finden sich hier: Google.com: Minimize round-trip times.

Veröffentlicht unter Allgemein, Java Script, Performance, Server, ServerSide JavaScript, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , | Ein Kommentar

Art des Refreshs programmatisch ermitteln

Um festzustellen, ob das Berechnen eines Elementes von einem Full Refresh oder einem Partial Refresh ausgelöst wird, kann die Klasse com.ibm.xsp.ajax.AjaxUtil verwendet werden. Die Klasse stellt die Methode isAjaxPartialRefresh() bereit, die das nötige Ergebnis zurückliefert.

Hier eine Beispiel-XPage, die die Verwendung demonstriert:

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

   <xp:label id="lblRefreshMe">
      <xp:this.value><![CDATA[#{javascript:
         var ajax = new com.ibm.xsp.ajax.AjaxUtil();
          if( ajax.isAjaxPartialRefresh(facesContext) == true)
             return "AJAX" ;
          
          return "NOT AJAX!"}]]>
      </xp:this.value>
   </xp:label>
   <xp:br></xp:br>
   <xp:br></xp:br>
   <xp:button value="Do Full Refresh" id="buttonFullRefresh">
      <xp:eventHandler event="onclick" submit="true"
         refreshMode="complete">
      </xp:eventHandler>
   </xp:button>
   <xp:br></xp:br>
   <xp:button value="Do Partial Refresh" id="buttonPartialRefresh">
      <xp:eventHandler event="onclick" submit="true"
         refreshMode="partial" refreshId="lblRefreshMe">
      </xp:eventHandler>
   </xp:button>
</xp:view>

Hier ein Screenshot der XPage beim Aufruf der XPage.

Durch Klick auf die Buttons läßt sich die Art des Refreshs bestimmen. Wird ein Partial Refresh durchgeführt, ändert sich das Label wie folgt:

Veröffentlicht unter Allgemein, Java, Java Script, Server, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , , | 2 Kommentare