A Legacy Notes Developer's journey into madness.

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.setRequestMethod("GET");
   httpUrlConnection.setRequestProperty("User-Agent", JavaAgent.USER_AGENT);
   httpUrlConnection.getResponseCode();

 } catch (final Exception e) {
   // YOUR_EXCEPTION_HANDLING_CODE
 }
}
}



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 String urlFilepath = "FILEPATH_ON_HOST_TO_YOUR_XPAGE_AND_OPTIONAL_QUERYSTRING";
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(JavaAgent.urlFilepath);
   sb.append(" HTTP/1.1\n");
   final String command = sb.toString();

   sb.setLength(0);
   sb.append("Host: ");
   sb.append(JavaAgent.hostName);
   sb.append("\n\n");
   final String hostinfo = sb.toString();

   out.write(command);
   out.write(hostinfo);
   out.flush();

   in.close();
   out.close();
   socket.close();

 } catch (final Exception e) {
   // YOUR_EXCEPTION_HANDLING_CODE
 }
}
}






Hope this helps!