I'm the father of ItsNat AJAX Java web framework, JNIEasy, LAMEOnJ, JEPLayer and JEPLDroid libraries (and very old stuff like XPDOM), supporter of the Single Page Interface (SPI) paradigm, writer of the SPI manifesto and currently Android native guy. Jose Maria has posted 28 posts at DZone. You can read more from them at their website. View Full User Profile

A simple Single Page Interface stateless example with ItsNat v1.3

06.27.2013
| 859 views |
  • submit to reddit

ItsNat is a LGPL licensed AJAX framework aimed at developing applications and websites Single Page Interface SEO compatible, within the philosophy of the Single Page Interface Manifesto 

Before version v1.3 normal operation mode was based on server state, ItsNat basically simulates a Java W3C browser on the server, the server keeps a copy of the DOM of the user page, DOM changes in server are propagated to client automatically to synchronize it with the server via JavaScript code generated on the fly normally as a result of AJAX requests (or <script> based requests). The result is web development based on pure HTML templates and Java W3C DOM code, basically the same type of programming you do with JavaScript on the client using W3C APIs but in Java in the same memory space where data going to be managed is, that is in the server memory.

In stateless mode there is no such as copy of the DOM of the client page in server, because with some data sent to the server via AJAX is possible to partially reconstruct the client state to be changed on the server, generating the corresponding JavaScript DOM modifications sent to the client, but that client state built in server is not longer kept in server so an ItsNat stateless application is scalable to multiple nodes without shared data in the user's session and without server affinity (of course your custom data in session is possible and up to you).

This new mode does not make full use of ItsNat however many powerful features are still ready to be used in this mode like pure markup templating, powerful DOM manipulation for view logic and components (without AJAX/script events).

Lets see an simple but powerful stateless example.

Create an empty web application, use your preferred IDE, in the most simple approach just copy the jars of ItsNat distribution into your WEB-INF/lib folder.

First of all we create a new servlet named servletstless, use your IDE and replace code with this:

public class servletstless extends HttpServletWrapper
{

  @Override
  public void init(ServletConfig config) throws ServletException
  {
  super.init(config);

  ItsNatHttpServlet itsNatServlet = getItsNatHttpServlet();

  String pathPrefix = getServletContext().getRealPath("/");

     pathPrefix += "/WEB-INF/pages/manual/";

  ItsNatDocumentTemplate docTemplate;

  docTemplate = itsNatServlet.registerItsNatDocumentTemplate(

    "manual.stless.example","text/html",

    pathPrefix + "stless_example.html");

  docTemplate.addItsNatServletRequestListener(

      new StlessExampleInitialDocLoadListener());

  docTemplate.setEventsEnabled(false); // Stateless

  docTemplate = itsNatServlet.registerItsNatDocumentTemplate(

     "manual.stless.example.eventReceiver","text/html",

     pathPrefix + "stless_example_event_receiver.html");

  docTemplate.addItsNatServletRequestListener(

      new StatelessExampleForProcessingEventDocLoadListener());

  docTemplate.setEventsEnabled(false); // Stateless 

  ItsNatDocFragmentTemplate docFragDesc;

  docFragDesc = itsNatServlet.registerItsNatDocFragmentTemplate(

      "manual.stless.example.fragment","text/html",

      pathPrefix + "stless_example_fragment.html"); 

  }

}

The following code snippet registers the template of the initial page:

  ItsNatDocumentTemplate docTemplate;

  docTemplate = itsNatServlet.registerItsNatDocumentTemplate(

    "manual.stless.example","text/html",

     pathPrefix + "stless_example.html");

  docTemplate.addItsNatServletRequestListener(

     new StlessExampleInitialDocLoadListener());

  docTemplate.setEventsEnabled(false); // Stateless

Note the setEventsEnabled(false) call, this configuration method call disables client to server stateful remote events, because there is no need of a ItsNatDocument kept in server to receive events, ItsNat does not kept the document in memory or in session, this setting is necessary if you want a true stateless document.

The stless_example.html file:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"  xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>ItsNat Stateless Example in Manual</title>
    <script>
    function sendEventStateless()
    {
        var counterElem = document.getElementById("counterId");
        var userEvt = document.getItsNatDoc().createEventStateless();
        userEvt.setExtraParam('counter',counterElem.firstChild.data);
        userEvt.setExtraParam('itsnat_doc_name','manual.stless.example.eventReceiver');        
        document.getItsNatDoc().dispatchEventStateless(userEvt,3 /*XHR_ASYNC_HOLD*/, 1000000);
    }    
    </script>    
    
</head>
<body>

    <h3>ItsNat Stateless Example in Manual</h3>

    <h4 id="presentationId" itsnat:nocache="true"></h4>
         
    <br /><br />
    <a href="javascript:sendEventStateless()">Send stateless event</a> 
        
    <br /><br />  
    <div>
        <div>Num. Events: <b id="counterId" itsnat:nocache="true">0</b></div>
    </div>
        
    <div>
        <div>
            <div id="insertHereId" /> 
        </div>
    </div>

    <br />
</body>
</html>

This document template will be the initial page of our micro Single Page Interface Stateless example, remember this template is registered with stateful remote events disabled, but watching this JavaScript code line:

document.getItsNatDoc().dispatchEventStateless(userEvt,3/*XHR_ASYNC_HOLD*/, 1000000);

We realize there is another kind of remote events: stateless events.

This method accepts three parameters:

1)  An event object: following the same conventions as in custom events seen before in this manual

2)  A transport mode: an integer with the same values of  org.itsnat.core.CommMode constants. Most of the time you’re going to pass 3 parameter (XHR_ASYNC_HOLD = AJAX asynchronous queued events).

3)  Timeout in milliseconds

Transport mode and timeout parameters should be familiar to you because they are typically provided for configuration of a stateful document template.

This line is interesting:

userEvt.setExtraParam('itsnat_doc_name','manual.stless.example.eventReceiver');   

ItsNat recognizes this standard “document load” parameter also in a stateless event, when received this stateless event in server, ItsNat server behavior is the following:

1)  A new ItsNatDocument is created based on the specified template. This document instance is stateless no matter the template was configured (setEventsEnabled(false) is just recommended for clarity and to avoid stateful loads using conventional URLs)

2)  Document load processing is as usual with a radical difference: final DOM state when loading phase ends is not serialized as HTML and initial JavaScript code generated in this phase is ignored/lost.

3)  The global event listener registered in load phase calling ItsNatDocument.addEventListener(EventListener) is executed receiving a stateless event. DOM modifications performed in this phase are sent to the client as return of the event in a similar way of a stateful remote event.

Used to the conventional, stateful mode of ItsNat, this stateless behavior seems non-sense, however, it has a lot of sense…

The main purpose of stateless event processing is:

1)  To reconstruct fully or partially the DOM client state in server: this is the purpose of the load phase, the final DOM state/tree of the document after loading must match fully or partially the client state.

2)  Modify accordingly the DOM state of the document to generate the necessary JavaScript to bring the client a new state: this is the purpose of the event processing phase.

As you easily can figure out, fully reconstructing the client state per-event may be very costly, in general stateless ItsNat mode is more costly on server processing than stateful, but it has the benefit of the unlimited scalability of stateless, adding more nodes and distributing events between nodes with no problem of data sharing or server affinity (this is an ItsNat point of view, use of session for your own custom data is up to you). 

Fortunately you do not need to fully reconstruct the client page in server, you just can reconstruct only the parts being to be changed by the concrete event, furthermore, ItsNat stateless mode is exceptional for injecting new markup to the client page.

Continuing our example, Java code to generate the initial page:

public class StlessExampleInitialDocLoadListener 
   implements ItsNatServletRequestListener
{
  public StlessExampleInitialDocLoadListener()
  {
  }

  public void processRequest(ItsNatServletRequest request,
         ItsNatServletResponse response)
 {
  ItsNatHTMLDocument itsNatDoc = 
         (ItsNatHTMLDocument)request.getItsNatDocument();

   new StlessExampleInitialDocument(itsNatDoc);
  }

}

public class StlessExampleInitialDocument
{
  protected ItsNatHTMLDocument itsNatDoc;

  public StlessExampleInitialDocument(ItsNatHTMLDocument itsNatDoc)
  {
    this.itsNatDoc = itsNatDoc;

    HTMLDocument doc = itsNatDoc.getHTMLDocument();

    Text node = (Text)doc.createTextNode(
     "This the initial stateless page (not kept in server)");

    Element presentationElem = doc.getElementById("presentationId");
    presentationElem.appendChild(node); 
  }

}

This code is just an excuse to show how the initial page is a conventional ItsNat-generated page (but stateless), nothing especial here.

The interesting stuff starts with stless_example_event_receiver.html template registered with the name manual.stless.example.eventReceiver going to process stateless events:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"  xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>ItsNat Stateless Document For Stateless Event Processing</title>
</head>
<body>
    
    <b id="counterId" itsnat:locById="true" itsnat:nocache="true">(num)</b>     
    
    <div id="insertHereId" itsnat:locById="true" itsnat:nocache="true" /> 
    
</body>
</html>

Before continuing with the associated Java code, let’s stop to compare the initial page template:

   <div>
       <div>Num. Events: <b id="counterId" itsnat:nocache="true">0</b></div>
   </div>

    <div>
        <div>
            <div id="insertHereId" /> 
        </div>
    </div>

and this template:

    <b id="counterId" itsnat:locById="true" itsnat:nocache="true">(num)</b>     
    <div id="insertHereId" itsnat:locById="true" itsnat:nocache="true" /> 

Both pairs of elements have the same id, this is not casual, for instance the containing of the insertHereId element of the initial page template is going to be the same as the “event processor” template, as you can see only the tag name (div) and the id attribute are the same, note how both elements are located in a different place (markup order related to body element) in the page, further we will explain this is the reason of the ItsNat attribute locById set to true.

The Java code associated to this template:

public class StatelessExampleForProcessingEventDocLoadListener 
		implements ItsNatServletRequestListener
{
  public StatelessExampleForProcessingEventDocLoadListener()
  {
  }
   
  public void processRequest(ItsNatServletRequest request,
       ItsNatServletResponse response)
  {
      new StatelessExampleForProcessingEventDocument(
			     (ItsNatHTMLDocument)request.getItsNatDocument()
        ,request,response);
  }
}

public class StatelessExampleForProcessingEventDocument 
		implements Serializable,EventListener
{
    protected ItsNatHTMLDocument itsNatDoc;
    protected Element counterElem;
    
    public StatelessExampleForProcessingEventDocument(
        ItsNatHTMLDocument itsNatDoc, 
			     ItsNatServletRequest request, 
        ItsNatServletResponse response)
    {
        this.itsNatDoc = itsNatDoc;
        
        if (!itsNatDoc.isCreatedByStatelessEvent())
            throw new RuntimeException(
         				"Only to test stateless, must be loaded by a stateless event");

        // Counter node with same value (state) than in client:
        String currCountStr = 
              request.getServletRequest().getParameter("counter");
        int counter = Integer.parseInt(currCountStr);
        
        HTMLDocument doc = itsNatDoc.getHTMLDocument();
        this.counterElem = doc.getElementById("counterId");
       ((Text)counterElem.getFirstChild()).setData(String.valueOf(counter));
        
        itsNatDoc.addEventListener(this);
    }

    public void handleEvent(Event evt)
    {
        ItsNatEventDOMStateless itsNatEvt = (ItsNatEventDOMStateless)evt;
        
        Text counterText = (Text)counterElem.getFirstChild();
        String currCountStr = counterText.getData();
        int counter = Integer.parseInt(currCountStr);
        counter++;
        counterText.setData(String.valueOf(counter));        
        
        Document doc = itsNatDoc.getDocument();        
        
        Element elemParent = doc.getElementById("insertHereId");
        ScriptUtil scriptGen = itsNatDoc.getScriptUtil();
        String elemRef = scriptGen.getNodeReference(elemParent);
        ClientDocument clientDoc = itsNatEvt.getClientDocument();
        clientDoc.addCodeToSend(elemRef + ".innerHTML = '';");        
        clientDoc.addCodeToSend("alert('Currently inserted fragment removed before');");        
                
        ItsNatServlet servlet = 
             itsNatDoc.getItsNatDocumentTemplate().getItsNatServlet();  
        ItsNatHTMLDocFragmentTemplate docFragTemplate = 
			(ItsNatHTMLDocFragmentTemplate)servlet.getItsNatDocFragmentTemplate("manual.stless.example.fragment");  

        DocumentFragment docFrag = 
             docFragTemplate.loadDocumentFragmentBody(itsNatDoc);  
  
        elemParent.appendChild(docFrag); // docFrag is empty now  
        
        // Umm we have to celebrate/highlight this insertion 
        Element child1 = ItsNatTreeWalker.getFirstChildElement(elemParent);
        Element child2 = ItsNatTreeWalker.getNextElement(child1);
        Text textChild2 = (Text)child2.getFirstChild();
        Element bold = doc.createElement("i");
        bold.appendChild(textChild2); // is removed from child2
        child2.appendChild(bold);  
        child2.setAttribute("style","color:red"); 
		      // <h3 style="color:red"><i>Inserted!</i></h3>  
    }    
}

In summary this code updates the counterId element with the current value in page and inserts a template with name manual.stless.example.fragment into the element with id insertHereId, into the document loaded for processing the stateless event. Later we will see that this insertion finally happens into the final client page.

The markup of the template registered with manual.stless.example.fragment is:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"  
			xmlns:itsnat="http://itsnat.org/itsnat">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Fragment</title>
</head>
<body>

    <h4 style="color:green"><b>Fragment...</b></h4>
    <h3>Inserted!</h3>        
    
</body>
</html>

Because this template is inserted calling  docFragTemplate.loadDocumentFragmentBody(itsNatDoc) ignore everything but the <body> content.

Let’s study the code:

        if (!itsNatDoc.isCreatedByStatelessEvent())
            throw new RuntimeException(
			   "Only to test stateless, must be loaded by a stateless event");

This check avoids trying to load this document, designed for processing stateless events, as a conventional page specifying a URL containing the itsnat_document parameter with the name of the template. The method ItsNatDocument.isCreatedByStatelessEvent() only returns true when the document has been loaded as part of processing of a stateless event.

Another more important use of ItsNatDocument.isCreatedByStatelessEvent() is to distinguish when the document was created to load a page as usual or to process a stateless event, because as you can easily figure out, in this very simple example the template for the initial page is so light than it also could be used for processing stateless events, thus this method could be used to separate the initial page load code (the presentation element construction) from the document load phase code when processing a stateless event (in this case registering the template-level event listener to receive the stateless event).

Note we are receiving a parameter counter with the current integer into the counterId element:

        // Counter node with same value (state) than in client:
        String currCountStr = request.getServletRequest().getParameter("counter");
        int counter = Integer.parseInt(currCountStr);
        
        HTMLDocument doc = itsNatDoc.getHTMLDocument();
        this.counterElem = doc.getElementById("counterId");
       ((Text)counterElem.getFirstChild()).setData(String.valueOf(counter));

With this code we “reconstruct” the DOM state of the counterId element in client.

Finally in the load phase:

  itsNatDoc.addEventListener(this);

It registers “this” document object as the event listener to process the stateless event causing the load of this document. After the phase load, this listener will be immediately called and the stateless event passed.

  public void handleEvent(Event evt)
  {
    ItsNatEventDOMStateless itsNatEvt = (ItsNatEventDOMStateless)evt;

This cast sounds familiar, ItsNat extends DOM Events with other kind of ItsNat-defined events, in this case stateless events are a new kind of “extended DOM event”.

The interface ItsNatEventDOMStateless inherits from ItsNatEventStateless, an object implementing ItsNatEventStateless interface is “a stateless event”. Later we will see a case of stateless event implementing ItsNatEventStateless but not ItsNatEventDOMStateless.

Now is time to really enter into the stateless approach of ItsNat:

  Text counterText = (Text)counterElem.getFirstChild();
  String currCountStr = counterText.getData();
  int counter = Integer.parseInt(currCountStr);
  counter++;
  counterText.setData(String.valueOf(counter)); 

We said before that the objective of the load phase when processing the stateless event is to bring the loaded document to the same DOM state than the client part we are going to change when processing the stateless event into the event listener. As you can see the text node has been updated with a new integer value, the necessary JavaScript code will be generated and sent to client to update the counterId element in client as the result of the stateless event processing.

More actions:

  ItsNatDocument itsNatDoc = itsNatEvt.getItsNatDocument();
  Document doc = itsNatDoc.getDocument();
  Element elemParent = doc.getElementById("insertHereId");
  ScriptUtil scriptGen = itsNatDoc.getScriptUtil();
  String elemRef = scriptGen.getNodeReference(elemParent);
  ClientDocument clientDoc = itsNatEvt.getClientDocument();
  clientDoc.addCodeToSend(elemRef + ".innerHTML = '';"); 
  clientDoc.addCodeToSend("alert('Currently inserted fragment removed before');"); 

In this case there is nothing done in the load phase related to insertHereId element because we want to reinsert the same DOM into this element again and again and custom JavaScript code is easier to clean the current state in client (ItsNat stateless mode ItsNat is much more client centric and JavaScript knowledge is recommended).  

This code requires more explanation, the document we are modifying was created based on the template with name manual.stless.example.eventReceiver, this template is similar but not the same as manual.stless.example, notwithstanding the JavaScript generated is sent to modify the later. 

Let’s repeat again the important (dynamic) elements:

·  manual.stless.example

    <div>
        <div>Num. Events: <b id="counterId">0</b></div>
    </div>

    <div>
        <div>
            <div id="insertHereId" /> 
        </div>
    </div>

·  ma ual.stless.example.eventReceiver

  
    <b id="counterId" itsnat:locById="true" itsnat:nocache="true">(num)</b>  
    <div id="insertHereId" itsnat:locById="true" itsnat:nocache="true" /> 

As you know ItsNat node localization is based on “counting nodes in the tree” from <html> to the concrete node, of course node localizing is smart and automatic caching happens to avoid traversing the tree from the <html> root node. Anyway in some occasion full traversing happens, according to previous localization of elements in the first template is not the same as the second template, unless we explicitly avoid top-down traversing from root. This is the reason of ItsNat attribute locById.

The locById attribute tells ItsNat to use the id attribute/property in generated JavaScript to locate the node just calling getElementById(). By using this trick you can make ad-hoc lighter templates with just the enough markup aligned with the client DOM state.

Some code remains to explain:

  ItsNatServlet servlet = itsNatDoc.getItsNatDocumentTemplate().getItsNatServlet(); 

  ItsNatHTMLDocFragmentTemplate docFragTemplate =
  (ItsNatHTMLDocFragmentTemplate)servlet.getItsNatDocFragmentTemplate(
  "manual.stless.example.fragment"); 

  DocumentFragment docFrag = 
         docFragTemplate.loadDocumentFragmentBody(itsNatDoc); 
  elemParent.appendChild(docFrag); // docFrag is empty now 

  // Umm we have to celebrate/highlight this insertion
  Element child1 = ItsNatTreeWalker.getFirstChildElement(elemParent);
  Element child2 = ItsNatTreeWalker.getNextElement(child1);
  Text textChild2 = (Text)child2.getFirstChild();
  Element bold = doc.createElement("i");
  bold.appendChild(textChild2); // is removed from child2
  child2.appendChild(bold); 
  child2.setAttribute("style","color:red");
    // <h3 style="color:red"><i>Inserted!</i></h3> 

This conventional ItsNat code inserts a modified version of the markup inside <body> of the loaded template manual.stless.example.fragment into the element insertHereId. The interesting detail of this code is most of the DOM modifications happen after insertion in document (remember that corresponding JavaScript DOM code is generated on the fly), any node involved, child1, child2 and textChild2, need to be located after insertion, are they located traversing the full tree from <html>?

No!

As said before, location caching avoids fully traversing the tree from top, for instance ItsNat looks for parent nodes already in tree and cached, in this case the node insertHereId is the candidate, or any other child node cached when inserted. These techniques provide a powerful server centric Java based manipulation of the client DOM tree beyond simple one-time insertion.

Demo online (with some more example).

Enjoy!


Published at DZone with permission of its author, Jose Maria Arranz.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)