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/

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/

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&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank">
            open chart
        </a>
    </div>
    <div>
        <button href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;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&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;chco=586F8E%7C7D858F"
            target="blank">
            open chart
        </a>
    </div>
    <div>
        <button href="http://chart.apis.google.com/chart?chs=200x200&amp;chdlp=b&amp;chtt=Uberman&amp;chdl=Asleep%7CAwake&amp;chd=t:1,11,1,11,1,11,1,11,1,11,1,11&amp;cht=p&amp;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