Theme:
Processing...
Description & Source Code

Comet server push is preferred over using polling when an application needs low latency events delivered from the server to the browser. Instead of repeatedly polling for new events, Ajax applications with Comet rely on a persistent HTTP connection between server and client.

comet.zul
<zk>
	<style dynamic="true">
		.comet-news {
			color: #0A94F3;
		}
	</style>
	<vbox apply="demo.server_push.comet.CometServerPushController"> 
		<hbox>
			<button id="start" label="Start" width="98px" /> 
			<button id="stop" label="Stop" width="98px" />
		</hbox>
		<image id="info" width="200px" height="200px"/>
	</vbox>
</zk>
			
ctrl.zul
<vlayout>
	Change maximum diameter:
	<listbox id="maxDiameter" rows="1" mold="select">
		<listitem label="20" value="20" selected="true" />
		<listitem label="40" value="40" />
		<listitem label="60" value="60" />
	</listbox>
</vlayout>
CometServerPushController.java
package demo.server_push.comet;

import java.util.Timer;
import java.util.TimerTask;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.DesktopUnavailableException;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Image;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Messagebox;

public class CometServerPushController extends SelectorComposer<Component> {

	@Wire
	private Listbox maxDiameter;

	@Wire
	private Image info;
	
	private Timer timer;
	private InfoRenderer infoRenderer;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		infoRenderer = new InfoRenderer(200, 200);
	}

	@Listen("onClick = #start") 
	public void startWorker() {
		final Desktop desktop = Executions.getCurrent().getDesktop();
		if (desktop.isServerPushEnabled()) {
			Messagebox.show("Already started");
		} else {
			desktop.enableServerPush(true);
			if(timer != null)timer.cancel();
			timer = new Timer();
			timer.schedule(createUpdateTask(), 0, 1000);
		}
	}
	
	@Listen("onClick = #stop") 
	public void stopWorker() {
		final Desktop desktop = Executions.getCurrent().getDesktop();
		if (desktop.isServerPushEnabled()) {
			desktop.enableServerPush(false);
		} else {
			Messagebox.show("Already stopped");
		}
		timer.cancel();
	}

	private TimerTask createUpdateTask() {
		return new TimerTask() {
			@Override
			public void run() {
				updateInfo();
			}
		};
	}

	private void updateInfo() {
		try {
			Desktop desktop = info.getDesktop();
			if(desktop == null) {
				timer.cancel();
				return;
			}
			
			Executions.activate(desktop);
			try {
				int drawSize = Integer.parseInt((String)maxDiameter.getSelectedItem().getValue());
				((Image) info).setContent(infoRenderer.render(drawSize));
			} finally {
				Executions.deactivate(desktop);
			}
		} catch (DesktopUnavailableException ex) {
			System.out.println("Desktop currently unavailable");
			timer.cancel();
		} catch (InterruptedException e) {
			System.out.println("The server push thread interrupted");
			timer.cancel();
		}
	}
}
InfoRenderer.java
package demo.server_push.comet;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.ArrayList;
import java.util.List;

public class InfoRenderer {

	private static Color colors[] = { Color.blue, Color.cyan, Color.green,
			Color.magenta, Color.orange, Color.pink, Color.red, Color.yellow,
			Color.lightGray, Color.white };

	private Stroke lineStroke = new BasicStroke(1.5f);

	private int width;
	private int height;

	public InfoRenderer(int width, int height) {
		this.width = width;
		this.height = height;
	}

	public RenderedImage render(int drawSize) {
		List<Shape> shapes = prepareShapes(drawSize);
		
		BufferedImage bufferedImage = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);

		Graphics2D g2d = createGraphics2D(bufferedImage);
		
		g2d.setStroke(lineStroke);
		for (Shape shape : shapes) {
			g2d.setColor(colors[(int)(Math.random() * colors.length)]);
			g2d.draw(shape);
		}
		
		g2d.dispose();
		return bufferedImage;
	}

	private List<Shape> prepareShapes(int drawSize) {
		int numShapes = 480 / drawSize;
		List<Shape> shapes = new ArrayList<Shape>(numShapes);
		for (int i = 0; i < numShapes; i++) {
			shapes.add(randomEllipse(drawSize));
		}
		return shapes;
	}

	private Shape randomEllipse(int drawSize) {
		float size = (float) (Math.random() * drawSize);
		float x = (float) (Math.random() * width) - size * 0.5f;
		float y = (float) (Math.random() * height) - size * 0.5f;
		return new Ellipse2D.Float(x, y, size, size);
	}

	private Graphics2D createGraphics2D(BufferedImage bufferedImage) {
		Graphics2D g2d = bufferedImage.createGraphics();
		g2d.setBackground(Color.BLACK);
		g2d.clearRect(0, 0, width, height);
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		return g2d;
	}
}