Monday, December 16, 2013
Getting Started of GWT
Simple Note
Just follow the official Getting Started document: http://www.gwtproject.org/gettingstarted.html
(MyWebApp can be an absolute path when creating webapp)
Really excellent document, I cannot say anything else further to make it better.
Sunday, December 15, 2013
ZK JMeter: Loop Controller with AURequest
Simple Note
This script is modified from TestSimpleRecord.jmx at https://github.com/benbai123/ZK_Practice/tree/master/General/JMeter/jmeterTestingScript
* Counter Increment: 2, since there are 2 au requests under loop controller
(i.e., Increment should match the amount of au requests under loop controller)
* Use ${ZSID_C} for ZK-SID of the 1st au request under loop controller,
* Use ${__intSum(${ZSID_C},1,ZSID_C)} for ZK-SID of the 2nd (and after if any) au requests under loop controller.
Pre-Request
Record JMeter Testing Script for ZK
http://ben-bai.blogspot.tw/2013/12/record-jmeter-testing-script-for-zk.html
Modify Demo
http://screencast.com/t/Uut8mUa3b0G
Download
Testing Project
https://github.com/benbai123/ZK_Practice/tree/master/General/JMeter/TestProjForJMeter
Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/General/JMeter/demo/AURequestLoop.swf
Jmeter Script
TestLoopAURequest.jmx at https://github.com/benbai123/ZK_Practice/tree/master/General/JMeter/jmeterTestingScript
Saturday, December 14, 2013
Record JMeter Testing Script for ZK
Simple Note
Tested with JMeter 2.9 and ZK 6.5.2
* Input address and port in HTTP Request Default so they will not be recorded in request and can be changed easily if address/port are changed.
* Need to remove UserDefinedVariable (dtid: 0) before each record then add it back.
* Add parameter tdtid: ${__intSum(${dtid},1,dtid)} to the request for loading page
* Change parameter dtid to ${dtid} for each au request.
* Included pattern:
.*/zkau.*
.*\.zul
.*/TestProjForJMeter.* (for welcome page or any other url matched by servlet, index.zul here)
* Excluded pattern:
.*/zkau/web/.* (ignore the requests for load resources)
Record Demo
http://screencast.com/t/4rUMLerhQg
Recorded requests:
1. Page load
2. Click "click to start tast"
3. Click "increase first intbox"
4. Click "increase second intbox" (it is wrong in the demo, fixed in latest project)
5. Click "increase first intbox" again
6. Click "increase second intbox" again
7. Remove desktop
Reference
ZK Document
http://books.zkoss.org/wiki/Small_Talks/2012/January/Execute_a_Loading_or_Performance_Test_on_ZK_using_JMeter
JMeter User Manual
http://jmeter.apache.org/usermanual/functions.html#__intSum
Download
Testing Project
https://github.com/benbai123/ZK_Practice/tree/master/General/JMeter/TestProjForJMeter
Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/General/JMeter/demo/SimpleRecord.swf
Jmeter Script
TestSimpleRecord.jmx at https://github.com/benbai123/ZK_Practice/tree/master/General/JMeter/jmeterTestingScript
Sunday, December 1, 2013
JAVA: ZIp or Unzip Files/Folders with zip4j
Simple Note
Testing Project extract resource/test.xlsx to tmp folder then compress files/folders under tmp folder to dist/test.xlsx with zip4j.
dist/test.xlsx is readable by MS Excel.
Testing Project
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/JAVA/IO/FileProcessing/ZIPorUNZIP
References
examples downloaded from http://www.lingala.net/zip4j/download.php
Saturday, November 30, 2013
Write ByteArrayOutputStream to File
Simple Note
test/Test.java shows how to write a ByteArrayOutputStream to a File (dist/test.txt)
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/JAVA/IO/WriteByteAarrayOutputStreamToFile
Polymorphism in C++
Simple Note
From reference:
One of the key features of derived classes is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature, that brings Object Oriented Methodologies to its full potential.
Also refer to http://ben-bai.blogspot.tw/2013/11/virtual-keyword-in-c.html
Tested with Dev-C++ 4.9.9.2
Code
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Extend_and_scope/polymorphism/basic/polymorphism_basic.cpp
Result
Refer to http://ben-bai.blogspot.tw/2013/11/virtual-keyword-in-c.html
Reference
http://www.cplusplus.com/doc/tutorial/polymorphism/
From reference:
One of the key features of derived classes is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature, that brings Object Oriented Methodologies to its full potential.
Also refer to http://ben-bai.blogspot.tw/2013/11/virtual-keyword-in-c.html
Tested with Dev-C++ 4.9.9.2
Code
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Extend_and_scope/polymorphism/basic/polymorphism_basic.cpp
Result
Refer to http://ben-bai.blogspot.tw/2013/11/virtual-keyword-in-c.html
Reference
http://www.cplusplus.com/doc/tutorial/polymorphism/
Friday, November 29, 2013
Virtual Keyword in C++
Simple Note
Use virtual keyword to define a function in a class that can be redefined in its derived class.
The only valid declaration (virtual or non-virtual) is the declaration within first level Class (first level here means the base class), redefine it in derived class will be ignored.
Also refer to http://ben-bai.blogspot.tw/2013/11/polymorphism-in-c.html
Tested with Dev-C++ 4.9.9.2
Program
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Extend_and_scope/virtual/virtual.cpp
Result
Reference
http://www.cplusplus.com/doc/tutorial/polymorphism/
Use virtual keyword to define a function in a class that can be redefined in its derived class.
The only valid declaration (virtual or non-virtual) is the declaration within first level Class (first level here means the base class), redefine it in derived class will be ignored.
Also refer to http://ben-bai.blogspot.tw/2013/11/polymorphism-in-c.html
Tested with Dev-C++ 4.9.9.2
Program
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Extend_and_scope/virtual/virtual.cpp
Result
Reference
http://www.cplusplus.com/doc/tutorial/polymorphism/
Sunday, November 10, 2013
Font Awesome Getting Started
Simple Note
Result
Steps
1. Go to official site http://fontawesome.io/
2. Download and extract file to target folder
3. Follow Getting started page (http://fontawesome.io/get-started/), create html page
4. See icons page (http://fontawesome.io/icons/), write some icons and see whether it works well.
Download
Testing folder at github
https://github.com/benbai123/HTML_CSS_Javascript_practice/tree/master/bootstrap/font_awesome/getting_started
Saturday, November 9, 2013
Bootstrap Getting Started
Simple Note
Steps
0. Preview official Getting started page (http://getbootstrap.com/getting-started/)
1. Go to github page of bootstrap (https://github.com/twbs/bootstrap) and download latest release (see Quick start section at that page)
NOTE: For some reason the download link at official site (http://getbootstrap.com/) not works for me.
2. Unzip downloaded file and copy dist folder into target folder.
3. Copy an official example (e.g., http://getbootstrap.com/examples/signin/) to see whether it works.
Done.
References
Bootstrap Getting Started
http://getbootstrap.com/getting-started/
Bootstrap github
https://github.com/twbs/bootstrap
Download
Testing folder at github
https://github.com/benbai123/HTML_CSS_Javascript_practice/tree/master/bootstrap/getting_started
Sunday, October 27, 2013
Compile LESS with Java
Simple Note
From official site:
For production and especially if performance is important, we recommend pre-compiling using node or one of the many third party tools.
This article describe how to compile .less file to .css file in java code on server start-up.
Pre-request
LESS Getting Started
http://ben-bai.blogspot.tw/2013/10/less-getting-started.html
Do Something on Server Startup/Shutdown
http://ben-bai.blogspot.tw/2013/10/do-something-on-server-startupshutdown.html
Result
The same as the result in previous post.
Required jars
lesscss-engine-1.3.3.jar
http://mvnrepository.com/artifact/com.asual.lesscss/lesscss-engine/1.3.3
rhino-1.7R4.jar
http://mvnrepository.com/artifact/org.mozilla/rhino/1.7R4
commons-logging-1.1.1.jar
http://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1
Code
test.less
Copied from previous post, nothing changed.
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/Practice/LESS/CompileLESSWithJava/WebContent/resources/less/test.less
less_test.html
Modified from previous post, load compiled css file instead of less file and do not load less.js.
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/Practice/LESS/CompileLESSWithJava/WebContent/less_test.html
TestServletContextListener.java
Implements ServletContextListener, will compile less file to css file on server start-up.
package test;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.asual.lesscss.LessException;
import test.util.LESSUtils;
public class TestServletContextListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// do nothing on server shutdown
}
// compile less on server startup
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
String webContentPath = sce.getServletContext().getRealPath("/");
LESSUtils.compile(new File(webContentPath + File.separator + "resources" + File.separator + "less" + File.separator + "test.less"),
new File(webContentPath + File.separator + "resources" + File.separator + "css" + File.separator + "test.css"));
} catch (LessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
LESSUtils.java
Contains utility function to compile less file, say Utils means (probably) will add more functions in future work.
package test.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.asual.lesscss.LessEngine;
import com.asual.lesscss.LessException;
public class LESSUtils {
public static void compile (File lessFile, File cssFile) throws LessException, IOException {
if (lessFile == null
|| !lessFile.exists()) {
// less file should exist
throw new FileNotFoundException("LESS file not exists !");
}
// create less engine
LessEngine engine = new LessEngine();
if (!cssFile.getParentFile().exists()) {
// create folders for css file
boolean cssFileCreated = cssFile.getParentFile().mkdirs();
if (!cssFileCreated) {
throw new IOException("Folders for CSS file are not created !");
}
}
// compile less file to css file
engine.compile(lessFile, cssFile);
}
}
web.xml
Define listener.
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/Practice/LESS/CompileLESSWithJava/WebContent/WEB-INF/web.xml
References
Java Compiler for Less CSS?
http://stackoverflow.com/questions/9739724/java-compiler-for-less-css
LESS Engine
https://github.com/asual/lesscss-engine
Download
Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/LESS/CompileLESSWithJava
Saturday, October 26, 2013
Do Something on Server Startup/Shutdown
Simple Note
Code
TestServletContextListener.java
Simply output message to console while server startup/shutdown.
package test;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class TestServletContextListener implements ServletContextListener {
// do something on server shutdown
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("On server shutdown");
}
// do something on server startup
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("On server startup");
}
}
web.xml
Define listener.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>DoStOnServerStartupOrShutdown</display-name>
<!-- Define Listener -->
<listener>
<listener-class>test.TestServletContextListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
References
Servlet Filters and Event Listeners
http://docs.oracle.com/cd/B14099_19/web.1012/b14017/filters.htm
Debug ServletContextListener.contextDestroyed() by setting the breaking point in eclipse
http://stackoverflow.com/questions/8802457/debug-servletcontextlistener-contextdestroyed-by-setting-the-breaking-point-in
Download
Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/BasicServletPractice/Projects/DoStOnServerStartupOrShutdown
Sunday, October 20, 2013
LESS Recursive Mixin
Simple Note
Pre-request
LESS Getting Started
http://ben-bai.blogspot.tw/2013/10/less-getting-started.html
Code
It is funny to write stylesheet with recursive function but probably not a good practice.
index.html
Simple html page
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/Practice/LESS/RecursiveMixin/WebContent/index.html
test.less
Define a mixin .box to create nested selectors recursively.
// comments to displayed in compiled css
/* compiled css */
// define variable
@base-color: #EEEEEE;
@dec-base: #111111;
// define Mixin behave like function
// parameter @idx with default value 1
.box(@idx: 1) when (@idx < 6) {
// create selector
.class@{idx} {
// border with calculated border color,
// e.g., #EEEEEE - #111111*1*2 = #CCCCCC
border: 10px solid (@base-color - @dec-base*@idx*2);
display: inline-block;
// increase @idx,
// call .box recursively to create nested selectors
.box(@idx + 1);
}
}
// call .box with default @idx
.box();
Result
Click to view large image
References
Official site
http://lesscss.org/
Do a loop with LESS css
http://blog.thehippo.de/2012/04/programming/do-a-loop-with-less-css/
Download
Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/LESS/RecursiveMixin
Saturday, October 19, 2013
LESS Getting Started
Simple Note
Code
Actually this is a pure html case, the only reason that I put it in a Dynamic Web Project is that I want to use the exiting run-jetty-run plugin in my Eclipse since I am lazy to config a server for it.
less_test.html
Simple html page, declare less file and load less.js. Say 'declare' since the .less file seems loaded by less.js with AJAX instead of load by link directly.
<html>
<head>
<!-- include .less file, need to include it before less.js -->
<link rel="stylesheet/less" type="text/css" href="resources/test.less" />
<!-- include less.js, will compile .less file at client side -->
<script src="resources/less-1.4.2.min.js" type="text/javascript"></script>
</head>
<body>
<!-- nested div -->
<div class="class1">
<div class="class2">
<div class="class3">
<div class="class4">
<div class="class5"></div>
</div>
</div>
</div>
</div>
</body>
</html>
test.less
Stylesheet written by LESS.
// use // to comment in less file only
// use /* */ to commment in less and compiled css
/* compiled css */
// define variable
@base-color: #EEEEEE;
// define Mixin behave like function
// parameter @dec with default value #111111
.box(@dec: #111111) {
// border with calculated border color,
// e.g., #EEEEEE - #111111*2 = #CCCCCC
border: 10px solid (@base-color - @dec*2);
display: inline-block;
}
// nest selectors inside other selectors
.class1 {
.box; // include .box with default value of parameter
.class2 { // .class1 .class2
.box(#222222); // include .box with @dec = #222222
.class3 { // .class1 .class2 .class3
.box(#333333);
.class4 { // .class1 .class2 .class3 .class4
.box(#444444);
.class5 { // .class1 .class2 .class3 .class4 .class5
.box(#555555);
}
}
}
}
}
And less.js downloaded from official site of LESS.
Result
Click to view large image
Reference
LESS
http://lesscss.org/
Download
Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/LESS/GettingStarted
Sunday, October 6, 2013
WebSocketServerPush vs ZKComet Performance Testing
Simple Note
Testing Environment
Server:
Tomcat 7.0.42
ZK Version:
6.5.2
WebSocket ServerPush Implementation:
ZK ServerPush with WebSocket - Rework
http://ben-bai.blogspot.tw/2013/10/zk-serverpush-with-websocket-rework.html
Testing Steps
1. Start Tomcat
2. Open visualVM
3. Open 5 testing pages
There are 2 cases for WebSocket ServerPush, push to desktop separately (index.zul) and push to all desktop at once (index_per_desktop.zul), 1 case for ZK Comet (push to desktop separately - index.zul)
4. Wait 2 minutes
5. Take snapshot
Testing Result
(Click snapshot to view large image)
Executing Result:
ZK Comet - index.zul
WebSocket ServerPush - index.zul
WebSocket ServerPush - index_per_desktop.zul
JVisualVM
ZK Comet - index.zul
WebSocket ServerPush - index.zul
WebSocket ServerPush - index_per_desktop.zul
Observation
Execution Speed
WebSocket ServerPush is two times faster than ZK Comet
Memory Consumption
Highest: WebSocket ServerPush (215/187 MB for per-desktop/push-to-all) is two times higher than ZK Comet (100MB), resonable since WebSocket is faster, do more work and use more memory at the same time.
Lowest: Almost the same (20 ~ 23 MB), most of the memory can be GCed.
CPU Usage
WebSocket AU (2%~8%/1%~6% for per-desktop/push-to-all after page loaded) is better than ZK Comet (1%~24% after page loaded).
Conclution
WebSocket do double work with 1/4 ~ 1/3 CPU Usage and double Memory Consumption.
Download
Testing projects at github
https://github.com/benbai123/ZK_Practice/tree/master/General/Performance_Testing/WebSocketServerPush_vs_ZKComet
Saturday, October 5, 2013
ZK ServerPush with WebSocket - Rework
Introduction
This article describe how to use WebSocket to do ServerPush in ZK MVVM, modified from previous article ZK: Server Push with WebSocket (http://ben-bai.blogspot.tw/2013/07/zk-server-push-with-websocket.html) with the desktop based implementation from ZK AURequest with WebSocket (http://ben-bai.blogspot.tw/2013/09/zk-aurequest-with-websocket.html).
It integrates WebSocket in ZK for ServerPush in more general way and has better performance (compared with previous component based version).
Environment: Tomcat 7.0.42, ZK 6.5.2
NOTE: This is just a POC with some customized components.
Pre-request
ZK AURequest with WebSocket
http://ben-bai.blogspot.tw/2013/09/zk-aurequest-with-websocket.html
ZK Create Helper Tag to Make Programmer Happier
http://ben-bai.blogspot.tw/2013/09/zkcreatehelpertag-to.html
ZK Basic MVVM Pattern
http://ben-bai.blogspot.tw/2012/12/zk-basic-mvvm-pattern.html
Result
View demo on line
http://screencast.com/t/zp1rcVhYa
Program
There are lots of source files, will only post code for some of them and post link for others.
index.zul
Entry page
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/WebContent/index.zul
push_to_all.zul
Push value to all desktop (i.e., Application scope) with button click.
<zk>
<!-- Tested with ZK 6.5.2 -->
<window apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('test.TestPushToAllVM')">
Counter:
<!-- intbox, listen to positive integer in the begining -->
<intbox readonly="true" />
<contextBinding field="value" context="@load(vm.counter)" />
listen to: <label value="@load(vm.counter)" />
<div />
inverse Counter:
<!-- intbox, listen to negative integer in the begining -->
<intbox id="inverseCounter" readonly="true" />
<contextBinding field="value" context="@load(vm.inverseCounter)" />
listen to: <label value="@load(vm.inverseCounter)" />
<div />
<!-- update value of positive/negative integer -->
<button id="updateCounterBtn" label="updaet counter and negative counter"
onClick="@command('updateCounter')" />
<!-- switch listening context of the two intboxess above -->
<button label="switch context" id="switchBtn"
onClick="@command('switchCounter')" />
</window>
</zk>
test.TestPushToAllVM.java
VM for push_to_all.zul
package test;
import impl.serverpush.ServerPushUtil;
import java.util.concurrent.atomic.AtomicInteger;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
/** Tested with ZK 6.5.2
*
* @author benbai123
*
*/
public class TestPushToAllVM {
/** context listened by intboxes */
private String _counter = "positive";
private String _inverseCounter = "negative";
/** desktop of this VM */
private Desktop targetDesktop;
/** counter used to update positive/negative integer */
private static AtomicInteger _cntCounter = new AtomicInteger(0);
// getters
public String getCounter () {
return _counter;
}
public String getInverseCounter () {
return _inverseCounter;
}
/**
* update value to context 'positive' and 'negative' via
* WebSocket
*
* All components that listen to these context will be updated
*/
@Command
public void updateCounter () {
int val = _cntCounter.incrementAndGet();
push(val, "positive", false);
push(-1*val, "negative", false);
}
/** switch listening components of 'positive' and 'negative' context
* current desktop only
*
*/
@Command
@NotifyChange({"counter", "inverseCounter"})
public void switchCounter () {
int val = _cntCounter.get();
// push to opposite (for self desktop) before switch context
push(val, "negative", true);
push(-1*val, "positive", true);
String tmp = _counter;
_counter = _inverseCounter;
_inverseCounter = tmp;
}
/** push value
*
* @param val
* @param context
*/
private void push (Object val, String context, boolean desktopOnly) {
try {
if (targetDesktop == null) {
targetDesktop = Executions.getCurrent().getDesktop();
}
if (desktopOnly) {
// push to current desktop only
ServerPushUtil.pushVlaue(val, context, targetDesktop);
} else {
// push to all desktop
ServerPushUtil.pushVlaue(val, context);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
automatic_push.zul
Update value with java thread, Desktop scope.
<zk>
<!-- Tested with ZK 6.5.2 -->
<window apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('test.TestAutoPushVM')">
Self:
<!-- intbox that will be updated -->
<intbox readonly="true" />
<contextBinding field="value" context="@load(vm.task)" />
<!-- start update -->
<button label="start" onClick="@command('start')" />
<!-- stop update -->
<button label="stop" onClick="@command('stop')" />
<!-- textbox that bind rows/cols to integer -->
<textbox />
<contextBinding id="cbd" field="@load(vm.prop)" context="@load(vm.task)" />
<button label="switch field (rows/cols)" onClick="@command('switchRowCol')" />
</window>
</zk>
test.TestAutoPushVM.java
VM for automatic_push.zul
package test;
import impl.serverpush.ServerPushUtil;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
/** Tested with ZK 6.5.2
*
* @author benbai123
*
*/
public class TestAutoPushVM {
/** Counter used to update integer */
private AtomicInteger _cnt = new AtomicInteger(0);
/** ServerPush task timer */
private Timer timer;
/** Desktop of this VM */
private Desktop targetDesktop;
/** Binded field of textbox */
private String _prop = "rows";
// getters
public String getTask () {
return "timerTask";
}
public String getProp () {
return _prop;
}
/**
* start server push with WebSocket for
* specific context "timerTask"
*
* current desktop only
*/
@Command
public void start () {
if (timer == null) {
// update once immediately when first time start
if (_cnt.get() == 0) {
push(_cnt.incrementAndGet(), "timerTask");
}
timer = new Timer();
timer.schedule(getTimerTask(), 1000, 1000);
}
}
/**
* stop server push with WebSocket for
* specific context "timerTask"
*
* current desktop only
*/
@Command
public void stop () {
if (timer != null) {
timer.cancel();
timer = null;
}
}
@Command
@NotifyChange("prop")
public void switchRowCol () {
if ("rows".equals(_prop)) {
_prop = "cols";
} else {
_prop = "rows";
}
}
// task to be scheduled to update context "timerTask" every second
private TimerTask getTimerTask () {
return new TimerTask() {
public void run () {
push(_cnt.incrementAndGet(), "timerTask");
}
};
}
/** push value to specific context
*
* @param val value to push
* @param context context to push
*/
private void push (Object val, String context) {
try {
if (targetDesktop == null) {
targetDesktop = Executions.getCurrent().getDesktop();
}
// push to current desktop only
ServerPushUtil.pushVlaue(val, context, targetDesktop);
} catch (Exception e) {
e.printStackTrace();
}
}
}
components.IWebSocketEnhancedComponent.java
Copied from AURequestWithWebSocket, completely the same.
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/components/IWebSocketEnhancedComponent.java
components.Intbox.java
Copied from AURequestWithWebSocket, change _useWebSocketAU default to true and override smartUpdate instead of setter.
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/components/Intbox.java
components.Textbox.java
Created for this POC, override smartUpdate instead of override setters separately.
Almost the same with Intbox (actually completely the same after serialVersionUID
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/components/Textbox.java
components.serverpush.IWebSocketServerPushEnhancedComponent.java
Define a "WebSocket Enhanced Component -- ServerPush type" for ServerPush pattern
package components.serverpush;
import impl.serverpush.Binding;
import components.IWebSocketEnhancedComponent;
/** Tested with ZK 6.5.2<br>
*
* Define a "WebSocket Enhanced Component -- ServerPush type"
* for ServerPush pattern
*
* @author benbai123
*
*/
public interface IWebSocketServerPushEnhancedComponent extends IWebSocketEnhancedComponent {
/** Add Binding for specific field/context pair
*
* The added Binding will be stored to a Binding List of a Component
*
* @param field field to bind
* @param context context to bind with field
* @return the Added Binding
*/
public Binding addSocketContextBinding (String field, String context);
/** Remove Binding for specific field/context pair
*
* Remove it from Binding List of a Component
*
* @param field field of field/context pair to remove
* @param context context of field/context pair to remove
*/
public void removeSocketContextBinding (String field, String context);
}
components.serverpush.Intbox.java
Created for this POC, implements IWebSocketServerPushEnhancedComponent to support WebSocket ServerPush Almost the same with Textbox (completely the same after serialVersionUID)
package components.serverpush;
import impl.serverpush.Binding;
import impl.serverpush.ServerPushUtil;
/** Tested with ZK 6.5.2<br>
*
* Created for this POC, implements IWebSocketServerPushEnhancedComponent to
* support WebSocket ServerPush
*
* Almost the same with Textbox (completely the same after serialVersionUID)
*
* @author benbai123
*
*/
public class Intbox extends components.Intbox implements IWebSocketServerPushEnhancedComponent {
private static final long serialVersionUID = -3277498174057967067L;
@Override
public Binding addSocketContextBinding(String field, String context) {
return ServerPushUtil.addContextBinding(this, field, context);
}
@Override
public void removeSocketContextBinding(String field, String context) {
ServerPushUtil.removeContextBinding(this, field, context);
}
}
components.serverpush.Textbox.java
Created for this POC, implements IWebSocketServerPushEnhancedComponent to support WebSocket ServerPush Almost the same with Intbox (completely the same after serialVersionUID)
package components.serverpush;
import impl.serverpush.Binding;
import impl.serverpush.ServerPushUtil;
/** Tested with ZK 6.5.2<br>
*
* Created for this POC, implements IWebSocketServerPushEnhancedComponent to
* support WebSocket ServerPush
*
* Almost the same with Intbox (completely the same after serialVersionUID)
*
* @author benbai123
*
*/
public class Textbox extends components.Textbox implements IWebSocketServerPushEnhancedComponent {
private static final long serialVersionUID = 2354643632056197764L;
@Override
public Binding addSocketContextBinding(String field, String context) {
return ServerPushUtil.addContextBinding(this, field, context);
}
@Override
public void removeSocketContextBinding(String field, String context) {
ServerPushUtil.removeContextBinding(this, field, context);
}
}
components.helper.ContextBinding.java
Helper component to help a component to bind field with ServerPush context
package components.helper;
import impl.serverpush.Binding;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Div;
import components.serverpush.IWebSocketServerPushEnhancedComponent;
/** Tested with ZK 6.5.2
*
* Helper component to help a component to bind field with ServerPush context
*
* @author benbai123
*
*/
public class ContextBinding extends Div {
private static final long serialVersionUID = -7156149643515776677L;
/** field to bind with context (required) */
private String _field = "";
/** context to bind with field (required) */
private String _context = "";
/** specified id of target component (optional) */
private String _target;
/** used to store current binding (relatively old) for changing field/context */
private Binding _oldBinding = null;
/** found target */
private IWebSocketServerPushEnhancedComponent _foundTarget;
@SuppressWarnings({ "unchecked", "rawtypes" })
public ContextBinding () {
// update target url while created
addEventListener(Events.ON_CREATE, new EventListener () {
public void onEvent (Event event) {
updateTargetBinding();
}
});
// do not output any html
setWidgetOverride ("redraw", "function (out) {}");
}
// setters
public void setTarget (String target) {
_target = target;
}
public void setField (String field) {
if (field == null) {
field = "";
}
if (!field.equals(_field)) {
_field = field;
updateTargetBinding();
}
}
public void setContext (String context) {
if (context == null) {
context = "";
}
if (!context.equals(_context)) {
_context = context;
updateTargetBinding();
}
}
/** Update Binding of target Component when field/context is changed
*
*/
private void updateTargetBinding () {
if (!_field.isEmpty() && !_context.isEmpty()) {
_foundTarget = findTarget();
if (_foundTarget != null) {
// remove old binding if exists
if (_oldBinding != null) {
if (_field.equals(_oldBinding.getField())
&& _context.equals(_oldBinding.getContext())) {
// already binded, do nothing
return;
}
_foundTarget.removeSocketContextBinding(_oldBinding.getField(), _oldBinding.getContext());
}
// add new binding and store it to _oldBinding
_oldBinding = _foundTarget.addSocketContextBinding(_field, _context);
}
}
}
/* package */ IWebSocketServerPushEnhancedComponent getFoundTarget () {
return _foundTarget;
}
/** Try to find target to update
* The order to try: <br>
* 1. Try to find fellow under the same space owner with specified "target" attribute.<br>
* 2. Try to find whether previous sibling is IWebSocketServerPushEnhancedComponent.<br>
* 3. Try to find whether previous sibling is ContextBinding and already found a target.<br>
* 4. Try to find whether parent is IWebSocketServerPushEnhancedComponent.<br>
*
* This way it can work without target attribute in most cases.
* @return IWebSocketServerPushEnhancedComponent if any
*/
/* package */ IWebSocketServerPushEnhancedComponent findTarget () {
Component comp;
Component previous = getPreviousSibling();
// Try to find fellow under the same space owner with specified "target" attribute.
if (_target != null && !_target.isEmpty()) {
comp = getSpaceOwner().getFellowIfAny(_target);
if (comp != null
&& (comp instanceof IWebSocketServerPushEnhancedComponent)) {
return (IWebSocketServerPushEnhancedComponent) comp;
}
}
// Try to find whether previous sibling is IWebSocketServerPushEnhancedComponent.
if (previous instanceof IWebSocketServerPushEnhancedComponent) {
return (IWebSocketServerPushEnhancedComponent) previous;
}
// Try to find whether previous sibling is ContextBinding and already found a target.
if (previous instanceof ContextBinding) {
IWebSocketServerPushEnhancedComponent previousTarget = ((ContextBinding) previous).getFoundTarget();
if (previousTarget != null) {
return previousTarget;
}
}
// Try to find whether parent is IWebSocketServerPushEnhancedComponent.
if (getParent() instanceof IWebSocketServerPushEnhancedComponent) {
return (IWebSocketServerPushEnhancedComponent)getParent();
}
return null;
}
}
impl.DesktopUtils.java
Modified from AURequestWithWebSocket, almost the same, add synchronized for ServerPush.
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/impl/DesktopUtils.java
impl.TestWebSocketServlet.java
Modified from AURequestWithWebSocket, almost the same, store channel at desktop so can send response with desktop itself.
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/impl/TestWebSocketServlet.java
impl.EventListener.java
Define an Event listener used to process event from Not used in this POC.
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/impl/EventListener.java
impl.RequestFromWebSocket.java
Copied from AURequestWithWebSocket, completely the same excepts this fragment. Not used in this POC
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/src/impl/RequestFromWebSocket.java
impl.serverpush.Binding.java
Binding that bind a field of a component to specific context for WebSocket ServerPush.
package impl.serverpush;
import java.io.Serializable;
/** Binding that bind a field of a component to specific context for WebSocket ServerPush
*
* @author benbai123
*
*/
public class Binding implements Serializable {
private static final long serialVersionUID = -1591783705902661276L;
/** field to bind of component */
private String _field;
/** context to bind with field */
private String _context;
// Constructor
public Binding (String field, String context) {
if (field == null) {
field = "";
}
if (context == null) {
context = "";
}
_field = field;
_context = context;
}
// getters, setters
public void setField (String field) {
if (field == null) {
field = "";
}
_field = field;
}
public String getField () {
return _field;
}
public void setContext (String context) {
if (context == null) {
context = "";
}
_context = context;
}
public String getContext () {
return _context;
}
// super
public int hashCode () {
return (_field + _context).hashCode() * 31;
}
public boolean equals (Object obj) {
if (obj != null
&& (obj instanceof Binding)) {
Binding b2 = (Binding) obj;
return (_field.equals(b2.getField())
&& _context.equals(b2.getContext()));
}
return false;
}
}
impl.serverpush.ServerPushUtil.java
Utilities for ServerPush with WebSocket, concept is similar to ServerPushWithWebSocket -- Component Oriented Version, rewritten it to Desktop Oriented Version.
package impl.serverpush;
import impl.TestWebSocketServlet;
import java.beans.Statement;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Desktop;
/** Tested with ZK 6.5.2<br>
*
* Utilities for ServerPush with WebSocket, concept is similar to
* ServerPushWithWebSocket -- Component Oriented Version, rewritten it to
* Desktop Oriented Version.
*
* @author benbai123
*
*/
@SuppressWarnings("unchecked")
public class ServerPushUtil {
/** keep all binded Desktop */
private static List<Desktop> bindedDesktops = new ArrayList<Desktop>();
/** Lock for push to all desktops */
private static Integer LOCK_FOR_PUSH_TO_ALL = 0;
/** Lock for access bindedDesktops */
private static Integer LOCK_FOR_ACCESS_BINDED_DESKTOPS = 0;
/** attribute kay for binding map */
private static final String BINDING_MAP = "BINDING_MAP_FOR_WEBSOCKET_SERVERPUSH";
/** Add a Binding to a Component
*
* @param comp Component to bind member field with update context
* @param field field of component to bind
* @param context context to bind with field
* @return Binding the added Binding object
*/
public static Binding addContextBinding (Component comp, String field, String context) {
Binding binding = null;
synchronized (comp.getDesktop()) {
// get binding list of specified component
List<Binding> bindingList = getBindingList(comp);
// create a binding object with specified field/context
binding = new Binding(field, context);
// add binding into binding list if not exists
if (!bindingList.contains(binding)) {
bindingList.add(binding);
}
}
// return created binding
return binding;
}
/** Remove a Binding from a Component
*
* @param comp Component to remove Binding
* @param field field of the field-context pair to remove
* @param context context of the field-context pair to remove
*/
public static void removeContextBinding (Component comp, String field, String context) {
synchronized (comp.getDesktop()) {
Map<Component, List<Binding>> bindingMap = getBindingMap(comp.getDesktop());
// get binding list of specified component
List<Binding> bindingList = bindingMap.get(comp);
if (bindingList != null) {
// create a binding object with specified field/context
Binding target = new Binding (field, context);
// remove binding if exists
for (Binding b : bindingList) {
if (b.equals(target)) {
bindingList.remove(b);
break;
}
}
}
}
}
/** Push a value to a context for all desktops
*
* @param value value to push
* @param context context to push to
* @throws Exception whatever
*/
public static void pushVlaue (Object value, String context) throws Exception {
synchronized (LOCK_FOR_PUSH_TO_ALL) {
// used to store dead desktop
List<Desktop> deadDesktops = new ArrayList<Desktop>();
// make a copy to reduce lock
List<Desktop> desktopToPush = new ArrayList<Desktop>();
synchronized (LOCK_FOR_ACCESS_BINDED_DESKTOPS) {
desktopToPush.addAll(bindedDesktops);
}
// for each binded desktop
for (Desktop bindedDesktop : desktopToPush) {
if (bindedDesktop.isAlive()) {
// push value to context if alive
execPush(value, context, bindedDesktop);
} else {
// store it to deadDesktops otherwise
deadDesktops.add(bindedDesktop);
}
}
// remove all dead desktops
synchronized (LOCK_FOR_ACCESS_BINDED_DESKTOPS) {
bindedDesktops.removeAll(deadDesktops);
}
desktopToPush.removeAll(deadDesktops);
for (Desktop dt : desktopToPush) {
// send response via WebSocket
TestWebSocketServlet.sendResponse(dt);
}
}
}
/** Push a value to a context for specified desktop
*
* @param value value to push
* @param context context to push to
* @param desktop desktop to apply this push
* @throws Exception whatever
*/
public static void pushVlaue (Object value, String context, Desktop desktop) throws Exception {
if (desktop.isAlive()) {
// push value to context for specified desktop if alive
execPush(value, context, desktop);
} else {
synchronized (LOCK_FOR_ACCESS_BINDED_DESKTOPS) {
// remove specified desktop from bindedDesktops otherwise
bindedDesktops.remove(desktop);
}
}
// send response via WebSocket
TestWebSocketServlet.sendResponse(desktop);
}
/** Apply "push value to context" for specified desktop
*
* @param value value to push
* @param context context to push to
* @param desktop desktop to apply
* @throws Exception whatever
*/
public static void execPush (Object value, String context, Desktop desktop) throws Exception {
// execute push if WebSocket of desktop is ready
if (TestWebSocketServlet.isWebSocketReady(desktop)) {
synchronized (desktop) {
// get binding map
// (Map<String, List> where String is component ID and List is Binding objects)
Map<Component, List<Binding>> bindingMap = (Map<Component, List<Binding>>)desktop.getAttribute(BINDING_MAP);
if (bindingMap != null) {
for (Map.Entry<Component, List<Binding>> e : bindingMap.entrySet()) {
// get component by ID
Component comp = e.getKey();
// get binding list
List<Binding> bindings = e.getValue();
// for each binding
for (Binding binding : bindings) {
// push value to component if
// context of binding is equal to specified context
if (binding.getContext().equals(context)) {
// get field from binding
String field = binding.getField();
// build setter name
String method = "set" + field.substring(0, 1).toUpperCase() + field.substring(1);
// call setter to set value to component
Statement stat = new Statement(comp, method, new Object[]{value});
stat.execute();
}
}
}
}
}
}
}
/** Get Binding list of a Component
*
* @param comp Component to get Binding list
* @return Binding list for specified Component
*/
private static List<Binding> getBindingList (Component comp) {
// get binding map
Map<Component, List<Binding>> bindingMap = getBindingMap(comp.getDesktop());
// try to get binding list
List<Binding> bindingList = bindingMap.get(comp);
// create and add binding list if not exists
if (bindingList == null) {
bindingList = new ArrayList<Binding>();
bindingMap.put(comp, bindingList);
}
// return (created) binding list
return bindingList;
}
/** Get Binding map of a Desktop
*
* @param dt specified Desktop
* @return Map<Component, List<Binding>> Binding map of specified Desktop
*/
private static Map<Component, List<Binding>> getBindingMap (Desktop dt) {
// try to get binding map
Map<Component, List<Binding>> bindingMap = (Map<Component, List<Binding>>)dt.getAttribute(BINDING_MAP);
// create and add binding map if not exists
if (bindingMap == null) {
bindingMap = new Hashtable<Component, List<Binding>>();
dt.setAttribute(BINDING_MAP, bindingMap);
synchronized (LOCK_FOR_ACCESS_BINDED_DESKTOPS) {
bindedDesktops.add(dt);
}
}
// return (created) binding map
return bindingMap;
}
}
zk.xml
Modified from AURequestWithWebSocket, add config for lang-addon.xml, also change the way to get path for WebSocket (line 61~)
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/WebContent/WEB-INF/zk.xml
lang-addon.xml
Define components used in this POC.
<!-- Tested with ZK 6.5.2
Define components
-->
<language-addon>
<addon-name>param</addon-name>
<language-name>xul/html</language-name>
<!-- It specifies what language addons this addon
depends on. If specified, this addon will be
parsed after all the specified addons are parsed -->
<depends>zul</depends>
<!-- define param -->
<component>
<component-name>contextBinding</component-name>
<extends>div</extends>
<component-class>components.helper.ContextBinding</component-class>
</component>
<component>
<component-name>intbox</component-name>
<extends>intbox</extends>
<component-class>components.serverpush.Intbox</component-class>
</component>
<component>
<component-name>textbox</component-name>
<extends>textbox</extends>
<component-class>components.serverpush.Textbox</component-class>
</component>
</language-addon>
Download
Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Integrate/WebSocket/ServerPushWithWebSocket
Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/Integrate/WebSocket/ServerPushWithWebSocket/ServerPushWithWebSocket.swf
Sunday, September 29, 2013
Python Loop Condition and ErrorHandling
Simple Note
Code
# display message and wait for input
# input is always a string
# ref http://stackoverflow.com/questions/5424716/python-how-to-check-if-input-is-a-number-given-that-input-always-returns-stri
x = input("Please enter an integer, or q to stop: ")
sum = 0
# while input is not 'q'
while x != 'q':
try: # try to convert x to integer
x = int(x)
if x < 0:
x = 0
print('Negative changed to zero')
elif x == 0: # keyword 'elif' is short for 'else if'
print('Zero')
else:
print('positive')
sum += x
print('sum = ' + str(sum))
x = input() # wait for next input
except ValueError: # error happened when convert x to integer
x = input("Please enter an integer, or q to stop: ")
Result
References
Python Doc:
http://docs.python.org/3/tutorial/controlflow.html
Python - How to check if input is a number
http://stackoverflow.com/questions/5424716/python-how-to-check-if-input-is-a-number-given-that-input-always-returns-stri
Download
Code at github
https://github.com/benbai123/Python/blob/master/Practice/Basic/flow_control/input_loop_condition_error-handling.py
Sunday, September 22, 2013
Python Basic Math Operators and Functions
Simple Note
Code
# ref:
# number concat string
# http://stackoverflow.com/questions/6981495/how-can-i-concatenate-a-string-and-a-number-in-python
#
# Complex Number
# http://en.wikipedia.org/wiki/Complex_number
#
# Python doc:
# http://docs.python.org/3/tutorial/introduction.html
# http://docs.python.org/3/library/stdtypes.html#typesnumeric
#
# +, -, *, /
print()
print(" +, -, *, /")
print(" 3+3 = " + str(3+3)) # 6
print(" 3-3 = " + str(3-3)) # 0
print(" 3*3 = " + str(3*3)) # 9
print(" 3/3 = " + str(3/3)) # 1.0
# +, -, *, / with ()
print(" +, -, *, / with ()")
print(" (4+5)*3 = " + str((4+5)*3)) # = 9*3 = 27
# negated
print(" negated")
x = 5;
print(" x = 5, -x = " + str(-x)) # -5
# use equal sign to assign value
print(" use equal sign to assign value")
a = 2
b = 3
print(" a=2, b=3, a*b = " + str(a*b)) # 6
# absolute value
print(" absolute value")
print(" abs(5) = " + str(abs(5))) # 5
print(" abs(-5) = " + str(abs(-5))) # 5
# division always returns a floating point number automatically
print(" division always returns a floating point number automatically")
print(" 8/5 = " + str(8/5)) # 1.6
print(" 32/3 = " + str(32/3)) # 10.666666666666666
# floor division
print(" floor division")
print(" 8//5 = " + str(8//5)) # 1
print(" 32//3 = " + str(32//3)) # 10
# MOD (the remainder of the division)
print(" MOD (the remainder of the division)")
print(" 32%3 = " + str(32%3)) # 2
# the pair (x // y, x % y)
print(" the pair (x // y, x % y)")
print(" divmod(32, 3) = " + str(divmod(32, 3))) # (10, 2)
# floor to specific digits with round()
print(" floor to specific digits with round()")
print(" round(8/3, 2) = " + str(round(8/3, 2))) # 2.67
# complex number with real part re, imaginary part im. im defaults to zero
print(" complex number with real part re, imaginary part im. im defaults to zero")
print(" complex(1, 2) = " + str(complex(1, 2))) # (1+2j)
# power
print(" power")
print(" 2**5 = " + str(2**5)) # 32
print(" pow(2, 5) = " + str(pow(2, 5))) # 32
# converted to integer
print(" converted to integer")
print(" int(1.23) = " + str(int(1.23))) # 1
# converted to floating point
print(" converted to floating point")
print(" float(5) = " + str(float(5))) # 5.0
Result
References
number concat string
http://stackoverflow.com/questions/6981495/how-can-i-concatenate-a-string-and-a-number-in-python
Complex Number
http://en.wikipedia.org/wiki/Complex_number
Python doc:
http://docs.python.org/3/tutorial/introduction.html
http://docs.python.org/3/library/stdtypes.html#typesnumeric
Download
Test folder at github
https://github.com/benbai123/Python/tree/master/Practice/Basic/Math/Simple_Calculation
Friday, September 20, 2013
ZK Create Helper Tag to Make Programmer Happier
Introduction
This article describe how to create helper tag by custom component to help programmer to do something easier/better.
NOTE:
This does not mean "this is a good way, follow this pattern"
Just show a possible way that you can give it a try and see if it makes the world better.
Result
Online demo:
http://screencast.com/t/gC2rGFg3KkuU
Program
index.zul
Contains a/button with very long url, also contains a/button/image/ with short url and specify parameters with custom helper component.
<zk>
<!-- Tested with ZK 6.5.2
test link available at wiki: http://en.wikipedia.org/wiki/Google_Chart_API
-->
<!-- basically you can write something as below,
now assume that you need to
find a typo
or
change some params...
-->
<div>
<a href="http://chart.apis.google.com/chart?chs=200x200&chdlp=b&chtt=Uberman&chdl=Asleep%7CAwake&chd=t:1,11,1,11,1,11,1,11,1,11,1,11&cht=p&chco=586F8E%7C7D858F"
target="blank">
open chart
</a>
</div>
<div>
<button href="http://chart.apis.google.com/chart?chs=200x200&chdlp=b&chtt=Uberman&chdl=Asleep%7CAwake&chd=t:1,11,1,11,1,11,1,11,1,11,1,11&cht=p&chco=586F8E%7C7D858F"
target="blank" label="open chart, too" />
</div>
<!-- or you can define a helper component to
make those params clear as below
to reduce server memory you can specify
stubonly="true" to helper tags
Of course this probably is not needed if
you load those params from DB/Java-Beans and
build link at server side
Just show a possibility that such kind of helper
can help you do some fast prototyping in zul page
without server side (AP level) support
And it is possible to change params dynamically so
probably will be useful when using it with MVVM
You can also define the helper tag as another component with
another name in lang-addon.xml, see index_defined.zul
-->
<div>
<a href="http://chart.apis.google.com/chart" target="blank">
also open chart
<div name="chs" value="200x200"
stubonly="true" use="helper.Param" />
<div name="chdlp" value="b"
stubonly="true" use="helper.Param" />
<div name="chtt" value="Uberman"
stubonly="true" use="helper.Param" />
<div name="chdl" value="Asleep%7CAwake"
stubonly="true" use="helper.Param" />
<div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
stubonly="true" use="helper.Param" />
<div name="cht" value="p"
stubonly="true" use="helper.Param" />
<div name="chco" value="586F8E%7C7D858F"
stubonly="true" use="helper.Param" />
</a>
</div>
<!-- for components that do not support child, you can
use give it an ID and specify the ID as target of param
-->
<div>
<button id="btn" label="still open chart" href="http://chart.apis.google.com/chart"
target="blank" />
<div name="chs" value="200x200"
target="btn"
stubonly="true" use="helper.Param" />
<div name="chdlp" value="b"
target="btn"
stubonly="true" use="helper.Param" />
<div name="chtt" value="Uberman"
target="btn"
stubonly="true" use="helper.Param" />
<div name="chdl" value="Asleep%7CAwake"
target="btn"
stubonly="true" use="helper.Param" />
<div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
target="btn"
stubonly="true" use="helper.Param" />
<div name="cht" value="p"
target="btn"
stubonly="true" use="helper.Param" />
<div name="chco" value="586F8E%7C7D858F"
target="btn"
stubonly="true" use="helper.Param" />
</div>
<div>
Display an image
<image id="img" src="http://chart.apis.google.com/chart" />
<div name="chs" value="200x200"
target="img"
stubonly="true" use="helper.Param" />
<div name="chdlp" value="b"
target="img"
stubonly="true" use="helper.Param" />
<div name="chtt" value="Uberman"
target="img"
stubonly="true" use="helper.Param" />
<div name="chdl" value="Asleep%7CAwake"
target="img"
stubonly="true" use="helper.Param" />
<div name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
target="img"
id="dynaParam"
use="helper.Param" />
<div name="cht" value="p"
target="img"
stubonly="true" use="helper.Param" />
<div name="chco" value="586F8E%7C7D858F"
target="img"
stubonly="true" use="helper.Param" />
and change it dynamically
<combobox onSelect="dynaParam.setValue(self.getValue());">
<comboitem label="t:1,11,1,11,1,11,1,11,1,11,1,11" />
<comboitem label="t:2,10,2,10,2,10,2,10,2,10,2,10" />
</combobox>
</div>
</zk>
index_defined.zul
Similar to index.zul, just use the custom component with another name defined in lang-addon.xml.
<zk>
<!-- test link available at wiki: http://en.wikipedia.org/wiki/Google_Chart_API -->
<!-- Do the same as index.zul with
predefined param component.
-->
<div>
<a href="http://chart.apis.google.com/chart?chs=200x200&chdlp=b&chtt=Uberman&chdl=Asleep%7CAwake&chd=t:1,11,1,11,1,11,1,11,1,11,1,11&cht=p&chco=586F8E%7C7D858F"
target="blank">
open chart
</a>
</div>
<div>
<button href="http://chart.apis.google.com/chart?chs=200x200&chdlp=b&chtt=Uberman&chdl=Asleep%7CAwake&chd=t:1,11,1,11,1,11,1,11,1,11,1,11&cht=p&chco=586F8E%7C7D858F"
target="blank" label="open chart, too" />
</div>
<div>
<a href="http://chart.apis.google.com/chart" target="blank">
also open chart
<param name="chs" value="200x200"
stubonly="true" />
<param name="chdlp" value="b"
stubonly="true" />
<param name="chtt" value="Uberman"
stubonly="true" />
<param name="chdl" value="Asleep%7CAwake"
stubonly="true" />
<param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
stubonly="true" />
<param name="cht" value="p"
stubonly="true" />
<param name="chco" value="586F8E%7C7D858F"
stubonly="true" />
</a>
</div>
<div>
<button id="btn" label="still open chart" href="http://chart.apis.google.com/chart"
target="blank" />
<param name="chs" value="200x200"
target="btn"
stubonly="true" />
<param name="chdlp" value="b"
target="btn"
stubonly="true" />
<param name="chtt" value="Uberman"
target="btn"
stubonly="true" />
<param name="chdl" value="Asleep%7CAwake"
target="btn"
stubonly="true" />
<param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
target="btn"
stubonly="true" />
<param name="cht" value="p"
target="btn"
stubonly="true" />
<param name="chco" value="586F8E%7C7D858F"
target="btn"
stubonly="true" />
</div>
<div>
Display an image
<image id="img" src="http://chart.apis.google.com/chart" />
<param name="chs" value="200x200"
target="img"
stubonly="true" />
<param name="chdlp" value="b"
target="img"
stubonly="true" />
<param name="chtt" value="Uberman"
target="img"
stubonly="true" />
<param name="chdl" value="Asleep%7CAwake"
target="img"
stubonly="true" />
<param name="chd" value="t:1,11,1,11,1,11,1,11,1,11,1,11"
target="img"
id="dynaParam"
use="helper.Param" />
<param name="cht" value="p"
target="img"
stubonly="true" />
<param name="chco" value="586F8E%7C7D858F"
target="img"
stubonly="true" />
and change it dynamically
<combobox onSelect="dynaParam.setValue(self.getValue());">
<comboitem label="t:1,11,1,11,1,11,1,11,1,11,1,11" />
<comboitem label="t:2,10,2,10,2,10,2,10,2,10,2,10" />
</combobox>
</div>
</zk>
Param.java
The implementation of custom helper component,
package helper;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.A;
import org.zkoss.zul.Button;
import org.zkoss.zul.Div;
import org.zkoss.zul.Image;
/** Tested with ZK 6.5.2
*
* @author benbai123
*
*/
public class Param extends Div {
private static final long serialVersionUID = 8842930664701238051L;
private String _name = "";
private String _value = "";
private String _target;
private String _oldValue = "";
@SuppressWarnings({ "unchecked", "rawtypes" })
public Param () {
// update target url while created
addEventListener(Events.ON_CREATE, new EventListener () {
public void onEvent (Event event) {
updateTargetUrl();
}
});
// do not output any html
setWidgetOverride ("redraw", "function (out) {}");
}
// setters
public void setTarget (String target) {
_target = target;
}
public void setName (String name) {
if (name == null) {
name = "";
}
_name = name;
}
public void setValue (String value) {
if (value == null) {
value = "";
}
_value = value;
updateTargetUrl();
}
private void updateTargetUrl () {
if (!_name.isEmpty() && !_value.isEmpty()) {
Component target = findTarget();
if (target instanceof A) {
A link = (A)target;
String url = updateUrl(link.getHref());
link.setHref(url);
} else if (target instanceof Button) {
Button btn = (Button)target;
String url = updateUrl(btn.getHref());
btn.setHref(url);
} else if (target instanceof Image) {
Image img = (Image)target;
String url = updateUrl(img.getSrc());
img.setSrc(url);
}
}
}
// try to find target to update
private Component findTarget () {
if (_target != null && !_target.isEmpty()) {
return getParent().getFellowIfAny(_target);
} else if (getParent() instanceof A) {
return getParent();
}
return null;
}
// update url with param/value
private String updateUrl (String url) {
String value;
if (url.indexOf("?") == -1) {
url += "?";
} else {
url += "&";
}
value = _name + "=" + _value;
// replace old value or append new value at tail
if (!_oldValue.isEmpty()) {
url.replace(_oldValue, value);
} else {
url += value;
}
return url;
}
}
lang-addon.xml
Define 'param' component.
<!-- Tested with ZK 6.5.2
Define param component
-->
<language-addon>
<addon-name>param</addon-name>
<language-name>xul/html</language-name>
<!-- It specifies what language addons this addon
depends on. If specified, this addon will be
parsed after all the specified addons are parsed -->
<depends>zul</depends>
<!-- define param -->
<component>
<component-name>param</component-name>
<!-- extends div and change java class -->
<extends>div</extends>
<component-class>helper.Param</component-class>
</component>
</language-addon>
zk.xml
Load lang-addon.xml.
<zk>
<!-- load lang-addon.xml -->
<language-config>
<addon-uri>/WEB-INF/lang-addon.xml</addon-uri>
</language-config>
</zk>
References
Language Definition
http://books.zkoss.org/wiki/ZK_Client-side_Reference/Language_Definition
The language-config Element
http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_language-config_Element
Download
Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Components_Customization/ParamTagForUrl
Demo Flash
Subscribe to:
Posts (Atom)