Richlet"
m |
|||
Line 50: | Line 50: | ||
} | } | ||
</source> | </source> | ||
− | + | ||
+ | As shown above, we have to invoke <javadoc method="setPage(Page page)">org.zkoss.zk.ui.Component</javadoc> explicitly to attach a root component to a page such that it will be available at the client. | ||
+ | |||
To have better control, you can implement the <javadoc method="init(org.zkoss.zk.ui.RichletConfig)">org.zkoss.zk.ui.Richlet</javadoc> and <javadoc method="destroy()">org.zkoss.zk.ui.Richlet</javadoc> methods to initialize and to destroy the resources required by the richlet when it is loaded. | To have better control, you can implement the <javadoc method="init(org.zkoss.zk.ui.RichletConfig)">org.zkoss.zk.ui.Richlet</javadoc> and <javadoc method="destroy()">org.zkoss.zk.ui.Richlet</javadoc> methods to initialize and to destroy the resources required by the richlet when it is loaded. | ||
Revision as of 08:07, 8 November 2010
A richlet is a small Java program that composes a user interface in Java for serving user's request.
When a user requests the content of an URL, the ZK Loader checks if the resource of the specified URL is a ZUML page or a richlet. If it is a ZUML page, then the ZK Loader creates components automatically based on the ZUML page's content as we described in the previous chapters.
If the resource is a richlet, the ZK Loader hands over the processing to the richlet. What and how to create components are all handled by the richlet. In other words, it is the developer's job to create all necessary components programmatically in response to the request.
The choice between ZUML pages and richlets depends on your preference. However, the performance shall not be a concern since the parsing of ZUML is optimized.
Implement a Richlet
It is straightforward to implement a richlet. First, implement the Richlet interface and then mapping URL to the richlet.
Implement a Richlet as a Java class
A richlet must implement the Richlet interface. However, you generally don't have to implement it from scratch. Rather, you could extends from GenericRichlet. With GenericRichlet, the only thing you have to do is to implement Richlet.service(Page). It is called when an associated URL is requested. For example,
package org.zkoss.zkdemo;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.GenericRichlet;
import org.zkoss.zk.ui.event.*;
import org.zkoss.zul.*;
public class TestRichlet extends GenericRichlet {
//Richlet//
public void service(Page page) {
page.setTitle("Richlet Test");
final Window w = new Window("Richlet Test", "normal", false);
new Label("Hello World!").setParent(w);
final Label l = new Label();
l.setParent(w);
final Button b = new Button("Change");
b.addEventListener(Events.ON_CLICK,
new EventListener() {
int count;
public void onEvent(Event evt) {
l.setValue("" + ++count);
}
});
b.setParent(w);
w.setPage(page);
}
}
As shown above, we have to invoke Component.setPage(Page page) explicitly to attach a root component to a page such that it will be available at the client.
To have better control, you can implement the Richlet.init(RichletConfig) and Richlet.destroy() methods to initialize and to destroy the resources required by the richlet when it is loaded.
In additions, you could implement Richlet.getLanguageDefinition() to use a different language as default (for example, implementing a richlet for mobile devices). By default, ZUL (aka., xul/html) is assumed.
Richlet Must Be Thread-Safe
Like a servlet, a single instance of richlet is created and shared for all users for all requests for the mapped URL. A richlet must handle concurrent requests, and be careful to syncrhronize access to shread resources. In other words, a richlet (the implementation of the service method) must be thread-safe.
As describe in Component-based UI section, components are not shareable in different desktops (i.e., requests). Each desktop has an independent set of component instances. Therefore, it is generally not a good idea to store components as a data member of a richlet. For example, the following code will cause an exception.
public class MyRichlet extends GenericRichlet {
private Window main; //Not a good idea to share
public void service(Page page) {
if (main == null) {
main = new Window();
}
main.setPage(main); //ERROR! Causes an exception if the same URL is requested twice!
...
Why? When a request (not Ajax request but regular HTTP request) is made by an user, a desktop is created before invoking Richlet.service(Page) (to serve an individual request). In the above example, the invocation of Component.setPage(Page) against the same component for different requests will eventually assign the same component to different desktops. It is not allowed.
There are many ways to solve this issue. A straightforward solution is to compose the complete UI when Richlet.service(Page) is called.
import org.zkoss.zk.ui.GenericRichlet;
import org.zkoss.zk.ui.Page;
public class MyRichlet extends GenericRichlet {
public void service(Page page) {
Window main = new Window();
//whatever UI composing...
main.setPage(page);
}
}
Remember that Richlet.service(Page) is called to serve the associated URL. It is not about Ajax requests. Ajax requests are handled in the same way as ZUML -- in event handlers.
Component Declarations in Richlets
As described in the Component-based UI section, components are presented at the client only if they belong to a Page.
Thus, making UI component declarations in your Richlet implementation involves the following:
- implement the Richlet.”service(Page method
- declare the root UI component(s)
- call the Component.setPage(Page page) method on the root component
The root component and all its children components would be bounded to the Page and rendered at the client.
For example:
import org.zkoss.zk.ui.GenericRichlet;
import org.zkoss.zk.ui.Page;
public class MyRichlet extends GenericRichlet {
public void service(Page page) {
Window main = new Window();
//whatever UI composing...
main.setPage(page);
}
}
Caution
Please note that components declared outside of
Richlet.”service(Page will not be shown at the client.
Components declared outside of Richlet.”service(Page would belong to the Desktop in associated with the URL request.
Suppose we have the following code:
public class MyRichlet extends GenericRichlet {
private Window main; //Not a good idea to share
public void service(Page page) {
if (main == null) {
main = new Window();
}
main.setPage(page); //ERROR! Causes an exception if the same URL is requested twice!
...
Each Desktop has its own set of component instances. When the URL associated MyRichlet is requested a second time, an exception will be thrown because the main Window is already instantiated and associated with the
Desktop created from the first request. A second URL request would invoke the creation of a second desktop and attempting to set the main window, which is global, to a different desktop.
Note that Richlet.service(Page) is called to serve the associated URL, ie. a HTTP request, not an Ajax request. Ajax requests are handled in the same way as ZUML -- in event handlers.
Map URL to a Richlet
To map URL to a richlet, there are two steps.
- Turn on the support of Richlet (in
WEB-INF/web.xml
) - Map URL pattern to Richlet (in
WEB-INF/zk.xml
)
Turn on Richlet
By default, richlets are disabled. To enable them, add the following declaration to WEB-INF/web.xml. Once enabled, you can add as many as richlets as you want without modifying web.xml any more.
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>/zk/*</url-pattern>
</servlet-mapping>
where you can use replace /zk/*
to any pattern you like, such as /do/*
. Notice that you cannot map it to an extension (such as *.do
) since it will be considered as a ZUML page (rather than a richlet).
Map URL pattern to Richlet
For each richlet you implement, you can define it in WEB-INF/zk.xml with the statement similar to the following:
<richlet>
<richlet-name>Test</richlet-name><!-- your preferred name -->
<richlet-class>org.zkoss.zkdemo.TestRichlet</richlet-class><!-- your class name, of course -->
</richlet>
After defining a richlet, you can map it to any number of URLs using the richlet-mapping element as shown below.
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/test</url-pattern>
</richlet-mapping>
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/some/more/*</url-pattern>
</richlet-mapping>
Then, you can visit http://localhost:8080/PROJECT_NAME/zk/test to request the richlet.
The URL specified in the url-pattern element must start with /. If the URI ends with /*, then it is matched to all request with the same prefix. To retrieve the request's actual URL, you can check the value returned by the getRequestPath method of the current page.
public void service(Page page) {
if ("/some/more/hi".equals(page.getRequestPath()) {
...
}
}
Tip: By specifying /* as the url-pattern, you can map all unmatched URLs to your richlet.
Version History
Last Update : 2010/11/8
Version | Date | Content |
---|---|---|