Everything you ever wanted to know about
Lotuscript
Bill Buchanhadsl
Thursday, 22 March 12
Who am I?
• Bill Buchan, CEO of hadsl - one of the sponsors of this show
• Come visit the booth - we don’t bite
• Dual PCLP in v3, v4, v5, v6, v7, v8, and v8.5
• Enterprise-level consultant on Domino since 1995
Thursday, 22 March 12
Agenda
• What is LotusScript? Where is it going?
• LotusScript Basics
• LotusScript Advanced
• Web Services
Thursday, 22 March 12
What is LotusScript
• LotusScript is:
• A p-code partially compiled, partially interpreted language
• Syntactically identical to Visual Basic 3
• In most Notes Applications.
Thursday, 22 March 12
What is LotusScript
• LotusScript
• is old. It was introduced with Notes v4, and was first implemented in Ami Pro
• Supports Object Orientated techniques
• Is fast, scalable, robust
• Is Multi-Platform
Thursday, 22 March 12
Where is it going?
• Number of LotusSphere 2012 sessions that mentioned LotusScript: one
• No language modifications since Notes v4.6
• (Every version has had class extensions to support some new version features)
• Its fair to say that it is not considered a priority for Lotus
Thursday, 22 March 12
So why are we here?
• I wanted to give you a single session that covered everything I knew about LotusScript
• We still have to maintain applications
Thursday, 22 March 12
Agenda
• What is LotusScript? Where is it going?
• LotusScript Basics
• LotusScript Advanced
• Web Services
Thursday, 22 March 12
Basics: How to code
• Code for maintenance.
• You will change this code. This code will run for 10+ years. Unchanged.
• Log everything.
• If the users find out before you, you’ve lost.
• Good log information means faster debugging
Thursday, 22 March 12
Basics: How to code
• Bugs cost money. The closer to production, the more expensive they are. More testing and debugging and logging costs less
• Isolate business logic from display logic. Your look and feel WILL change
• Simplicity is cheap
Thursday, 22 March 12
Basics: Option Declare
• You should use ‘Option Declare’ when possible. Why?
• Not using Option declare, and not declaring your variables in advance, makes every variable a VARIANT
• Variants move all errors to run-time
• Variants are slower to use.
Thursday, 22 March 12
Basics: lsi_info
• LSI_Info was a student intern project, and is a global variable created and maintained by the LotusScript environment
• It contains lots of ‘interesting’ values
• Superseded by getThreadInfo
• Its not thread-safe. Heavily loaded servers may crash if its accessed.
Thursday, 22 March 12
Basics: lsi_info
• lsi_info(10) gives the calling class
• lsi_info(11) gives the calling function.
• So we can write a pretty cool error handler..
Thursday, 22 March 12
Function RaiseError()Dim thisType As String Dim es as StringthisType = Typename(Me)
' Not a class, use the calling module insteadIf (thisType = "") Then thisType = Getthreadinfo(11)es = thisType & "::" & Getthreadinfo(10) & ": "If (Err = 0) Then
es = es + "Manually raised an error"Else
es = es + "Run time error: (" + Trim(Str(Err)) + ") " + _
Error$ + " at line: "+ Trim(Str(Erl)) End IfPrint es
end function
' calling code...
ExitFunction:
exit function
errorhandler: Call RaiseError()
resume exitFunction
end function
Thursday, 22 March 12
Basics. NotesSession
• This piece of code:
dim s as new NotesSession
• Doesn’t actually create anything. It just references the same global NotesSession object, built in advance
• No performance hit for declaring it (But that doesn’t excuse bad code!)
Thursday, 22 March 12
Basics: AdminP
• AdminP documents have special fields for ReplicaID
• Stored as Notes Date/Time fields
Dim dt As New NotesDateTime(targetDatabase.ReplicaID) Call doc.replaceItemValue("ProxyReplicaID", dt)
Thursday, 22 March 12
Basics: AdminP
Thursday, 22 March 12
Basics: Performance
• A crude agent profile mechanism was introduced in Notes 7
• You enable it from the agent/web service properties pane:
Thursday, 22 March 12
Basics: Performance
Thursday, 22 March 12
Basics: Lists
• A list is a collection of values, which has a fixed lookup value
dim Surnames list as String
Surnames(“Bill”) = “Buchan”Surnames(“Paul”) = “Mooney”Surnames(“Chris”) = “Coates”
Print “Bill’s surname is: “ + Surnames(“Bill”)
if (isElement(Surnames(“Charlie”))) then Print “Charlies Surname is: “ + Surnames(“Charlie”)else
Print “I can’t find a surname for Charlie”end if
forall thisName in SurnamesPrint listtag(thisName) + “ “ + thisName
end forall
erase Surnames(“Buchan”)
Thursday, 22 March 12
Basics: Lists
• So lists can collect together similar values in an ordered way
• Lists have no real overhead
• Lists can contain millions of items
• Lists are very fast
Thursday, 22 March 12
Basics: Lists
• You cannot read and write Lists directly from Documents
• Convert into a array first, and store as a multi-value
• You have to iterate a list to see how many items it contains
• Lists can only store values of the same type
Thursday, 22 March 12
Agenda
• What is LotusScript? Where is it going?
• LotusScript Basics
• LotusScript Advanced
• Web Services
Thursday, 22 March 12
Advanced: Classes
• LotusScript allows you to define classes:
• Classes allow you to bundle variables and code together in reusable objects
• Classes can inherit from other classes (but not the ‘notes*’ classes)
• Classes can help build complex systems quickly and easily
Thursday, 22 March 12
Advanced: Classes
• Example class: define ‘Person’class Person public personName as NotesName public UNID as String ‘ UNID to doc in NAB sub new(doc as NotesDocument) set personName = new NotesName( _ doc.getItemValue(“FullName”)(0) ) set UNID = doc.UniversalID end subend class
Thursday, 22 March 12
Advanced: Classes
dim P as new Person(personDoc)
print “Person: “ + P.personName.Common + _ has document ID: “ + P.UNID
• Example class: Using ‘Person’ class
Thursday, 22 March 12
• Create another class that encapsulates ‘Person’class People public people list as Person public count as long
sub new(nabView as NotesView) dim doc as NotesDocument set doc = nabView.getFirstDocument() while not doc is nothing dim P as new Person(doc) set people(P.personName.Abbreviated) = P count = count + 1 set doc = nabView.getNextDocument(doc) wend end subend class
Thursday, 22 March 12
Advanced: Classes
• Using ‘People’
dim folks as new People(nabView)
forall thisPerson in folks.people
print “Person: “ + thisPerson.personName.Common + _ has document ID: “ + thisPerson.UNID
end forall
Thursday, 22 March 12
Advanced: Classes
• Lets add logging functionality to these classes by creating a new ‘layer’.
Class log sub new() end sub
public sub logMsg(msg as String) print msg end sub
end classThursday, 22 March 12
class Person as log public personName as NotesName public UNID as String ‘ UNID to doc in NAB sub new(doc as NotesDocument) set personName = new NotesName( _ doc.getItemValue(“FullName”)(0) ) set UNID = doc.UniversalID logMsg(“created new person: “ + P.personName.Common) end subend class
• We can ‘inherit’ ALL functionality from ‘log’ by creating ‘Person’ as a subclass of log.
Advanced: Classes
Thursday, 22 March 12
Advanced: Classes
• You can only ‘inherit’ from one class at a time (other languages have mechanisms for more)
• Every class you inherit from gets to run its constructor in the sequence you defined inheritance
• This allows you to quickly segment large problems and delegate work
Thursday, 22 March 12
Advanced: Calling C-API
• Not all Lotus Notes API calls are defined in LotusScript. Sometimes you want to execute these.
• You can define ANY library/DLL function in LotusScript and call it
• Its dangerous...
Thursday, 22 March 12
• You have to define EXACTLY the function using the correct sized primitive variable types (such as integer, etc)
• If you get any part of it wrong, you will crash the client. Badly.
• Its Platform-specific. So you have to rewrite this for every single platform you support
Advanced: Calling C-API
Thursday, 22 March 12
• Example: NSFGetServerLatency
• Find out how many milliseconds it takes to ping a Domino server.
• Defined as:
Advanced: Calling C-API
STATUS LNPUBLIC NSFGetServerLatency( char far *ServerName, DWORD Timeout, DWORD far *retClientToServerMS, DWORD far *retServerToClientMS, WORD far *ServerVersion);
Thursday, 22 March 12
• We can define this in LotusScript using:
Advanced: Calling C-API
' This is a constant for our windows-based' Library file:Const LIB_W32 = "nnotes.dll"' Declare our function for windows
Declare Function W32_NSFGetServerLatency _ Lib LIB_W32 Alias {NSFGetServerLatency} (_ Byval ServerName As Lmbcs String, _ Byval Timeout As Long, _ retClientToServerMS As Long, _ retServerToClientMS As Long, _ ServerVersion As Integer) As Integer
Thursday, 22 March 12
• And we can use this (on a 32-bit windows client or server at least) by:
Advanced: Calling C-API
' A function to get network latency time...Public Function getServerLatency (strServer As String) As Long Dim nnServer As New NotesName(strServer) Dim ToServer As Long, fromServer As Long Dim ver As Integer Dim timeout As Long timeout = 1000 ' 1000ms == 1 second Call W32_NSFGetServerLatency(nnServer.Canonical,_ timeout, toServer, fromServer, ver) ' Return both directional latencies added together
getServerLatency = fromServer + ToServerEnd Function
Thursday, 22 March 12
• C-API can get to functionality that would be impossible to implement in LotusScript alone
• It requires FAR more testing than anything else - use it as a last resort
• Good reference: http://www.ls2capi.com
Advanced: Calling C-API
Thursday, 22 March 12
Advanced: Execute
• ‘Evaluate’ in LotusScript allows you to evaluate @Formula language:
myString = Evaluate(|@Unique|)
• Execute allows you to execute LotusScript code
• You can write code that writes code
Thursday, 22 March 12
Advanced: Execute
Dim executeString as StringexecuteString = |print “Hello world”dim s as new NotesSessiondim db as NotesDatabaseset db = s.currentDatabaseprint “Current Database name is: “ + db.Title|execute (executeString)
• Example
Thursday, 22 March 12
Advanced: Execute
• I use it to dynamically calculate C-API function signatures
• http://www.hadsl.com/HADSL.nsf/Documents/LS2CAPI+-+Calling+Notes+C-API+from+LotusScript
• It can also be used for evil...
Thursday, 22 March 12
Advanced: TriggerHappy
• Imagine we wish to profile ALL our agents in ALL our databases, ALL the time.
• We could monitor each database to see when a profile document with form name $BEProfileR7 is saved
• Save it in a central database...
Thursday, 22 March 12
Advanced: TriggerHappy
• Damien Katz (now of CouchDb, ex-Iris) wrote a server addin called ‘TriggerHappy’, available on OpenNtf.
• It allows you to define lotusscript which gets executed on ANY database event at a server level.
• RunningWithScissors++
Thursday, 22 March 12
Advanced: TriggerHappy
• Its detailed (with some code) on my blog at:
• http://www.billbuchan.com/imported-20091119232548/2010/2/2/universal-agent-profiling-in-domino.html
• We ran it in our TEST environment for months without issue
• It also tracked Web services as well as LotusScript and Domino agents
Thursday, 22 March 12
Agenda
• What is LotusScript? Where is it going?
• LotusScript Basics
• LotusScript Advanced
• Web Services
Thursday, 22 March 12
Web Services: Introduction
• Web services are a language and platform independent way of wiring applications together
• Fundamentally they’re usually web service http calls passing back and forth XML encoded information
Thursday, 22 March 12
• Lotus Domino 7 (now out of support!) provided a simple lotusscript based Web services provider
• Lotus Domino nd8 provides a consumer and a provider
Web Services: Introduction
Thursday, 22 March 12
Web Services: Experience
• They’re fast. 10+ calls per second. Much faster than I expected
• But if you want performance, implement xPages REST based web service providers - 5x faster
• Reliable
• Look and feel like an agent
Thursday, 22 March 12
Web Services: Example
• A single function that returns a single string
Thursday, 22 March 12
Web Services: Testing
• Download SoapUi from http://www.soapui.org
Thursday, 22 March 12
Web Services: Authentication
• Two general types of Domino Authentication:
• Session based - uses cookies back and forth
• Username and Password based - username and password is sent with each transaction
Thursday, 22 March 12
• Client Example:
• We use Flex as a client
• It automatically inherits cookies from the web page its launched from
• Launch it from an Authenticated page
• Keep the session active by pinging every 10 minutes
Web Services: Authentication
Thursday, 22 March 12
• Server Example:
• We use a windows-based service to perform work
• We encrypt a username/password pair in the registry, and use username/password authentication
• It wakens up every 5 minutes and calls home
Web Services: Authentication
Thursday, 22 March 12
Web Services: Complex
• You can pass back simple types. Or classes.
• Lotuscript limitation - you cannot return an array
• So how can I return an array?
Thursday, 22 March 12
• Define a class which contains an Array
Web Services: Complex
Thursday, 22 March 12
SoapUI says...
Thursday, 22 March 12
Web Services: Thoughts
• Web service clients are not under your control
• Decide how complicated your client can handle
• More granular == more likely to change
• Choose wisely
Thursday, 22 March 12
Resources
• The LS to C-API programming manual
• http://www.ls2capi
• Calling C-API from LotusScript resource:
• http://www.hadsl.com/HADSL.nsf/Documents/LS2CAPI+-+Calling+Notes+C-API+from+LotusScript
• Steve McConnell, Code Complete 2 (MS Press, 2004).
• http://www.amazon.com/gp/product/0735619670
Thursday, 22 March 12
The End?
• This presentation, like all my others, is available at
• http://www.hadsl.com
Thursday, 22 March 12