- Copy all zk jars needed in a bundle
- Add them in the Bundle-Classpath
- Copy all the Export-Package and Import-Package statements from each bundelized zk jar given by the bnd tool (previous post) or by the eclipse method.
Web Apps and so forth..
Thursday, August 13, 2009
ZK on OSGi - Unique ZK bundle
A user from the zk forum, galdasc, pointed out a way to create a unique ZK bundle working flawlessly with the osgi classloader. Here's the recipe :
Tuesday, July 07, 2009
ZK on OSGi - Dynamic & Asynchronous Richlets
After some days struggling I finally managed to integrate ZK, which allow you to easily build rich webapps, with the OSGi framework. This combination allow to dynamically register .zul files or Richlets and most interestingly, to update UI components asynchonously from OSGi bundles or events.
We will see in this post how to bundelize zk libs, how to configure and start zk engines, how to register zul files or richlets and finally how to update a richlet through a java method accessible in OSGi.
Bundelization of ZK jars
The first step is to make all zk jars suitable for OSGi environment by editing their manifest file. Thanks to the bnd tool, this tasks is pretty straightforward. I've written an ant script to do the job. Just put in a directory each zk jar you want to bundelize, the bnd jar, an optional bnd file (for setting manifest properties like Bundle-Version), the following ant script and launch "ant" in a console.
Configure and launch ZK engine
The Zk architecture is constituted by two servlets, named zkLoader and auEngine in the docs. Integrate Zk on OSGi is as simple as register servlets with an osgi http service. However these servlets require specific URI mapping which can't be set with a basic implementation of the http service. Hopefully the Pax Web implementation extends the http service with such functionalities.
Here follow the code to configure and register Zk engine servlets with Pax Web.
Note: Launching ZK servlets from those newly ZK bundles run flawlessly but cause http requests to abort :
This is due to a natural incompatibility between the ZK config file loader (which load metainfo files inside zk jars) and the OSGi ClassLoaders.
I've tried to wrap a unique ZK bundle and merge metainfo config files but this attempt was unsuccessful. The only workaround I found is to include zk jars in the bundle which register servlets. Anyway ZK api bundles are still useful for other bundles implementing Richlets.
Hello World zul
A .zul or .zhtml file can be registered using the registerRessource() method from the http service.
Create a directory "rsc" in your project and add a hello.zul file :
This method to register zul files over OSGi don't seems to be more dynamic than with a classical web container. Even if it is possible to re-register the resource folder, a better solution to achieve dynamism would be to use Richlets.
Hello World richlet
Add a new class extending GenericRichlet in your project :
Asynchronous UI modification from OSGi
Now that we're able to register Richlets on OSGi, we are going to modify them at runtime without using ZK event listeners. Of course it is still possible to use them, but the idea is to update components asynchronously from java methods. There are few things to do :
To enable Comet in Ajax compliant browsers add the following line when configuring Zk.
Conclusion
Using ZK on OSGi allow to use the benefits of a service oriented architecture. It is easier to handle dynamism and easier to achieve modularity. For example we can imagine bundles exporting richlets as services and another bundle registering them dynamically.
With the asynchronous modification it becomes easier to automatically represent a service through the web. If for instance we have some "light" or "temperature sensor" services, corresponding to real objects in a house, it would then be possible to handle asynchronous notification of state change and build a solid web interface.
Download full source code here.
We will see in this post how to bundelize zk libs, how to configure and start zk engines, how to register zul files or richlets and finally how to update a richlet through a java method accessible in OSGi.
Bundelization of ZK jars
The first step is to make all zk jars suitable for OSGi environment by editing their manifest file. Thanks to the bnd tool, this tasks is pretty straightforward. I've written an ant script to do the job. Just put in a directory each zk jar you want to bundelize, the bnd jar, an optional bnd file (for setting manifest properties like Bundle-Version), the following ant script and launch "ant" in a console.
A maven pom would certainly be more handy as it will download the jars and manage bundle name / version easily but the first solution satisfied me so i didn't bother make a second one.
<project name="ZK OSGi Multi Bundles" default="all" basedir=".">
<property name="bundle.version" value="3.6.2"/>
<property name="bundle.base" value="org.zkoss.zk.osgi"/>
<target name="bnd">
<taskdef resource="aQute/bnd/ant/taskdef.properties"
classpath="bnd/bnd-0.0.337.jar"/>
<bndwrap
jars="bsh.jar,commons-collections.jar,commons-fileupload.jar,commons-io.jar,
commons-logging.jar,jasperreports.jar,zcommon.jar,zcommons-el.jar,zk.jar,
zhtml.jar,zkex.jar,zkmax.jar, zkplus.jar,zml.jar,zul.jar,zweb.jar"
classpath=""
output="."/>
<!--output="./output/${bundle.base}.*-${bundle.version}.jar"-->
</target>
<target name="deploy">
<move todir="output">
<fileset dir=".">
<include name="*.bar"/>
</fileset>
<mapper type="glob" from="*.bar" to="${bundle.base}.*-${bundle.version}.jar"/>
</move>
</target>
<target name="all" depends="bnd, deploy"/>
</project>
Configure and launch ZK engine
The Zk architecture is constituted by two servlets, named zkLoader and auEngine in the docs. Integrate Zk on OSGi is as simple as register servlets with an osgi http service. However these servlets require specific URI mapping which can't be set with a basic implementation of the http service. Hopefully the Pax Web implementation extends the http service with such functionalities.
Here follow the code to configure and register Zk engine servlets with Pax Web.
//Get Pax Web http service
ServiceReference ref = context.getServiceReference("org.ops4j.pax.web.service.WebContainer");
if(ref == null) return;
http = (WebContainer)context.getService(ref);
if(http == null) return;
//Configure zkLoader servlet
DHtmlLayoutServlet zkLoader = new DHtmlLayoutServlet(); //Class of the zkLoader servlet
Hashtable<String, String> loader_initparam = new Hashtable<String, String>(); //set init parameters
loader_initparam.put("servlet-name", "zkLoader");
loader_initparam.put("update-uri", "/zkau"); //URI mapped to auEngine
String loader_mapping[] = {"*.zul", "*.zhtml"}; //mapping of UI files
//Configure auEngine servlet
DHtmlUpdateServlet auEngine = new DHtmlUpdateServlet();
Hashtable<String, String> engine_initparam = new Hashtable<String, String>();
engine_initparam.put("servlet-name", "auEngine");
String engine_mapping[] = {"/zkau/*"}; //same URI as the parameter "update-uri" of zkLoader
//Get the http context (zk servlets should be registered with the same http context)
HttpContext ctx = http.createDefaultHttpContext();
//Register zk session listener
http.registerEventListener(new HttpSessionListener(), ctx);
//Register zk servlets
http.registerServlet(zkLoader, loader_mapping, loader_initparam, ctx);
http.registerServlet(auEngine, engine_mapping, engine_initparam, ctx);
Note: Launching ZK servlets from those newly ZK bundles run flawlessly but cause http requests to abort :
This is due to a natural incompatibility between the ZK config file loader (which load metainfo files inside zk jars) and the OSGi ClassLoaders.
I've tried to wrap a unique ZK bundle and merge metainfo config files but this attempt was unsuccessful. The only workaround I found is to include zk jars in the bundle which register servlets. Anyway ZK api bundles are still useful for other bundles implementing Richlets.
Hello World zul
A .zul or .zhtml file can be registered using the registerRessource() method from the http service.
Create a directory "rsc" in your project and add a hello.zul file :
Then register it :
<window title="OMG a ZK Window !" border="normal" width="210px">
Hello from OSGi .zul ressource !
</window>
Don't forget to include this resource and zk jars (unfortunately) in the manifest. With bnd it can looks like :
http.registerResources("/", "/rsc", ctx);
Include-Resource: lib/bsh.jar=./lib/zk/bsh.jar, \It works !
...
lib/zweb.jar=./lib/zk/zweb.jar, \
rsc/hello.zul = ./rsc/hello.zul
Bundle-ClassPath: .,\
lib/bsh.jar, \
...
lib/zweb.jar
This method to register zul files over OSGi don't seems to be more dynamic than with a classical web container. Even if it is possible to re-register the resource folder, a better solution to achieve dynamism would be to use Richlets.
Hello World richlet
Add a new class extending GenericRichlet in your project :
public class HelloRichlet extends GenericRichlet {Then you can register it using the zk api :
@Override
public void service(Page page) {
page.setTitle("Richlet from OSGi");
Window window = new Window("OMG a ZK Window !", "normal", false);
label = new Label("Hello from OSGi !!");
label.setParent(window);
window.setWidth("230px");
window.setPage(page);
}
}
//Get the configurationIn order to tell Zk to parse this configuration when the user requests the richlet, add "/*" in the URI mapping of zkLoader :
Configuration config = WebManager.getWebManager(zkLoader.getServletContext()).getWebApp().getConfiguration();
//Register Richlet
config.addRichlet("hello", HelloRichlet.class.getName(), null);
config.addRichletMapping("hello", "/hello");
String loader_mapping[] = {"*.zul", "*.zhtml", "/*"}; //mapping of UI filesNow you can access the richlet in http://localhost:8080/hello.
Asynchronous UI modification from OSGi
Now that we're able to register Richlets on OSGi, we are going to modify them at runtime without using ZK event listeners. Of course it is still possible to use them, but the idea is to update components asynchronously from java methods. There are few things to do :
- Activate the server push functionality (Comet)
- Create a waiting thread which will update the component
- Start this thread when the richlet is requested
- Add the update method in the richlet which will notify the thread
To enable Comet in Ajax compliant browsers add the following line when configuring Zk.
//Enable server push with the zk Comet implementationHere is an example of the thread class, updating a label :
Devices.setServerPushClass("ajax", CometServerPush.class);
/**Finally update the hello richlet :
* Thread waiting for an update. It has to be launched in a zk richlet.
*/
public class LabelAsynchronousUpdate extends Thread {
private Label label;
private Desktop desktop;
private ArrayBlockingQueue<String> text; //used to notify the thread and give the argument
public LabelAsynchronousUpdate(Label label) {
this.label = label;
this.desktop = label.getDesktop();
this.text = new ArrayBlockingQueue<String>(1);
}
/**
* Call to update the label.
* The thread will be notified through the BloquingQueue.
*/
public void updateLabel(String text) {
try {
this.text.put(text); //Add the text object in the blocking queue
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void run() {
while (true) {
if (!desktop.isServerPushEnabled())
desktop.enableServerPush(true);
try {
//Wait for text message to appear in the queue
String message = text.take(); //take and remove from queue
Executions.activate(desktop); //active the desktop for server-push
try {
label.setValue(message); //update
} finally {
Executions.deactivate(desktop);
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
public class HelloRichlet extends GenericRichlet {In order to test you have to get the instance of the richlet from the Configuration object, and call the changeLabel() method.
private Label label;
private LabelAsynchronousUpdate labelupdate;
@Override
public void service(Page page) {
page.setTitle("Richlet from OSGi");
//Enable Comet
page.getDesktop().enableServerPush(true);
Window window = new Window("OMG a ZK Window !", "normal", false);
label = new Label("Hello from OSGi !!");
label.setParent(window);
window.setWidth("230px");
window.setPage(page);
//Create and launch an updating thread. It needs to be launched from the richlet
//in order to let zk hold the connection
this.labelupdate = new LabelAsynchronousUpdate(label);
this.labelupdate.start();
}
/**
* Change the label asynchronously.
*/
public void changeLabel(String message) {
if (labelupdate != null)
this.labelupdate.updateLabel(message);
}
}
Thread.sleep(10000); //test 10 sec after bundle start
HelloRichlet hr = (HelloRichlet) config.getRichlet("hello");
if (hr != null)
hr.changeLabel("OMFG ! This label is updatable asynchronously from OSGi !");
Conclusion
Using ZK on OSGi allow to use the benefits of a service oriented architecture. It is easier to handle dynamism and easier to achieve modularity. For example we can imagine bundles exporting richlets as services and another bundle registering them dynamically.
With the asynchronous modification it becomes easier to automatically represent a service through the web. If for instance we have some "light" or "temperature sensor" services, corresponding to real objects in a house, it would then be possible to handle asynchronous notification of state change and build a solid web interface.
Download full source code here.
Saturday, July 04, 2009
Hi
hi.
public class TestHi extends Hi {
/**
* lalalala, hi
* @author me
*/
private Object hi;
//TODO
@Override
public void method(Arg arg) {
hi.setHi("hello");
}
}
Subscribe to:
Posts (Atom)