Posts Tagged: ‘Agenten’

Domino & Spring Boot: ScheduledTasks

2. Mai 2018 Posted by Sven Hasselbach

When developing Spring Boot applications running on Domino, there is a feature which runs out of the box and makes developers happy: ScheduledTasks.

These are the equivalent for agents, but they are running directly in the HTTP task (which allows to access the complete Spring Application at runtime) and can be scheduled exactly to the milisecond.

To enable a scheduled task, you first have to add the @EnableScheduling annotation in your Application class:

package domino_spring.plugin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application extends org.springframework.boot.web.support.SpringBootServletInitializer {

   public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
   }

}

Next step is to declare the TaskScheduler used. Just add a bean to your Application class:

@Bean
 public TaskScheduler taskScheduler() {
      return new ConcurrentTaskScheduler();
 }

After completing the setup you can define your scheduled task:

package domino_spring.plugin;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

   private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);

   private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

   @Scheduled(fixedRate = 5000)
   public void reportCurrentTime() {
      log.info("The time is now {}", dateFormat.format(new Date()));
   }
 
}

When the Sprint Boot application is now started, the current time is  printed to the logs every 5 seconds.

How To Crash a Domino Server in 500ms

21. Februar 2016 Posted by Sven Hasselbach

How To Crash a Domino Server in 500ms

1. Create a Java agent and do something in your finally routine (or in a ThreadDeath exception handling) which runs longer than 500ms

import lotus.domino.AgentBase;

public class JavaAgent extends AgentBase {

    public void NotesMain() {
        try {
            int i = 0;
            while (i < 1000) {
                i++;
                System.out.println("Round " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
            }
        } finally {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {}
        }
    }
}

2. Run the agent on the server

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator1

3. Quit the Agent Manager

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator2

4. And then restart it

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator3

5. Enjoy the silence

2016-02-20 11_31_17-SH Domain - Dev01_Hasselba_CH - IBM Domino Administrator4

Fun fact: This works well in a IBM Notes Client too! Just start the agent and cancel it a few times (<CTRL> + <BREAK>).

XPages: WebContent Files (1) – Create a file using the Java NAPI

18. November 2014 Posted by Sven Hasselbach

Quick-n-Dirty: Use “isDocEditable” in an old school Java Agent

14. März 2014 Posted by Sven Hasselbach

If you want to check if a document is editable, you can do this in an old school Java agent with the NAPI function isDocEditable provided by the XSPNative class.

First you have to add the required JARs to your agent. Then, you have to call XSPNative.isDocEditable with the document you want to test:

import lotus.domino.AgentBase;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.Session;

import com.ibm.domino.napi.c.xsp.XSPNative;

public class JavaAgent extends AgentBase {

    public void NotesMain() {

      try {
          Session session = getSession();
          Database db = session.getCurrentDatabase();
          Document doc = db.getAllDocuments().getFirstDocument();
          System.out.println( "Is Editable: " + XSPNative.isDocEditable( doc ) );
      } catch(Exception e) {
          e.printStackTrace();
       }
   }
}

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

Quick-n-Dirty: How to use the NAPI in a Java Agent

23. Januar 2014 Posted by Sven Hasselbach

The undocumented NAPI for direct API calls in Java can easily used in normal agents. You just need to add two jar files to your java agent:

  • lwpd.commons.jar
  • lwpd.domino.napi.jar

These files can be found in <Notes>\osgi\shared\eclipse\plugins.

After importing, you do lot of funny things, f.e. opening a Session in Full Administration Mode, etc.

NotesSession nSession = new NotesSession();
nSession.setFullAdminMode( "SERVERNAME", true );

But don’t forget: These calls are native API calls and can easily crash your system.

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

The Haunted Field

17. Juli 2013 Posted by Sven Hasselbach

I am currently working on a huge application which exists for many years now, and has a long history with different developers and just a few manuals and/or documentations. But as often it is a critical business application which is in use across different countries 24/7. The more danger, the more less honor: Every fault is just a further nail in the coffin of the developer…

Today I had to develop an extension for this application, and this has driven me crazy, because it was a pain to identify to problem: After creating a document from a XPage the workflow stopped. When filling out the form in in the NotesClient the problem did not occur. But using the same form in a XPage (with computeWithForm) did not work.

After investigating I found out that the problem was caused by a required field which was not created. It was just not there, nothing, nada.

In the form it was defined as a computed field…

… but even with a simple XPage …

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

    <xp:this.data>
        <xp:dominoDocument var="document1" formName="Doc"
            computeWithForm="onsave">
        </xp:dominoDocument>
    </xp:this.data>

    <xp:button value="Save" id="buttonSubmit">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete" immediate="false" save="true" />
    </xp:button>
    <xp:br />
    <xp:messages id="errMessages" />

</xp:view>

… the field was not created:

[I have tried the computeWithForm parameter with every available option.]

Searching for a quick workaround I tried to use an oldschool LS agent, but even then the mystery was not solved. Instead, it grew:

%REM
    Agent ComputeWithForm
    Created Jul 17, 2013 by Sven Hasselbach/Hasselba/CH
%END REM
Option Public
Option Declare

Sub Initialize
    Dim session As New NotesSession
    Dim db As NotesDatabase
    Dim doc As NotesDocument

    Set db = session.Currentdatabase
    Set doc = New NotesDocument( db )
    doc.Form = "Doc"
    doc.Computewithform True, true
    doc.Save true, false
End Sub

This was the resulting error message:

When opening the form in the NotesClient and saving the document, everything works as expected:

What’s going on here? It was really interesting to find the reason. In the last years I always thought that computed fields have always values defined in the form. But this is not a requirement for author fields:

Adding a simple @UserName to the field, and the issue was solved.

XPages: Capture Signatures with the jQuery-Plugin ‘jSignature’

26. September 2012 Posted by Sven Hasselbach

In one of my current projects it is one of the goals that the members of the field staff have the possibility to sign a report directly on their iPad. After some research I found the very cool jQuery plugin jSignature, which easily allows to add a signature capture field to a XPage.

The plugin is very easy to use: Just add a <div> to your XPage and initialize the plugin with the .jSignature() constructor and that’s it! The API is very simple, but provides everything needed: The captured signatures can be stored in different formats like PNG or SVG or as native Vector. Additionally they can be encoded as BASE64 or BASE30. The data can restored as easy as they can be saved: Just one API call and it’s done.

To save the signatures to a Notes document, the resulting data can be copied to a hidden input field. In the provied example above I additionally added the format of the signature. For a better handling of the generated data I decided to store the data in the SVG format, because this allows to save it directly in standard notes text field (without having problems because of the 32 K limit). This works well and the data can be displayed in standard browsers without any problems. Only in XPiNC this will not work, because the SVG format is not supported. PDFs doesn’t support SVG too, that’s why I created a converted agent using the Apache Batik framework.

I will add a demo database asap. A description of parameters to customize the plugin can be found here.

P.S. The compressed Version is not running on teamstudio Unplugged. You have to use the uncompressed Version. The example uses the x$ function of Mark Roden.

 XPage example

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

    <xp:this.resources>
        <xp:script src="/x$.js" clientSide="true"></xp:script>
        <xp:script src="/jSignatureHandler.js" clientSide="true" />
        <xp:styleSheet href="/jSignature.css"></xp:styleSheet>
    </xp:this.resources>
    <xp:this.data>
        <xp:dominoDocument var="documentSig" 
           formName="frmSignature" />
    </xp:this.data>

    <script src="js/jquery-1.8.1.min.js" />
    <script src="js/jSignature/jSignature.min.js" />

    <xp:div id="jSignature" style="width:400.0px"></xp:div>
    <xp:scriptBlock id="scriptBlockSignature">
        <xp:this.value><![CDATA[
        $(document).ready(function() { 
            sigHandler.init( "#{id:jSignature}", 
               "#{id:inputHiddenSignatureData}", 
               "#{id:inputHiddenSignatureFormat}" );
        }
        )]]></xp:this.value>
    </xp:scriptBlock>

    <xp:button id="button1" value="Reset">
        <xp:eventHandler event="onclick" submit="false">
            <xp:this.script><![CDATA[
                sigHandler.reset();
            ]]></xp:this.script>
        </xp:eventHandler>
    </xp:button>

    <xp:button id="button2" value="Save">
        <xp:eventHandler event="onclick" submit="true"
           refreshMode="complete">
            <xp:this.script><![CDATA[ 
                return sigHandler.save();
            ]]></xp:this.script>
            <xp:this.action>
                <xp:saveDocument var="documentSig" />
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>

    <xp:inputHidden id="inputHiddenSignatureData" 
       value="#{documentSignature.SignatureData}" />
    <xp:inputHidden id="inputHiddenSignatureFormat"
       value="#{documentSignature.SignatureFormat}" />

</xp:view>

CSJS library “jSignatureHandler.js”

/**
 * signatureHandler
 * JS Object for handling the signature data
 * Requires jSignature jQuery plugin & special 
 * function "x$" from Mark Roden
 * 
 * @author Sven Hasselbach
 * @category JavaScript
 * @category jQuery
 * @category UI
 * @version 1.0
 */
var signatureHandler = function() {
    _module = "signatureHandler";

    _idJSignature: null; // DOM id of JSignatue DIV
    _idItemData: null; // DOM id of INPUT for signature data
    _idItemFormat: null; // DOM id of INPUT for signature format
    _objDOMJSignature: null; // handle to DOM object
    _objDOMItemData: null; // handle to DOM object
    _objDOMItemFormat: null; // handle to DOM object
    _maxSize = 32000; // max characters to store (32K limit!)
    _format = "svg"; // format used

    /**
     * set DOM id of JSignature DIV
     * @param String id
     */
    this.setJSignatureId = function( id ){
        this._idJSignature = id;
    }
    /**
     * get DOM id of JSignature DIV
     * @return String id
     */
    this.getJSignatureId = function(){
        return this._idJSignature;
    }
    /**
     * set DOM id of data item
     * @param String id
     */
    this.setItemDataId = function( id ){
        this._idItemData = id;
    } 
    /**
     * get DOM id of data item
     * @return String id
     */
    this.getItemDataId = function(){
        return this._idItemData;
    }
    /**
     * set DOM id of format item
     * @param String id
     */
    this.setItemFormatId = function( id ){
        this._idItemFormat = id;
    } 
    /**
     * get DOM id of format item
     * @return String id
     */
    this.getItemFormatId = function(){
        return this._idItemFormat;
    }

    /**
     * get handle to DOM object of JSignature DIV
     * @return Object
     */
    this.getJSignatureDOMObj = function(){
        return this._objDOMJSignature;
    }
    /**
     * set handle to DOM object of JSignature DIV
     * @param Object
     */
    this.setJSignatureDOMObj = function( obj ){
        this._objDOMSignature = obj;
    }
    /**
     * get handle to DOM object of data item
     * @return Object
     */
    this.getItemDataDOMObj = function(){
        return this._objDOMItemData;
    }
    /**
     * set handle to DOM object of data item
     * @param Object
     */
    this.setItemDataDOMObj = function( obj ){
        this._objDOMItemData = obj;
    }
    /**
     * get handle to DOM object of format item
     * @return Object
     */
    this.getItemFormatDOMObj = function(){
        return this._objDOMItemFormat;
    }
    /**
     * set handle to DOM object of format item
     * @param Object
     */
    this.setItemFormatDOMObj = function( obj ){
        this._objDOMItemFormat = obj;
    }

    /**
     * initialize object
     * 
     * @param String id of jSignature DIV
     * @param String id of data item INPUT
     * @param String id of format item INPUT
     * 
     */
    this.init = function( idJSig, idItemData, idItemFormat ){
        try{

            // init jSignature
            this._idJSignature = idJSig;
            this._objDOMSignature = x$( this._idJSignature ) ;
            this._objDOMSignature.jSignature();
            // init data item
            this._idItemData = idItemData;
            this._objDOMItemData = x$( this._idItemData );

            // init format item
            this._idItemFormat = idItemFormat;
            this._objDOMItemFormat = x$( this._idItemFormat );

            return true;
        }catch(e){
            var errMsg = _module + "::" + arguments.callee.name + "\n";
            for( p in e ){
                errMsg += p + ": '"  + e[p] + "'\n";
            }
            console.error( "Error!\n\n" + errMsg );
            return false;
        }
    }

    /**
     * reset jSignature
     */
    this.reset = function(){
        try{
            this._objDOMSignature.jSignature("reset");
            return true;
        }catch(e){
            var errMsg = _module + "::" + arguments.callee.name + "\n";
            for( p in e ){
                errMsg += p + ": '"  + e[p] + "'\n";
            }
            console.error( "Error!\n\n" + errMsg );
            return false;
        }
    }
    /**
     * saves the data from jSignature
     * 
     */
    this.save = function(){
        try{
            var datapair =  this._objDOMSignature.jSignature( "getData", _format );
            var format = "data:" + datapair[0];
            var data = datapair[1];
            // check max size!
            if( data.length >  _maxSize){
                alert( "The size of the signature is too large. Please retry!" );
                return false;
            }

            this._objDOMItemData.val( data );

            this._objDOMItemFormat.val( format )
            return true;

        }catch(e){
            var errMsg = _module + "::" + arguments.callee.name + "\n";
            for( p in e ){
                errMsg += p + ": '"  + e[p] + "'\n";
            }
            console.error( "Error!\n\n" + errMsg );
            return false;
        }
    }
}

// init JS instance
sigHandler = new signatureHandler();

Terminate – Event von LotusScript Agenten

22. Mai 2012 Posted by Manfred Meise

Versucht man LotusScript anzuwenden, um Anwendungen mit entsprechendem Qualitätsanspruch zu erstellen, ist man leider nie vor Überraschungen sicher. So haben wir im Rahmen unserer Entwicklungsrichtlinien festgelegt, dass alle Agenten das Terminate Event verwenden, um zu erkennen, ob ein Agent ordnungsgemäß beendet wurde oder nicht (Abbruch durch Benutzer oder den Agentmanager bei Laufzeitüberschreitungen). Unser Codingpattern:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
     End If
     
End Sub


Dieses Vorgehen funktionierte bislang ganz gut - bis heute. Da haben wir (anders als sonst) im Rahmen der Abbruchsituation nicht nur einen einfachen Eintrag in ein Protokolldokument oder die Serverkonsole gemacht, sondern umfangreichere Logik eingesetzt. Hier wurde dann plötzlich die Abarbeitung an beliebigen Stellen (meistens "If Then Else", "Select Case" oder weiteren Unterprogramm-/Methodenaufrufen) beendet (ohne weitere Hinweise). Schnell wird mir klar: hier gibt es eine maximale Ausführungszeit, nach der der Code abgebrochen wird (macht eigentlich viel Sinn!). Frage: Was ist die Ausführungsbegrenzung im Terminate-Event?

Ein einfacher Test sollte Aufschluss geben:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     Dim i As Integer
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
             Do While i < 20000
              'Sleep (1)
             Print "Durchgang: " & Cstr(i)
             i = i + 1
     Loop
             
     End If
     
End Sub


Meine Erwartungshaltung war, dass dieser interaktive Agent nach irgendeiner Zeit die Arbeit einstellt. Weit gefehlt ! Für eine gewisse Zeit ist der Client "Busy" (Sanduhr), die dann verschwindet. Der Zähler in der Statusleiste läuft weiter, während einige Funktionen im Client möglich sind. So kann ich z.B. einen Datenbank-Öffnen Dialog starten, jedoch keine Datenbank öffnen. Ich kann in der Datenbankansicht der aktuellen Datenbank wechseln, doch kein Dokument öffnen. Ich kann die laufenden Funktionen nicht abbrechen (z.B. mit Strg-Break) sondern nur indem ich den Client beende.

Warte ich in der Behandlungsschleife pro Durchgang ein wenig, so läuft dieser Code kontrolliert bis zum Ende durch. Überraschend!

Leider habe ich somit immer noch keinen Hinweis, was die maximale Ausführungszeit oder -beschränkung im Terminate Event angeht. Schade - aber vielleicht hat jemand mehr Erkenntnisse gewonnen?

Terminate – Event von LotusScript Agenten

22. Mai 2012 Posted by Manfred Meise

Versucht man LotusScript anzuwenden, um Anwendungen mit entsprechendem Qualitätsanspruch zu erstellen, ist man leider nie vor Überraschungen sicher. So haben wir im Rahmen unserer Entwicklungsrichtlinien festgelegt, dass alle Agenten das Terminate Event verwenden, um zu erkennen, ob ein Agent ordnungsgemäß beendet wurde oder nicht (Abbruch durch Benutzer oder den Agentmanager bei Laufzeitüberschreitungen). Unser Codingpattern:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
     End If
     
End Sub


Dieses Vorgehen funktionierte bislang ganz gut - bis heute. Da haben wir (anders als sonst) im Rahmen der Abbruchsituation nicht nur einen einfachen Eintrag in ein Protokolldokument oder die Serverkonsole gemacht, sondern umfangreichere Logik eingesetzt. Hier wurde dann plötzlich die Abarbeitung an beliebigen Stellen (meistens "If Then Else", "Select Case" oder weiteren Unterprogramm-/Methodenaufrufen) beendet (ohne weitere Hinweise). Schnell wird mir klar: hier gibt es eine maximale Ausführungszeit, nach der der Code abgebrochen wird (macht eigentlich viel Sinn!). Frage: Was ist die Ausführungsbegrenzung im Terminate-Event?

Ein einfacher Test sollte Aufschluss geben:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     Dim i As Integer
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
             Do While i < 20000
              'Sleep (1)
             Print "Durchgang: " & Cstr(i)
             i = i + 1
     Loop
             
     End If
     
End Sub


Meine Erwartungshaltung war, dass dieser interaktive Agent nach irgendeiner Zeit die Arbeit einstellt. Weit gefehlt ! Für eine gewisse Zeit ist der Client "Busy" (Sanduhr), die dann verschwindet. Der Zähler in der Statusleiste läuft weiter, während einige Funktionen im Client möglich sind. So kann ich z.B. einen Datenbank-Öffnen Dialog starten, jedoch keine Datenbank öffnen. Ich kann in der Datenbankansicht der aktuellen Datenbank wechseln, doch kein Dokument öffnen. Ich kann die laufenden Funktionen nicht abbrechen (z.B. mit Strg-Break) sondern nur indem ich den Client beende.

Warte ich in der Behandlungsschleife pro Durchgang ein wenig, so läuft dieser Code kontrolliert bis zum Ende durch. Überraschend!

Leider habe ich somit immer noch keinen Hinweis, was die maximale Ausführungszeit oder -beschränkung im Terminate Event angeht. Schade - aber vielleicht hat jemand mehr Erkenntnisse gewonnen?

Terminate – Event von LotusScript Agenten

22. Mai 2012 Posted by Manfred Meise

Versucht man LotusScript anzuwenden, um Anwendungen mit entsprechendem Qualittsanspruch zu erstellen, ist man leider nie vor berraschungen sicher. So haben wir im Rahmen unserer Entwicklungsrichtlinien festgelegt, dass alle Agenten das Terminate Event verwenden, um zu erkennen, ob ein Agent ordnungsgem beendet wurde oder nicht (Abbruch durch Benutzer oder den Agentmanager bei Laufzeitberschreitungen). Unser Codingpattern:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
     End If
     
End Sub


Dieses Vorgehen funktionierte bislang ganz gut - bis heute. Da haben wir (anders als sonst) im Rahmen der Abbruchsituation nicht nur einen einfachen Eintrag in ein Protokolldokument oder die Serverkonsole gemacht, sondern umfangreichere Logik eingesetzt. Hier wurde dann pltzlich die Abarbeitung an beliebigen Stellen (meistens "If Then Else", "Select Case" oder weiteren Unterprogramm-/Methodenaufrufen) beendet (ohne weitere Hinweise). Schnell wird mir klar: hier gibt es eine maximale Ausfhrungszeit, nach der der Code abgebrochen wird (macht eigentlich viel Sinn!). Frage: Was ist die Ausfhrungsbegrenzung im Terminate-Event?

Ein einfacher Test sollte Aufschluss geben:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     Dim i As Integer
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
             Do While i < 20000
              'Sleep (1)
             Print "Durchgang: " & Cstr(i)
             i = i + 1
     Loop
             
     End If
     
End Sub


Meine Erwartungshaltung war, dass dieser interaktive Agent nach irgendeiner Zeit die Arbeit einstellt. Weit gefehlt ! Fr eine gewisse Zeit ist der Client "Busy" (Sanduhr), die dann verschwindet. Der Zhler in der Statusleiste luft weiter, whrend einige Funktionen im Client mglich sind. So kann ich z.B. einen Datenbank-ffnen Dialog starten, jedoch keine Datenbank ffnen. Ich kann in der Datenbankansicht der aktuellen Datenbank wechseln, doch kein Dokument ffnen. Ich kann die laufenden Funktionen nicht abbrechen (z.B. mit Strg-Break) sondern nur indem ich den Client beende.

Warte ich in der Behandlungsschleife pro Durchgang ein wenig, so luft dieser Code kontrolliert bis zum Ende durch. berraschend!

Leider habe ich somit immer noch keinen Hinweis, was die maximale Ausfhrungszeit oder -beschrnkung im Terminate Event angeht. Schade - aber vielleicht hat jemand mehr Erkenntnisse gewonnen?

Terminate – Event von LotusScript Agenten

22. Mai 2012 Posted by Manfred Meise

Versucht man LotusScript anzuwenden, um Anwendungen mit entsprechendem Qualitätsanspruch zu erstellen, ist man leider nie vor Überraschungen sicher. So haben wir im Rahmen unserer Entwicklungsrichtlinien festgelegt, dass alle Agenten das Terminate Event verwenden, um zu erkennen, ob ein Agent ordnungsgemäß beendet wurde oder nicht (Abbruch durch Benutzer oder den Agentmanager bei Laufzeitüberschreitungen). Unser Codingpattern:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
     End If
     
End Sub


Dieses Vorgehen funktionierte bislang ganz gut - bis heute. Da haben wir (anders als sonst) im Rahmen der Abbruchsituation nicht nur einen einfachen Eintrag in ein Protokolldokument oder die Serverkonsole gemacht, sondern umfangreichere Logik eingesetzt. Hier wurde dann plötzlich die Abarbeitung an beliebigen Stellen (meistens "If Then Else", "Select Case" oder weiteren Unterprogramm-/Methodenaufrufen) beendet (ohne weitere Hinweise). Schnell wird mir klar: hier gibt es eine maximale Ausführungszeit, nach der der Code abgebrochen wird (macht eigentlich viel Sinn!). Frage: Was ist die Ausführungsbegrenzung im Terminate-Event?

Ein einfacher Test sollte Aufschluss geben:

 
'1. LS-Tests\4- AgentTerminate:

Option Public
Option Declare


Public blnIsRunnning As Boolean

Sub Initialize
     
     blnIsRunnning = True
     
     ' - - Hier folgt die Anwendungslogik ---
     
     blnIsRunnning = False
     
End Sub

Sub Terminate
     
     Dim i As Integer
     
     If blnIsRunnning = True Then
             
             ' - - Behandlung der Abbruchsituation
             
             Do While i < 20000
              'Sleep (1)
             Print "Durchgang: " & Cstr(i)
             i = i + 1
     Loop
             
     End If
     
End Sub


Meine Erwartungshaltung war, dass dieser interaktive Agent nach irgendeiner Zeit die Arbeit einstellt. Weit gefehlt ! Für eine gewisse Zeit ist der Client "Busy" (Sanduhr), die dann verschwindet. Der Zähler in der Statusleiste läuft weiter, während einige Funktionen im Client möglich sind. So kann ich z.B. einen Datenbank-Öffnen Dialog starten, jedoch keine Datenbank öffnen. Ich kann in der Datenbankansicht der aktuellen Datenbank wechseln, doch kein Dokument öffnen. Ich kann die laufenden Funktionen nicht abbrechen (z.B. mit Strg-Break) sondern nur indem ich den Client beende.

Warte ich in der Behandlungsschleife pro Durchgang ein wenig, so läuft dieser Code kontrolliert bis zum Ende durch. Überraschend!

Leider habe ich somit immer noch keinen Hinweis, was die maximale Ausführungszeit oder -beschränkung im Terminate Event angeht. Schade - aber vielleicht hat jemand mehr Erkenntnisse gewonnen?

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.

Man sollte die Mailfiles seiner Benutzer schon richtig benennen …

16. Februar 2012 Posted by Manfred Meise

Administratoren legen beim Registrieren neuer Benutzer unter Anderem den Namen des Mailfile fest. Diese (sowie andere erforderliche) Angaben kann man im Domino Administrator Client eingeben oder z.B. aus Textdateien einlesen. Hierbei sollte man dem Dateinamen des Mailfiles besondere Aufmerksamkeit schenken.

Was ist mir jüngst passiert?


Zur Registrierung zahlreicher Benutzer (mit ungewöhnlichen ausländischen Namen) haben wir die Datenerfassung dem Kunden in Form eines vorbereiten EXCEL-Sheets überlassen. Diese als .csv Datei (in geeigneter Struktur) abgespeichert dient uns in diesem Fall zur fehlerfreien Registrierung der Benutzer. Hierbei lag unser Hauptaugenmerk auf der korrekten Schreibweise der Benutzernamen.

Leider ist das Formelwerk des EXCEL Sheets durcheinander gekommen, sodass die Festlegung des Dateinamens für die Benutzermailfiles das Sonderzeichen "." enthielten. Somit wurde die Generierung von z.B. "John.Doe", ja ohne ".nsf" enthielt.

Wie verhält sich ein Domino Server in diesem Fall?


1. Der AdminP legt das Mailfile ohne Fehlermeldung an (sodass die enstandene Datenbank NICHT die Endung ".nsf" enthält)
2. Der Router stellt eingehende Mails problemlos in die Datenbank zu
3. Lotus Notes Clients können diese Mailfile problemlos öffnen

allerdings
1. Der AdminP kann keine Replik dieser Datenbank im Cluster erzeugen (Fehlerstatus in Admin4.nsf "Datei nicht vorhanden")
2. Der Abwesenheitsagent läuft in diesem Mailfile NICHT, da der AgentManager keine Agenten aus dieser Datenbank queued (zu erkennen im Domino Administrator Client auf Reiter Server - Status, Ansicht Agenten)

Wie kann man das Problem beheben, ohne Daten zu verlieren?


Die Lösung war simpel.
  • Server herunter fahren. Dateien auf Betriebssystemebene umbenennen (sodass sie die Endung ".nsf" enthalten und keinen weiteren "." im Namen haben).
  • Server neu starten und Personendokumente korrigieren (Feld "Mailfile")
  • ggf. manuell Repliken von Mailfiles im Cluster verteilen (oder hierfür unser Administrationswerkzeug "just:NSF" einsetzen - erzeugt automatisch im Cluster fehlende Repliken)

Man sollte die Mailfiles seiner Benutzer schon richtig benennen …

16. Februar 2012 Posted by Manfred Meise

Administratoren legen beim Registrieren neuer Benutzer unter Anderem den Namen des Mailfile fest. Diese (sowie andere erforderliche) Angaben kann man im Domino Administrator Client eingeben oder z.B. aus Textdateien einlesen. Hierbei sollte man dem Dateinamen des Mailfiles besondere Aufmerksamkeit schenken.

Was ist mir jüngst passiert?


Zur Registrierung zahlreicher Benutzer (mit ungewöhnlichen ausländischen Namen) haben wir die Datenerfassung dem Kunden in Form eines vorbereiten EXCEL-Sheets überlassen. Diese als .csv Datei (in geeigneter Struktur) abgespeichert dient uns in diesem Fall zur fehlerfreien Registrierung der Benutzer. Hierbei lag unser Hauptaugenmerk auf der korrekten Schreibweise der Benutzernamen.

Leider ist das Formelwerk des EXCEL Sheets durcheinander gekommen, sodass die Festlegung des Dateinamens für die Benutzermailfiles das Sonderzeichen "." enthielten. Somit wurde die Generierung von z.B. "John.Doe", ja ohne ".nsf" enthielt.

Wie verhält sich ein Domino Server in diesem Fall?


1. Der AdminP legt das Mailfile ohne Fehlermeldung an (sodass die enstandene Datenbank NICHT die Endung ".nsf" enthält)
2. Der Router stellt eingehende Mails problemlos in die Datenbank zu
3. Lotus Notes Clients können diese Mailfile problemlos öffnen

allerdings
1. Der AdminP kann keine Replik dieser Datenbank im Cluster erzeugen (Fehlerstatus in Admin4.nsf "Datei nicht vorhanden")
2. Der Abwesenheitsagent läuft in diesem Mailfile NICHT, da der AgentManager keine Agenten aus dieser Datenbank queued (zu erkennen im Domino Administrator Client auf Reiter Server - Status, Ansicht Agenten)

Wie kann man das Problem beheben, ohne Daten zu verlieren?


Die Lösung war simpel.
  • Server herunter fahren. Dateien auf Betriebssystemebene umbenennen (sodass sie die Endung ".nsf" enthalten und keinen weiteren "." im Namen haben).
  • Server neu starten und Personendokumente korrigieren (Feld "Mailfile")
  • ggf. manuell Repliken von Mailfiles im Cluster verteilen (oder hierfür unser Administrationswerkzeug "just:NSF" einsetzen - erzeugt automatisch im Cluster fehlende Repliken)

Man sollte die Mailfiles seiner Benutzer schon richtig benennen …

16. Februar 2012 Posted by Manfred Meise

Administratoren legen beim Registrieren neuer Benutzer unter Anderem den Namen des Mailfile fest. Diese (sowie andere erforderliche) Angaben kann man im Domino Administrator Client eingeben oder z.B. aus Textdateien einlesen. Hierbei sollte man dem Dateinamen des Mailfiles besondere Aufmerksamkeit schenken.

Was ist mir jngst passiert?


Zur Registrierung zahlreicher Benutzer (mit ungewhnlichen auslndischen Namen) haben wir die Datenerfassung dem Kunden in Form eines vorbereiten EXCEL-Sheets berlassen. Diese als .csv Datei (in geeigneter Struktur) abgespeichert dient uns in diesem Fall zur fehlerfreien Registrierung der Benutzer. Hierbei lag unser Hauptaugenmerk auf der korrekten Schreibweise der Benutzernamen.

Leider ist das Formelwerk des EXCEL Sheets durcheinander gekommen, sodass die Festlegung des Dateinamens fr die Benutzermailfiles das Sonderzeichen "." enthielten. Somit wurde die Generierung von z.B. "John.Doe", ja ohne ".nsf" enthielt.

Wie verhlt sich ein Domino Server in diesem Fall?


1. Der AdminP legt das Mailfile ohne Fehlermeldung an (sodass die enstandene Datenbank NICHT die Endung ".nsf" enthlt)
2. Der Router stellt eingehende Mails problemlos in die Datenbank zu
3. Lotus Notes Clients knnen diese Mailfile problemlos ffnen

allerdings
1. Der AdminP kann keine Replik dieser Datenbank im Cluster erzeugen (Fehlerstatus in Admin4.nsf "Datei nicht vorhanden")
2. Der Abwesenheitsagent luft in diesem Mailfile NICHT, da der AgentManager keine Agenten aus dieser Datenbank queued (zu erkennen im Domino Administrator Client auf Reiter Server - Status, Ansicht Agenten)

Wie kann man das Problem beheben, ohne Daten zu verlieren?


Die Lsung war simpel.
  • Server herunter fahren. Dateien auf Betriebssystemebene umbenennen (sodass sie die Endung ".nsf" enthalten und keinen weiteren "." im Namen haben).
  • Server neu starten und Personendokumente korrigieren (Feld "Mailfile")
  • ggf. manuell Repliken von Mailfiles im Cluster verteilen (oder hierfr unser Administrationswerkzeug "just:NSF" einsetzen - erzeugt automatisch im Cluster fehlende Repliken)