Server Push and the Dial Chart
Sândalo Bessa, Engineer, Sansys Company
March 15 2011
ZK 5.0
Introduction
In this Small Talk we will discuss two innovative tools for dealing with web development. The first is the concept of "Server Push". And the second is the use of the graph, "Dial Chart". "Server Push" opens a new paradigm in how we build web systems, increasing the range of options for web solutions. However, few frameworks provide such an intuitive and easy way to use that technology. Moreover the use of graphics has become an invaluable tool for creating rich interfaces and working with "Server Push" can make the systems much more interesting. In this article we will show how easy it is to integrate these two features using ZK.
The example
To demonstrate the use of these new features, I built a small application that will measure the percentage of memory and cpu usage of the application server.
The index.zul file
The file below is very simple, and demonstrates how easy it is to integrate the view layer and control layer. Most of the lines is intended to generation layout and only four are actually needed to integrate server-push and dial chart.
<window width="100%" id="d">
<grid pagingPosition="top" width="100%" align="center" style="horizontal-align:center">
<rows height="100%">
<row align="center" valign="top" style="horizontal-align:center" spans="2" zclass="x">
<label id="label" value="Monitoring" style="horizontal-align:center;font-size:30px"></label>
</row>
<row align="center" valign="top" style="horizontal-align:center" zclass="x">
<chart id="dial" title="Dial Plot" width="300px" height="300px" type="dial" threeD="false" fgAlpha="128" />
<chart id="dial2" title="Dial Plot" width="300px" height="300px" type="dial" style="backgrooud-color:blue" threeD="false" fgAlpha="128" />
</row>
<row align="center" valign="top" style="horizontal-align:center" spans="2" zclass="x">
<button onClick="br.minasgerais.demos.zk.DialWindow.startMonitoring(dial,dial2)" label="START" width="200px" height="40px"></button>
</row>
<row align="center" valign="top" style="horizontal-align:center" spans="2" zclass="x">
<button onClick="br.minasgerais.demos.zk.DialWindow.stoptMonitoring()" label="STOP" width="200px" height="40px"></button>
</row>
</rows>
</grid>
</window>
Only these lines are necessary,
<chart id="dial" title="Dial Plot" width="300px" height="300px" type="dial" threeD="false" fgAlpha="128" />
<chart id="dial2" title="Dial Plot" width="300px" height="300px" type="dial" style="backgrooud-color:blue" threeD="false" fgAlpha="128" />
<button onClick="br.minasgerais.demos.zk.DialWindow.startMonitoring(dial,dial2)" label="START" width="200px" height="40px"></button>
<button onClick="br.minasgerais.demos.zk.DialWindow.stoptMonitoring()" label="STOP" width="200px" height="40px"></button>
On server-side
To better organize this talk, I divided the processing on the server side into three classes. However, only one deserves special attention. The other two are just to illustrate the example.
Starting the service
The two methods of the class below are triggered by buttons placed on the page index.zul. The main function of this method is to enable and disable the conversation between the server and client page.
package br.minasgerais.demos.zk;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.Chart;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;
public class DialWindow extends Window {
public static void startMonitoring(Chart chartMemo,Chart chartCPU) throws InterruptedException {
final Desktop desktop = Executions.getCurrent().getDesktop();
if (desktop.isServerPushEnabled()) {
Messagebox.show("Already started");
} else {
desktop.enableServerPush(true);
DialModel dialmodel = new DialModel();
DialModelScale scale = dialmodel.newScale(0.0, 100.0, -120.0, -300.0, 10.0, 10);// scale's configuration data
scale.setText("Memory Usage(%)");
scale.newRange(80, 100, "#FF0000", 0.83, 0.89);
scale.newRange(60, 80, "#FFC426", 0.83, 0.89);
chartMemo.setModel(dialmodel);
DialModel dialmodelCPU = new DialModel();
DialModelScale scaleCPU = dialmodelCPU.newScale(0.0, 100.0, -120.0, -300.0, 10.0, 10);// scale's configuration data
scaleCPU.setText("CPU Usage(%)");
scaleCPU.newRange(80, 100, "#FF0000", 0.83, 0.89);
scaleCPU.newRange(60, 80, "#FFC426", 0.83, 0.89);
chartCPU.setModel(dialmodelCPU);
new WorkingThread(chartMemo, chartCPU).start();
}
}
public static void stoptMonitoring() throws InterruptedException {
final Desktop desktop = Executions.getCurrent().getDesktop();
if (!desktop.isServerPushEnabled()) {
Messagebox.show("Already stoped");
} else {
desktop.enableServerPush(false);
}
}
}
Working
The next class(WorkingThread) will use a thread to do the job of monitoring. Every second thread the thread will wake up and do the monitoring. Note that the start method takes two charts, which must be updated every second with the monitoring information.
package br.minasgerais.demos.zk;
import org.zkoss.lang.Threads;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.DesktopUnavailableException;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.Chart;
public class WorkingThread extends Thread {
private final Desktop _desktop;
private final Chart _dialMemo;
private final Chart _dialCPU;
public WorkingThread(Chart dialMemo, Chart dialCPU) {
_desktop = dialMemo.getDesktop();
_dialMemo = dialMemo;
_dialCPU = dialCPU;
}
public void run() {
try {
while (true) {
if (_dialMemo.getDesktop() == null || !_desktop.isServerPushEnabled()) {
_desktop.enableServerPush(false);
return;
}
Executions.activate(_desktop);
try {
Monitor.refreshDialMemo(_dialMemo);
Monitor.refreshDialCPU(_dialCPU);
} finally {
Executions.deactivate(_desktop);
}
Threads.sleep(1000);
}
} catch (DesktopUnavailableException ex) {
System.out.println("The server push thread interrupted");
} catch (InterruptedException ex) {
System.out.println("The server push thread interrupted");
}
}
}
The run method starts the process and controls the scheduling of monitoring. Every second, another class is called to update the processing.
Capturing data
The Monitor class is used to retrieve data related to operating system features such as: CPU usage and memory usage. However, this class is only for the purpose of this small talk and does not have the required accuracy for a real application.
package br.minasgerais.demos.zk;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import org.zkoss.zul.Chart;
import org.zkoss.zul.DialModel;
import org.zkoss.zul.DialModelScale;
import sun.management.ManagementFactory;
import com.sun.management.OperatingSystemMXBean;
public class Monitor {
private static final int CPUTIME = 1000;
private static final int PERCENT = 100;
private static final int FAULTLENGTH = 10;
public static void refreshDialMemo(Chart chart) {
int val = getMemoryUsage();
DialModel dialmodel = (DialModel) chart.getModel();
DialModelScale scale = dialmodel.getScale(0);// scale's configuration data
if (val > 100) {
val = 100;
} else if (val < 0) {
val = 0;
}
scale.setValue(val);
if (val > 80) {
scale.setNeedleColor(scale.getRange(0).getRangeColor());
} else if (val > 60) {
scale.setNeedleColor(scale.getRange(1).getRangeColor());
} else {
scale.setNeedleColor(dialmodel.getFrameFgColor());
}
chart.setModel(dialmodel);
}
public static void refreshDialCPU(Chart chart) {
int val = getCpuRatioForWindows();
DialModel dialmodel = (DialModel) chart.getModel();
DialModelScale scale = dialmodel.getScale(0);// scale's configuration data
if (val > 100) {
val = 100;
} else if (val < 0) {
val = 0;
}
scale.setValue(val);
if (val > 80) {
scale.setNeedleColor(scale.getRange(0).getRangeColor());
} else if (val > 60) {
scale.setNeedleColor(scale.getRange(1).getRangeColor());
} else {
scale.setNeedleColor(dialmodel.getFrameFgColor());
}
chart.setModel(dialmodel);
}
private static int getMemoryUsage() {
OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
long totalMemory = osmxb.getTotalPhysicalMemorySize();
long freePhysicalMemorySize = osmxb.getFreePhysicalMemorySize();
Double memoryUsage = (Double) (1 - freePhysicalMemorySize * 1.0 / totalMemory) * 100;
int val = memoryUsage.intValue();
return val;
}
public static int getCpuRatioForWindows() {
try {
String procCmd = System.getenv("windir") + "\\system32\\wbem\\wmic.exe process get Caption, KernelModeTime, ReadOperationCount, ThreadCount, UserModeTime, WriteOperationCount";
// Get process information
long[] c0 = readCpu(Runtime.getRuntime().exec(procCmd));
Thread.sleep(CPUTIME);
long[] c1 = readCpu(Runtime.getRuntime().exec(procCmd));
if (c0 != null && c1 != null) {
long idletime = c1[0] - c0[0];
long busytime = c1[1] - c0[1];
return Double.valueOf(PERCENT * (busytime) * 1.0 / (busytime + idletime)).intValue();
} else {
return 0;
}
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
// Read the cpu information
private static long[] readCpu(final Process proc) {
long[] retn = new long[2];
try {
proc.getOutputStream().close();
InputStreamReader ir = new InputStreamReader(proc.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
String line = input.readLine();
if (line == null || line.length() < FAULTLENGTH || line.equals("")) {
return null;
}
String[] labels = line.replaceAll("\\s\\s+",",").split(",");
long idletime = 0;
long kneltime = 0;
long usertime = 0;
String[] fields = null;
while ((line = input.readLine()) != null) {
if (line.length() == 0) {
continue;
}
fields = line.replaceAll("\\s\\s+",",").split(",");
String caption = fields[0];
if (caption.indexOf("WMIC.exe") >= 0) {
continue;
}
//KernelModeTime
String s1 = fields[1];
//UserModeTime
String s2 = fields[4];
if (caption.equals("System Idle Process") || caption.equals("System")){
if (s1.length() > 0)
idletime += Long.valueOf(s1).longValue();
if (s2.length() > 0)
idletime += Long.valueOf(s2).longValue();
continue;
}
if (s1.length() > 0)
kneltime += Long.valueOf(s1).longValue();
if (s2.length() > 0)
usertime += Long.valueOf(s2).longValue();
}
retn[0] = idletime;
retn[1] = kneltime + usertime;
return retn;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
proc.getInputStream().close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
Conclusion
In this small talk we demonstrated how easy it is to integrate "server push" and the "dial chart" using ZK. I hope this work will serve as a base for people who would like to explore both these tools.
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |