Seems like I do this a lot… so after running into a bug in the Domino JSON view output I was faced with a few choices. The first, and probably easier option, was to just rewrite the views I was using to fix the encoding problem I ran into. I ended up not using that option because it would mean that I would have to have custom views for all the data I was accessing. Even though I had created the views new for this exercise, ultimately I wanted to transition to preexisting views in the apps once I was ready to move to production. So now that I was faced with having to make design changes to the back end apps anyway, I decided to explore my other option which was to create a custom agent to serve up the data.
A custom agent has a couple disadvantages. First, it will require some special considerations when it comes time to deploy this regarding ACLs and agent signers. Second, it means that I have to re-implement something that already exists in Domino. The first one is just a fact of life and can be dealt with. The second one is more problematic because, I hate doing it, and I won’t be able to implement all the possible options and it will require a lot more testing.
On the up side, a custom agent does have many advantages also. First, I can make the output anything I want, or include as much or as little of the data. Second, I now have a chance to standardize an API that I can easily port/re-implement on other back-end systems without impacting my front end (big plus at the moment). Third, the ReadViewEntries does not support JSONP, which might come in handy at some point in this project I’m sure. And Finally, it can give me access to data a view cannot, like rich text. With these in mind the choice to custom code the agent became much easier to accept.
The dataProxy Agent
I wrote the agent in Java because LotusScript is just no fun anymore, and it gives me the most flexibility when I comes time to port this to some other platform. The basics of the agent was to try and get as close to the ReadViewEntries format as possible so I took the output from that and loaded it up in a text compare program so I could see it side-by-side with my output. Form there I just tweaked my code until I had the same, or nearly the same output. In about an hour I had the following code:
private void writeViewJSON(Database db, String viewName) throws NotesException {
// get the view
View v = db.getView(viewName);
v.setAutoUpdate(false);
// get the navigator
ViewNavigator nav = null;
if (params.containsKey("RestrictToCategory")) {
nav = v.createViewNavFromCategory((String) params.get("RestrictToCategory"));
} else {
nav = v.createViewNav();
}
// write the view out
PrintWriter ao = getAgentOutput();
ao.println("Content-type: application/json");
ao.println("{");
ao.println("\"@timestamp\": \"" + v.getLastModified() + "\",");
ao.println("\"@toplevelentries\": \"" + v.getTopLevelEntryCount()
+ "\",");
ao.println("\"viewentry\": [");
ViewEntry e = nav.getFirst();
for (int i = 0; i < nav.getCount(); i++) {
ao.println("{");
ao.println("\"@position\": \"" + e.getPosition('.') + "\",");
ao.println("\"@unid\": \"" + e.getUniversalID() + "\",");
ao.println("\"@noteid\": \"" + e.getNoteID() + "\",");
ao.println("\"@siblings\": \"" + e.getSiblingCount() + "\",");
ao.println("\"entrydata\": [");
int writeCount = 0;
for (int j = e.getIndentLevel(); j < e.getColumnValues().size(); j++) {
ViewColumn c = (ViewColumn) v.getColumns().elementAt(j);
if (!c.isHidden()) {
if (writeCount++ > 0) {
ao.println(",");
}
Vector cv = writeColumn(ao, e, c);
if (c.isCategory()) {
break;
}
}
}
ao.println("]");
ao.println("}");
if (i + 1 < nav.getCount()) {
ao.println(",");
}
ao.println();
e = nav.getNext(e);
}
ao.println("]");
ao.println("}");
}
Some of the differences, though minor, are to do with categories and the NoteId and UNID. In the Domino output you get special NoteId’s and in my version they are just empty strings. Also, the timestamp that I output is a different format than the domino version.
The agent supports a few URL parameters:
- s= Lets you specify the server.
- db= Lets you specify the database the view is defined in.
- v= Lets you specify the view to read.
using these, I can get to any view in any database.
That was all I needed at this point on the server. On the client side, it only required a single change to the Javascript to change the URL in the JSON calls.
Not the end of the story…
This isn’t the end of the agent though. I have also added another function that makes a huge difference in the client that I didn’t have the ability to do before. I will cover that in my next post.