Статьи

ZK vs. GWT: вопросы, связанные с сервером!

 

содержание

  1. Аннотация
  2. Ориентированный на сервер и ориентированный на клиента
    • Где работают приложения
  3. Ориентированные на сервер и клиент Ajax фреймворки на практике: ZK и GWT
  4. ZK против GWT в действии: Google Maps Locator
    • ZK (1 файл, 36 строк кода)
    • GWT (5 файлов, 190 строк кода)
    • DWR (2 файла, 77 строк кода)
  5. ZK против GWT в действии 2: привязка данных
    • ZK (1 файл, 39 строк кода)
    • GWT (4 файла, 200 строк кода)
  6. ZK против GWT в действии 3: живые данные
    • ZK (1 файл, 15 строк кода)
    • GWT (я даже не хочу его кодировать)
  7. ZK Anywhere

    • А как насчет устройств без браузеров?
    • ZK на телефонах Java
    • ZK на Google Android

  8. Вывод
    • Правильный инструмент для правильной работы

 

1. Аннотация

С 18 февраля — го , 2005 Джесси Джеймс Гарретт первым ввел термин «AJAX» в своей статье «Ajax: Новый подход к веб — приложениям.» С тех пор Ajax стал очень популярной темой в сообществах разработчиков веб-приложений. За последние 2 года множество сообществ Ajax было построено для сообщества разработчиков. Эти фреймворки отличаются от чистых фреймворков JavaScript, таких как Yahoo! Пользовательский интерфейс (YUI) для Flash-приложений, таких как Adobe® AIR (Adobe Integrated Runtime). Но СМИ и широкая аудитория часто игнорируют одну важную концепцию: ориентированы ли они на сервер или клиент? Эта статья пытается прояснить некоторые заблуждения с реальными примерами ZK и GWT.

 

2. Ориентированный на сервер и ориентированный на клиента

Где запускаются приложения:

In short, the main difference between server-centric and client-centric is where the application runs. In a server-centric framework, the application is hosted on the application server and all processing is done on the server. The client browser is only used for data presentation.

In contrast, applications that do all of their processing on the client side, such as GWT, use JavaScript running in the client browser.

 



Figure 1 — Server-Centric vs. Client-Centric

3. Server and Client Centric Ajax Frameworks in Practice: ZK and GWT

ZK and GWT bring a similar mantra to Java developers — developing Ajax applications using Java rather than JavaScript. To developers who rely on Java as their tool of choice, both frameworks offer a great escape from the JavaScript nightmare.

 

At the first glance, developers will consider that these 2 frameworks are similar: both allow the developer to use Java, and they both, support AJAX user interface components. Probe a little more deeply into the mechanism behind both frameworks and you will see that ZK and GWT work in an entirely different fashion. Generally GWT compiles Java code into JavaScript allowing the application to run in the client browser rather than on the server’s. Applications built of GWT interact with the server only when data retrieval is necessary. ZK framework uses a different approach from GWT: the application runs on the server and ZK takes care of the presentation layer.

 

 

To Know more how ZK works as a server-centric framework, refer to the following:

4. ZK vs. GWT in Action : Google Maps Locator

In the following section a simple Google Maps Locator application will be implemented by ZK and GWT in order to demonstrate the difference between the frameworks. Assuming that you are coding a Google Maps Locator for a client, the requirements might contain:

  1. An UI with a text input field, a button and a Google map.
  2. When user clicks the button the text in the textbox will be used as a keyword to find the latitude, longitude and description for that location.
  3. Displaying the location on the Google Map and display the description as an Info Window.

Figure.2 — Google Maps Locator

 

Let us assume you have already implemented a magical Java class which is called ocator.?It will take a string as input and return the required information. The only work left is implementing the UI and data retrieval parts. Let see code from both frameworks in real action.

ZK (1 File, 36 Lines of Code)

Gmap.zul:

<?xml version="1.0" encoding="utf-8"?>
<zk>
<script src="http://maps.google.com/maps?file=api&v=2&key=KEY"
type="text/javascript">
</script>
<zscript>
import com.macroselfian.gps.Locator;
public void locate(){
String location = tb.getValue();
double[] pos = Locator.locate(location);
mymap.panTo(pos[0], pos[1]);
mymap.setZoom(16);
ginfo.setOpen(true);
ginfo.setLat(pos[0]);
ginfo.setLng(pos[1]);
ginfo.setContent(Locator.getInfo(location));
mymap.openInfo(ginfo);
}
</zscript>
<window>
<div align="center">
<separator spacing="50px" />
<vbox>
<label value="Macroselfian Google Map Locator"/>
<hbox width="600px">
<textbox width="550px" id="tb"/>
<button width="50px" onClick="locate();" label="Search"/>
</hbox>
<gmaps id="mymap" zoom="16" ...>
<ginfo id="ginfo"/>
</gmaps>
</vbox>
</div>
</window>
</zk>

GWT (5 Files, 190 Lines of Code)

Client Side Files:

  1. Map.java ?The main application class
  2. LocationGenerator.java ?The interface for RPC.
  3. LocationGeneratorAsync.java- The interface for RPC.
  4. LocatorData.java ?The data wrapper class for passing data from server.

Server Side Files:

  1. LocationGeneratorImpl.java ?The data provider class in server side.

Code Snippet:

Map.java:

?br />public class Map implements EntryPoint {
?
public void onModuleLoad() {
?br /> mapWidget = new GMap2Widget("400", "600");
gmaps = mapWidget.getGmap();
gmaps.addControl(GControl.GMapTypeControl());
gmaps.addControl(GControl.GLargeMapControl());
gmaps.setZoom(16);
VerticalPanel vPanel = new VerticalPanel();
HorizontalPanel hPanel = new HorizontalPanel();
hPanel.add(location);
hPanel.add(search);
vPanel.add(title);
vPanel.add(hPanel);
vPanel.add(mapWidget);
RootPanel.get().add(vPanel);
}

public class ClickListenerImpl implements ClickListener{
public void onClick(Widget widget) {
LocationGeneratorAsync async =
LocationGenerator.Util.getInstance();
async.locate(location.getText(), new LocationCallback());
}
}

public class LocationCallback implements AsyncCallback {
public void onFailure(Throwable error) {
response.setText("Ops..!");
}

public void onSuccess(Object resp) {
LocatorData data = (LocatorData)resp;
double lat = data.getLat();
double lng= data.getLng();
String inf = data.getInfo();
Label info = new Label(inf);
info.setStyleName("map-info");
GLatLng pos = new GLatLng(lat,lng);
gmaps.setCenter(pos);
gmaps.openInfoWindow(pos,info);
}
}
}

LocationGenerator.java:

	
public interface LocationGenerator extends RemoteService {
public static final String SERVICE_URI = "/locationgenerator";
public static class Util {
public static LocationGeneratorAsync getInstance() {
LocationGeneratorAsync instance =
(LocationGeneratorAsync)GWT.create(LocationGenerator.class);
ServiceDefTarget target = (ServiceDefTarget) instance;
target.setServiceEntryPoint(GWT.getModuleBaseURL()SERVICE_URI);
return instance;
}
}
public LocatorData locate(String location);
}

LocationGeneratorAsync.java:

public interface LocationGeneratorAsync {
public void locate(String location, AsyncCallback callback);
}

LocatorData.java:

public class LocatorData implements IsSerializable {
private double _lng;
private double _lat;
private String _info;
private String _title;

public LocatorData(double lat, double lng, String info,? {
_lng = lng;
_lat = lat;
_info = info;
_title = title;
}
?br />}

LocationGeneraotrImpl.java:

public class LocationGeneratorImpl extends RemoteServiceServlet 
implements LocationGenerator {
public LocatorData locate(String location) {
com.macroselfian.gps.Locator locator =
new com.macroselfian.gps.Locator(location);
LocatorData data = new
LocatorData(locator.getLat(),locator.getLng(),locator.getInfo(),
locator.getTitle());
return data;
}
}

 

Using the ZK framework, the component state is maintained by the server. One of the advantages of server-centric applications is that data retrieval and business logic processing are straight-forward. Since the component states are maintained on the server side, retrieving data or processing business logic requests require no extra work.

In contrast, a GWT-RPC call is required when GWT applications needs data from the server. In Map.java, async.locate(? is called when onClick is trigged, then a callback object, LocationCallback is required to process the returned data. If you have any JavaScript programming experience, you will soon find out that it is very similar to calling XMLHttpRequest functions in JavaScript. Up to this point you have probably figured out that by using ZK server-centric approach, developers don have to make an RPC call and handle the returned data manually.

DWR (2 Files, 77 Lines of Code)

Here is a sample code for achieve the same functionality by another client-centric framework, DWR.

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoting.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
</web-app>

Map.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 

"http://www.w3.org/TR/html4/loose.dtd">


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="dwr/engine.js"></script>
<script type="text/javascript" src="dwr/util.js"></script>
<script type='text/javascript' src="dwr/interface/locator.js"> </script>
<script src="http://maps.google.com/maps?file=api&v=2" type="text/javascript"></script>
<script type="text/javascript" src="http://www.google.com/jsapi?key=ABCDEFG"></script>
</head>
<body>
<script language="javascript" >

var lng, lat, map;

function goto(location){
locator.locate(location, callBackLat);
}

function callBackLat(data){
lat = data[0];
lng = data[1];
load();
}

function load(){
if (GBrowserIsCompatible()) {
var level = 16;
map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(lat, lng), level);
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
var point = new GLatLng(lat,lng);
setGmarker();
setGinfo();
}
}

function setGmarker(){
var point = new GLatLng(lat,lng);
map.addOverlay(new GMarker(point));
}

function setGinfo(){
locator.getInfo(location, callBackGinfo);
}

function callBackGinfo(data){
var point = new GLatLng(lat,lng);
span_element = document.createElement("span");
span_element.setAttribute("style","font-size:11px;font-family:verdana;");
txt_node = document.createTextNode(data);
span_element.appendChild(txt_node);
map.openInfoWindow(point,span_element);
}

</script>
<input id="location" type="textbox" onchange="goto(this.value);" />
<div id="map_canvas" style="width: 500px; height: 300px"></div>
</body>
</html>

 

Both GWT and DWR use the client-centric approach, but DWR is quite different from GWT. Compared to GWT, DWR requires less codes. However, as a result of its flexibility, developers have to handle the browser dependency issues. In short, DWR only builds the ridge?between the client and the server.

 

5. ZK vs. GWT in Action 2 : Data-binding

Every developer will encounter this in his/her career, displaying master and detail problem. For example, there is list of names when the user clicks on one of names; the details behind the name need to be displayed in the details panel. ZK comes with a very neat solution for this task: annotation data-binding. For more information about ZK annotation data-binding, please refer to Y-Grid Support Drag-Drop and DataBinding.

To achieve this task in GWT, developers need to make an RPC call, find which cell in the grid which needs updating and so on. It is not a bad idea, but you have to do all that work by yourself.

 

ZK (1 File, 39 Lines of Code)

ygrid-databinding2.zul

<?init class="org.zkforge.yuiext.zkplus.databind.AnnotateDataBinderInit" ?> 
<window xmlns:y="http://www.zkoss.org/2007/yui" width="500px">
<zscript src="Person.zs"/>
<y:grid height="200px" model="@{persons}" selectedItem="@{selected}">
<y:columns>
<y:column label="First Name"/>
<y:column label="Last Name"/>
<y:column label="Full Name"/>
</y:columns>
<y:rows>
<y:row self="@{each=person}">
<y:label value="@{person.firstName}"/>
<y:label value="@{person.lastName}"/>
<y:label value="@{person.fullName}"/>
</y:row>
</y:rows>
</y:grid>
<!-- show the detail of the selected person -->
<textbox value="@{selected.firstName}"/>
<textbox value="@{selected.lastName}"/>
<label value="@{selected.fullName}"/>
<zscript>
//init each person
setupPerson(Person person, int j) {
person.setFirstName("First "+j);
person.setLastName("Last "+j);
}
//prepare the example persons List
int count = 30;
List persons = new ArrayList();
for(int j= 0; j < count; ++j) {
Person personx = new Person();
if(j==0)
selected = personx;
setupPerson(personx, j);
persons.add(personx);
}
</zscript>
</window>

 

GWT (4 Files, 200 Lines of code)

Client Side Code:

  1. FooExt.java ?The main application class
  2. DataSourceGenerator.java ?The interface for RPC.
  3. DataSourceGeneratorAsync.java- The interface for RPC.

Server Side Code:

  1. DataSourceGeneratorImpl.java ?The actual data provider class in server side.

Code Snippet:

FooExt.java:

package test.gwtExt.client;
import com.google.gwt.core.client.EntryPoint;
?br />public class FooExt implements EntryPoint {

?br />
public void onModuleLoad() {
_response = new Label();
_first = new TextBox();
_first.setName("first");
_first.addChangeListener(new OnTextChangeListenerImpl());
_last = new TextBox();
_last.setName("last");
_last.addChangeListener(new OnTextChangeListenerImpl());
DataSourceGeneratorAsync async =
DataSourceGenerator.Util.getInstance();
async.getData(new DataCallback());
RootPanel.get().add(_first);
RootPanel.get().add(_last);
RootPanel.get().add(_response);
}

public class DataCallback implements AsyncCallback {

public void onFailure(Throwable error) {
?
}

public void onSuccess(Object resp) {
Object[][] data = (Object[][])resp;
createGrid(data);
}
}

public void createGrid(Object[][] data){

MemoryProxy proxy = new MemoryProxy(data);
_recordDef = new RecordDef(
new FieldDef[]{
new StringFieldDef("first"),
new StringFieldDef("last"),
new StringFieldDef("full")
}
);

ArrayReader reader = new ArrayReader(_recordDef);

Store store = new Store(proxy, reader);
store.load();
ColumnModel columnModel = new ColumnModel(new ColumnConfig[]{
new ColumnConfig() {
{
setHeader("First Name");
setWidth(160);
setSortable(true);
setLocked(false);
setDataIndex("first");
}
},
new ColumnConfig() {
{
setHeader("Last Name");
setWidth(100);
setSortable(true);
setDataIndex("last");
}
},
new ColumnConfig(){
{
setHeader("Full Name");
setWidth(260);
setRenderer(new Renderer(){
public String render(?;

_grid = new Grid("grid-example1", "460px", "300px", store, ?;
_grid.addGridRowListener(new RowClickListener());
_grid.render();

}

public class RowClickListener implements GridRowListener{

public void onRowClick(Grid grid, int rowIndex, EventObject e) {
FieldDef[] fs = _recordDef.getFields();
Record record = grid.getStore().getAt(rowIndex);
_first.setText(record.getAsString(fs[0].getName()));
_last.setText(record.getAsString(fs[1].getName()));
_selectedRow = rowIndex;
}

?br />
}

public class OnTextChangeListenerImpl implements ChangeListener {
public void onChange(Widget widget) {
/*
* TODO: Modify data in server side by RPC
*/
TextBox target = (TextBox)widget;
Record r = _grid.getStore().getAt(_selectedRow);
if(target.getName().equals("first")){
r.set("first", target.getText());
}else if(target.getName().equals("last")){
r.set("last",target.getText());
}
}
}
}

DataSourceGenerator.java:

public interface DataSourceGenerator extends RemoteService {
public static final String SERVICE_URI = "/datasourcegenerator";
public static class Util {
public static DataSourceGeneratorAsync getInstance() {
DataSourceGeneratorAsync instance =
(DataSourceGeneratorAsync)GWT.create(DataSourceGenerator.class);
ServiceDefTarget target = (ServiceDefTarget) instance;
target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI);
return instance;
}
}
public String[][] getData();
}

DataSourceGeneratorAsync.java:

public interface DataSourceGeneratorAsync {
public void getData(AsyncCallback callback);
}

DataSourceGeneratorImpl.java:

public class DataSourceGeneratorImpl extends RemoteServiceServlet 

implements DataSourceGenerator {
public String[][] getData() {
org.zkoss.demo.DataSource ds = new org.zkoss.demo.DataSource();
return ds.getData();
}
}

6. ZK vs. GWT in Action 3 : Live data

One of the most frustrating problems for web application developers is displaying large amounts of data without the latency of data loading. ZK comes out with a very neat solution to this problem, live data (load-on-demand). For more information about ZK live data, refer to How to realize the idea of live data in a Grid.

 

ZK (1 File, 15 Lines of Code)

Ygrid-livedata.zul:

<window xmlns:y="http://www.zkoss.org/2007/yui" title="Y-Grid Live Data"  
width="200px" border="normal">
<zscript><![CDATA[
String[] data = new String[200];
for(int j=0; j < data.length; ++j) {
data[j] = "option "+j;
}
ListModel strset = new SimpleListModel(data);
]]></zscript>
<y:grid height="200px" model="${strset}">
<y:columns>
<y:column label="options"/>
</y:columns>
</y:grid>
</window>


GWT (I Don Ever Want to Code It)

The concept is like this: add a listener for the grid bodyscroll event then, when the body is scrolled, get the roper?data from server by GWT-RPC. After that parse the data and add the data into the grid model and then finally, call the grid render method. If you know a better way to accomplish this task, please contact me.

 

7. ZK Anywhere

What about the devices without browsers?

Due to the iPhone phenomenon, some Ajax solutions support smart phones with browsers. But, what about 1.8 billion Java phones which do not come with such resources (computing power, memory, browser, etc.) that iPhone provides? The server-centric/thin-client approach by ZK brings benefits to those resource constrained devices. Your client devices will not be limited to robust ones with browser. With ZK, you can run your Web applications anywhere.

 

ZK on Java Phones

Figure 3 — ZK on Java Phone

More articles about ZK Mobile

 

 

ZK on Google Adroid

Figure 4 — ZK on Android

More aritcles about ZK Android

 

 

8. Conclusion

Right Tool for the Right Job

In the process of collecting information for this article, I found out that there are thousands of articles comparing Ajax frameworks. Many of them are trying to convince the reader that one framework is a etter?choice than others, thus treating the Ajax framework market as a Zero-Sum Game. I personally see it from a different point of view. After having seen both the server-centric and client-centric frameworks, making a comparison for them is like comparing apples to oranges. Both frameworks come with the initiative of developing Ajax applications with less efforts, but they take an entirely different approach to it.

 

To me, both of them are great tools to use. The real question is what kind of problem are you trying to solve?

Find the right tool for the right job. For heavy data access applications and projects that require a higher level of security, I prefer the server-centric approach. If the application requires fancy client side actions and less server requests, the client-centric approach could be my choice.