„It’s not a feature, it’s a bug!“

In meinem letzten Beitrag habe ich einen Bug entdeckt, den ich an dieser Stelle noch etwas ausführlicher darstellen möchte, denn es handelt sich hierbei nicht um ein normales Verhalten von JSF, sondern schlichtweg um einen Bug während der Transformation nach Java.

Im Vorfeld möchte ich jedoch auf einen sehr guten Artikel von Paul Withers aufmerksam machen, in dem ausführlich dargestellt wird, wie es sein müsste:

http://www.intec.co.uk/xpages-bindings-when-runs-at-page-load/

Der Einfachheit halber greife ich das von Paul gegebene Beispiel auf, um den Bug zu verdeutlichen. Ergänzt man nämlich den Code um Anführungszeichen, dann wird der „On Page Load„-Code nicht mehr ausgeführt:

<xp:text id="computedField2" escape="true"
 value="You are logged in as '${javascript:@UserName()}'.
The fields id is #{id:computedField1}"></xp:text>

[Fett: Der „On Page Load“-Code // In Rot: Die zusätzlichen Anführungszeichen]

Das Ergebnis ist dann folgendes:

Zurückzuführen ist das auf einen Fehler bei der Transformierung, der generierte Javacode sieht wie folgt aus:

Dies ist ein Bug im Designer, denn jedwede Form der ${}-Syntax wird ungeprüft als „On Page Load“ interpretiert. So wird der folgende Code trotz Fehler in EL übersetzt…

… hingegen wird diese Variante ordnungsgemäß als Fehler markiert und lässt sich nicht speichern:

Irrtümlicherweise habe ich in meinem vorigen Artikel weitere Beispiele aufgeführt, die Code enthalten, der ohne das Anführungszeichen ausgeführt wird. Dies war im Zuge des Schreiben des letzten Artikels, als ich noch weitere Test gemacht habe. Hier ist das Verhalten natürlich JSF-konform und der Code wird ordnungsgemäß ausgeführt.

Meine Beobachtung bezüglich der fehlerhaften Transformation jedoch bezieht sich nicht nur auf Output Scripts, sondern um jede Art des Value Bindings: Sobald eine x-beliebige Kombination von ${} (auch über mehrere Zeilen etc.) vorkommt, tritt der Fehler auf.

Veröffentlicht unter Bug, Expression Language, JSF, XPages | Verschlagwortet mit , , , , , , , | 2 Kommentare

Bug: ${} in Output Script-Blöcken

Bei der Verwendung eines Output Scripts muss darauf geachtet werden, dass kein Code verwendet wird, der eine Zeichenfolge beinhaltet, die eine „Compute On Load„-ähnliche Syntax hat: Ein Bug sorgt dafür, das bei der Verwendung von ${} (mit oder ohne Inhalt) einiges durcheinander gerät, und der komplette SSJS-Code falsch verarbeitet wird.

So gibt folgender Code wie zu erwarten eine Messagebox mit der Id des Labels aus…

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

   <xp:label value="Label" id="label1"></xp:label>

   <xp:scriptBlock id="scriptBlock1">
      <xp:this.value>
         <![CDATA[
            var id = '#{id:label1}';
            alert( id );
         ]]>
      </xp:this.value>
   </xp:scriptBlock>

</xp:view>

… wird aber an irgendeiner Stelle im Script Block die genannte Kombination verwendet, gerät alles in Schieflage:

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

   <xp:label value="Label" id="label1"></xp:label>

   <xp:scriptBlock id="scriptBlock1">
      <xp:this.value>
         <![CDATA[
            var id = '#{id:label1}';
            var bug = '${id:label1}';
             alert( "id: " + id + "\nbug: " + bug);
         ]]>
      </xp:this.value>
   </xp:scriptBlock>

</xp:view>

Die Variable id ist leer, und die Variable bug wird nicht verändert:

Es spielt keine Rolle an, welcher Stelle im Script Block die fehlerhafte Variante vorkommt, auch der Inhalt zwischen den eckigen Klammern ist unbedeutent: Ein auskommentierter Code über mehrer Zeilen hat die gleiche Auswirkung!

Varianten wie z.B.

//var bug = '${
// Kein Text!
//}';

oder

var bug = ${X}

werfen keine Fehler, sondern generieren im besten Fall „nur“ fehlerhaften CSJS-Code.

Erst wenn keine Anführungszeichen verwendet werden und die EL-Syntax fehlerhaft ist, tritt ein Laufzeitfehler auf.

var bug = ${/EL}

 

Der Bug existiert in 8.5.2 als auch in 8.5.3. Andere Versionen können ebenfalls betroffen sein.

 

EDIT:

Die unteren beiden Beispiele OHNE Anführungsszeichen sind soweit JSF-konform. Details siehe hier.

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

Quick-n-Dirty: Locale setzen

Um die Locale-Einstellung einer XPage programmatisch zu beeinflussen, kann die Methode setLocaleString bzw. setLocale des context-Objektes verwendet werden.  Damit die Änderungen übernommen wird, muss die Einstellung im BeforePageLoad gesetzt werden.

So ändert ein…

context.setLocaleString("zh-tw")

… bzw. ein …

context.setLocale(java.util.Locale.TAIWAN)

…die Spracheinstellungen der generierten XPage. Sowohl das lang-Attribute der HTML-Seite als die Dojo-Konfiguration wird dabei gesetzt:

<!DOCTYPE html>
<html lang="zh-tw">
<script type="text/javascript"
src="/xsp/.ibmxspres/dojoroot-1.6.1/dojo/dojo.js"
djConfig="locale: 'zh-tw', parseOnLoad: true"></script>

Durch diese Änderung wird z.B. der Datepicker-Dialog des DojoToolkit auf Taiwanisch gerendert.

Eine Liste der möglichen Einstellungen findet sich in der Beschreibung des java.util.Locale-Objektes.

Alternativ kann die Änderung auch mit SSJS in einer Aktion o.ä. durchgeführt werden. Allerdings muss darauf die XPage neu geladen werden. Dies kann dann wie folgt aussehen:

context.setLocale(java.util.Locale.TAIWAN);
context.reloadPage()
Veröffentlicht unter Dojo Toolkit, HTML, Java Script, ServerSide JavaScript, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , | 3 Kommentare

Security: Fernsteuerbare ViewDataSources

Ist bei einer Datenbank die Option „Don’t allow URL open“ gesetzt, ist sie nicht mehr im Web erreichbar. Mit den URL-Parametern databaseName und viewName lässt sich in Verbindung mit einer ViewDatasource dieser Schutzmechanismus jedoch aushebeln, und könnte ein Problem darstellen, wenn sich nur auf diese Option verlassen wurde.

Als Beispiel dient hier eine lokales NAB, dass zu Demonstrationszwecken eine offene ACL hat (Default = Manager), bei dem die genannte Option jedoch aktiviert ist:

Öffnet man diese Datenbank im Browser, so erscheint eine Fehlermeldung:

Legt man jedoch eine XPages mit einer ViewDatasource in einer anderen Datenbank an, kann dieser einfache Schutzmechanismus umgangen werden, da die beiden Parameter die in der XPage definierte Quelle überschreiben; dadurch wird eine „Fernsteuerung“ der ViewDataSource ermöglicht.

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

   <xp:viewPanel rows="30" var="viewCol">
      <xp:this.data>
         <xp:dominoView var="view1" viewName="" />
      </xp:this.data>
      <xp:viewColumn
         value="#{javascript:viewCol.getUniversalID()}">
     </xp:viewColumn>
   </xp:viewPanel>

</xp:view>

Öffnet man folgende XPage, ist die darzustellende Ansicht natürlich leer…

… hängt man allerdings die passenden URL-Parameter an, sieht die gleiche XPage entsprechend anders aus:

Es handelt sich dabei um die beiden Dokumente aus dem persönlichen Addressbuch.

Zwar müssen immer noch Berechtigungen auf die jeweilige Datenbank gegeben sein, damit Daten ausgelesen werden können, dennoch sollte prinzipiell immer ignoreRequestParams=“true“ gesetzt sein.

Veröffentlicht unter JSF, Security, Server, Web, XPages | Verschlagwortet mit , , , , , , | Ein Kommentar

Domino Datasources On-the-Fly (1): Basics

Um eine Datasource On-the-Fly mittels SSJS zu erstellen, muss zum Einen ein neues Datasource-Objekt instanziert werden, zum Anderen die neue Datenquelle der XPage bekannt gegeben werden.

Mit dem folgenden Code wird eine neue Document Datasource namens document1 erstellt und dann dem UIViewRoot-Element bekannt gemacht:

var data = new com.ibm.xsp.model.domino.DominoDocumentData();
data.setVar("document1");
view.addData(data);

Direkt nach der Anlage lässt sich die Datenquelle leider noch nicht im Code verwenden, d.h. ein „document1.save()“ schlägt an dieser Stelle fehl. Die neue Datasource kann aber in einem anderen Code-Segment wie gewohnt verwendet werden.

Hier eine Beispiel-Implementierung in eine XPage:

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

<xp:button value="Create Datasource" id="button1">
   <xp:eventHandler event="onclick" submit="true"
      refreshMode="complete">
      <xp:this.action>
         <![CDATA[#{javascript:
            var data = new com.ibm.xsp.model.domino.DominoDocumentData();
            data.setVar("document1");
            data.setFormName("DynData");  
            view.addData(data);
         }]]>
      </xp:this.action>
   </xp:eventHandler>
</xp:button>
<xp:button value="Use Datasource" id="button2">
   <xp:eventHandler event="onclick" submit="true"
     refreshMode="complete">
      <xp:this.action>
         <xp:actionGroup>
            <xp:executeScript>
               <xp:this.script>
                  <![CDATA[#{javascript:
                     document1.setValue("Feld", "ABC");
                  }]]>
               </xp:this.script>
              </xp:executeScript>
           <xp:saveDocument var="document1" />
         </xp:actionGroup>
      </xp:this.action>
   </xp:eventHandler>
</xp:button>
</xp:view>

Die XPage besteht aus zwei Buttons: Mit „Create Datasource“ wird eine neue Datasource angelegt, mit „Use Datasource“ die neue DocumentDatasource verwendet und gespeichert. Wird der Button „Use Datasource“ geklickt, bevor die neue Datasource angelegt wurde, tritt (logischerweise) ein Fehler auf.

Im nächsten Teil geht es um die möglichen Eigenschaften des Domino Document Data– und des Domino View Data-Objektes.

Veröffentlicht unter Java, JSF, ServerSide JavaScript, XPages | Verschlagwortet mit , , , , | Ein Kommentar

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

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

Der Fluch des Partial Refreshs (2)

Anscheinend liest jemand bei Google meinen Blog und reagierte auf meinen Artikel:

Heise Newsticker: Googlebot erfasst künftig mehr dynamische Seiteninhalte

Jetzt müssen nur noch die Browser-Hersteller einlenken…

Veröffentlicht unter HTML, Server, Web, XPages | Verschlagwortet mit , , , , | Schreib einen Kommentar