Richlet"

From Documentation
m (remove empty version history (via JWB))
 
(54 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{ZUMLReferencePageHeader}}
+
{{ZKDevelopersReferencePageHeader}}
  
 
__TOC__
 
__TOC__
  
A richlet is a small Java program that composes a user interface in Java for serving user's request.
+
=Overview=
 +
A richlet is a small Java program that composes a user interface in Java for serving the user's request. Before composing UI in Java, we suggest you to know basic concept:[[ZK Developer's Reference/UI Composing/Component-based UI| UI Composing/Component-based UI]] first.
  
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.
+
When a user requests the content of an URL, ZK Loader checks if the resource of the specified URL is a ZUML page or a richlet. If it is a ZUML page, ZK Loader will create 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.
+
If the resource is a richlet, 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 the 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.
+
The choice between the ZUML pages and richlets depends on your preference. However, the performance should not cause any concern since parsing ZUML is optimized.
  
 
= Implement a Richlet=
 
= Implement a Richlet=
It is straightforward to implement a richlet. First, implement the <javadoc typpe="interface">org.zkoss.zk.ui.Richlet</javadoc> interface and then mapping URL to the richlet.
+
It is straightforward to implement a richlet. First, you have to implement the <javadoc typpe="interface">org.zkoss.zk.ui.Richlet</javadoc> interface before mapping a URL to the richlet.
  
== Extend from org.zkoss.zk.ui.GenericRichlet==
+
==Implement a Richlet as a Java class==
All richlets must implement the <javadoc type="interface">org.zkoss.zk.ui.Richlet</javadoc> interface. To simplify the task of implementing the required methods, you can extend <javadoc>org.zkoss.zk.ui.GenericRichlet</javadoc>. Then, when the specified URL is requested, the <tt>service</tt> method is called, and you can create the user interface then.
 
  
<source lang="java" >
+
A richlet must implement the <javadoc type="interface">org.zkoss.zk.ui.Richlet</javadoc> interface. However, you generally do not have to implement it from scratch. Rather, you could extend <javadoc>org.zkoss.zk.ui.GenericRichlet</javadoc>, and the only thing you have to do is to override <javadoc method="service(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Richlet</javadoc>. The method is called when an associated URL is requested. For example,
  package org.zkoss.zkdemo;
+
 
 +
<source lang="java" highlight="27, 31, 37">
 +
  package org.zkoss.reference.developer.uicomposing;
 +
 
 +
import org.zkoss.zk.ui.*;
 +
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);
 +
}
 +
 
 +
@Override
 +
public void init(RichletConfig config) {
 +
super.init(config);
 +
//initialize resources
 +
}
 +
 
 +
@Override
 +
public void destroy() {
 +
super.destroy();
 +
//destroy resources
 +
}
 +
}
 +
 
 +
</source>
 +
 
 +
In Richlet, you have to compose UI on your own, but some components only support specific child components. We recommend you to read [[ZK Component Reference]] before you start to build.
 +
 
 +
As shown above (line 27), we have to invoke <javadoc method="setPage(Page page)">org.zkoss.zk.ui.Component</javadoc> explicitly to attach a root component to a page so it will be available at the client.
 +
 
 +
To have better control, you can even 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 any resources required by the richlet when it is loaded.
 +
 
 +
In addition, you could implement <javadoc method="getLanguageDefinition()">org.zkoss.zk.ui.Richlet</javadoc> to use a different language as default (for example, implementing a richlet for [http://code.google.com/p/zkreach/ mobile devices]). By default, [[ZUML Reference/ZUML/Languages/ZUL|ZUL]] (aka., xul/html) is assumed.
 +
 
 +
== Richlet Must Be Thread-Safe ==
  
import org.zkoss.zk.ui.Page;
+
Like a servlet, a single instance of richlet is created and shared with all users for all requests for the mapped URL. A richlet must handle the concurrent requests, and be careful to synchronize access to shared resources. In other words, a richlet (the implementation of the <code>service</code> method) must be thread-safe.
import org.zkoss.zk.ui.GenericRichlet;
 
import org.zkoss.zk.ui.event.*;
 
import org.zkoss.zul.*;
 
  
public class TestRichlet extends GenericRichlet {
+
== Don't Share Components ==
    //Richlet//
 
    public void service(Page page) {
 
        page.setTitle("Richlet Test");
 
  
        final Window w = new Window("Richlet Test", "normal", false);
+
When a request (not Ajax request but regular HTTP request) is made by a user, a <javadoc>org.zkoss.zk.ui.Desktop</javadoc> and a <javadoc>org.zkoss.zk.ui.Page</javadoc> are created first, and then <javadoc method="service(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Richlet</javadoc> is invoked to serve the request<ref>A normal HTTP request; not an Ajax request. Ajax requests are handled in the same way as ZUML. For more information please refer to the [[ZK Developer's Reference/Event Handling|Event Handling]] section</ref>. In other words, each request is served with an individual desktop and page. Therefore, we ''cannot'' share components among different invocations of <javadoc method="service(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Richlet</javadoc>.
        new Label("Hello World!").setParent(w);
 
        final Label l = new Label();
 
        l.setParent(w);
 
  
        final Button b = new Button("Change");
+
For example, the following code is illegal:
        b.addEventListener(Events.ON_CLICK,
 
            new EventListener() {
 
                int count;
 
                public void onEvent(Event evt) {
 
                    l.setValue("" + ++count);
 
                }
 
            });
 
        b.setParent(w);
 
  
        w.setPage(page);
+
<source lang="java" highlight=7">
    }
+
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!
 +
...
 
</source>
 
</source>
 
Like servlets, you can implement the <tt>init</tt> and <tt>destroy</tt> methods to initialize and to destroy the richlet when it is loaded. Like servlet, a richlet is loaded once and serves all requests for the URL with which it is associated.
 
  
=== One Richlet per URL ===
+
Why?  Each desktop should have its own set of component instances<ref>For more information, please refer to [[ZK_Developer's Reference/UI Composing/Component-based UI|Component-based UI]] section</ref>. 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 first desktop created from the first request. We cannot assign it to the second desktop.
Like servlets, a single richlet is created and shared for all users. In other words, the richlet (at least the <tt>service</tt> method) must be thread-safe. On the other hand, components are not shareable. Each desktop has an independent set of components. Therefore, it is generally not a good idea to store components as a data member of a richlet.
+
 
 +
<blockquote>
 +
----
 +
<references/>
 +
</blockquote>
 +
 
 +
= Map URL to a Richlet =
 +
 
 +
To map URL to a richlet, there are two steps.
  
There are many ways to solve this issue. A typical one is to use another class for holding the components for each desktop, as illustrated below.
+
# Turn on the support of Richlet (in <code>WEB-INF/web.xml</code>)
 +
# Map URL pattern to Richlet (in <code>WEB-INF/zk.xml</code>)
  
<source lang="java" >
+
==Turn on Richlet==
import org.zkoss.zk.ui.Page;
+
By default, richlets are disabled. To enable them, please add the following declaration to <code>WEB-INF/web.xml</code>. Once enabled, you can add as many richlets as you want without modifying <code>web.xml</code>.
import org.zkoss.zul.Window;
 
  
 +
With servlet-mapping:
  
class MyApp { //one per desktop
+
<source lang="xml" >
    Window _main;
+
<servlet-mapping>
    MyApp(Page page) {
+
    <servlet-name>zkLoader</servlet-name>
        _main = new Window();
+
    <url-pattern>/zk/*</url-pattern>
        _main.setPage(page);
+
</servlet-mapping>
    }
 
}
 
 
</source>
 
</source>
  
<source lang="java" >  
+
{{versionSince| 7.0.0}} You can use <code>RichletFilter</code> instead.
import org.zkoss.zk.ui.GenericRichlet;
+
<source lang="xml" highlight="3,8">
import org.zkoss.zk.ui.Page;
+
<filter>
 +
<filter-name>RichletFilter</filter-name>
 +
<filter-class>org.zkoss.zk.ui.http.RichletFilter</filter-class>
 +
</filter>
  
class MyRichlet extends GenericRichlet {
+
<filter-mapping>
public void service(Page page) {
+
<filter-name>RichletFilter</filter-name>
new MyApp(page); //create and forget
+
<url-pattern>/zk/*</url-pattern>
    }
+
</filter-mapping>
}
 
 
</source>
 
</source>
  
== Configure web.xml and zk.xml ==
+
where you can replace <code>/zk/*</code> with any pattern you like, such as <code>/do/*</code>. Notice that you ''cannot'' map it to an extension (such as <code>*.do</code>) since it will be considered as a ZUML page (rather than a richlet).
After implementing the richlet, you can define the richlet in <tt>zk.xml</tt> with the following statement:
+
 
 +
==Map URL pattern to Richlet==
 +
For each richlet you implement, you can define it in <code>WEB-INF/zk.xml</code> with the statement similar to the following:
  
 
<source lang="xml" >
 
<source lang="xml" >
 
<richlet>
 
<richlet>
     <richlet-name>Test</richlet-name>
+
     <richlet-name>Test</richlet-name><!-- your preferred name -->
     <richlet-class>org.zkoss.zkdemo.TestRichlet</richlet-class>
+
     <richlet-class>org.zkoss.zkdemo.TestRichlet</richlet-class><!-- your class name, of course -->
 
</richlet>
 
</richlet>
 
</source>
 
</source>
  
After declaring a richlet, you can map it to any number of URLs using the <tt>richlet-mapping</tt> element as shown below.
+
After defining a richlet, you can map it to any number of URLs using the <code>richlet-mapping</code> element as shown below.
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 105: Line 157:
 
</source>
 
</source>
  
By default, richlets are disabled. To enable them, add the following declaration to <tt>web.xml</tt>. Once enabled, you can add as many as richlets as you want without modifying <tt>web.xml</tt> any more.
+
'''Note:''' With Richlet Filter (since ZK 7.0.0), you should add the prefix of url-pattern of the filter-mapping into the url-pattern of richlet-mapping.
 +
For example,
 +
<source lang="xml" highlight="7">
 +
<richlet-mapping>
 +
    <richlet-name>Test</richlet-name>
 +
    <url-pattern>/test</url-pattern>
 +
</richlet-mapping>
 +
<richlet-mapping>
 +
    <richlet-name>Test</richlet-name>
 +
    <url-pattern>/zk/some/more/*</url-pattern>
 +
</richlet-mapping>
 +
</source>
 +
As you can see in the highlight above, the '''/zk''' is added which is according to the filter-mapping.
  
<source lang="xml" >
 
<servlet-mapping>
 
    <servlet-name>zkLoader</servlet-name>
 
    <url-pattern>/zk/*</url-pattern>
 
</servlet-mapping>
 
</source>
 
  
 
Then, you can visit [http://localhost:8080/PROJECT_NAME/zk/test http://localhost:8080/PROJECT_NAME/zk/test] to request the richlet.
 
Then, you can visit [http://localhost:8080/PROJECT_NAME/zk/test http://localhost:8080/PROJECT_NAME/zk/test] to request the richlet.
  
The URL specified in the <tt>url-pattern</tt> element must start with <tt>/</tt>. If the URI ends with <tt>/*</tt>, 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 <tt>getRequestPath</tt> method of the current page.
+
The URL specified in the <code>url-pattern</code> element must start with <code>/</code>. If the URI ends with <code>/*</code>, it is matched to all requests with the same prefix. To retrieve the request's actual URL, you can check the value returned by the <code>getRequestPath</code> method of the current page.
  
 
<source lang="java" >
 
<source lang="java" >
Line 125: Line 183:
 
  }
 
  }
 
</source>
 
</source>
 
'''Tip''': By specifying <tt>/*</tt> as the <tt>url-pattern</tt>, you can map all unmatched URLs to your richlet.
 
  
=Version History=
+
<blockquote>
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
+
----
{| border='1px' | width="100%"
+
'''Tip''': By specifying <code>/*</code> as the <code>url-pattern</code>, you can map all unmatched URLs to your richlet.
! Version !! Date !! Content
+
</blockquote>
|-
+
 
| &nbsp;
+
=Load ZUML in Richlet=
| &nbsp;
+
 
| &nbsp;
+
<javadoc>org.zkoss.zk.ui.Execution</javadoc> provides a collection of methods, such as <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Execution</javadoc>, allowing developers to load ZUML documents dynamically. You could load a ZUML document from any source you like, such as database. Please refer to the [[ZK Developer's Reference/UI Composing/ZUML/Load ZUML in Java|Load ZUML in Java]] for details.
|}
+
 
 +
=Use Spring in Richlet=
 +
To use Spring-managed beans in richlets you need the context loader listener that creates spring application context as described in  [[ZK Spring Essentials/Getting Started with ZK Spring/Setting Up ZK Spring]]. Then you could load Spring beans by using a utility class <javadoc>org.zkoss.zkplus.spring.SpringUtil</javadoc>:
 +
 
 +
<source lang="java" >
 +
    Object bean = SpringUtil.getBean(beanName);
 +
</source>
 +
 
 +
 
  
{{ZUMLReferencePageFooter}}
+
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 05:53, 6 February 2024

Overview

A richlet is a small Java program that composes a user interface in Java for serving the user's request. Before composing UI in Java, we suggest you to know basic concept: UI Composing/Component-based UI first.

When a user requests the content of an URL, ZK Loader checks if the resource of the specified URL is a ZUML page or a richlet. If it is a ZUML page, ZK Loader will create components automatically based on the ZUML page's content as we described in the previous chapters.

If the resource is a richlet, 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 the necessary components programmatically in response to the request.

The choice between the ZUML pages and richlets depends on your preference. However, the performance should not cause any concern since parsing ZUML is optimized.

Implement a Richlet

It is straightforward to implement a richlet. First, you have to implement the Richlet interface before mapping a URL to the richlet.

Implement a Richlet as a Java class

A richlet must implement the Richlet interface. However, you generally do not have to implement it from scratch. Rather, you could extend GenericRichlet, and the only thing you have to do is to override Richlet.service(Page). The method is called when an associated URL is requested. For example,

 package org.zkoss.reference.developer.uicomposing;

import org.zkoss.zk.ui.*;
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);
	}

	@Override
	public void init(RichletConfig config) {
		super.init(config);
		//initialize resources
	}

	@Override
	public void destroy() {
		super.destroy();
		//destroy resources
	}
}

In Richlet, you have to compose UI on your own, but some components only support specific child components. We recommend you to read ZK Component Reference before you start to build.

As shown above (line 27), we have to invoke Component.setPage(Page page) explicitly to attach a root component to a page so it will be available at the client.

To have better control, you can even implement the Richlet.init(RichletConfig) and Richlet.destroy() methods to initialize and to destroy any resources required by the richlet when it is loaded.

In addition, 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 with all users for all requests for the mapped URL. A richlet must handle the concurrent requests, and be careful to synchronize access to shared resources. In other words, a richlet (the implementation of the service method) must be thread-safe.

Don't Share Components

When a request (not Ajax request but regular HTTP request) is made by a user, a Desktop and a Page are created first, and then Richlet.service(Page) is invoked to serve the request[1]. In other words, each request is served with an individual desktop and page. Therefore, we cannot share components among different invocations of Richlet.service(Page).

For example, the following code is illegal:

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? Each desktop should have its own set of component instances[2]. 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 first desktop created from the first request. We cannot assign it to the second desktop.


  1. A normal HTTP request; not an Ajax request. Ajax requests are handled in the same way as ZUML. For more information please refer to the Event Handling section
  2. For more information, please refer to Component-based UI section

Map URL to a Richlet

To map URL to a richlet, there are two steps.

  1. Turn on the support of Richlet (in WEB-INF/web.xml)
  2. Map URL pattern to Richlet (in WEB-INF/zk.xml)

Turn on Richlet

By default, richlets are disabled. To enable them, please add the following declaration to WEB-INF/web.xml. Once enabled, you can add as many richlets as you want without modifying web.xml.

With servlet-mapping:

<servlet-mapping>
    <servlet-name>zkLoader</servlet-name>
    <url-pattern>/zk/*</url-pattern>
</servlet-mapping>

Since 7.0.0 You can use RichletFilter instead.

<filter>
	<filter-name>RichletFilter</filter-name>
	<filter-class>org.zkoss.zk.ui.http.RichletFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>RichletFilter</filter-name>
	<url-pattern>/zk/*</url-pattern>
</filter-mapping>

where you can replace /zk/* with 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>

Note: With Richlet Filter (since ZK 7.0.0), you should add the prefix of url-pattern of the filter-mapping into the url-pattern of richlet-mapping. For example,

<richlet-mapping>
    <richlet-name>Test</richlet-name>
    <url-pattern>/test</url-pattern>
</richlet-mapping>
<richlet-mapping>
    <richlet-name>Test</richlet-name>
    <url-pattern>/zk/some/more/*</url-pattern>
</richlet-mapping>

As you can see in the highlight above, the /zk is added which is according to the filter-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 /*, it is matched to all requests 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.

Load ZUML in Richlet

Execution provides a collection of methods, such as Execution.createComponents(String, Component, Map), allowing developers to load ZUML documents dynamically. You could load a ZUML document from any source you like, such as database. Please refer to the Load ZUML in Java for details.

Use Spring in Richlet

To use Spring-managed beans in richlets you need the context loader listener that creates spring application context as described in ZK Spring Essentials/Getting Started with ZK Spring/Setting Up ZK Spring. Then you could load Spring beans by using a utility class SpringUtil:

    Object bean = SpringUtil.getBean(beanName);




Last Update : 2024/02/06

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.