|
Processing... Press "Start" to start the background thread updating the robot position!
Description & Source Code
This simple example allows to compare the benefits of a WebSocket based Server Push approach with the Comet method. In ZK 8.5 the UI Engine can be configured to use WebSockets.. Once enabled, updates triggered by user e.g. an "onClick"-Event or server-side triggered updates (Server Push) can use this persistent WebSocket channel.
In order to see the difference between Comet and WebSocket Server Push you can inspect the current network activity
in your browser's developer tools before and after changing the Server Push Mode above - e.g. in Chrome
websocket.zul
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?> <zk> <style src="/widgets/server_push/websocket/style.css.dsp"/> <div viewModel="@id('vm') @init('demo.server_push.websocket.RobotTrackViewModel')"> <hlayout valign="middle"> <button label="@load(vm.running ? 'Stop' : 'Start')" onClick="@command(vm.running ? 'stop' : 'start')"/> <separator orient="horizontal"/> Push Interval: <slider minpos="200" maxpos="2000" curpos="500" slidingtext="{0} ms" width="160px" onScroll="@global-command('updateInterval', interval=event.pos)"/> </hlayout> <separator/> <hlayout> <div sclass="mapArea"> <div sclass="@load(('robot' += (vm.running ? '' : ' idle'))) " top="@load((vm.position.y += '%'))" left="@load((vm.position.x += '%'))"/> </div> <div> X: <decimalbox readonly="true" value="@load(vm.position.x)" format="#.000" width="55px"/> <separator/> Y: <decimalbox readonly="true" value="@load(vm.position.y)" format="#.000" width="55px"/> </div> </hlayout> </div> </zk> ctrl.zul
<vlayout apply="demo.server_push.websocket.ServerPushCtrl"> <groupbox title="Server Push Mode"> <radiogroup id="serverPushMode" orient="vertical"> <radio value="websocket" label="WebSocket (new in 8.5)" checked="true"/> <separator/> <radio value="comet" label="Comet"/> <separator/> </radiogroup> </groupbox> </vlayout> RobotTrackViewModel.java
package demo.server_push.websocket; import org.zkoss.bind.BindUtils; import org.zkoss.bind.annotation.*; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class RobotTrackViewModel { private static final String RUNNING = "running"; public static final String POSITION = "position"; public static final String INTERVAL = "interval"; public static final String UPDATE_INTERVAL = "updateInterval"; private Coord position = new Coord(50, 50); private AtomicBoolean running = new AtomicBoolean(false); private int interval = 500; @Command @NotifyChange(RUNNING) public void start(@ContextParam(ContextType.DESKTOP) Desktop desktop) { updatePosition(); this.running.set(true); startBackgroundThread(desktop); } @Command @NotifyChange(RUNNING) public void stop(@ContextParam(ContextType.DESKTOP) Desktop desktop) { this.running.set(false); } @GlobalCommand(UPDATE_INTERVAL) @NotifyChange(INTERVAL) public void updateInterval(@BindingParam("interval") int interval) { this.interval = Math.min(Math.max(interval, 200), 2000); } private void startBackgroundThread(Desktop desktop) { new Thread(() -> { while (running.get()) { try { activated(desktop, () -> { this.updatePosition(); }); Thread.sleep(interval); } catch(Exception e) { //leave background thread, no error handling in this Demo return; } } }).start(); } private void activated(Desktop desktop, Runnable task) { try { Executions.activate(desktop); task.run(); } catch (Exception e) { throw new RuntimeException(e); } finally { Executions.deactivate(desktop); } } private void updatePosition() { double now = (double) System.nanoTime() / TimeUnit.SECONDS.toNanos(1); this.position = new Coord(Math.cos(now / Math.PI / 2.0) * 40 + 50, Math.sin(now / Math.PI / 2.0) * 40 + 50); BindUtils.postNotifyChange(null, null, this, POSITION); } public boolean getRunning() { return running.get(); } public Coord getPosition() { return position; } } ServerPushCtrl.java
package demo.server_push.websocket; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.event.CheckEvent; import org.zkoss.zk.ui.select.SelectorComposer; import org.zkoss.zk.ui.select.annotation.Listen; import org.zkoss.zk.ui.sys.DesktopCtrl; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zkmax.au.websocket.WebSocketServerPush; import org.zkoss.zkmax.ui.comet.CometServerPush; import org.zkoss.zul.Radio; import javax.servlet.http.HttpServletRequest; /** * This controller allows toggling between websocket and comet mode. ONLY for demonstration purposes. * In a real application the fallback to comet will happen automatically if websockets are not available */ public class ServerPushCtrl extends SelectorComposer { @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); Desktop desktop = Executions.getCurrent().getDesktop(); desktop.enableServerPush(true); DesktopCtrl desktopCtrl = (DesktopCtrl) desktop; } @Listen("onCheck=#serverPushMode") public void toggleWebsocket(CheckEvent event) { Execution execution = Executions.getCurrent(); Desktop desktop = execution.getDesktop(); DesktopCtrl desktopCtrl = (DesktopCtrl) desktop; boolean enabled = event.isChecked(); String mode = ((Radio)event.getTarget()).getValue(); switch (mode) { case "comet": { desktop.enableServerPush(false); ((DesktopCtrl) desktop).enableServerPush(new CometServerPush()); Clients.evalJavaScript("zWs.stop()"); break; } case "websocket": { desktop.enableServerPush(false); ((DesktopCtrl)desktop).enableServerPush(new WebSocketServerPush()); String wsurl = desktop.getWebApp().getAttribute("websocketUrl").toString(); boolean secure = ((HttpServletRequest) execution.getNativeRequest()).isSecure(); Clients.evalJavaScript(String.format("zWs.start('%s', %s);", wsurl, secure)); break; } } } } Coord.java
package demo.server_push.websocket; public class Coord { private double x; private double y; public Coord(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } }
Copyright © 2005-2024 Potix Corporation All rights reserved.
|
Processing... |