Notes on Mobile
Getting the URL parameters in a Domino Java web agent

In my dataProxy agent I needed an easy way to get all the URL parameters. I figured I would post that in a separate entry so it wouldn’t get lost.

	private HashMap getURLParameters() throws NotesException {
        String url = agentContext.getDocumentContext().getItemValueString("QUERY_STRING");

        String[] vals = url.split("&");
        HashMap m = new HashMap();
        for( int i = 0; i < vals.length; i++){
        	String[] p = vals[i].split("=");
        	if( p.length == 2 ){
	        	try {
					m.put(p[0], URLDecoder.decode(p[1],"UTF-8") );
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        	}        	
        }
		return m;
	}

I can then save them in a member of my agent as a HashMap like param.get(“db”) or param.containsKey(“db”) and can get at them where ever I am.

Notes documents as JSON

There are a couple of ways to do this online, but having a Java agent to do view JSON gave me a good opportunity to do it my own way. I have a couple things going for me at the moment - I don’t need rich text (yet), and I don’t need to be able to create or update documents (yet).

The ‘Document’ class in Java has a function called generateXML which has been around for a long time (5.0.3 according to the doc) so it should be relatively stable, but as with all things Domino… we shall see if it is in practice. That got me the DXL version of a Notes document. To get the JSON I went to github and got the org.json classes and imported them into a Java script library in Domino:

This has a lot of really handy classes in it, but what I really wanted was the XML class that has a toJSONObject method. With all these pieces, it was really easy to put the following method together:

    private void writeDocument(Database db, String documentId, boolean asJSON) throws NotesException, IOException, JSONException {
    	Document doc = db.getDocumentByID(documentId);
    	if( null== doc){
    		doc = db.getDocumentByUNID(documentId);
    	}
     	if( null== doc){
     		System.out.println("document not found");
     		return;
     	}
     	
		// write the document out
		java.io.Writer sw = new java.io.StringWriter();
     	doc.generateXML(sw);
     	sw.flush();     
		PrintWriter ao = getAgentOutput();
		if( asJSON ){
			ao.println("Content-type: application/json");
			ao.print(XML.toJSONObject(sw.toString()).toString());
		}
		else{
			ao.println("Content-type: text/xml");
			ao.print(sw.toString());
		}
     	ao.flush();
    }

I threw that into the dataProxy agent and added 2 URL parameters:

  • documentId= Lets me specify the document I want to access
  • outputformat=JSON Omitting this parameter keeps the output in DXL.

That’s it! With the next post I’ll show you how to integrate this into the mobile app.

Working around Domino

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.