A Legacy Notes Developer's journey into madness.

IBM Connect 2016 - Final Thoughts

Devin Olson  February 8 2016 09:29:35 AM
Before I give you my thoughts about Connect 2016, I need to first provide a bit of a background.

I have faithfully made the annual pilgrimage to Orlando every year since my first one back in 1998.  I have been there for the highs and lows, representing 6 different employers (and several times just as myself).  I have forged long-term SOLID friendships with people from all over the world.   Lotusphere (and then Connect, then ConnectED, and now Connect again) has been a VERY important part of my life, and career, for 18 years.  

I say this so you fully understand how big of a decision it was for me to NOT attend Connect 2016.  

I, and all of my teammates at work, had decided it just wasn't worth the effort to attend this year.

The last several Lotusphere / Connect events at the Swan / Dolphin have consistently gotten worse each year, and listening to the death knell of a product I loved ringing ever more loudly by IBM had become too painful to bear.  It felt as though people were trying to be upbeat and excited, but everybody knew things were coming to an end.  The customers, partners, vendors, and even IBM staff all bore the stink of death on their souls.  The last two conferences felt as though we had moved to hospice, and were sitting around waiting for the final bell.  

So last year, on the final day, I said my farewells to my friends, and left the Swan and Dolphin knowing something special and magical had gone forever.  

And then this year, something rather unexpected happened.  I was named an IBM Champion.  

My wife Tanya (whom many of you know) told me I had no choice in the matter -I would be attending Connect 2016.  

So last minute travel arrangements were made, phone calls and meetings set up, and last week I found myself at Connect 2016.  I was busy has hell -helping out with the IBM BP Beer Tour (huge thanks to Amanda Bauman and Duffy Fron, and IBM for turning SpankyBrews into a proper, officially sponsored event), giving a SpeedGeeking session (which I totally ROCKED), attending other sessions, meeting with vendors (including several NEW ones), attending social events with friends, sitting on the GuruPalooza panel (which also ROCKED), and doing all the other crazy / insane / busy things that one does when stuff matters and people are counting on you.  

The venue had changed (the Orlando Hilton at the Orlando Conference Center); and while I miss the Dolphin / Swan, I think the choice of the Hilton was a good one.  The food / beer was WAY better, the layout was better, getting to and from sessions was easier, the Vendor Showcase was better.  Basically every thing about the Hilton (other than fond memories of times past) was better.  

And I noticed something different.  Something had CHANGED.   The feeling of dread from the last several conferences was gone.  It has been replaced with something else.  This new thing has not yet fully formed -it is more of an anticipation of good tidings in the future.  It is the vestiges of excitement, the beginning of a promise of good things to come.  

To quote and hold song: "Hope is like a lighthouse keeper's beam; Hope the master cobbler of our dreams"  

Hope has returned -you could see it in the faces of the people there.   And that is what really matters -the people.  Not the product, not the number of seats / licences / partners / etc.  It is people, and their relationships with one another, that matter.    The feeling of hope has invigorated me -I am filled with excitement about my projects / code for the coming year.  The stuff I am going to make and do will be amazing.

I am very glad I went.  And I hope to attend again.

IBM Connect 2016 - I’m Going

Devin Olson  January 22 2016 08:37:43 AM
I will be there.  Will you?


Standalone Sametime on OSX El Capitan

Devin Olson  January 6 2016 09:48:54 AM
If you, like me, use the stand-alone Sametime client on OSX and you upgrade to El Capitan; you may run into the same issue I have.  

Namely, the stand-alone Sametime client will no longer start.  

Click, click, click, clickity clickity click.    Nothing.  

The solution (for me at least) was fairly simple: I downloaded and installed Java for OS X 2015-001 from Apple's website.  

The whole thing (from download to install to successfully launching the stand-alone Sametime client) took less than three minutes.  

Hope this helps!

IBM Champion 2016

Devin Olson  November 23 2015 01:33:44 PM
I just found out I have been named an IBM Champion for 2016.  

I am....  

...at a complete loss for words.

thank you.

IBM Champions 2016  

MWLUG 2015: Great Content, Less Fluff

Devin Olson  July 16 2015 05:18:12 PM
Just to be clear and so there is NO CONFUSION, MWLUG 2015 at the Ritz-Carlton in Downtown Atlanta, GA next month (August 19-21) is all about content and value and crap we YellowBleeders care about.

It is NOT and IBM Marketing event.  It is a TECHNICAL EVENT.

IBM is a huge sponsor, but so is redpill development, and so are BCC, DOCOVA, HADSL, Panagenda, PSC Group, Riva,
teamstudio, Ytria, along with all the other MWLUG 2015 Sponsors.

Without these sponsors, awesome geek-focused conferences such as MWLUG simply could not exist.  The costs putting together a decent conference at a good venue are phenomenonally high, and if the sponsors were not there with their money then the conference fee would be prohibitively expensive (take the cost of the last Lotusphere you attended and triple it).  

MWLUG is a technical content focused conference, CREATED BY YellowBleeders FOR YellowBleeders.   Quit your bitching, fork over your measly $50, and join me in Atlanta this August. I'll see you there.  

Scheduled XAgents

Devin Olson  June 25 2015 10:39:50 AM
Using scheduled agents that can access the XPages runtime would be awesome, would it not?  

If you, like me, have built up a library of way cool Java API stuff that runs in XPages, you may have noticed one frustrating little problem.  You cannot access the JSF / XPages runtime stuff from a scheduled agent.

The OpenNTF Domino API (ODA) has received a lot of unfair blame about this, much of which has come from me.   The OpenNTF Domino API (ODA) is truly awesome.  If you are building XPages applications, you need to use it.  

However, it has one small little issue.  

You see, it really should not be called Domino API -because that implies it works across all of Domino, and that really is not the case. It is designed to work within the JSF / XPages architecture of your Domino server, and as such it requires that those things be present in order to work.

Now, this really is not that big of a deal, until we try to run code on the Domino server in a non JSF / XPages environment -specifically speaking: scheduled agents  

Like I said, I have unfairly blamed the OpenNTF ODA for this -but the problem is actually an architectural one within the Domino server itself.  Scheduled agents run in their own little space, and the boundary between their space and the JSF / XPages environment is strong.  This means your awesome XPage code (and any Java code using the OpenNTF API) simply won't work in a scheduled agent.  

Again, this is not the fault of the ODA -it just gets hit hard with it because it is so very good at what it does.  We developers who use it have built all kinds of dependencies into our own APIs, and when we suddenly realize we can't use our APIs in a scheduled agent we tend to blame the ODA, instead of the design separation built into the server itself.

For now there really is no good solution to this conundrum.  

Please allow me to present an acceptable hack that will work for most instances.  

Scheduled XAgents

An XAgent is an XPage that does NOT render, but executes code during the beforePageLoad event.  This code runs within the JSF / XPages realm, which means it can touch your kickass ODA-dependent Java code.  

The way we schedule the XAgent is to create a scheduled agent that reaches out and hits your XAgent XPage.  We can pass information to the XAgent via QueryString parameters, or we could also (you need to do this yourself, I'm not writing that code today) pass the information via a back-end document on the server, similar to the way you can write information to a document from within the Notes Client and then call an agent on the server and have it read that information from the document.  

We can trigger our XAgent either via a NON-ENCRYPTED connection (which I do not recommend), or via an SSL-ENCRYPTED connection. Note that for Anonymous ACL to work on the database containing the XAgent XPage, it must be set to at minimum of READER. Also note that the scheduled agent code must be at minimum of security level 2 (Allow restricted operations), or it will not be able to connect to a URL.

UNSECURE connection code

import java.net.HttpURLConnection;
import java.net.URL;

import lotus.domino.AgentBase;

public class JavaAgent extends AgentBase {
// Change these settings below to your setup as required.
static final String targetURL = "http://URL_OF_YOUR_XPAGE_AND_OPTIONAL_QUERYSTRING";
static final String USER_AGENT = "Mozilla/5.0";

public void NotesMain() {
 try {
   final URL url = new URL(JavaAgent.targetURL);
   final HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();

   httpUrlConnection.setRequestProperty("User-Agent", JavaAgent.USER_AGENT);

 } catch (final Exception e) {

SSL-ENCRYPTED connection code

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

import javax.net.ssl.SSLSocketFactory;

import lotus.domino.AgentBase;

public class JavaAgent extends AgentBase {
// Change these settings below to your setup as required.
static final String hostName = "DNS_HOST_NAME_OF_YOUR_SERVER";
static final int sslPort = 443;

public void NotesMain() {
 try {
   final SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
   final Socket socket = factory.createSocket(JavaAgent.hostName, JavaAgent.sslPort);

   final BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
   final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

   final StringBuilder sb = new StringBuilder();
   sb.append("GET ");
   sb.append(" HTTP/1.1\n");
   final String command = sb.toString();

   sb.append("Host: ");
   final String hostinfo = sb.toString();



 } catch (final Exception e) {

Hope this helps!

Tim’s Magic Button

Devin Olson  March 31 2015 03:05:12 PM
Whenever I set up a new Notes Client, there are a certain set of "things" I do to tweak it a bit and make it easier for me to work.

I'm sure everybody has their own set of these "things" as well.  

Today was no different.  I had just finished a clean install of a new development VM, and was working in my freshly installed Notes Client when I remembered (because I needed it) that I had forgotten to install a bit of code I call Tim's Magic Button.  

Several years ago my partner Tim Tripcony and I were working at a client's site, and he showed me this custom Smart Icon Toolbar button code.   It is concise, clean, and absolutely brilliant (much like Tim -RIP my friend).  

You may ask "What does it do?"  (Go ahead, ask out loud)  

Well, it allows you to select a document from a view and then add, edit, or delete any field on the document. Nothing more, nothing less.   But sometimes, that is exactly what you need.

Anyway, here is the code:

targetField := @Prompt([OkCancelEditCombo]; "Select Field"; "Select a field to override:"; ""; @DocFields );
updateTypes := "Text":"Number":"Time":"Delete Field";
updateType := @Prompt([OkCancelList]; "Select Type"; "Choose a type or action:"; "Text"; updateTypes );
@If(updateType = "Delete Field"; @Return(@SetField(targetField;@DeleteField)); "" );
newValue := @Prompt([OkCancelEdit];"New Value";"Enter the new value:"; @Text(@GetField(targetField )));
newTypeValue := @Select(@TextToNumber(@Replace(updateType; @Subset(updateTypes;3);"1":"2":"3" )); newValue; @TextToNumber(newValue); @TextToTime(newValue));
@SetField(targetField; newTypeValue)

Some time after giving me this code Tim wrote a blog post about it (my how time flies) .  You can read his post here.

Presenting at Das EntwicklerCamp 2015

Devin Olson  February 23 2015 02:01:35 PM
I am honored to have been selected as a speaker for Das EntwicklerCamp 2015 next week in Gelsenkirchen, Germany.  

I will be presenting 2 sessions:

Introduction to XPages Development (eng)  - Montag, 2. März 2015

XPages technology, generally released in December 2008, has been available for over 6 years now; yet many Domino developers are still not yet using it. There are many reasons for this, but I believe the primary issue is simply fear of the unknown.
This session will introduce Domino Developers to XPages development and hopefully alleviate any fears and concerns.

Topics covered will include:

- A brief history of XPages, JSF (Java Server Faces) and J2EE (Java 2 Enterprise Edition)s
- Explanation of basic concepts and technologies used by XPages
- Introduction to SSJS (Server-Side JavaScript)
- Custom Controls and their use
- JSF Scopes used by XPages
- Coding for XPages events

The demonstration application will be fully CRUD (Create, Read, Update, Delete) capable, and will be developed in real time during the session.

A beginning shell demonstration application and a completed application will be available for download by attendees.

Target Audience: Notes & Domino developers who are new to XPages.

Requirements: If attendees wish to follow along or create the demonstration application in their own environment, they will need Domino Designer Client version 8.5.3 or greater.


XPages, Java, Expression Language + Source Control (eng)  - Dienstag, 3. März 2015
Attendees will dive into to more advanced topics such as:

- Why Java is so important to XPages
- Implementing Java in an XPages application
- The power of EL (Expression Language)
- Source Control
- CSJS (Client Side JavaScript) and Dojo
- Partial Refresh of single or multiple named elements
- The IBM DataObject interface and why it is important
- Java Enums and their use

The demonstration application used in the previous session will be used as a starting point, and will be enhanced during this session to implement the technologies discussed. Multiple versions of this demonstration application will be available for download by attendees.

Target Audience: Notes & Domino developers who are new to, or familiar with, XPages.

Requirements: If attendees wish to follow along or create the demonstration application in their own environment, they will need at minimum Domino Designer Client version 8.5.3; although version 9 or greater is recommended. If attendees wish to implement source control they may use their tool of choice, but SourceTree is the one which will be demonstrated.

Celebrating 25 Years of AWESOME

Devin Olson  January 8 2015 05:33:57 PM

Confusing the not very intelligent Expression Language Processor

Devin Olson  November 6 2014 06:14:12 PM
If you write a lot of Java (and I do), you will likely find yourself making use of Method Overloading on a regular basis.  But beware, for with great power comes great responsibility, and part of that responsibility involves writing your code so that the "STUPID #&$%ing EL PROCESSOR doesn't crap out.

Yes, I know I was recently praising the intelligence of the ELP, but the ELP has a few quirks.  One of those quirks is that the ELP doesn't properly COMPLETE it's introspection check.  What I mean here is that when the ELP is checking a Java Object for the existence of a given method, it ONLY checks to see if that method exists.  Once it finds a match for the method, it stops.  It does not check to see if the method is overloaded, nor does it check to see if the method has any arguments.  It just...frakin...stops.  

Now normally this would not be a problem, but if you decide to overload a method this can (and will) jump up and bite you in the ass.  How do I know this?  Indulge me for a bit more and i will explain.  

I have a Java class that I use for configuration management, and a fine class it is. It gets (and if I need it to, sets) configuration information across a multitude of sources.   It has a method called getDescription(), which returns a List of Strings containing description information.  It also has a method called  getDescriptionString(), which as you have probably guessed, returns a String consisting of concatenated description information.  

Behold, actual source code:

public String getDescriptionString() {
  return Core.join(this.getDescription(), " \n");

I also have an XPage upon which exists a Repeat Control.   The Repeat Control displays information from multiple Configuration objects, and some of this information includes the description (to keep this code simple, I have removed facets and other non-relevant bits of XML).
I have a Bean object instance called XSPqueueSet, which has a method called getQueueHandles().  This method returns an iterable set, of which each member is an instance of class Queue.  



So far everything was working awesome.   Until I realized that some of the descriptions being returned were thousands of characters long.  

This made my nice concise display look crappy.   So I decided to add an additional method getShortDescription() that would cut down on the amount of text being returned. I also realized that I might need to adjust the amount of text being returned in the future, so I overloaded the getDescriptionString() method to accept a maximum length argument as well.  These methods are as follows:

public String getShortDescription() {
  return this.getDescription(80);

public String getDescription(final int maxlength) {
  if (maxlength < 1) { return this.getDescriptionString(); }
  if (maxlength < 4) { return "..."; }

  final String string = this.getDescriptionString();
  if (string.length() <= maxlength) { return string; }

  final String left = string.substring(0, maxlength - 3);
  final int idx = left.lastIndexOf(' ');
  final StringBuffer sb = new StringBuffer((idx > 0) ? left.substring(0, idx) : left);

  return sb.toString();

I then changed the EL in my markup to reference the new method:


So far this was working out great.  I tested the page, and it was beautiful.  The descriptions in the repeat that were super long before were now all nicely trimmed up and looked great.  I was happy.  

Until I opened another page that was displaying the full description.   Then I was not happy at all.  
Image:Confusing the not very intelligent Expression Language Processor

ARRGGHHH!   We have all had these, but they still frustrate the heck out of me.    
Here is the offending markup:

   value="Description" />


Now this is code that had NOT CHANGED.  This code was working before I updated the Java object, but was now failing.   It took me a while to figure out, but I finally began to suspect the ELP as the culprit; and it was getting confused about the which of the two versions of the getDescriptionString() method to use.  

I made a quick change to my markup to verify this:


And it worked perfectly -because the ELP was removed from the process.

What does this mean?   Well, the ELP gets confused when your code references an overloaded method, because the ELP is not smart enough to know which particular method signature is being referenced.   If the ELP were to perform a complete introspection, it would be able to determine which method signature to use -based on the parameters (or lack thereof) passed to the method.   But because it just...frakin...stops, it simply cannot work with overloaded methods.

Now, I could have left my quick fix in place and called it done, but there are two reasons that isn't the proper solution,

1) It involves SSJS, which I am trying to avoid
2) More importantly, there could be other EL code in place which references the descriptionString method, and I would have to go hunting all of that down as well.  

No, the better solution here is to change my back-end Java code so that it allows the stupid ELP to work.   Which means replacing the overloaded method name:

public String getShortDescription() {
  return this.getAbridgedDescription(80);

public String getAbridgedDescription(final int maxlength) {
  if (maxlength < 1) { return this.getDescriptionString(); }
  if (maxlength < 4) { return "..."; }

  final String string = this.getDescriptionString();
  if (string.length() <= maxlength) { return string; }

  final String left = string.substring(0, maxlength - 3);
  final int idx = left.lastIndexOf(' ');
  final StringBuffer sb = new StringBuffer((idx > 0) ? left.substring(0, idx) : left);
  return sb.toString();

This is not a solution I am happy with, but until the ELP is enhanced so it performs a complete introspection check, this will have to do.