Search

Mobile tag

About Me

I am the "IBM Collaboration & Productivity Advisor" for IBM Asia Pacific. I'm based in Singapore.
Reach out to me via:
Follow notessensei on Twitter
(posts)
Skype
Sametime
IBM
Facebook
LinkedIn
XING
Amazon Store
Amazon Kindle

Twitter

Domino Upgrade

VersionSupport end
5.0
6.0
6.5
7.0
8.0
8.5
Upgrade to 9.x now!
(see the full Lotus lifcyle) To make your upgrade a success use the Upgrade Cheat Sheet.
Contemplating to replace Notes? You have to read this! (also available on Slideshare)

Languages

Other languages on request.

Visitors

Useful Tools

Get Firefox
Use OpenDNS
The support for Windows XP has come to an end . Time to consider an alternative to move on.

« 9 essential skills | Main| Make Java code for XAgents easy to test »

Reuse web agents that PRINT to the browser in XPages

QuickImage When upgrading classic Domino applications to XPages one particular problem arises constantly: "what to do with the PRINT statements in existing agents that write back directly to the browser?" Currently there is no automatic way to capture this output.
However with a little refactoring of the agent the output can be recycled. You can use a computed field for the result showing it on a page that maintains the all over layout of your new application or use the XAgent approach to replace the whole screen (I'm not discussing the merits of that here). These are the steps:
  1. Make sure your agent is set to "Run as web user" in the agent properties
  2. Add the AgentSupport LotusScript library to your agent
  3. Initialize the ResultHandler class: Dim result as ResultHandler
    SET result = new ResultHandler
    that will take in the print statements
  4. Use Search & Replace in your agent and replace Print  with result.prn 
  5. Add at the end call result.save()
  6. In your XPage or CustomControl add the AgentSupportX SSJS library
  7. Get the result with a call to agentResult("[name-of-your-agent]"). You can process it further or display in in a computed field etc.
The sample code is for illustration only. You want to add proper error handling to it. My test XPage looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.resources>
        <xp:script src="/AgentSupportX.jss" clientSide="false"></xp:script>
    </xp:this.resources>
    This comes from an agent:
    <xp:text escape="false" id="computedField1">
        <xp:this.value><![CDATA[#{javascript:agentResult("SampleAgent");}></xp:this.value>
    </xp:text>
</xp:view>
The sample agent like this:
Option Public
Option Declare

Use "AgentSupport"

Dim result As ResultHandler

Sub Initialize
    Set result = New ResultHandler
   
    result.prt "Some message"
    result.prt "<h1> a header </h1>"
   
    Call result.save()
   
End Sub
Of course the interesting part are the two script libraries.
The LotusScript library looks like this:
Update: (thx Tim): Altered the code to work when the agent doesn't print anything
%REM
    Library AgentSupport
    Created Mar 16, 2012 by Stephan H Wissel/Singapore/IBM
    Description: Routines to improve agent calling from XPages
%END REM

Option Public
Option Declare

%REM
    Class ResultHandler
    Description: Capture all the print statements and feed them back
%END REM

Public Class ResultHandler
    Private out As NotesStream 'This is where the result goes
    Private m_fieldName As String 'The field name for the result
    Private m_noPrintMessage as String 'The message if the agent didn't print anything
    Private m_printResult as Boolean 'If the agent is also used classic we need the print output
    Private s As NotesSession
    Private doc As NotesDocument 'The document handed over by XPages
   
    %REM
        Sub new
        Description: Initialize the variables
    %END REM

    Public Sub New
        Set me.s = New NotesSession
        Set me.doc = s.Documentcontext
        me.m_fieldName = "AgentPrintOutputResult"
        me.m_noPrintMessage = "[NoPrintOutput]"
        me.m_printResult = False
    End Sub
   
    %REM
        Sub prt
        Description: Replacement for the print command in web agents
    %END REM

    Public Sub prt(msg As String)
        If out Is Nothing Then
            Set out = s.Createstream()
        End if
        Call out.Writetext(msg, EOL_PLATFORM)
        If me.m_printResult Then
           Print msg
        End If
    End Sub
   
    %REM
        Property Set fieldName
        Description: ability to overwrite the field name
    %END REM

    Public Property Set fieldName As String
        me.m_fieldName = fieldName
    End Property

    %REM
        Property Set noPrintMessage
        Description: ability to overwrite the message if nothing was printed
    %END REM

    Public Property Set noPrintMessage As String
        me.m_noPrintMessage = noPrintMessage
    End Property
   
    %REM
        Property Set printResult
        Description: If the agent is used in classic, we need to print
    %END REM

    Public Property Set printResult As Boolean
        me.printResult = printResult
    End Property
   
    %REM
        Sub save
        Description: Saves the output back into the document
        One could consider naming it "Delete" so it runs automatically on object deletion
    %END REM

    Public Sub save
        Dim mimeFlag As Boolean
        Dim mimeEntry As NotesMIMEEntity
       
        'We need to make sure we don't have the item in the document
        If me.doc.Hasitem(me.m_fieldName) Then
            Call me.doc.Removeitem(me.m_fieldName)
        End If
       
        'Set mime conversion to false - we want native
        mimeFlag = me.s.Convertmime
        me.s.Convertmime = False

        'There might not be a print output
        If me.out Is Nothing then
            Call me.agentDidNotPrintAnything()
        End If

       
        'Now write out
        me.out.Position = 0 'Make sure we write everything
        Set mimeEntry = doc.Createmimeentity(me.m_fieldName)
        Call mimeEntry.Setcontentfromtext(me.out,"text/plain;charset=UTF-8" ,ENC_NONE)
        Call me.doc.Closemimeentities(true, me.m_fieldName)
       
        'Close stream
        Call me.out.Close()
        Set me.out = Nothing 'If we call it again in prt it gets reinitialized
       
        'Restore mime conversion to what it was
        me.s.Convertmime = mimeFlag
    End Sub

    Private Sub agentDidNotPrintAnything
        Set out = s.Createstream()
        Call out.Writetext(me.m_noPrintMessage, EOL_PLATFORM)
    End Sub
   
End Class
The SSJS library looks like this:
function agentResult(agentName) {
    var doc = database.createDocument();
    var agent = database.getAgent(agentName);

    // Here is the run
    try {
        agent.runWithDocumentContext(doc);
    } catch(e) {
        return e.message;
    }  
    var out = session.createStream();
    var mimeEntry = doc.getMIMEEntity("AgentPrintOutputResult");
    mimeEntry.getContentAsText(out);
    out.setPosition(0);
    var result = out.readText();

    //Cleanup
    mimeEntry.recycle();
    doc.recycle();
    out.recycle();
    agent.recycle();

    return result;
}
I'm using MIME as intermediate storage format as pioneered by Tim Tripcony, so I don't need to worry about content length. You might need to play with the encoding/decoding mechanism. I haven't checked the edge cases yet.
Update: To successfully run such an agent it needs to have the property set "Run as web user", otherwise you get an error. Also you need to design your own handover of the document if you want to manipulate that.
As usual: YMMV

Comments

Gravatar Image1 - Hi Stephan,

Thanks for sharing...
a little thing in sub prt:

printResult should be m_printResult

Greetings,
Erik van der Arend

Gravatar Image2 - Looks great. Could you pls put on OpenNTF?

Gravatar Image3 - Cool technique... glad to see you found a use for the MIME storage approach.

Just a suggestion: make the ResultHandler's out a property and move the instantiation to a Property Get... unless I'm misreading that class, save() will throw an "Object variable not set" error if prt() never gets called.

Gravatar Image4 - @Tim: added a "Agent didn't print anything message"

@Niklas: added to XSnippets

Post A Comment

Please note: Comments without a valid and working eMail address will be removed. This is my site, so I decide what stays here and what goes.

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::rolleyes:;-)

Disclaimer

This site is in no way affiliated, endorsed, sanctioned, supported, nor enlightened by Lotus Software nor IBM Corporation. I may be an employee, but the opinions, theories, facts, etc. presented here are my own and are in now way given in any official capacity. In short, these are my words and this is my site, not IBM's - and don't even begin to think otherwise. (Disclaimer shamelessly plugged from Rocky Oliver)
© 2003 - 2014 Stephan H. Wissel - some rights reserved as listed here: Creative Commons License
Unless otherwise labeled by its originating author, the content found on this site is made available under the terms of an Attribution/NonCommercial/ShareAlike Creative Commons License, with the exception that no rights are granted -- since they are not mine to grant -- in any logo, graphic design, trademarks or trade names of any type. Code samples and code downloads on this site are, unless otherwise labeled, made available under an Apache 2.0 license. Other license models are available on written request and written confirmation.