Building an Ajax Login with jMaki

july 21 2008

A couple of months ago, I joined Carla and Greg at the Community One convention for a presentation on “jMaki: the Power of Ajax made easy”. And although the topic was of great interest, the allotted time of 15 minutes per person wasn’t enough to run our respective demos. So we’ve decided to follow up with a series of online articles to supplement our original presentations.

This article deconstructs a jMaki-powered Ajax login widget that I implemented for the TravelMuse website. It showcases jMaki’s exciting “Publish/Subscribe” event system that allows the creation of loosely-coupled widgets that fluidly communicate with one another. Basically, a “Publish/Subscribe” event system is one where the different components interact by means of event messages, or topics. A real-life analogy would be that you (in this case our login widget) would subscribe to your favorite weekly newspaper or daily email newsletter (in our case, the login subscribes to a login and logout topics) and would receive a new issue or email every time one is released (the widget is notified when the user clicks on the links). You can learn more about this amazing event system by reading Carla’s Working with jMaki Events article.

We’ll be implementing a stripped-down version of the login widget, the focus being on the highlight of the event system, which will become clear as we start writing the widget code. But first, let’s review the widget architecture.

1. Widget Architecture

Following is a diagram outlining the design of the login widget and how it interacts with the different elements on the page.

jMaki login widget diagram

Let’s go through the main 3-step sequence of the design.

  1. Step 1: We declare the widget and have it subscribe to the "topic:login" and "topic:logout" topics that will trigger the display of the login panel
  2. Step 2: Whenever the user clicks on either the “Sign in” link in the main banner or the contextual “Add to trip” links below each destination, the login widget gets notified of the event and displays the login form at the appropriate location.
  3. Step 3: We make the Ajax requests (login, logout) and update the UI to display the proper state of the application.

2. Setting up the project

To get started, download the “jmakiAjaxLogin” Netbeans project flle and save it in your preferred folder. Then open it as a project in Netbeans:

  • Go to File, then Open project…
  • Browse to the "jmakiAjaxLogin" project folder you just downloaded, then click Open folder
  • You can quickly test the project by right-clicking the "jmakiAjaxLogin" project, and selecting Run…. Try logging in from either the top “login” link or through the contextual “add to trip” links.

Notes:
You can also download the “jmakiAjaxLogin” application WAR file file, deploy it and follow along.

The main focus of the demo is on the Javascript code of the widget as well as the DOM Scripting codes, so we’ll be skimming over the XHTML and CSS codes. Please review them and send me comments if necessary. The code assumes basic knowledge of widget creation; it also makes a heavy use of the awesome Prototype library, so I’d invite you to visit the site and familiarize yourself with the API. The nice article on how Prototype extends the DOM is definitely worth a good read. The project has been setup in Netbeans but you should be able to set it up in your preferred IDE.

3. Writing the login widget code

Now that we’re familiar with the widget architecture and have setup the project, let’s go over the declaration of the widget. Put the following declaration in the markup of your JSP page.


<a:widget id="ajaxLogin" name="ajax.loginWidget"
     args="{loginServiceURL : '${pageContext.servletContext.contextPath}/ajaxLogin',
     screenName : '${webUser.screenName}' }"
/>

In this declaration, we instruct jMaki to assign the ID of "ajaxLogin" to our widget instead of the default ID (referred to by the uuid parameter) and to look for the required widget files (component.htm and component.js; the CSS is inherited from the page) in the /resources/ajax/loginWidget folder. The args property contains widget parameters that will be made available to us in the Javascript code.

At this point, you may want to take a look at the structure of the main index.jsp as well as that of the component.htm widget markup.

A widget consist of 3 main files:

  • component.htm: this file contains the (X)HTML markup of the widget; the main container is usually assigned a unique ID that’s referred to as the widget’s UUID. You can override jMaki’s default ID with your own by explicitly defining it in the markup.
  • component.css: this file contains the CSS rules for the widget. In our case, we’re re-using the declarations inherited from the page, so this file is not needed.
  • component.js: this is the main file and the widget controller. It contains all the Javascript code related to the widget constructor, behaviors and events. It’s automatically invoked on page load once jMaki bootstraps the widget.

Let’s quickly review the content of the widget’s markup (component.htm):


<div id="${uuid}" class="loginDialog standard" style="display:none">
    <h4>Sign in</h4>
    <form id="loginForm" class="login" action="#">
         ...
    </form>
</div>

Remember that we assigned an "ajaxLogin" ID to our widget during its declaration; well, that ID will now replace the ${uuid} variable in our current markup and be passed to the Javascript controller for scripting.

The rest of the script focuses on the widget controller (component.js). The first thing we do is to declare a jMaki namespace for our widget.


    jmaki.namespace("jmaki.widgets.ajax.loginWidget");

We then declare the widget constructor class in an Object-Oriented fashion:


jmaki.widgets.ajax.loginWidget.Widget = Class.create({
    initialize : function(params) {
       this.container = $(params.uuid);
       this.screenName = params.args.screenName;
       this.serviceURL = params.args.loginServiceURL;
       this.form = $("loginForm");

       this.updateUI();
       this.registerEvents();
    },
   ...

Here we’re using the Object-Oriented javascript extension that Prototype provides us to add more power, clarity and style to our code. Our constructor now looks more like a Javascript “class” (actually, a Javascript object since there’s no actual notion of class in Javascript and inheritance is based on the concept of object prototype) that encapsulates its properties and methods. The initialize method is automatically called by the jMaki engine on page load, and the params argument contains the properties that we set when declaring the widget. We simply assign them to the appropriate widget properties. We also update the UI in case the user if already logged in to reflect the current login status, and register the different events.

Our implementation will fully decouple the widget from the page, to demonstrate the jMaki event system: as such, the login will subscribe to topics that will be fired by the main page controller: ui-core.js. So the registerEvents method will only be responsible for the logic related to the widget behavior: showing the login panel when an event is triggered, taking care of the Ajax requests and updating the UI accordingly. Here’s the code:


  registerEvents : function() {
        jmaki.subscribe("topic:login", this.showLoginDialog.bind(this));
        jmaki.subscribe("topic:logout", this.onUserLogout.bind(this));

        $("cancelLogin").observe("click", this.hideLoginDialog.bind(this));
        this.form.observe("submit", this.processLogin.bindAsEventListener(this));
  }

At this point, our widget is full operational: it just subscribed to the topic:login topic for logging in and to the topic:logout for logging out and ending the session. But first, we need to publish those two topics, and that’s what we’ll be doing next. This time, we’ll be sending the event through the main page controller, which I usually implement as a reusable piece of generic behavior shared by all pages. In our case, we’ll define those event handlers in our main scripts/ui-core.js file. Open the file and notice that we’ve written our ui.init method to run once the page has loaded and the DOM tree is available for scripting:


 var ui = {
     init : function() {
           $$("a.login-link").invoke("observe", "click", ui.onUserLogin);
           $("signout").observe("click", ui.onUserLogout);
       },
       onUserLogin : function(event) {
          event.stop();
          jmaki.publish("topic:login", { eventSource : event.element() });
       },
      onUserLogout : function(event) {
          event.stop();
          jmaki.publish("topic:logout", {});
      }
 };

document.observe("dom:loaded", ui.init);

Let’s examine the code in detail. First, we attach a click event on all DOM nodes with a class name of "login-link", which includes the main login link as well as the contextual ones. Then we attach a separate event to the “signout” button to handle the logout scenario. Those respective events will publish the jMaki topics (topic:login and topic:logout) that our widget has already subscribed to. You might have noticed that I’m passing a reference to the DOM node that triggered the event as an extra property to the topic data. The coordinates of the node will be used to properly position the widget.

Go ahead, run the code and sign in with any username and password. It works! We’ve just created a working widget, at least not to industry security standards but we can use it as a framework. You’ll also notice that the session is maintained by the widget, so you remain logged in even after refreshing your browser. Now click the Sign out link and you’re asynchronously signed out. Cool! Our Ajax login widget works as expected. And if you’re using Firebug, you can view the status of our Ajax calls in the console.

Following are the widget methods that handle the Ajax requests for the login (processLogin) and logout (onUserLogout) scenarios (Note: in a real application, the login form will be thoroughly validated and the password will be strongly encrypted.).


    processLogin : function(event) {
        event.stop();

        var params = $H({
             action : "login",
             email : $F("email"),
             password : $F("password")
        }).toQueryString();

        new Ajax.Request(this.serviceURL, {
             parameters : params,
             requestHeaders : { Accept : "application/json" },
             onSuccess : this.onUserAuthenticated.bind(this),
             onFailure : function(transport) {
                 $("errorMsg").update("Login error, please try again later.").show();
             }
        });
    },

    onUserLogout : function(data) {
         new Ajax.Request(this.serviceURL, {
             parameters : "action=logout",
             requestHeaders : { Accept : "application/json" },
             onSuccess : this.onUserAuthenticated.bind(this),
             onFailure : function(transport) {
                 $("screenname").update("Logout error, please try again later.");
             }
         });
     }
    ...

4. Server-side script

The server-side script is a basic servlet that will simply receive the request and return a JSON object containing the hard-coded generic user ID and the username part of the email address. There’s no servlet filter involved, nor is there any actual database query to retrieve the user. Those are definite musts in a real application.


public class ajaxLogin extends HttpServlet {

    protected void processRequest(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {   

        response.setContentType("text/html;charset=utf-8");
        response.setHeader("Accept", "application/json");

        Map userInfo = new HashMap();
        HttpSession session = request.getSession();
        String action = request.getParameter("action");

        if(action.equalsIgnoreCase("login")) {
            int userId = 123;
            String email = request.getParameter("email");
            String screenName = email.contains("@")?
                 email.substring(0, email.indexOf("@")) : email;
            User webUser = new User(userId, screenName);
            session.setAttribute("webUser", webUser);

            userInfo.put("userId", userId);
            userInfo.put("screenName", screenName);
        } else {
            session.invalidate();
            userInfo.put("screenName", "");
        }

        PrintWriter out = response.getWriter();   

       try {
            out.write(new JSONObject(userInfo).toString());
            out.flush();
        } finally {
            out.close();
        }
    }
    ...
}

Well, as you can see, our servlet won’t win us any Java design award but our jMaki widget will definitely open the door to advanced widget designs and Ajax application architectures.

I hope you find this article useful. In the upcoming ones, we’ll deconstruct other interesting jMaki widget designs and features. Please feel free to send comments about parts of the demo I might have overlooked. You can also play with actual interesting jMaki widgets on the TravelMuse website.

5. Additional Resources

Advertisements