Login | Register
My pages Projects Community openCollabNet

Introduction: WTF is this all about?

The NAJA (Native Asynchronous JavaScript) is basically an RPC protocol that allows a quick and easy way to get things working between JavaScript and a NAJA enabled Server.

It abstracts all the comunication layer from applications built in it taking all the encoding/decoding job from the hands of the end-users (web-developers). Its serializers support complex data-types to be sent over the wire between the server and client, for that it uses a pseudo object model that extends the native javascript OO programming capabilites (interfaces, true inheritance, type casting, and others...).

It has a built-in cache that stores sent and received messages, allowing the client to recognize the server's message signatures avoiding repeated data being sent back and forth over the network.


Motivation: Why should I use it?

Nowadays there's a bunch of ajax driven web-applications, and also tons of out-of-the-box solutions that makes the job easier for the developers to build them. Most of then handles all the problem between the client and the server, but makes it usable only for a specific server type to use. While others only handles the JavaScript part, leaving all the services wrapping job for the developer to implement on the server side, witch can be rather a painfull job to do.

NAJA tries to please both ends, by using a server side descriptor (just like the WSDL does), and building the server proxies on the fly to the client side. Like this, the client developer (JavaScript) doesn't have to worry about changes on the server having to rewrite its code for the client, the client's code is updated automatically. On the other hand, the server developer doesn't have to rewrite-regenerate extensive WSDL documents, the server creates it on the fly and caches it automatically.

Both client and server can use several serializers, so a single client can talk to any server that supports one of its serializers. By default the client comes with the JSON serializer, so any server that supports it too, can talk with it. The idea behind this is to allow a same client code to be used on diferent servers, without a single line changed.

For now it supports only JSON and PHP serialization (on both ends), but other serialization methods should be supported in the future (like XML and others).

One of the issues that makes javascript quite a pain for beginners (and even for experienced) OO programmers to get along with is that it doesn't really have all the features of a true OO language. With this new Object Model, makes easier for OO programmers to dive in the JavaScript, as it looks more friendly for them. Class inheritance is supported, so the instances know their "family tree" and CAN call one of its parents method as his own, while keeping his methods untouched. Interfaces are also supported, with the declaration of "blank" methods that MUST be implemented by the end user. And along with some other nice features that I came up while I was coding it :).

With all this stuff, its easy for the client/server connection to send complex objects tru the wire (assuming that the class is declared on both ends). So if you got a bookstore software and wants to send a "Book" instance from the server to the client, its not necessary for you to dismantle the object and send it in pieces (its attributes in an array for example), just send the whole instance and let the NAJA do its magic. So the client will get an instance of a real Book, not just a bunch of data that the client application should know how to reassemble it.

This project was made with the purpouse to be simple, so it was kept simple. There is no need to use a specific library, or any special dependencies. So it could be easily plugged into any existing application without any load of pain (at least I hope so...). And it was build to work transparently on the two major browsers that are nowadays (Firefox and Internet Explorer), but with some more time (and hopefully some help) other browsers should be supported as well.


Tech Features: Some geek talk...

As mentioned before, there are some tricky stuff that happens behind the scenes to make all this magic work. We'll get a quick tour over them all to give you an idea of what goes on.

One of the top priorities of this project was to make it able to handle complex data-types (Objects) to be sent back and forth. Many of the scripting engines on the servers support these kind of data, but JavaScript painfully does (if we are allowed to say that it does). To fulfill that missing part, the Pseudo Object Model was created, allowing the developers to do Pseudo Object Oriented Programming (POOP, hehe I couldnt resist :P). Allowing JavaScript to handle and recognize different instances and types of user defined data (our written classes...).

So here an example of what a piece of POOP should look like:

function Book() {
	var $object = new $class("Book");

	$object.author  = null;
	$object.edition = null;
	$object.release = null;
	/* ... */

	$object.getAuthor = function() {
		return $this.author;
	}

	$object.setAuthor = function(author) {
		$this.author = author;
	}

	/* ... */

	$registerClass($object);

	return $object;
}

This is only an example of what a class declaration would look like, but we'll get into details later on. But (personally) I think this would be more friendly for an OO developer than something like this:

var Book = Class.create();
Book.prototype = {
	author:null,
	edition:null,
	release:null,
	/* ... */
	getAuthor:function() {
		return this.author;
	},
	setAuthor:function(author) {
		this.author = author;
	}
	/* ... */
};

Another nice feature that NAJA comes with is the ability to have multiple serializers. So like this we don't have to drop all the client code if for example we switch the server side, just change the serializer.

Say that now you have a PHP enabled server that serves an AJAX application, and you need to change your whole server side platform for a new almighty server that runs only Java applications (by some reason). Normally you would have to dump all you server and client coding and make it all from the scratch (either because the solution used in the older server could have the client side attached to the ajax engine or you have a "hand made" AJAX application, what in 99% cases its even worse to detach it). With this feature, you could save a lot of work on the client side, worrying about the server side basically.

Most AJAX applications makes the same request many times, and gets the same response every time from the server. With this in mind, NAJA has a built-in cache system that stores every received message in both ends, server and client. So when the client executes a request, first it sends only a message signature (an md5 hash) to the server. If the server recognizes the signature (already has it stored) then it starts processing the response for that message, taking out the job from the client to send the whole message at a time. The same deal happens when the server sends a message to the client, it first sends the response signature and if needed, the whole response message.

This feature might take a little longer than if it would send the message straight away. But on the long run, it should lower the traffic on the network (and naturally the response time from your application). But you still can configure the client without the use of its cache, sending the whole message every time.


Requirements: What do I need to get things running?

Well, for the client side I've tested it against Internet Explorer 6 and Firefox 1.5 and it seems to work fine on both of them.

For the server side, For now there's only support for PHP5 now, and I don't really plan to code this lib for others languages on my own. But at least that I can assure that pure XML serialization will be supported in future releases, aswell as SOAP support in some distant future.

It takes time to do stuff all alone you know... :/


QuickStart: Work! Move! Do something!

Here is the place where you might be looking for some action by this whole thing. By now, you can look things working following this link: http://porto.elin.com.br/~pablo/naja/. The source code of this simple example is explained step by step right here . So read on. I`ll try to make more complex examples later, like an ajax driven chat and some other cools stuffs.

First we need to setup our NajaServer. As we only have a PHP5 implementation of it, we'll just have to deal with it. The server itself differs from other web-services libraries by letting the server to handle not only one object as service handler, but many of them. Like this, in theory, you can register as many objects you wish to serve the remote calls. But first lets create a single class to handle all the requests. PS: Don't forget to give write permissions to web-server user on php/rpc/NajaCache/data/*

[file:rpc.php]

<?php
/**
 * Defines the path to the Naja sources for later includes
 */
$pathToNajaSrc = "/wwwroot/naja/src/";

/**
 * Includes the NajaServer library and the JSSerializer aswell (doesn't comes by default
 * inside the server)
 */
include_once($pathToNajaSrc."php/rpc/NajaServer.class.php");
include_once($pathToNajaSrc."php/serializer/JSSerializer.class.php");

/**
 * Our first service class, its a class just like any other on PHP :D
 */
class MyService {
	public function sayHelloWorld() {
		return "hello world!";
	}

	public function sayThis($pThis) {
		return "Saying ...\n".$pThis;
	}
}

/**
 * Create the NajaServer
 */
$server = new NajaServer();

/**
 * The PHPSerializer class already comes built in the NajaServer class itself
 * but we might need the JSSerializer for our client (witch is a javascript :P)
 */
$jsSerializer = new JSSerializer();
$server->addSerializer($jsSerializer);

/**
 * Now that we have our service class and our server initialized,
 * we need to register it inside the server
 */
$myService = new MyService();
$server->registerModule("myKillerService", $myService);

/**
 * Now that we have the server up and with a service registered
 * on it (under the name of "myKillerService"), we just need to trigger the
 * request handling...
 */
$server->handleRequest();
?>

Thats what we need on the server side to get our applications running, now on to the client side...

To get things running on the client side, we need to initialize our NajaClient class, for that we need to create it, register some serializers (just like the server side) and set the target URL for calls (where our 'rpc.php' should go. The javascript side, uses an "include" method that is used on the whole library. So to get things started, we need to call this main script like we would do with any other javascript, and from that point we must use this "include" statement. Its something like the "require" from the "dojo" toolkit, but with some neat constants to use within each file (like "%SCRIPT_FILE%", "%SCRIPT_PATH%" and among others). So here is the code:

[file:index.html]

<html>
	<head>
		<title>A simple naja call</title>
		<!-- we gotta call our naja engine -->
		<script type="text/javascript" src="[path_to_naja_source]/src/js/naja.js"></script>

	</head>
	<body>
		<script type="text/javascript">
			/**
			 * Here we call our external script
			 * (we don't want a bunch of JS code within our clean HTML right?)
			 */
			$include("index.js");
		</script>
	</body>

</html>

Heres is where the magic happens :)

[file:index.js]

/**
 * The %SCRIPT_PATH% holds the string of the exact location of this file on the server, so we can
 * use it to include other files relative to this one
 */
var pathToNajaSource = %SCRIPT_PATH%+"naja/src/";

/**
 * Now we include the NajaClient library along with the PHPSerializer (we will need it for this example)
 */
$include(pathToNajaSource+"js/rpc/NajaClient.class.js");
$include(pathToNajaSource+"js/serializer/PHPSerializer.class.js");

/**
 * Now we create our NajaClient instance and set up the URL for our just created server
 */
var client = new NajaClient();
client.setEndPoint(%LOCAL_PATH%+"rpc.php");

/**
 * As said before we will need a PHPSerializer to deal with PHP Serialized data from the server
 */
var phpSerializer = new PHPSerializer();
client.addSerializer(phpSerializer);

/**
 * Having the client targeted to our server and with its serializers ready to run,
 * we initialize our client.
 * This action should make a request for our server asking the registered encodings on the server
 * ("PHP" and "JS" on our case) and its registered "modules" with its "methods".
 * The encoding says witch serializers does the server supports and the "modules" are simply our
 * service classes registered on our server ("myKillerService" in our case is the only module available).
 */
client.initialize();

/**
 * With this, we've just created all proxies within the NajaClient (well, just one to be honest :P).
 * From now, we just to get it out of our client using the 'getModule' method, and use it as it were a
 * local object, as it follows.
 */
var myKillerService = client.getModule("myKillerService");

/**
 * Each module that is created on the client has to modes for calling its methods: synchronously and
 * asynchronously. For each one of them, a diferent method on the module is created. The asynchronous mode
 * takes the original name from the server, and the synchronous mode takes the name from the server with
 * a "Sync" suffix on it.
 *
 * So say that a module has a method called "doIt" on the server, it would have two methods on the
 * client side: "doIt" and "doItSync", for asynchronous and synchronous modes respectively.
 *
 * With this in mind, lets first experience the synchronous methods (as its easier to understand...)
 */

var helloWorldResponse = myKillerService.sayHelloWorldSync();
alert(helloWorldResponse);

/**
 * ... and now the other one with some parameters ...
 */

var sayThisResponse = myKillerService.sayThisSync("Something!");
alert(sayThisResponse);

/**
 * Quite easy huh? Now lets get some asynchronous calls.
 * The async calls behave a bit different from sync ones it doenst wait for its return, so we have to
 * prepare a call-back function to handle its response. To set up the call-back function within a
 * request, we need to inform an extra parameter to the method at the end of its parameter list. So if a
 * method has two parameters, the third one would be our call-back function for it, if it has none then
 * our only parameter would be the call-back function.
 * Now back to the code
 */

/**
 * Here is our generic call-back function, the responses are always passed as the first parameter and
 * our "pReturn" will receive its value.
 */
function myCallBackFunction(pReturn) {
	alert(pReturn);
}

/**
 * Now we actually make the call with our call-back function as last parameter (first and only in this
 * particular case)
 */
myKillerService.sayHelloWorld(myCallBackFunction);


/**
 * ... following by the second async call, again with our call-back function as last parameter...
 */

myKillerService.sayThis("Something else!!!", myCallBackFunction);