Distigme

A little learning is a dangerous thing.


Leave a comment

SMD (Service Mapping Description)

A decade ago, XML web services would describe themselves using the WSDL standard, which tools could use to generate a strongly typed and convenient wrapper for calling the service, such as a Java class. Fast forward halfway to today, and the challenge has moved on from accessing XML services from the server side to accessing JSON services from the browser.

Not that it’s ever been too difficult. JSON first became popular because it was already valid JavaScript (though that fact was only relevant to those willing to call the evil eval()). And JavaScript is dynamically typed. So calling a simple ajax method in modern Dojo might look like this:

require(["dojo/request"], function(request) {
	request.get("/ajax/comments/", {
		handleAs: "json",
		query: { id: 123 }
	}).then(function(result) {
		console.log(result.length + " comments.");
	});
});

JQuery code tends to have these direct ajax calls scattered everywhere. They look similar:

$.ajax({
	url: "/ajax/comments/",
	dataType: "json",
	data: { id: 123 }
}).then(function(result) {
	console.log(result.length + " comments.");
});

But the burden can be still lighter. What exactly was the URL? Does it go like comments/123 or comments?id=123? Was it a POST or a PUT? What if I forget a parameter that the server requires?

So, the natural evolution is to start writing wrapper functions in a new module:

define(["dojo/request"], function(request) {
	return {
		getComments: function(id) {
			return request.get("/ajax/comments/", {
				handleAs: "json",
				query: { id: id }
			});
		}
	};
});

Now the caller can be simplified:

require(["my/service"], function(service) {
	service.getComments(123).then(function(result) {
		console.log(result.length + " comments.");
	});
});

That’s progress, but it’s somewhat expensive progress, and you may question whether it’s really worthwhile for a method you only call once.

Enter SMD. Just write a JSON object (the SMD) that describes the API of your service, and Dojo can generate the methods.

define(["dojox/rpc/Service"], function(RpcService) {
	var smd = {
		"SMDVersion": "2.0",
		"id": "http://www.example.org/ajax/",
		"description": "Example Service",
		"target": "/ajax/", 
		"envelope": "URL",
		"services": {
			"getComments": {
				"target": "comments",
				"transport": "GET",
				"parameters": [
					{ "name": "id", "type": "integer" }
				]
			},
	return new RpcService(smd);
});

This example shows the SMD embedded in a Dojo module, but you can also put it in a separate file.

You can imagine that if a team has one Java or PHP expert writing the API and one JavaScript expert consuming the API, a single file to define the interface is a big help. If the back-end guy needs to change a URL, instead of sifting through the mountain of unfamiliar JavaScript and changing every ajax call, he just changes it in one place in one file. The front-end guy, meanwhile, can look at the SMD and see at a glance what services are available.

Better yet, it should be possible to generate the SMD from, say, a Spring application. I’m not aware of that having been done yet, though. But Struts seems to have something.

In fact, SMD seems to have not yet caught in the five years since Dojo began to support it. There is an old post on SitePen introducing it, and then… not a peep. The Dojo Toolkit now has some basic documentation on dojox/rpc (note that dojo/rpc is legacy), along with the SMD 2.0 proposal, but that’s about it. In contrast, JSON Schema, on which SMD is built, seems quite active. The lack of resources on modern SMD is what motivated me to write this post.

SMDs for a few popular sites (Google, Wikipedia, Twitter) are included in dojox/rpc/SMDLibrary. Naturally, since you would use these in a cross-domain scenario, they all go over JSONP.

Here is a longer example:

define(["dojox/rpc/Service", "dojox/rpc/Rest"], function(RpcService, RpcRest) {
	var smd = {
		"SMDVersion": "2.0",
		"id": "http://www.example.org/ajax/",
		"description": "Example Service",
		"target": "/ajax/", 
		"envelope": "URL",
		"services": {
			"setPassword": {
				"target": "my-password/",
				"transport": "POST",
				"parameters": [
					{ "name": "passwordOld", "type": "string" },
					{ "name": "password", "type": "string" }
				],
				"returns": { 
					"type": "object",
					"properties": {
						"isValidPasswordOld": { "type": "boolean" }
					}
				}
			},
			"status": {
				"target": "status",
				"envelope": "PATH",
				"transport": "GET",
				"parameters": [
					{ "name": "id", "type": "integer" }
				],
				"returns": "string"
			},
			"comment": {
				"target": "comment/",
				"transport": "REST",
				"parameters": [
					{ "name": "id", "type": "integer" }
				]
			}
	};
	return new RpcService(smd);
});

Here we see the setPassword method as a POST, returning a complex object. The status method builds a URL from its parameter like /ajax/status/123 and returns some text. comment is actually a REST endpoint supporting multiple methods (we have to explicitly require dojox/rpc/Rest for this to work). So now we can make calls like these:

require(["my/service"], function(service) {
	service.setPassword("old", "new");
	service.setPassword({ passwordOld: "old", password: "new" });
	service.comment.delete(86);
	service.status(42).then(function(status) {
		alert(status);
	});
});

You can call service methods two different ways, as illustrated here by setPassword: you can pass the parameters in order, the same order specified in the SMD, or you can pass an object that maps parameter names (if you named your parameters in the SMD) to their values, which is handy for when you can load such an object from a form, for example.

Every service method returns a promise, so you can and should handle the states of pending, succeeded, and failed, and you need then() or when() to access return values once they arrive.

SMD is pretty straightforward to use and has clear benefits for structuring your code on a large project. Better still, you can adopt it gradually, using SMD-generated services in some places and direct ajax calls in others. There is also potential for tools to make use of SMD in other ways (e.g., a handy test bench for manually invoking methods), but I don’t expect much of these until SMD sees wider adoption.

Advertisements


Leave a comment

Serving Dojo from Spring

If you have a project using Dojo (or any comparable framework), you have a few options for how to serve Dojo itself, including:

  1. Use the Google CDN. This is the most straightforward and a great place to start. You might even hope for caching efficiencies by using such a common CDN, but in practice this does not work out so well. And if you do development from somewhere with a flaky WiFi, every page reload is a spin of the revolver.
  2. Use Dojo’s sweet build system to package up all the JavaScript+CSS that your site needs. This approach can be a big win, but you have to figure out how to set everything up in your automated build system.
  3. Use Maven to pull in the Dojo WAR. You can then deploy this separately or integrate it via an overlay. Then you discover the joy of waiting for m2e to unpack 8,000 Dojo files over and over again.

There is a another very convenient method that I have never seen suggested but have found to work quite well.

There is a middle ground of serving the pre-packaged Dojo Toolkit yourself, without going through a WAR, and without ever unpacking everything on disk. First, download the ZIP, rename the extension to .jar, and put it in your WEB-INF/lib. Then, in Spring, you can map to this JAR via the classpath:

<mvc:resources 
    mapping="/dojo-1.8.0/**" 
    location="classpath:dojo-release-1.8.0/" 
    cache-period="31556926" />

When someone requests from your site, say, /dojo-1.8.0/dojo/dojo.js, Spring will use the first part of the path to map into your zip-renamed-as-jar, then find the folder and file within the ZIP itself, and serve it directly.


1 Comment

Returning a value from a function with AMD

These days I’m still getting used to AMD in Dojo. It’s a shift in thinking, that whenever you want to use a module, all you can really do is schedule something to be executed when the module finally gets loaded, quite possibly millions of nanoseconds in the future.

This pattern fundamentally breaks the request-response paradigm inherent in calling a function and getting its return value. Let’s say you have some old code like this (pretend it’s not trivial):

function getLogoTitle() {
    var logoNode = dojo.byId("logo");
    return logoNode.title;
}
function displayWelcome() {
    alert(getLogoTitle());
}

Now, you go to convert this to modern AMD style, and a first pass looks like:

function getLogoTitle() {
    require(["dojo/dom"], function(dom) {
        var logoNode = dom.byId("logo");
        return logoNode.title;
    });
}
function displayWelcome() {
    alert(getLogoTitle());
}

This will not work, and it may not be obvious why. The return is now returning from the inner anonymous function rather than getLogoTitle(), and the latter now returns nothing. If you move the return outside the require, it doesn’t have access to logoNode, and if you move that declaration outside the require, like this:

function getLogoTitle() {
    var logoNode;
    require(["dojo/dom"], function(dom) {
        logoNode = dom.byId("logo");
    });
    return logoNode.title;
}
function displayWelcome() {
    alert(getLogoTitle());
}

You can still get nothing, since the assignment happens in the future. This whole approach is fundamentally flawed, since you are trying to use at present a value that will not exist until the distant future.

The way to fix that is to schedule what you want to do with the value once it is obtained. But that stuff you want to do is actually back in the calling function displayWelcome, so you need to coordinate with the caller via a callback.

The simplest solution is to restructure like this:

function getLogoTitle(callback) {
    require(["dojo/dom"], function(dom) {
        var logoNode = dom.byId("logo");
        callback(logoNode.title);
    });
}
function displayWelcome() {
    getLogoTitle(function(title) {
        alert(title);
    });
}

As your needs grow more complex, you will appreciate the more full-featured promise-based solution offered by Deferred. The tricky part can be accessing the Deferred module itself. One crude way is to take it as a parameter:

function getLogoTitle(Deferred) {
    var result = new Deferred();
    require(["dojo/dom"], function(dom) {
        var logoNode = dom.byId("logo");
        result.resolve(logoNode.title);
    });
    return result;
}
function displayWelcome() {
    require(["dojo/Deferred", "dojo/when"], function(Deferred, when) {
        when(getLogoTitle(Deferred), function(title) {
            alert(title);
        });
    });
}

This when style accommodates both functions that return values directly and functions that return promises. So, rather than rely on knowledge of which type of function you are calling, a good habit is to rely on when for handling return values.

Now, passing in the Deferred module is ugly. There are ways to load it synchronously in the global scope, but the preferred solution is to wrap a function like getLogoTitle in a module. If both of the function and its caller are already in a module together, you’re all set. If the caller is somewhere else, though, the idea is to expand its require to include the module, so that by the time the callback executes, Deferred and any other modules needed immediately by getLogoTitle have already been loaded and hooked up and can be used directly. But writing a module involves breaking code out into another file, so the time to do that is when you your code is complex enough to benefit from separate modules.

There is a shortcut, though, which is to make a simple promise without using Deferred. Basically, you return an object with a then() function:

function getLogoTitle() {
    return {
        then: function(callback) {
            require(["dojo/dom"], function(dom) {
                var logoNode = dom.byId("logo");
                callback(logoNode.title);
            });
        }
    };
}
function displayWelcome() {
    require(["dojo/when"], function(when) {
        when(getLogoTitle(), function(title) {
            alert(title);
        });
    });
}

The flow goes like this:

  1. Let’s start with displayWelcome() having loaded the when module.
  2. getLogoTitle() is called.
  3. getLogoTitle() immediately returns a promise object containing an embedded then function.
  4. when is called with this promise and with another anonymous callback function.
  5. when sees that its first parameter has a then function and calls it, passing the callback as a parameter.
  6. The then function begins executing.
  7. The require starts off an asynchronous module load. But is the module already loaded?
    • If already loaded, proceed directly to the callback in the next step.
    • If not, register the callback and return immediately, unwinding the stack. If, later on, the module loading ever finishes, then the loader will call the callback in the next step.
  8. The require callback, which is the anonymous function(dom), executes, using the now-loaded dom do to its business.
  9. Having done its business, it passes its result to yet another callback, the anonymous function(title).
  10. At last, this callback has the return value from the original code, and it proceeds to display the alert.

It seems a little convoluted, with functions flying everywhere, but if you can get your head around it, you will have a solid grasp of asynchronous JavaScript.