Friday, July 27, 2007

Working around the 64k print limit in web-agents

In LS, there seems to be a 64k limit per Print statement

If you have agents that print a large amount of HTML/XML (concatenating into a string), one workaround is to check the string-length frequently:

If Len(yourString) > safeNumber Then
Print yourString
yourString = ""
End If

Where yourString is the string containing the HTML/XML/etc., and safeNumber is a number that is smaller than the 64k - the maximum amount of characters you can imagine is added per concatenation.

If there is A LOT (5000+) of string concatenation in the agent, I suggest using Julian Robichaux' StringBuffer class. You would have to add a getLength-method, to check the length of the buffer.

Something like this:

'in stringbuffer class
Public Function getLength() As Long
getLength = Len(buffer)
End Function

'in the printing agent
If bufferObject.getLength > safeNumber Then
Print bufferObject.toString()
bufferObject.erase()
End If


The above If-statements .. > safeNumber .. should be put inside the loop that you use to concatenate the string. Thanks for making me aware of the bad "documentation", Thomas.


If you have a lot of different content you want to print, I'd suggest having one agent per content-type, and that the agents only contains method calls to their respective script libraries (which in turn can use other scriptlibs). Having 20+ agents in an application quickly gets messy (at least from my point of view).

Example:
..xml?OpenAgent&action=report&type=wages&employeenum=2134212

In agent (untested code):

Use "PrintXML"
Dim s As New NotesSession
Dim action As Variant

action = Evaluate( |@UrlQueryString("action")|,_
s.DocumentContext)
'the evaluate above returns a text-list with one item
Select Case( action(0) )
Case "report"
Call report( s.DocumentContext )
End Select

64k limit on "Print" in web agents

I knew there was a limit, but I didn't take the time to test it before today.

According to my tests, the limit is 65 534 characters per Print statement. When the string you're printing is above this, something strange happens.

I made an agent with a For .. To loop to concatenate a string. The string contains the counter and a line-break per loop.

String length: 65 193
Last line printed: 5100 (Correct, For 1 To 5100)

String length: 70 393:
Last line printed: 414 (Wrong)

String length: 78 193
Last line printed: 1059 (Wrong)

Thursday, July 19, 2007

Simple LotusScript-library to fetch html/transform xml

I rewrote some of the code so that there only will be created one object per task. In my first version, I created an object per URL-call. In this version, only one object is created per task.

Text-file with code
Put code under '(declarations) in the file, into the declarations part of the scriptlib, and 'Terminate into the terminate part. The functions place themselves when you paste them.

'(declarations)
Dim httpobj As Variant
Dim source As Variant, style As Variant

'Terminate
Set httpobj = Nothing
Set source = Nothing
Set style = Nothing


If you want to get several XML-/XSL-files from the same source, you only need to login once per agent. If you're going to use the same XSL template for each xml-source, you only need to load this the first time.

'..
login = "https://server.com/names.nsf?login&"+_
"username=foo&password=bar"

xmlurl = "https://server.com/db.nsf/first.xml"
xslurl = "https://server.com/db.nsf/first.xsl"

'first call
Print transformXML( login, xmlurl, xslurl )
'..
'second call, same xsl-template
xmlurl = "https://server.com/db.nsf/second.xml"
Print transformXML( "", xmlurl, "" )
'..
'third call, different xsl-template
xmlurl = "https://server.com/db.nsf/view?ReadViewEntries"
xslurl = "https://server.com/db.nsf/view.xsl"
Print transformXML( "", xmlurl, xslurl )
'..

Tuesday, July 17, 2007

LS XSL transformation from URL's (inc. https)

Most of this code is borrowed from a bloke named David Frahm. He posted that he had problems using the Microsoft.XMLDOM-object for transformation when the DB required authentication as a comment to Jakes article, "Putting Domino's XML to use", on Codestore.net.

Excerpt:

I have been working with MSXML Parser in LotusScript agent for weeks now. Works great except with secure data. By that I mean that when the MSXML object requests a url from the domino server, it does not pass the userid/password and all I get back are documents that an Anonymous user would.


The post on codestore

The problem


I needed to transform 5-6 views to HTML-tables (reports) inside a form. Ajax wasn't an option, since the page that should contain the transformed views should be readable to both machine (DominoPDF) and man.

The database I was going to "read the view entries" from is on a secure server, which made it nearly impossible to use a Java-agent (from what I read on "the forum", you need to alter some settings in java security, make certificate, etc), which I thought was the only solution when transforming from URLs in Notes.

The solution


Simple function that takes three parameters in, and returns the result of the transformation:

Function transformXML( login As String, xml As String, xsl As String) As String
%REM
xml is the url to the XML to transform, e.g. http://your.domain.com/db.nsf/view?readviewentries

xsl is the url to the XSL-template, e.g. http://your.domain.com/db.nsf/transformview.xsl
%ENDREM

Dim source As Variant, style As Variant
Set source = CreateObject( "Microsoft.XMLDOM" )
source.async = False

'login is optional. Only use when you need to authenticate on
'the server

'form of login-url:
'http://your.domain.com/names.nsf?login&username=you&password=theuser

If login <> "" Then
source.load( login )
End If

source.load( xml )

Set style = CreateObject( "Microsoft.XMLDOM" )
style.async = False
style.load( xsl )

If Not(source.parseError.errorCode = 0) Then
Error source.parseError.errorCode , source.parseError.reason
Else
'transform source
transformXML = source.transformNode(style)
End If

'not sure if this is necessary to avoid memory leaks
Set style = Nothing
Set source = Nothing
End Function

Download in text-file below


I believe the code above can be used to convert from whatever source (rss, xhtml, application xml, etc), but I've only tested ?ReadViewEntries with https.

My use is a WebQueryOpen-agent that prints html to a RichText field in a form. The reports are collected from several categorized view, per user, "..?ReadViewEntries&RestrictToCategory=[userid]"

The agent currently transform 5-6 views. I also use WinHTTP.WinHTTPRequest.5.1 to get HTML from a document in the same report-form.

Function getHTML(url As String) As String
If Datatype ( httpobj ) = 0 Then 'ikke initialisert
Set httpobj = CreateObject("WinHTTP.WinHTTPRequest.5.1")
End If
Call httpobj.Open("GET", url,False)
' Send the HTTP Request.
Call httpobj.Send

getHTML = httpobj.ResponseText
Set httpobj = Nothing
End Function


The two functions in a text-file

I'm not sure if (server) performance is an issue on this application. But if it comes to that, I'll only run the WQO-agent if some parameter is in the url, e.g. "https://my.domain.no/application.nsf/report?openform&category=expenses&print", and use sarissa for web-clients.

Wednesday, July 11, 2007

My Springfield alter-ego

\