Overview

The Javascript binding, like other Etch language bindings, aims to allow developers a way to access Etch-based services in a manner which is as natural to the developer as possible. Ideally, this means that by simply looking at an Etch IDL, one should be able intuit how one would invoke the methods of such a service with understanding just a few simple conventions.

Since most developers speak code, let's look at some examples of what this really means for Javascript.

Consider a simple, server-directed add method defined in an Etch IDL file:

@Direction(server)
int add(int arg1, int arg2)

Then we consider how one would invoke that method in a Javascript script:

svc.add({
    arg1: 1,
    arg2: 2,
    onSuccess: function(returnValue)
    {
        alert ("Got a result: " + returnValue);
    },
    onError: function(error)
    {
        alert("failure");
    }
});

Using the above example, let's take a second to talk about how a method in an Etch IDL translates into a Javascript method.

The svc variable in the above snippet is the object which represents the client-side reference to the service. There are just a few lines of boilerplate service initialization not shown here, but all one needs to know is that every method associated with the Etch service is represented as a function on this svc object, such as the add method defined in the Etch IDL above.

The next convention in use here is that each argument is passed into the method as a named parameter. In this example, that's the arg1: and arg2:. If we had omitted arg1, arg2, or both, nulls would have been passed to the server for those omitted values.

Finally, every server-directed method support a onSuccess and onError argument, which expect you to pass a function. These functions serve as callbacks that are fired once the method has completed. The onSuccess callback is invoked if the operation completed successfully. Passed back as the only argument to this onSuccess callback is the return value of the method (if void is the return then no argument is passed). The onError callback is invoked if the operation did not complete successfully; for instance, if the method threw an exception, or if communication was interrupted between client and server on that method call. Note that we use callbacks instead of the return value of the svc.add method because Javascript is implemented as a single-threaded language in every browser. This thread is shared between the browser UI and the Javascript script code, so we must not block the browser waiting on a response from the server.

Ideals

There are a number of considerations one must make when dealing with browsers, Javascript, and the web in general. The ideals below represent the general mindset and direction of the binding. At this point in time, these ideals are not necessarily fully implemented.

  • No dependency on browser plugins – such as applets or Flash
  • Cross-browser support
  • Small Javascript script size – we don't want to ruin your user download experience
  • No Javascript type pollution – we don't add fields to native Javascript types, such as Object
  • Hide the transport – Etch is transport agnostic, so you should not see any HTTP requests/responses in your code. This is important; what if someday there is an alternate Etch Javascript implementation which uses Flash sockets? In such a case, ideally your code could use either transport with just a simple switch during initialization
  • Hide the encoding – Etch is encoding agnostic. There may be valid reasons to encode messages in XML, JSON, or something else over the transport, but your code should not be exposed to these technology choices
  • Client-directed messages must be supported – we have first implemented a COMET (alternatively known as hang-polling or long-polling) mechanism within the underlying transport to achieve low-latency, but it only makes sense to allow a web page to use polling instead, if great latency is not demanded
  • Support all popular browser-abstraction libraries – jQuery, prototype, and other libraries do a great job of abstracting away the complexities of browser-specific differences for a number of AJAX operations. These libraries are becoming indispensable in any rich web application, and ideally, this binding can detect which library is in use and use it, rather than this binding force you to include an entire library to your page when the one already in your page would work just as well
  • Small server-side footprint – to achieve this, we need to support leveraging your already-deployed web server technology, whatever that may be
  • Support any Etch service – This guided the use of XMLHttpRequest instead of <script> tag or iFrame requests, since an XMLHttpRequest can POST huge amounts of data, whereas <script>/iFrame requests can not send much data since they only support GET. This means that a server-side proxy is very likely required since XMLHttpRequest has cross-domain request restrictions, but, the overall solution has less restrictions on what types of services can be consumed by this binding without heartache
  • Namespacing must be supported – Etch is inherently namespaced, and maximum compatibility with all services require this binding be namespaced. When using the binding, it should be noted that namespaces are hidden from the developer as much as possible without sacrificing flexibility

Mapping Etch IDL to Javascript

Every Etch IDL construct should have a intuitive analogy in Javascript. This section details those mappings.

Server-directed method

Etch IDL

@Direction(server)
int add(int arg1, int arg2)

Javascript

svc.add({
    arg1: 1,
    arg2: 2,
    onSuccess: function(returnValue)
    {
        alert ("Got a result: " + returnValue);
    },
    onError: function(error)
    {
        alert("failure");
    }
});

Server-directed method that uses structs

Etch IDL

module etch.examples

service Calculator
{
    struct Values
    {
        int arg1,
        int arg2
    }

    struct AddResult
    {
        int addResult
    }

    @Direction(server)
    AddResult add(Values toBeAdded)
}

Javascript

var values = new etch.examples.Calculator.Values();
values.arg1 = 1;
values.arg2 = 2;

svc.add({
    toBeAdded: values,
    onSuccess: function(returnValue)
    {
        // returnValue instanceof etch.examples.Calculator.AddResult == true
        alert ("Got a result: " + returnValue.addResult);
    },
    onError: function(error)
    {
        alert("failure");
    }
});

Server-directed method with exception

Etch IDL

module etch.examples

service Calculator
{
    exception OverflowException
    {
         string message
    }

    @Direction(server)
    int add(int arg1, int arg2) throws OverflowException
}

Javascript

svc.add({
    arg1: 1,
    arg2: 2,
    onSuccess: function(returnValue)
    {
        alert ("Got a result: " + returnValue);
    },
    onError: function(error)
    {
        if(error instanceof etch.examples.Calculator.OverflowException)
        {
            alert("Overflow exception.  Message: " + error.message);
        }
    }
});

Client-directed method

Etch IDL

/** Example method  server tells client about some state change */
@Direction(client)
void stateChange(string username, boolean online)

Javascript

svc.callbacks.stateChange = function (username, online)
{
    // define your custom logic in this method, to indicate
    // what happens when the server invokes 'stateChange'
}

Client-directed method with a return

Etch IDL

/** Example method  server tells client about some state change, client acks it */
@Direction(client)
boolean stateChange(string username, boolean online)

Javascript

svc.callbacks.stateChange = function (username, online)
{
    // because stateChange defines a boolean return, we should return a value
    return true;
}

Client-directed method that defines an exception

Etch IDL

/** Example method  server tells client about some state change, client can throw exception */
module etch.examples

service Chat
{
    struct NotSupportedException
    {
        string reason
    }

    @Direction(client)
    boolean stateChange(string username, boolean online) throws NotSupportedException
}

Javascript

svc.callbacks.stateChange = function (username, online)
{
    // say we want to let the server know that this client does not support
    // the stateChange callback

    var notSupported = etch.examples.Chat.NotSupportedException();
    notSupported.reason = "Not currently supported.";

    throw notSupported;
}

Current State

  • Only one server-side implementation currently, built for C#/.NET using System.Net.HttpListener. SVN location: https://svn.apache.org/repos/asf/etch/branches/etch-javascript
  • Support for the following types: boolean, int, byte, short, long, string, List, struct, datetime
  • Exceptions are supported.
  • Notification is given on session "DOWN" and session "UP"
  • jQuery is supported.
  • Etch compiler successfully generates Javascript stub code (etch -b javascript)

Roadmap

  • Build up unit tests while also refactoring/cleaning up existing code
  • Work on scale for C# implementation (need to vet System.Net.HttpListener sooner rather than later)
  • Implement the following types: Map and Set
  • Implement onUnload handler logic to keep browser resource usage low
  • Implement cookie session handling (currently the session dies when window is closed)
  • Add support for another browser-abstraction library, such as prototype.
  • Implement Authorize on client.
  • Assuming C# implementation is stable and mostly complete, implement Java implementation (presumably using Jetty).