Currently XPages only allow for scripting
an XPage. However, Server Side Java Script does have access to the full
Notes API. Through this you can call any agents you have in your databases.
If you have business logic that you need called already in agents, then
this is sufficient. If not, agents themselves, can call into your script
libraries and access business logic contained in those libraries. It should
be remembered that the LotusScript UI classes (NotesUIWorkspace, etc) cannot
be called in this library, since the invoking code is an agent. All that
remains is to be able to pass parameters from your XPage to the agent,
and receive return values from the agent and use them in your XPage. We
will demonstrate that here using a profile document as an intermediary
store for the parameters and return values.
The Lead Manager is a sample application
that shows how to create an XPage front end to an existing suite of Notes
databases. In the picture above we can see a sub panel (implemented with
an XPage custom control) where the details of a company are produced. Several
of the fields (e.g. Name, Business Type, etc) are directly presented from
the data in the Notes document representing the company. However, the "Credit
Rating" field is not. It is a calculation made by business logic.
It's very easy to display a field that
come directly from back end data. For the Annual Revenue field, we create
a computed label. The formula for the label can very simply be set from
the data source tied to the page using Expression Language.
To display a value based on an agent,
it is more complicated. We add another label with a computed value to the
formula. The return value from this formula is displayed as the label contents.
Lets walk through that code
var unid = "GetCreditRatings"+java.lang.System.currentTimeMillis();
var companyName =
var db = session.getDatabase(DB_SERVER(),
create profile document for parameters
var profDoc = db.getProfileDocument("GetCreditRatings",
var profDocID = profDoc.getNoteID();
get agent and call it with profile doc
var agent = db.getAgent("GetCreditRatings");
extract returned value
if (profDoc == null)
creditRating = profDoc.getItemValue("creditRating");
operate on parameters, and return a value.
For this technique, a profile document
is used as an intermediary storage place for the parameters to be passed,
and the return value to be retrieved. A profile document was chosen over
a normal document since we can be sure that there will be no view that
inadvertently shows this document. So it should work in a wide variety
of databases. To use a profile document, though, we need a profile name
and a unique key. The name we can tie to the functionality we are
invoking. On line (1) we create a unique key based on the current time
in milliseconds. Although this is not necessarily a perfect mechanism (there
is a small chance on a very busy, very fast server that two requests could
be processed in a single millisecond) it is a pretty safe bet.
The back end business logic we are calling
requires a company name to be passed to it as a key for the lookup. At
line (2) we extract the key information from the data source on the page.
We then place that into the profile document in line (6).
We can direct an agent to operate on
a specific document. We notify it that we want it to work on this specific
profile document by extracting it's NotesID (9) and passing that when we
invoke the agent (13). However that does not tell the agent how to respond
to us. We don't have it write back to the same in-memory document, since
that can creating problems with caching. To be sure both halves of the
operation are in sync, we want to delete the profile document and recreate
it. So, what we do is pass to the agent the unique key we used to create
this profile document in a field in the document itself (7).
Once everything is set up, we save the
profile document (8), retrieve the agent from the database (), and invoke
it (13). This operation blocks until the agent has completed running. Since
this is done during a page refresh, it is very important not to call long
running business logic in this matter or even slightly time consuming business
logic on a very busy server.
Once the agent is finished, we need
to get our return value back. We recall the profile document on line (15).
We extract the value for the field from the document (25) and set the value
into a request scope variable (26). If some failure occurs we set a neutral
value into the request scope (19), (31). We use the request scope here
code. We could just as easily use a local variable. But because this value
is "expensive" to retrieve, we wanted to leave the possibility
open that we might use this value elsewhere on the page. For example we
might want to display a graphic of a thermometer with a different level
set based on the credit rating. As long as we can guarantee that the display
of that graphic will always be within the same request call as the display
of this label then the image graphic could make use of that request scope
variable instead of calling the agent again.
Lastly we remove the profile document
(35) and return the value we retrieved (36). The results are shown in Image
On the other side of the fence we have
the agent's code. This can be done by any valid agent. For this example
we use LotusScript. The first task of the agent is to work out what document
contains the parameters.
Dim ssn As New NotesSession
Dim db As NotesDatabase
Dim agent As NotesAgent
Dim doc As NotesDocument
REM This agents reads the pased in doc for parameters,
REM and writes the result back to the document.
Set db = ssn.CurrentDatabase
Set agent = ssn.CurrentAgent
Set doc = db.GetDocumentByID(agent.ParameterDocID)
If doc Is Nothing Then
doc is nothing"
process db, doc
Caption (?): LotusScript function to
calculate the parameter doc, and pass it tot he processing function.
The Document ID can be retrieved by
a function on the agent and used to get at reference to the document (11).
We then pass that document to the processing function (15).
As NotesDatabase, doc As NotesDocument)
Dim companyName$, credit$, replyID$
Dim replyDoc As NotesDocument
companyName$ = doc.GetItemValue("companyName")(0)
replyID$ = doc.GetItemValue("replyID")(0)
credit$ = calculateCreditRating(db, companyName$)
Set replyDoc = db.getProfileDocument("GetCreditRatings",
replyDoc.save True, True
Caption (?): LotusScript function to
process the agent's arguments, call the business logic, and pass back a
The processing function first extracts
the parameters from the passed in profile document (5). It also retrieves
the unique ID of the profile document to create as a response (6).
Once it has all the required parameters,
it can call the business logic (7). This could be more code in the agent,
or code residing in a LotusScript library. Existing LotusScript code on
a form cannot be called (although validation logic is called). If you want
to make use of that, it is suggested that you first promote that code to
a LotusScript library. Then change your form to use the code form the library.
Then both the agent and your rich form use the same code base. Any changes
or improvements you make to that code will be used by both.
Once the parameters are extracted, the
profile document can be removed (8) and recreated (9). Again, this is done
to avoid cashing issues that might otherwise come up. More advanced programmers
can explore other ways of refreshing the in-memory document for more optimal
The return value is written back to
the profile document (10) and saved (11). The value is now all ready for
So, the above technique is useful for
exposing business logic contained in script libraries via an agent. This
is the best way to make business logic written in LotusScript accessible
to XPages. There is a more efficient and more native way to incorporate
business logic that is implemented in Java. For this and other techniques
please see this