Wednesday, March 11, 2009

Can we talk...

I get to spend a lot of my time (most of my time actually) working on web apps.

Nowadays I use Geronimo for my server, Firefox for my browser, and Dojo for the two to talk to each other.

Last time (actually, the time before that) I showed how I parse the XML that I am sending from the browser to the server. But, I did not show how that XML gets created.

Well, since I like to make things simple for myself, I wrote a couple of wrapper functions to abstract the Dojo functions so that I would not have to rewrite large blocks of code if the syntax changes in Dojo (which it has done since I started). Also, having a single way to send messages to my server means that I can have a consistent way of parsing those messages.

I broke the sending of messages into two parts:
  1. Building the message
  2. Sending the message and specifying the callback
And go figure, there is a JavaScript function (that I wrote) to do each of those things.

For building the message, I created a function called 'queueCommand'. The idea was that you could create several message queues that all needed to be sent to the same servlet (and whose output would be consumed by the same callback function). I actually set up a number of functions and made it possible to build several message queues - but for simplicity, we'll pretend there is only one at a time.

Here is queueCommand:
function queueCommand(command) {
var commandXML = encapsulateCommand(command);

if (window.commandQueue === undefined) {
window.commandQueue = [];
}

window.commandQueue[window.commandQueue.length] = commandXML;
}

Pretty simple right? Ooops! You caught me. There is a third method that I neglected to mention. The encapsulateCommand function tries to remove characters that would cause the generated XML to be invalid as it puts together a simple (and standardized for my purposes) XML snippet.

Here is encapsulateCommand (with a helper function):
function encapsulateCommand(command) {
var nodes = "";

for (i in command) {
nodes += "<" + i + ">" + escapeString(command[i]) + "";
}

var commandXML = "" + nodes + "";

return commandXML;
}

function escapeString(inputData) {
var outputData = inputData;

if (typeof inputData == 'string') {
if (inputData != null && inputData != "") {
outputData = inputData.replaceAll("& ", "& ");
outputData = outputData.replaceAll("<", "<"); outputData = outputData.replaceAll(">", ">");
outputData = outputData.replaceAll("\"", """);
outputData = outputData.replaceAll("\\", "~1~");
outputData = outputData.replaceAll("%", "~2~");
outputData = outputData.replaceAll("\'", "~3~");
outputData = outputData.replaceAll("\n", "~4~");
}
}

return outputData;
}

And here is an example of how you would call it:
var pushCommand = queueCommand({
action: "doSomething",
fieldValue: dojo.byId('fieldID').value
});

I don't actually send back a return value. But, I do capture the result in a variable because if something goes wrong - receiving the result into a variable prevents the error from stopping program execution. If I were being more diligent, I would send back an actual result status (or maybe even the assembled command) - But I didn't do that.

So, when the above command (var pushCommand = ...) is executed, the following XML snipped gets added to the queue (shown here 'pretty'):

<command>
<action>doSomething</action>
<fieldValue>SomeValue</fieldValue>
</command>

The pushQueue function bundles up all of the commands that have been placed into the queue inside of a proper XML header and a 'envelope'.

Here is pushQueue:
function pushQueue(url, onLoad) {
var queue = window.commandQueue;

if (queue == undefined) {
window.commandQueue = [];
queue = window.commandQueue;
}

var xmlDoc = "";

xmlDoc += "";
xmlDoc += "";

for (var i = 0; i <>";

window.commandQueue = [];

var parser = new DOMParser();

var xmlDocument = parser.parseFromString(xmlDoc, "text/xml");

var ajax = dojo.rawXhrPost({
url: url,
postData: xmlDocument,
load: onLoad,
headers: {
"Content-Type": "application/xml"
},
handleAs: "xml"
});
}

You would call pushQueue with the URL of the servlet (or whatever resource is going to handle the message) and the callback function that should get the result.

Here is an example of calling pushQueue:
var sendTheMessage = pushQueue("/Handler", callBackFunction);

After that call, Dojo would send the following XML document to '/Handler':

<?xml version='1.0' encoding='utf-8' ?>
<commands>
<command>
<action>doSomething</action>
<fieldValue>SomeValue</fieldValue>
<command>
<commands>

Now, just in case you were wondering, there is nothing special about the 'command' and 'commands' tags that I used. They are really entirely arbitrary, but they make sense for what I am sending (a list of commands, each or which is a command).

And, when the result is sent back from 'Handler', it will send the result to my JavaScript function called 'callBackFunction'.

So, with a couple of functions placed in a JavaScript file that is included on all of my pages, I am able to have a standard way of sending messages to my server. And because the format of the messages is the same every time, I can use the same XML parsing code (shown on a previous post) to extract the information.

No comments: