Creating a clock in a web page is easy, but what about a clock controlled by the server? This demo shows how use use a separate server side thread to control a number of browsers.
We turn active Reverse Ajax on: dwr.engine.setActiveReverseAjax(true);
.
Reverse Ajax will work without comet, by using piggyback, however this
gives you very long latency, not what we need for a clock, so we turn on
comet or polling (whatever the server is configured to prefer).
Next we specify our errorHandler and retry logic. DWR has a retry feature for reverse AJAX which automatically re-attempts failed calls. With the default configuration failed calls will be re-attempted twice with one second between each attempt. If both retry attempts fail the errorHandler will be called. After the errorHandler is called DWR will re-attempt the call at 10 second intervals until successfull. At this time the pollStatusHandler will be called. You can read more about this feature on our site.
The server code uses a ScheduledThreadPoolExecutor to call update the screen once a second. The setClockDisplay looks like this:
String page = ServerContextFactory.get().getContextPath() + "/reverseajax/clock.html"; Browser.withPage(page, new Runnable() { public void run() { Util.setValue("clockDisplay", output); } });
ServerContextFactory is a lot like WebContextFactory, however it will work outside a DWR thread. Since we're using the ScheduledThreadPoolExecutor thread here, we need to use ServerContext and not WebContext.
We need to work out the page to update. You may know the contextPath without needing to ask, but in this case we're making the web-app relocatable by asking for the contextPath.
We then create a scope where the target browsers includes
everyone looking at the given page, and we use the Util.setValue()
method to alter the target browsers.
<input type="button" value="Start / Stop" onclick="Clock.toggle();"/> <h2 id="clockDisplay"></h2>
window.onload=function() { dwr.engine.setActiveReverseAjax(true); // Initiate reverse ajax polling dwr.engine.setErrorHandler(errorHandler); // Optional - 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! Tabs.init('tabList', 'tabContents'); // Initialize the tabs for this display } function errorHandler(message, ex) { dwr.util.setValue("error", "<font color='red'>Cannot connect to server. Initializing retry logic.</font>", {escapeHtml:false}); setTimeout(function() { dwr.util.setValue("error", ""); }, 5000) } function updatePollStatus(pollStatus) { dwr.util.setValue("pollStatus", pollStatus ? "<font color='green'>Online</font>" : "<font color='red'>Offline</font>", {escapeHtml:false}); }
public class Clock implements Runnable { public Clock() { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory()); executor.scheduleAtFixedRate(this, 1, 1, TimeUnit.SECONDS); } public void run() { if (active) { setClockDisplay(new Date().toString()); } } /** * Called from the client to turn the clock on/off */ public synchronized void toggle() { active = !active; if (active) { setClockDisplay("Started"); } else { setClockDisplay("Stopped"); } } /** * Actually alter the clients. * @param output The string to display. */ public void setClockDisplay(final String output) { String page = ServerContextFactory.get().getContextPath() + "/reverseajax/clock.html"; Browser.withPage(page, new Runnable() { public void run() { Util.setValue("clockDisplay", output); } }); } protected transient boolean active = false; }