JSSP["JavaScript Server Pages"] = keeping.simple.things.simple;

Dervish Description and Reference

Home   Downloads   JSSP Docs   JSSQL Docs   Dervish Docs   License   Examples   Feedback

Dervish is a small framework that allows you to execute JavaScript server side code by calling JavaScript functions in the browser. It completely hides all AJAX request handling behind an easy-to-use interface. In other words, Dervish is a way to do Remote Method Invocation (RMI) in JavaScript.

Dervish requires you to use JavaScript Server Pages (JSSP) on the server.

For a simple walkthrough please go to the Dervish Example. A quite comprehensive test page is provided in the JSSP distribution (follow the instructions in the JSSP tutorial). Some more sophisticated examples can be found in the dervish-examples.war file that is available from the download section.

To use Dervish you need to install JSSP on a Java-capable web server (a "servlet container"). See Quickstart for how to get started. More information about JSSP can be found in the JSSP documentation.

Bug reports, feature requests and forum posts can be submitted on the JSSP Project Page.

Note: This document uses Google's code prettifier. In Internet Explorer you may get warning messages about active content. You can safely allow active content or simply ignore these messages.

Table of Contents


Introduction
Quickstart
Download and installation
1. Code and install the server side library
2. Add the necessary files to your web application
3. Write the client side code
Reference
Initializing the Dervish library
Publishing objects
Retrieving published objects
Executing remote functions
Synchronous calls
Asynchronous calls
Errors
Results from the server
Freeing server side objects
Side effects of server side code
Passing parameters to the server
Precautions
Request pooling
Browser compatibility and known problems
 

Introduction

Remote Method Invocation (RMI), Remote Procedure Calls (RPC), and other technologies are available for many programming languages. They all work similarly: First you obtain a reference to a "remote object", then you call its functions or methods and transmit and receive data in the process. The transport layer takes care of properly wrapping and unwrapping the transported data and does some error handling for you.

Dervish is such a transport layer. It connects client side JavaScript and server side JavaScript and makes data transfer really easy. While it uses "AJAX" in the background it completely hides the complexities of request handling and data wrapping, letting you work with objects on the server almost as easily as with browser-side objects.

Dervish calls to the server can either be made synchronously or asynchronously. Asynchronous calls should almost always be preferred. You can pass parameters to the server and receive results. Because you are working with JavaScript on both ends of your application, the data structures you use remain the same, so you can streamline your code much more than if you were crossing language boundaries.

Dervish has some powerful features that allow you to use it with sophisticated applications:

Dervish consists of two parts: A client side JavaScript library and a server side libary integrated in JSSP.

Quickstart

The fastest way to get started with Dervish is the following:

Alternatively you can deploy the file dervish-examples.war on a servlet container like Tomcat. If you'd like to try this please follow the instructions in the JSSP tutorial.

The file dervish-examples.war can be opened with a zip-capable unpacker. It contains all the files you need to make a basic Dervish application.

Download and installation

Dervish is distributed as part of the JSSP project. You can download the current JSSP release from the download section. Unzip it and add the file jssp-*.jar to your web application's Java library folder. Add the following lines to the <web-app> element of your web.xml file:

 <servlet>
  <servlet-name>JSSPDefaultApplication</servlet-name>
  <servlet-class>net.sf.jssp.JSSPServlet</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>JSSPDefaultApplication</servlet-name>
  <url-pattern>*.jssp</url-pattern>
 </servlet-mapping>

This will run the JSSP servlet for all files ending in .jssp.

To use Dervish you have to do three things:

1. Code and install the server side library

The server side Dervish code has to be put into JSSP library files. The library files can be anywhere on your server. It is recommended to put them into a WEB-INF/jssp folder of your web application. This is an example for a Dervish library (WEB-INF/jssp/helloworld.jssp):

<%
// define the server object
function HelloWorld() {
    // this is the function we're going to call from the client
    this.sayHello = function() {
        return "Hello world!";
    }
}
// make the server object available to remote callers Dervish.publish("HelloWorld", new HelloWorld()); %>

See the JSSP documentation about library modules for details.

JSSP needs to load and evaluate the library on startup. You have to tell it about the library in the jssp.xml configuration file by adding a <module> section to the <jssp-servlet> element:

<module>
	<name>Dervish example library</name>
	<file>WEB-INF/jssp/helloworld.jssp</file>
</module>

See the JSSP documentation about the jssp.xml config file for details.

2. Add the necessary files to your web application

To use Dervish in the browser you have to load the client library (currently dervish_0.1.js) along with your HTML. You can put the file dervish_0.1.js in the root folder of your web application and include it like this:

<script type="text/javascript" src="dervish_0.1.js"></script>

On the server there's a special JSSP file that handles Dervish requests. Its name is dervish.jssp and it's a part of the JSSP distribution. Put this file in the root folder of your web application.

Note: You can disable Dervish by removing or renaming the file dervish.jssp from the web application.

3. Write the client side code

This example shows how to call the Dervish library object's functions:

<html>
<body>
<script type="text/javascript" src="dervish_0.1.js"></script>
<script type="text/javascript"><!--
    var HelloWorld = Dervish.getObject("HelloWorld");
    alert(HelloWorld.sayHello().execute());
//--></script>
</body>
</html>

The execute() function synchronously invokes the sayHello() function on the server that returns the string "Hello World!"

Reference

Initializing the Dervish library

After including the Dervish JavaScript code on the client side, you can initialize the Dervish client by overriding a number of parameters.

In a simple application it is not necessary to initialize the Dervish client as there are reasonable defaults provided. You would want to set parameters mainly if the Dervish client pages are not in the same folder as dervish.jssp or if you want to adjust the aysnchronous timeout.

You can initialize Dervish by calling the function Dervish.init:

Dervish.init({baseUrl: "..", timeout: 5000});

The init function takes a JavaScript object as its parameter. The example specifies an anonymous object with two properties: baseUrl and timeout.

The following properties are supported as parameters:

baseUrl: String. Specifies the base URL folder of the dervish handler script (dervish.jssp) relative to the currently loaded page. This URL can also be absolute, but this is not recommended (be aware that browser security settings may prohibit requests across different domains). The example specifies the parent folder as base URL. The default is the folder of the current page.

timeout: Integer number. The timeout in milliseconds for asynchronous Dervish calls. The default is 3000 (three seconds).

dervishHandler: String. The name of the script that handles dervish requests on the server. The default is /dervish.jssp. For Dervish calls, this value is appended to the baseUrl to determine the URL for the request. This value should always start with a slash (/).

pooling: Boolean. If set to false, disables request pooling. Request pooling is enabled for all browsers except Internet Explorer 6.0 (which doesn't support it properly).

Publishing objects

Before objects are available to clients they have to be published. Publishing means that an object is made available under a unique name.

Dervish objects are defined and published in JSSP libraries on the server.

Published objects serve as entry points to Dervish applications. Typically they provide a number of service methods that the client can use to obtain data from the server. They live in the global scope and are available to all clients at the same time. They may serve requests concurrently. This means that these objects should normally not manage state (i.e. have variables) themselves. An example from dervish_example.jssp:

<%
function CustomerLoader() {
var Customer = function(firstName, lastName, birthday) { this.firstName = firstName; this.lastName = lastName; this.birthday = birthday; }
this.loadCustomer = function() { // return example values return new Customer("Fred", "Smith", new Date("04/03/1968"));
} }
// make the CustomerLoader available to remote callers Dervish.publish("CustomerLoader", new CustomerLoader()); %>

The CustomerLoader is a function that contains a "private" inner function Customer. An instance of the CustomerLoader function (an "object") is published under the name "CustomerLoader".

The CustomerLoader object does not have any inner variables (it does not have a state). Its only function loadCustomer returns a new Customer object when it is called.

It is not recommended to have variables in published objects. In most cases it won't be necessary, either, because JSSP provides the application object to share state between sessions. If you decide to code against this advice please see the section Sharing common data on how to avoid concurrency problems.

Retrieving published objects

Clients can retrieve the published object of the above example by executing this code on the client:

var CustomerLoader = Dervish.getObject("CustomerLoader");

Of course, the retrieved object is not the "real" object on the server. That's not possible because the client runs in a different address space. The object that is returned is a "stub" that points to the server object's functions. These functions, for example the loadCustomer function, are now available to the client. They can't be called directly however; instead, the execute method has to be called on them.

getObject should always be called during the page is being loaded or shortly after that (e.g. in the body.onload event) because it uses a synchronous call to the server. Avoid to call getObject in other event handlers to minimize the risk that the application hangs due to a failed network connection or a server problem.

Executing remote functions

Server side functions of published objects are executed by an indirect call. First you have to call the function (with necessary parameters, if appropriate), then you have to call execute() on the result of that function. Depending on how you call execute() the call is done either synchronously or asynchronously.

Synchronous calls

In the above example the loadCustomer function of the CustomerLoader object can be called using this code:

var customer = CustomerLoader.loadCustomer().execute();

This is a synchronous call to the loadCustomer function. Program execution will stop on the client at this point (for some browsers, at least). The server will execute the actual code of the loadCustomer function and the program control will return to the client when the server is done. When the above line has been executed, the customer variable will have been assigned a Customer object - except if an error occurred.

Errors may occur if the server has become unreachable, there is a bug in the server side code or if an exception occurred on the server.

If there is a bug in the server side code an exception will be thrown on the client. The same is the case if an exception has been thrown on the server. The detail level of server side error messages can be adjusted by configuring the JSSP servlet.

If the server is unreachable or takes too long for processing the request, a timeout may occur on the client. Timeout handling for synchronous calls is not consistent between browsers. Firefox and Internet Explorer 6 seem to block forever. Opera, Safari and Konqueror seem to execute the request as if it was asynchronous. Synchronous calls, for this reason, do not behave in a reliable way.

Using synchronous calls is not recommended. Use asynchronous calls instead.

getObject uses a synchronous call. This is usually safe because, as pointed out above, it should be called only during or shortly after the page is being loaded. If the server or network goes down in between this short time the problem is not likely to affect many users.

Asynchronous calls

Calling server code asynchronously avoids the above problems. It also implements a reliable timeout mechanism (except for Konqueror).

Calling Dervish asynchronously is not very different from AJAX programming either directly or with other frameworks. You just need to pass a function to execute() that is called when the server is done:

 CustomerLoader.loadCustomer().execute(
	function(cid) {
		var customer = Dervish.getResult(cid);
		// fill in the form
fillForm(document.asynchronousForm, customer);
}
);

The passed-in function (which can be anonymous like in the example) must take a parameter cid (call ID) which identifies the Dervish request. This is important because more than one Dervish request may be active at a time.

If the server side function returns a result it can be retrieved by calling Dervish.getResult with the call ID cid. Note that you should always call this function even if you don't want to retrieve a result because this function implements error handling as well as request pooling. Calling it is important for Dervish's inner well-being.

Dervish.getResult may throw an error if a timeout occurs or if the server encountered a problem. The detail level of server side error messages can be adjusted by configuring the JSSP servlet.

It is usually a good idea to handle errors gracefully, e.g. like this:

 CustomerLoader.loadCustomer().execute(
	function(cid) {
		try {
			var customer = Dervish.getResult(cid);
		} catch (e) {
			if (e.message === "Timeout") {
				// timeout handling
			} else {
				// handle other error
			}
		}
		// fill in the form
fillForm(document.asynchronousForm, customer);
}
);

For larger applications it is better to move the catch handler to a separate function to centralize error handling.

The timeout for asynchronous calls is determined by the timeout property of the Dervish.init parameter. The default is three seconds. Timeouts in most JavaScript implementations are not guaranteed to occur in time, however. For an excellent discussion of JavaScript timeouts see this article: JavaScript Timers.

Errors

When errors occur during server side execution they are passed to the client.

In synchronous calls the execute function will throw an Error object.

In asynchronous calls the Dervish.getResult function will throw an Error object.

The message property of the Error contains the error text from the server. The configuration parameter verboseErrors of the JSSP servlet (see Configuring the JSSP servlet) determines whether detailed error information is sent to the client. If verboseErrors is true this includes error message, file name and line number. It is recommended to use this during development only. For production environments you should set verboseErrors to false.

Results from the server

The results of server side function calls are native JavaScript objects that can be used in the browser. These objects can either be elementary types such as strings, booleans, numbers or their "boxed" counterparts String, Boolean and Number. The result may also be null or undefined. The result may also be an array of arbitrary element types, or a complex data structure composed of multiple objects that can also contain cycles.

Functions and native Java objects cannot be transferred as Dervish results.

Dervish will take care of how to transmit the data structures from the server to the client. However, there are some important points to note for results that are objects:

  1. If the result contains functions it lives on in the user's session on the server until it is manually freed or the session ends.
  2. If the server side object does not contain functions it serves as a "container object" that is used only for data transmission. On the server it is thrown away immediately.

These two cases cover the most common scenarios:

  1. An object that has functions lives in the user's session and manages its state on the server. An example is a shopping cart object that can add or delete items at the client's request. By retrieving the object the whole content of the shopping cart is transferred to the client.
  2. Objects without functions are throwaway objects that do not maintain state on the server. Typically they represent some encapsulated data from another source, such as a database record. An example is the Customer object presented above which is essentially a combined structure of properties fit for transport.

You can also transport server side code to the client by returning objects whose functions start with a dollar sign ($). These functions are called dollar functions. Objects with dollar functions will be treated like container objects, i.e. they will not be retained on the server (unless they contain other functions without the $ prefix). You can call the dollar functions on the client as if they were native functions (which in fact, they are). No Dervish calls are involved when calling these functions.

This mechanism serves as a way to exchange code between server and client. JSSP provides a better way which should usually be preferred (see COMMON sections). However, dollar functions may be used when the function code is generated dynamically.

It is not advised, however, to use dollar functions on container objects that are transmitted in larger quantities because of the network load incurred. After all, the function code is transferred each time such an object is encountered. It would not be advised, e.g., to have a dollar function $toString on the Customer object of the above example. In this case it would be much better to define one CustomerPrinter object that contains the $toString function and transfer this once.

The dollar function support is somewhat experimental. If you can, avoid it and use COMMON sections instead.

Server side objects with functions are stored in the user's session. Dervish manages these objects using randomly generated keys. In order to be able to access such objects use the following pattern:

<%
function ShoppingCartManager() {

	var ShoppingCart = function() {
		this.items = [];
		this.addItem = function(item) { ...; return this; }
		this.removeItem = function(item) { ...; return this; }
		this.clear = function() {
			this.items = [];
			return this;
		}
	}

	this.getShoppingCart = function() {
		if (session.shoppingCart == null)
			session.shoppingCart = new ShoppingCart();
		return session.shoppingCart;
	}


	this.freeCart = function() {
		session.shoppingCart = null;
	}
}

Dervish.publish("ShoppingCartManager", new ShoppingCartManager());
%>

When the client calls getShoppingCart a ShoppingCart object is returned along with all items that it contains:

var shoppingCart = null;
ShoppingCartManager.getShoppingCart().execute(
	function(cid) {
		shoppingCart = Dervish.getResult(cid);
	});

The shopping cart object on the server contains functions, thus it is retained in the user's session. The code in getShoppingCart makes the object accessible in the session under the name shoppingCart. Of course, other JSSP pages or libraries can access this object and call its functions, too.

Freeing server side objects

The server side shopping cart object may be deleted by the client by calling:

Dervish.freeObject(shoppingCart);

Addidionally the client has to call freeCart to remove the reference in the session:

ShoppingCartManager.freeCart().execute(function(cid) {
		Dervish.getResult(cid);
		shoppingCart = null;
	});

The shoppingCart object on the server will then be eligible for garbage collection.

The Dervish.freeObject function can take either one object or an array of objects. The objects must be server side objects. The server will attempt to free all of the objects; if one or more objects can't be freed the server will throw an error.

Server side objects need only be freed if they contain large amounts of data that will impact the performance on the server or if lots of sessions are anticipated. When a session is terminated (this timeout depends on the web server's settings) all objects in it are automatically freed. It is, however, good practice to clean up after oneself (and to free the shopping cart in the example after the order has been processed).

Side effects of server side code

Server side code may have side effects. Suppose an object A that lives in the session has been transferred to the client. The client calls a server function that modifies A in the session but does not return A. Now the state of A on the client and A on the server has become inconsistent. The objects may not have the same content any more.

The programmer must take care that situations like these do not occur. This is not so hard as it may seem. You only have to take care that you always return all affected objects to the client. The easiest way to do this is to return a reference to this, as the clear() function does in the ShoppingCart example:

shoppingCart.clear().execute(function(cid) {
		shoppingCart = Dervish.getResult(cid);
	});

Thus you can make sure that you always have the most recent content of the shopping cart on the client.

Passing parameters to the server

You can pass in almost arbitrary data structures as parameters to the server side functions. These parameters can either be elementary types such as strings, booleans, numbers or their "boxed" counterparts String, Boolean and Number. The parameters may also be null or undefined. The parameters may also be arrays of arbitrary element types, or complex data structures composed of multiple objects that can also contain cycles.

Exceptions are functions and native browser objects like e.g. window, document, DOM elements, XMLHttpRequest objects and ActiveX objects. The behaviour it is not specified by Dervish as it is browser dependent what happens when you try to pass these objects.

Code is never transported from the client to the server for security reasons. No piece of code or data from the client is ever evalled on the server by Dervish, and you should also not do so in your application.

An example for a call with parameters:

var item = new Array("USB cable", 1);
shoppingCart.addItem(item).execute(function(cid) {
		shoppingCart = Dervish.getResult(cid);
	});

This example passes the newly created item to the addItem function. This function then returns the updated shopping cart.

Of course, you can also pass multiple parameters to functions. If you do not specify a parameter the value on the server will be undefined.

You can also pass objects as parameters that you have obtained from the server. If an object is stored in the session, it is updated on the server: The properties of the object on the server are overwritten with the properties of the passed-in object before the function call is executed. However, it is recommended to avoid situations like this as there are some corner cases that may not behave as expected.

Precautions

Do not use the out object to generate output in Dervish server side code. This would disturb the automatically generated response and most likely result in errors.

Request pooling

Dervish uses request pooling on the client side to avoid creating too many XMLHttpRequest objects. Not all browsers, notably Internet Explorer below 7.0, support request pooling, so for these browsers it is disabled by default.

You can explicitly disable request pooling by setting the init property pooling to false. This will be rarely necessary, however. Note that this can also slow down Dervish a bit and probably consumes more resources on the client.

Browser compatibility and known problems

Dervish has been tested with the following browsers:


Document version: 0.1, 2008-03-03. Author: Leo Meyer, leo_meyer@users.sourceforge.net

This file is a part of the JSSP project documentation at http://jssp.sourceforge.net.

The JSSP project is hosted by Sourceforge. SourceForge.net Logo