原文:http://www.theserverside.com/tt/articles/article.tss?l=GWTandJSF
翻译:icess http://blog.matrix.org.cn/page/icess 讨论
AJAX, AJAX, AJAX
如果你听说过 GWT,那么你一定也听说过它的一个核心特性就是 AJAX 支持.在 GWT中, the "X" part,在客户端和服务器端来回传递数据, 该功能是通过RPC (Remote Procedure Call) 机制实现的而不是使用 XMLHttpRequest object.
不管这些,你想在服务器端做什么是有你决定的. Google称它为"Server Agnostic".
让我们看看如何添加 AJAX 方式的异步通信到我们的程序中.
Asynchronous Communication in Hosted Mode
在下个阶段中,我们将改变该程序的环境.我们添加一个用户可以输入姓名的文本域,一旦按钮被点击了,请求将会发送到服务器端.服务器将返回出现在弹出窗口的消息.为了跟踪请求和响应,我们添加一个文本标签来显示请求的状态.
为了在服务器和客户端传送数据.我们使用两个Java beans. – 一个把数据带回服务器端,一个把响应带回客户端. .
Client-Side Coding
28. 在JavaSource中创建一个 demo.gwt.client.EventData 类,内容如下:
package demo.gwt.client;
import org.ajax4jsf.gwt.client.GwtFacesEvent;
public class EventData extends GwtFacesEvent {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用来在客户端到服务器端传送数据的类(发送事件)应该继承 G4jsf的 GwtFacesEvent类.
29. 创建另外一个类 demo.gwt.client.ResultGreeting.
内容如下:
package demo.gwt.client;
import org.ajax4jsf.gwt.client.GwtFacesResult;
public class ResultGreeting extends GwtFacesResult {
String greetingText;
public String getGreetingText() {
return this.greetingText;
}
public void setGreetingText(String greetingText) {
this.greetingText = greetingText;
}
}
从服务器端带来响应的类应该继承G4jsf的 GwtFacesResult 类.
GwtFacesEvent 和GwtFacesResult 都实现了 com.google.gwt.user.client.rpc.IsSerializable 接口,用来序列化在客户端和服务器端传递的被GWT使用的数据.
让我们更新我们的widget 类.
30. 打开demo.gwt.client.HelloWidgetEntryPoint 使用下面代码替换它:
package demo.gwt.client;
import java.util.Map;
import org.ajax4jsf.gwt.client.ComponentEntryPoint;
import org.ajax4jsf.gwt.client.GwtFacesServiceAsync;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class HelloWidgetEntryPoint extends ComponentEntryPoint {
Label status;
TextBox input;
protected Widget createWidget(final String id) {
Map m = getWidgetParams(id);
final String buttonLabel = (String) m.get("buttonLabel");
HorizontalPanel panel = new HorizontalPanel();
input = new TextBox();
status = new Label("Loaded.");
final GwtFacesServiceAsync service = createFacesService(id);
final AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
if (null != result) {
status.setText("Loaded");
String greeting = ((ResultGreeting)result).getGreetingText();
Window.alert(greeting);
} else {
status.setText("Request finished, but the result is empty");
}
}
public void onFailure(Throwable caught) {
status.setText("Error call :" + caught.getMessage());
}
};
Button btn = new Button(buttonLabel, new ClickListener() {
public void onClick(Widget sender) {
EventData eventData = new EventData();
eventData.setName(input.getText());
service.sendEvent(eventData, callback);
status.setText("Loading...");
}
});
panel.add(input);
panel.add(btn);
panel.add(status);
return panel;
}
}
这里有3个重要的部分.1.组件的布局.我们创建了一个水平面板,然后添加了一个input box,button,和text label.
2.声明了异步服务和事件,它继承至异步模型的AJAX请求.
...
final GwtFacesServiceAsync service = createFacesService(id);
...
Button btn = new Button(buttonLabel, new ClickListener() {
public void onClick(Widget sender) {
EventData eventData = new EventData();
eventData.setName(input.getText());
service.sendEvent(eventData, callback);
status.setText("Loading...");
}
});
...
:
我们添加ClickListener 到button上. 当 On Click 事件发生时,我们创建并且使用输入的数据填充 EventData bean 然后使用异步服务发送该事件.在代码的组后一行,我们设置text label的文本为 "Loading…",因此在 AJAX request 开始的时候,用户可以看到变化 .
3.发送事件,我们注册了一个回调函数.
.........
.........
final AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
if (null != result) {
status.setText("Loaded");
String greeting = ((ResultGreeting)result).getGreetingText();
Window.alert(greeting);
} else {
status.setText("Request finished, but the result is empty");
}
}
public void onFailure(Throwable caught) {
status.setText("Error call :" + caught.getMessage());
}
};
...........
...........
Callback 是 Google toolkit中AsyncCallback 类的一个接口.我们实现了两个方法 onSucess and onFailure. 在onSuccess情况下, 我们添加了一个附加的检测到来的结果.如果我们没有受到一个期望的类,我们标记在状态文本中.
现在我们完成了客户端的代码.如果你使用 Hosted Mode (with the "ant shell" command)来启动程序, 你将看到 "Request finished, but the result is empty", 因为我们还没有写服务器端代码.
Server-Side Coding
在Hosted Mode中的服务器端代码, 在server包中的一个类扮演了一个重要角色.
31. 打开JavaSource中的demo.gwt.server.MockHelloWidget java 文件.
sendEvent 函数复杂发送响应到客户端
32. 把sendEvent函数替换为下面的内容.:
public GwtFacesResult sendEvent(GwtFacesEvent event){
ResultGreeting result = new ResultGreeting();
result.setGreetingText( "Hello " +
((EventData)event).getName() );
return result;
}
33. 导入需要的类:
import demo.gwt.client.ResultGreeting;
import demo.gwt.client.EventData;
The parameter of the method points to the event content that came from the client. What we do here is create the Result bean filling it with a greeting message and then returning.
34.在Hosted Mode中启动ant:
ant shell
现在程序如下:
Adding JSF Listeners for Run-Time Mode
处理JSF的事件和Hosted Mode的代码差不多. 客户端代码不变.在服务器端 , G4jsf使用 JSF 监听器机制来处理 Ajax events.
35. 打开 WebContent/pages/Base.xhtml并添加监听器组件声明.
<widget:component id="main" buttonLabel="#{bundle.buttonLabel}"
greeting="Hello #{greetingBean.name}!" >
<gwt:gwtListener method="#{greetingBean.takeGreeting}"
event ="demo.gwt.client.EventData"/>
</widget:component>
gwtListener 元素有两个属性. "method"使用JSFEL指向处理器."event"用来定义事件类型. 事件类型是类的全限定名 .最后一步是实现该函数.
36. 打开demo.gwt.app.GreetingBean添加下面的代码:
public ResultGreeting takeGreeting(EventData event) {
name = event.getName();
ResultGreeting result = new ResultGreeting();
result.setGreetingText("Hello " + name + " from JSF!");
return result;
}
37. 和需要导入的类:
import demo.gwt.client.ResultGreeting;
import demo.gwt.client.EventData;
该函数的签名(signature )很容易记住,该函数使用来自于客户端的事件作为他的唯一一个参数.返回值为 用来返回结果的事件类型(type of the method equals is just the type of the class used to return the result).在这个函数中,我们合成响应数据并且返回它.
38. 创建war文件,部署它.
如果你启动服务器,可以看导:
The Last Word
就如你看到的,通过G4JSF使用两种互补的技术(GWT and JSF)可以做很多漂亮的事情. 但是,仍然还有很多东西可以进一步添加到G4jsf中.作为一个开源项目, , G4jsf依靠一个开源社区来支持和开发它.如果你只是使用它,那是很好的.但是你也可以加入到G4JSF社区中来, 帮助G4JSF让他做的更好. Come visit us at:
https://ajax4jsf.dev.java.net
About the Author
Sergey Smirnov is Senior Product Manager at Exadel where he oversees the development of major products including Exadel Visual Components Platform and Exadel Studio. He has more than 15 years of in-depth development experience, primarily in the area of Web applications. His experience with JSF goes back to the very early days. For two years, he has served as a member of the JSF expert group. In addition to this, he manages a site for JSF-related resources, www.jsftutorials.net. Finally, Sergey is the co-lead of the open source project Ajax4jsf (https://ajax4jsf.dev.java.net). He can be reached at ssmirnov@exadel.com.