[ DWR Website | Web Application Index ]

Reverse Ajax Table Update

Creating a table in a web page is easy, but what about a table controlled by the server? This demo shows how to use a separate server side thread to control a number of browsers. When enabled updates will be sent to this pages table every 10 seconds.

Server status:

Id Name Address Age Is Superhero?

This example demonstrates reverse AJAX and the ability to filter what pages receive updates. The filtering is performed by setting an attribute on the ScriptSession. This is done through the addAttributeToScriptSession() function which is called by the "Enable page updates" button. Please see the commented code on the source tab for detailed implementation details.

HTML source:

<input type="button" value="Enable page updates" onclick="addAttributeToScriptSession();"/>
<input type="button" value="Disable page updates" onclick="removeAttributeToScriptSession();"/>
  <table>
    <thead>
      <th>Id</th>
      <th>Name</th>
      <th>Address</th>
      <th>Age</th>
      <th>Is Superhero?</th>
    </thead>
    <tbody id="peopleTable"></tbody>
  </table>    

Javascript source:

window.onload=function()
{
    dwr.engine.setActiveReverseAjax(true); // Initiate reverse ajax polling
    dwr.engine.setErrorHandler(errorHandler); // Called when a call and all retry attempts fail
	dwr.engine.setPollStatusHandler(updatePollStatus); // Optional function to call when the reverse ajax status changes (e.g. online to offline)
	updatePollStatus(true); // Optional - We are online right now!  Until DWR determines we are not!
	dwr.engine.setNotifyServerOnPageUnload(true); // Optional - When the page is unloaded, remove this ScriptSession.	
    Tabs.init('tabList', 'tabContents'); // Initialize the tabs for this display    
    PeopleTable.updateTableDisplay(); // Make a call to the server to begin updating the table!   
    addAttributeToScriptSession(); // Make a remote call to the server to add an attribute onto the ScriptSession which will be used in determining what pages receive updates!
}
	  
function errorHandler(message, ex) {
    dwr.util.setValue("error", "Cannot connect to server. Initializing retry logic.", {escapeHtml:false});
    setTimeout(function() { dwr.util.setValue("error", ""); }, 5000)
}
	  
function updatePollStatus(pollStatus) {
    dwr.util.setValue("pollStatus", pollStatus ? "Online" : "Offline", {escapeHtml:false});
}

// Make a remote call to add an attribute on the ScriptSession.
// Only clients that have this attribute set will receive updates.	  
function addAttributeToScriptSession() {
    PeopleTable.addAttributeToScriptSession();
}

// Make a remote call to remove an attribute from the ScriptSession.
// Clients that call this will no longer receive updates (unless addAttributeToScriptSession is called again).	  	  
function removeAttributeToScriptSession() {
    PeopleTable.removeAttributeToScriptSession();
}

Java source: (please see comments inline)

public class PeopleTable implements Runnable
{
	/**
	 * Constructor - Creates a thread pool that runs every 10 seconds.
	 */
    public PeopleTable()
    {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());
        executor.scheduleAtFixedRate(this, 1, 10, TimeUnit.SECONDS);
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run()
    {
        updateTableDisplay();
    }

    public void updateTableDisplay()
    {
        // Get the current page.
        String page = ServerContextFactory.get().getContextPath() + "/reverseajax/peopleTable.html";
        // Create a new AttributeScriptSessionFilter which will look for an attribute on the ScriptSession
        ScriptSessionFilter attributeFilter = new AttributeScriptSessionFilter(SCRIPT_SESSION_ATTR);
        // Update the page, filters ScriptSessions using attributeFilter.  If the SCRIPT_SESSION_ATTR
        // has not been set on the ScriptSession the page in question will not receive updates.
        Browser.withPageFiltered(page, attributeFilter, new Runnable()
        {
            @Override
            public void run()
            {
                // Creates a new Person bean.
                Person person = new Person(true);
                // Creates a multi-dimensional array, containing a row and the rows column data.
                String[][] data = {
                    {person.getId(), person.getName(), person.getAddress(), person.getAge()+"", person.isSuperhero()+""}
                };
                // Call DWR's util which adds rows into a table.  peopleTable is the id of the tbody and 
                // data contains the row/column data.  
                Util.addRows("peopleTable", data);
            }
        });
    }
	
	/**
	 * Called from the client to add an attribute on the ScriptSession.  This
	 * attribute will be used so that only pages (ScriptSessions) that have 
	 * set this attribute will be updated.
	 */
    public void addAttributeToScriptSession() {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.setAttribute(SCRIPT_SESSION_ATTR, true);
    }
    
    /**
	 * Called from the client to remove an attribute from the ScriptSession.  
	 * When called from a client that client will no longer receive updates (unless addAttributeToScriptSession)
	 * is called again.
	 */
    public void removeAttributeToScriptSession() {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.removeAttribute(SCRIPT_SESSION_ATTR);
    }
    
    /**
     * This is the ScriptSessionFilter that will be used to filter out all ScriptSessions
     * unless they contain the SCRIPT_SESSION_ATTR attribute. 
     */
    protected class AttributeScriptSessionFilter implements ScriptSessionFilter
    {
        public AttributeScriptSessionFilter(String attributeName)
        {
            this.attributeName = attributeName;
        }

        /* (non-Javadoc)
         * @see org.directwebremoting.ScriptSessionFilter#match(org.directwebremoting.ScriptSession)
         */
        @Override
        public boolean match(ScriptSession session)
        {
            Object check = session.getAttribute(attributeName);
            return (check != null && check.equals(Boolean.TRUE));
        }

        private final String attributeName;
    }

    private final static String SCRIPT_SESSION_ATTR = "SCRIPT_SESSION_ATTR";
}