Skip to main content

An (Almost) CPU-Free MVC Pattern with Ajax

June 21, 2007

{cs.r.title}



The Web is switching slowly from the mainframe computing
paradigm (static HTML pages generated on the server) to a high-performance computing (HPC) paradigm, where the computing power is
distributed along the network. This shift gained its momentum with
the explosion of Ajax applications.

Ajax is an exciting technology. However, programming Ajax
means hard work, mainly because of the asynchronous programming
model and XML parsing between different environments or programming
languages. Ajax helped the Web to be faster by reducing the CPU
load on the servers for tasks that are mostly cosmetic and have
almost no interference in the application business logic.

A logical next step is to use more of the CPU power of web
clients, making the dull web browser into an more powerful fat
client. We already share the control logic between the server and
the client; how hard would it be to also share the views and even
the models?

The answer is, not that hard! To solve the model sharing,
all that is needed is a simple mechanism to get the data from the
server into the client in a transparent way (and this is what Ajax
is all about). The view problem can be addressed within the web
browser itself; today's browsers include an XML Stylesheet
Processor (XSLT). XSLT is used on the server side to generate
dynamic content; we can relieve the server of that job if we do it
on the client side. To control all of this, we need some
programming language, and again inside our browsers' JavaScript has
been a standard feature for quite a long time.

It is clear we can do it, but why we should do it is
the next question to arise. Everything works fine with the
current paradigm, so why make a change? To me, it is clear that this
change should be made for the same reasons we started using Ajax in
the first place: web applications were slow, suffered from network
latency, etc. Ajax brought the data layer to the web browser;
now we bring the layout layer to the web browser, too. By doing this,
we get a clear separation of code and data, and simplify the code
necessary for displaying a view in the browser by using XSLT
transformations in a template instead of a big HTML file.

From here we have less code, easier deployment, and easier
maintenance of the application. Surely we will have a win-win
scenario. The developer wins by having to code less, saving time
and money, and the user wins with faster response times and less
network data to transfer.

The RAJAX Project

RAJAX is a project
that set out to test this idea. At its heart it is a
Java library, a tag library, and two JavaScript files. The tag library
with the Java library will address the Model problem just
described. It works by using runtime reflection to take any class
that extends the RAJAX base class and transform it into an
Ajaxified JavaScript object. For example, consider the following
Java class:

[prettify]package demo;

import java.sql.*;
import java.util.*;
import javax.naming.*;
import javax.sql.DataSource;
import net.java.rajax.RAJAX;

public class ShoppingItem extends RAJAX {
    
    private DataSource ds;
    
    /**
     * Creates a new instance of ShoppingItem
     */
    public ShoppingItem() {
        try {
            Context initContext = new InitialContext();
            ds = (DataSource) initContext.lookup(
                    "java:/comp/env/jdbc/rajaxDB");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
    }
    
    @net.java.rajax.RAJAXMethod
    public Map findById(String id) {
        try {
            Connection conn = ds.getConnection();
            PreparedStatement pstmt = conn.prepareStatement(
                    "SELECT name, description, price FROM " +
                    "items WHERE id = ?");
            
            pstmt.setString(1, id);
            ResultSet rs = pstmt.executeQuery();
            
            Map item;
            if(rs.next()) {
                item = new HashMap();
                item.put("name", rs.getString("name"));
                item.put("description",
                        rs.getString("description"));
                item.put("price", rs.getString("price"));
            }
            pstmt.close();
            rs.close();
            conn.close();
            
            return item;
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return null;
    }
}
[/prettify]

RAJAX will generate the following corresponding JavaScript
object:

[prettify]function ShoppingItem() {
    var sU=null;
    var sP=null;
    var cN='ShoppingItem';
    var oN='/rajax/demo.ShoppingItem.rajax';
    this.findById = function(p0) {
        if(typeof(this.findById_Callback) == 'undefined')
            this.findById_Callback=XMLHttp.defaultCallback;
        if(Rajax.isString(p0)) {
            var u=oN+'?m=findById';
            var p='p='+Rajax.toJSON(p0,true);
            XMLHttp.exec(u,p,false,sU,sP,this.findById_Callback);
        } else
            this.testInt_Callback('Bad Request',400);
    };
}
[/prettify]

There is no need to write configuration files; all the developer
needs to do is create a Java class that extends RAJAX, and every
public method with a
@net.java.rajax.RAJAXMethod annotation will be mapped
to a method with the same signature in the generated JavaScript
object. The original code is a simple POJO; this helps the
developer to focus on the problem and avoid other concerns. Also,
since we are handling POJOs, testing is easier (using
JUnit for example), since there is no need to deploy
the code before testing. Testing can start at the same time we are
coding our RAJAX objects.

Note that the generated JavaScript object makes use of two
JavaScript objects: Rajax and XMLHttp.
Starting from the end, XMLHttp is a simple abstraction
of the XMLHttpRequest object that will make it easier
to expand the library to other browsers. At the moment, this
object has been tested with success on Microsoft Internet Explorer
6, Microsoft Internet Explorer 7, Mozilla Firefox 2, and Opera 9.
With time and other platforms to test, the browser platform is
supposed to be expanded. The Rajax object contains some
helper functions to cast variables and encode and decode JSON messages
back to and from the back-end server.

These abstractions may seem like re-inventions of the wheel,
with other projects like "http://dev.abiss.gr/sarissa/">Sarissa, which is about 28K of
source code (62K with extras). RAJAX keeps it simple with only the
basic features within 12K (21K with the MVC framework).

One of the goals of the project was to write an Ajax library that
would require no configuration. While this is somewhat true, there is
one thing that needs to be configured; you need to instruct your
JSP/servlet container to handle the RAJAX requests. This is done
using a special Servlet in the webapp.
This servlet will be a dispatcher that will map a request to a Java
object and if (and only if) that object is an instance of the superclass net.java.rajax.RAJAX, it will instantiated and
the requested method executed. This is what the web.xml
file should look like:

[prettify]<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
            http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>RajaxDispatcher</servlet-name>
        <servlet-class>net.java.rajax.SimpleServletDispatcher</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>RajaxDispatcher</servlet-name>
        <url-pattern>*.rajax</url-pattern>
    </servlet-mapping>
    ...
</web-app>
[/prettify]

Implementing a Model-View-Controller (MVC) Pattern

The title says this pattern is "almost" CPU-free. "Almost"
because RAJAX will implement it entirely on the client browser, so
no CPU is used on the server for that computation. There are six
classes available to the developer (Figure 1).

<br "Object Model of the JavaScript MVC Framework" width="450" height="806" />
Figure 1. Object model of the JavaScript MVC framework

Of these classes, the developer typically only cares about
three:

  • Nimble: The main MVC class. It is called "nimble"
    because it tries to be as simple as possible.
  • NimbleModel: The model representation for the
    MVC.
  • NimbleView: The view representation for the MVC,
    with methods to render in the browser and other important functions
    such as event handling.

Putting Everything Together

As with any software program, the application needs an entry
point. In the case of RAJAX, it is a HTML page. In the above
example, a simple display/edit ShoppingItem is to be implemented.
The user enters the application and sees the shopping item in the
display view (Figure 2).

Display view
Figure 2. Display view

Once the edit view is clicked, a form will enable its editing
(Figure 3).

Edit view
Figure 3. Edit view

When switching back to the display view, the data will reflect
the changes (Figure 4).

<br "Display view with updated content" width="432" height="406" />
Figure 4. Display view with updated content

The application entry HTML is just a plain JSP page with the
inclusion of the MVC tag library:

[prettify]&lt;%@page contentType="text/html" pageEncoding="UTF-8"%&gt;
&lt;%@ taglib uri="http://rajax.dev.java.net" prefix="rajax" %&gt;
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd"&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;JSP Page&lt;/title&gt;
        &lt;rajax:object className="demo.ShoppingItem" /&gt;
        &lt;rajax:mvc controller="/js/controller.js" /&gt;
    &lt;/head&gt;
    &lt;body onLoad="Rajax.enableThrobber(Rajax.THROBBER_IMG);
            startUp()"&gt;
        &lt;h1&gt;RAJAX Nimble MVC&lt;/h1&gt;
        &lt;div id="content"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
[/prettify]

The display view is a simple XML stylesheet file:

[prettify]&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

&lt;xsl:template match="Item"&gt;    
&lt;div&gt;
    &lt;h3&gt;&lt;xsl:value-of select="name" /&gt;&lt;/h3&gt;
    &lt;p&gt;&lt;xsl:value-of select="description" /&gt;&lt;/p&gt;
    &lt;p&gt;&lt;em&gt;Price: &lt;xsl:value-of select="price" /&gt;&lt;/em&gt;&lt;/p&gt;
    &lt;p&gt;&lt;a href="#" id="editLink"&gt;edit&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match="text()" /&gt;
&lt;/xsl:stylesheet&gt;
[/prettify]

The edit view is another simple XML stylesheet file:

[prettify]&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

&lt;xsl:template match="Item"&gt;    
  &lt;form method="post" action="#" class="editForm"&gt;
    &lt;h3&gt;&lt;input id="Item/name" type="text" value="{name}" /&gt;&lt;/h3&gt;
    &lt;p&gt;&lt;textarea id="Item/description"&gt;&lt;xsl:value-of
        select="description" /&gt;&lt;/textarea&gt;&lt;/p&gt;
    &lt;p&gt;&lt;input id="Item/price" type="text" value="{price}" /&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;a href="#" id="displayLink"&gt;display&lt;/a&gt;
      &lt;input type="submit" value="ok" /&gt;
    &lt;/p&gt;
  &lt;/form&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match="text()" /&gt;
&lt;/xsl:stylesheet&gt;
[/prettify]

Earlier in this article, we defined a model with the RAJAX
objects and a view using the XSLT stylesheets. To complete the MVC
framework, only the business logic is missing. The controller is a
JavaScript file as stated before; if one pays particular attention
to the base application HTML, there was an extra JavaScript script,
/js/controller.js. This script is where all the business
logic is: it will handle all the event handling from the view,
execute actions against the model, and finally update the view.

Before the controller can handle the events, one needs to attach
them to the view. To do this, RAJAX uses the Observer pattern, since
it is also related to the way browsers handle events on the
Document Object Model. The code for our example
is:

[prettify]var data,view,edit;

function startUp() {
    // load the XSLT files
    view = Nimble.loadView('views/display.xsl');
    edit = Nimble.loadView('views/edit.xsl');
    // RAJAX will get the data from the DB
    data = (new ShoppingItem()).findById('tomato soup');

    // attach the event handlers
    view.attachEvent('editLink', 'click', function () {
        edit.render('content', data);
    });

    edit.attachEvent('displayLink', 'click', function () {
        getData();
    });

    // render the initial view
    view.render('content', data);
}

function getData() {
    view.updateModel(data);
    view.render('content', data);
}
[/prettify]

The attachEvent function takes three arguments: the
first is the DOM element to which the MVC framework will attach the
event; the second is the event type, (one of blur,
focus, contextmenu, load,
resize, scroll, unload,
click, dblclick, mousedown,
mouseup, mouseenter,
mouseleave, mousemove,
mouseover, mouseout, change,
reset, select, submit,
keydown, keypress, keyup,
or error). Of course, not all events can be attached
to all DOM elements. For example, the load event can
only be attached to Document elements, and the
submit to Form elements. The last argument is the notify
function; since JavaScript has closure support, the API is simplified
by not having the need to write a specific Observer
object.

Final Considerations

In this example, no Ajax-specific code was (manually) created and
no CPU is used on the server to render the user interface. The load
is distributed between the server and the client, and the client
will have all the load for user interface processing.

Resources

width="1" height="1" border="0" alt=" " />
Paulo Lopes is a software architect and developer for Topforce BV, where he works on Java technologies for the enterprise world.
Related Topics >> Programming   |