Consuming web services in Notes and Domino 8
Tim Tripcony
I haven't seen much discussion about how easy it really is to consume web services in Notes/Domino 8 (perhaps precisely because it is so easy), so I thought I'd outline exactly how one goes about providing and consuming services now.
Let's use a very basic example. Create a script library and toss this in the Declarations:
Public Class House
Private currentColor As String
Private currentValue As Long
Public Sub buy (price As Long)
Let Me.currentValue = price
End Sub
Public Function color() As String
Let Me.color = Me.currentColor
End Function
Public Function value() As Long
Let Me.value = Me.currentValue
End Function
Public Sub sell (price As Long)
Let Me.currentValue = price
End Sub
Public Sub paint (newColor As String)
Let Me.currentColor = newColor
End Sub
End Class
Save that library, and create a web service. Include the library you just created via a Use statement in the Options, then in the properties of the service, specify House as the PortType class. The service doesn't need any code of its own, because it's aware of the House class via the Use statement and knows that's the class that defines the service methods.
Now it's time to consume the service. From within the service, click "Export WSDL", and save the file anywhere you can get to it later. In any Domino database (including, but not limited to, the database containing the web service), create a new script library (can be Java if you prefer, but in this example, we'll use LotusScript). At the bottom of the window you'll see a WSDL drop-down button. Click that and select "Import WSDL". It'll warn you that this will overwrite your script library (which is fine, since that's precisely what we want); click OK and select the file you just exported. Here's what you'll see:
%INCLUDE "lsxsd.lss"
Class House As PortTypeBase
Sub NEW
Call Service.Initialize ("UrnDefaultNamespaceHouseService", _
"HouseService.Domino", "http://localhost", _
"House")
End Sub
Sub BUY(PRICE As Long)
Call Service.Invoke("BUY", PRICE)
End Sub
Function COLOR() As String
Let COLOR = Service.Invoke("COLOR")
End Function
Function VALUE() As Long
Let VALUE = Service.Invoke("VALUE")
End Function
Sub SELL(PRICE As Long)
Call Service.Invoke("SELL", PRICE)
End Sub
Sub PAINT(NEWCOLOR As String)
Call Service.Invoke("PAINT", NEWCOLOR)
End Sub
End Class
Any of this seem familiar?
Only one change to the library is needed: replace the reference to "http://localhost" (in the Service.Initialize call) with "http://server/path/db.nsf/servicename?OpenWebService" (where server is the IP or DNS address of your Domino server, path/db.nsf is the full filepath of the database containing the service, and servicename is the name of the web service design element).
At this point, you can save the new library and include it anywhere you need to consume the service. And actually consuming it is this easy:
Dim myHouse As New House()
Call myHouse.buy(200000)
Call myHouse.paint("Purple")
Call myHouse.sell(5000000) 'Keep dreaming
But the real beauty of web services is that it allows you to execute functions against an application you know nothing about, whether it's inside your network or not. For example, if you download this file and import it into a script library in any of your applications, you can pull a random quote from my site as easy as this:
Dim Quotes As New Quotes()
Dim Quote As Quote
Set Quote = Quotes.getRandomQuote()
Msgbox Quote.Content & Chr(13) & "- " & Quote.Source
Tee hee
Tim Tripcony
import lotus.domino.*;
public class JavaAgent extends AgentBase {
public void NotesMain() {
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
DxlFactory.createAgent("James Bond", DxlAgent.LOTUSSCRIPT).addCodeEvent("Initialize", "Sub Initialize\n'Shaken, not stirred.\nEnd Sub").toDesignElement(agentContext.getCurrentDatabase());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Running multiple versions of IE
Tim Tripcony
Thought this might come in handy for some of you... if you've avoided upgrading to IE7 because you're supporting IE6 users, but want to test your applications in IE7 to be sure they won't break when your users do upgrade, there's a way to run multiple versions on the same machine. Upgrade to IE7, then download the executable hosted here. This allows you to install versions 3 through 6 (or any subset thereof) without actually installing them... it uses "DLL redirection" to allow each version to behave as it would if it were the only version installed.
Chat logging not enabled on im.bleedyellow.com
Tim Tripcony
Official statement here
A lot of folks have been discussing the availability of the im.bleedyellow.com Sametime community. At the moment, there are 101 members of the community logged in (more than 1,400 have now registered for a BleedYellow account, by the way). This morning I was told that someone had raised a concern that we might be logging chats on the server... we're not. We weren't, and we won't be. You can still feel free to save chat transcripts locally, but the server won't be saving a copy as well.
Disappointed in SearchDomino
Tim Tripcony
Last month, Greyhawk expressed frustration with a pattern he'd detected in the whitepaper emails they'd been sending, and unsubscribed from their distribution lists (as did many of us). Ed later took them to task on the same issue. I'm keeping them in my RSS strictly because I've also noticed a decline in the quality of tips submitted, and I would like to occasionally serve as some small bulwark against the dissemination of bad advice to noobs. Messiah complex? Yeah, maybe a little. Earlier this week, I spotted a real winner.
In a remarkable display of over-engineering (sorry, Christian), the tip linked to above demonstrates one approach to downloading the contents of a server's INI file. It uses approximately 220 lines of code (including comments and whitespace) to issue a remote console command and store the response in a text file... using Notes API calls to issue the command and Windows API calls to write the response to the file using Notepad. Assuming you're using one of the last three major releases of Notes/Domino, the same exact operation can be accomplished with 8 lines of code. And (gasp) it'll work on all supported platforms.
Sub downloadServerINI (Byval server As String, destination As String)
Dim session As New NotesSession
Dim stream As NotesStream
Set stream = session.CreateStream()
Call stream.Open(destination)
Call stream.WriteText(session.SendConsoleCommand(server, "Show Config *"))
Call stream.Close()
End Sub
I used their comment link (a mailto:... they haven't quite gotten around to implementing the comment feature you'll now find on almost any blog) to send Gervais a comment summarizing the above. He responded less than an hour later, thanking me for the feedback and indicating that he'd update the tip as soon as he got a chance. I just checked, and although it's been over two days since then, there's been no change... except that it now shows that, thus far, it's been rated a 5.00 out of 5.00. So anyone reading this tip that doesn't know any better might think this is the most elegant approach available. I rated it a 1, but apparently the average isn't real-time.
NULL and void
Tim Tripcony
By now, we all know (hopefully) not to use GetNthDocument to loop through document collections, but here's something that often gets overlooked: don't use NULL in formula to determine if a field is blank. NULL doesn't exist.
In LotusScript, the keyword Null basically means "unknown" or
"invalid". For example, ArrayGetIndex returns Null if the search value
does not exist in the source array. You can manually assign Null to a
variable - but only if it's a Variant - and then use IsNull to see if
it's still Null, but I'd advise against that, since the only way a
value can be Null is if you specifically tell it to be or assign it to
an expression that returns Null. IsNull( "" ), for example, returns False.
In formula, although @IsNull does exist, NULL simply has no meaning. It's not a reserved keyword, so much like extended syntax in LotusScript,
it doesn't know what it means, so it assumes you're referring to a
field. Since there is no field (again, hopefully) named NULL, NULL
returns "". So technically you get the same result, just more slowly.
This reminds me of trying to navigate one of those voice-activated
menus when I try to call the customer service department of an ISP,
utility company, insurance agent, etc. After trying unsuccessfully to
determine how to contact an actual human, I'll just say "human". More
often than not, the response is: "I'm sorry, I didn't understand you.
I'll transfer you to a representative." The result was precisely what I
wanted, it just took longer than it would have if I'd known to just
push 0 at the start of the call.
In other words, the following formulae all return the same Boolean value:
Subject = NULL
Subject = ""
@IsNull(Subject)
@Length(Subject) = 0
The last is the most efficient, but arguably the least readable. I'd still recommend it, though, for use in column formulae for potentially large views and view selection for any view in a potentially large database. For performance purposes, hide-whens on forms/subforms should be kept to a minimum anyway, but the more you have the more noticeable it becomes if you're asking if some field value = NULL in each: Notes has to check each time whether a field named NULL exists, find out that it doesn't, evaluate NULL to "", and then do a string comparison between "" and the field you've specified. With @Length(FieldName) = 0, it's just checking the length of the field value, then comparing one number to another. Again, slightly less readable, but faster every time.
Let's put it to a vote
Tim Tripcony
I just tried to initiate a chat with Nathan in Sametime 8, and was asked the following question:
At the moment, I'm leaning toward clicking "No", but I'm curious to see what I'd be agreeing to by clicking "Yes".
UPDATE: I clicked "Yes", and now I see this:
Uh....... Nathan, what am I sending you? I just double-clicked your name.
UPDATE 2: I clicked "OK", and now Notes tells me:
Something tells me I should be glad the "Specified command" is not available. Although I do wish they'd tell me what command was specified. Not too much to ask, right?
"Notes Error - Specified command is not available from the workspace: EMAIL_MY_HARD_DRIVE_TO_NATHAN"
Go Go Gadget Replication
Tim Tripcony
A friend of mine recently set up a Domino server at home running on Fedora, and is currently replicating a few databases with my personal server. He's locked out of the rest of the databases on my server, of course, so the connection document is set to only synch the ones he has access to. But in order to trigger a manual replication (outside of the schedule defined in the connection), he had to issue a separate Replicate command for each database to avoid getting errors about all the databases with shared replica IDs that his server can't access. He wanted a single command that would allow him to just type "go go gadget replication" and have it do everything he wanted, but only what he wanted. As it turns out, IBM posted instructions for this just last Friday.
If you issue a console command with the following syntax:
< "filename"
...Domino looks for the specified filename relative to the data directory, and treats each line in the specified file as a separate console command. So, for example, a file named "go go gadget replication" (no extension necessary) in the data directory containing:
Replicate Coyote/ACME names.nsf
Replicate Coyote/ACME mail/coyote.nsf
Replicate Roadrunner/ACME stuff.nsf
Broadcast "(!)Selective replication manually initiated." "Wile E Coyote/ACME"
Wile E can now issue the following console command (the < indicates the rest of the command is a filename):
< "go go gadget replication"
The server will kick off three replications, then pop up a message confirming the comands.
NOTE: on Linux, the file must be owned by notes:notes (or whatever user:group Domino is running under) in order for this to work.
Finding the remote
Tim Tripcony
In a recent dicussion with a client, we stumbled upon an analogy for
resource allocation in software development. We had been musing about
the role that technology plays in the delicate balance between laziness
and efficiency, and the conversation drifted to the concept of the
remote control. I mentioned the oft-discussed irony of a willingness to
spend several minutes locating a misplaced remote rather than simply
walking to the device (TV, DVD, etc.) and pressing the button. "But,"
he responded, "once I've found the remote, I've solved the problem; if
I keep getting up to push the button, I'm just treating the symptom
over and over."
That took us directly to a discussion of requirements management, and
the tendency for many organizations to focus on symptoms rather than
root cause. It's typically quicker to fix the result of a
software bug than to fix the bug itself. More specifically, when an
entire application, framework, or platform fails to adequately meet the
needs of its users, it can often be difficult to convince the
stakeholders to invest the time, effort, and/or money to overhaul or
even replace it with something that will. Instead, they choose to live
with - and, in many cases, constantly patch - the inefficient tool that
they already have despite the time, effort, and money that is wasted
by the shortcomings of the tool. They keep "getting up to push a
button" (for example, manually fixing incorrect data) instead of
"finding the remote" (dedicating a resource for a few days to determine
what's causing the data to be incorrect). Put another way, they keep
taking the car into the shop long after it would have been cheaper to
just buy a new car.
I'm reminded again of my philosophy concering a distinction between
"good lazy" and "bad lazy". More to the point, it strikes me as a
distinction between short-term priorities and long-term investment. "A
stitch in time saves nine", so to speak.
How do you sell stakeholders on this distinction? One of the luxuries
of my current position is that I rarely have to: by the time I'm
involved in the process, I'm typically interacting with folks already
open to suggestion on how best to obtain value from the services we
provide. That helps to facilitate discussions on which changes to an
application would best improve performance; which template/element
inheritance hierarchy would best improve maintainability; which
additional Lotus software offerings would most empower their user
community. While our recommendations may not always be accepted, the
nature of being the "outside help" is that someone has already decided
that an investment now may pay off later. But I'm curious: for those of
you who have to make this case "from the inside", what arguments have
you found to be most effective?

