GWT通信机制初探

GWT RPC:GWT提供类似Swing的界面开发模式(基于UI组件重用,事件注册及监听等机制),遵循此开发模式的客户端Java代码将被编译为Javascript代码(并优化,压缩),以运行于客户端浏览器中。然而,客户端Java代码不仅仅包含即将呈现在HTML页面中的UI元素,还包含提供服务的接口和相对应的代理接口(实现异步调用服务),服务接口声明即将被
客户端通过RPC调用的方法。服务器端实现该方法来提供具体服务。服务架构参考下图:

[img]http://dl.iteye.com/upload/attachment/199986/2101d225-193e-38e3-a60c-dedfbba54365.gif[/img]

服务接口必须继承一个空接口RemoteService:

//提供getAddresses()服务的接口类
import java.util.List;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("address")
public interface AddressService extends RemoteService{
public List<Address> getAddresses();
}

/*--------------------------------------------------------*/
//代理接口,提供异步调用服务
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;

public interface AddressServiceAsync {
public void getAddresses(AsyncCallback<List<Address>> callback);
}


服务接口的注释@RemoteServiceRelativePath("address")指定了代理接口调用的服务的相对路径(通常为servlet访问路径);

服务实现类

public class AddressServiceImpl extends RemoteServiceServlet implements AddressService{

private static final long serialVersionUID = 7819604306802209305L;

//实现从数据库查询地理信息的数据集
@Override
public List<Address> getAddresses(){
List<Address> addr_list = new ArrayList<Address>();
DBConnectionPool pool = new DBConnectionPool("pool01", "com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/db?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8",
"admin", "admin123", 5);
System.out.println("[INFO] Connection Pool created!");
ResultSet rs = null;
String sqlStmt = "SELECT addr_id,state,city,address,zip,description FROM address";
try {
Connection conn = pool.getConnection();
System.out.println("[INFO] Connection to DB established!");
PreparedStatement ps = conn.prepareStatement(sqlStmt);
rs = ps.executeQuery();
if(rs == null){
System.out.println("[ERROR] Failed to query data from database!");
return null;
}
System.out.println("[INFO] Execution of query for address list finished!");
Address addr = null;
int cnt = 0;
while(rs.next()){
addr = new Address();
++cnt;
System.out.println("[INFO] No. " + cnt + " of the result records.");
addr.setId(rs.getLong("addr_id"));
addr.setState(rs.getString("state") == null ? "N/A" : rs.getString("state"));
addr.setCity(rs.getString("city"));
addr.setAddress(rs.getString("address"));
addr.setZip(rs.getString("zip"));
addr.setDescription(rs.getString("description") == null ? "N/A" : rs.getString("description") );
addr_list.add(addr);
}
} catch (SQLException e) {
System.out.println("[ERROR] Failed to get field value from resultSet!");
e.printStackTrace();
}
return addr_list;
}

@Override
public void onBeforeRequestDeserialized(String serializedRequest){
System.out.println("[INFO] Received Serialized Request Diagram: " + serializedRequest);
}

@Override
public void onAfterResponseSerialized(String serializedResponse) {
System.out.println("[INFO] Serialized Response Diagram to be sent: " + serializedResponse);
}
}


实现类所继承的[color=red][b]RemoteServiceServlet[/b][/color]是GWT提供的核心Servlet处理类,该类接收客户请求,反序列化RPC请求数据包(GWT提供了序列化及反序列化类库),将反序列化之后的数据交由开发者做业务处理,处理结果将被序列化并组织为响应对象返回客户端。以下为该Servlet处理类的对请求的处理过程:

/**
* Standard HttpServlet method: handle the POST.
*
* This doPost method swallows ALL exceptions, logs them in the
* ServletContext, and returns a GENERIC_FAILURE_MSG response with status code
* 500.
*
* @throws ServletException
* @throws SerializationException
*/
@Override
public final void processPost(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException,
SerializationException {
// Read the request fully.
String requestPayload = readContent(request);

// Let subclasses see the serialized request.
onBeforeRequestDeserialized(requestPayload);

// Invoke the core dispatching logic, which returns the serialized
// result.
String responsePayload = processCall(requestPayload);

// Let subclasses see the serialized response.
onAfterResponseSerialized(responsePayload);

// Write the response.
writeResponse(request, response, responsePayload);
}

//本方法处理反序列化后的请求数据
public String processCall(String payload) throws SerializationException {
try {
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
onAfterRequestDeserialized(rpcRequest);
return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy(),
rpcRequest.getFlags());
} catch (IncompatibleRemoteServiceException ex) {
log(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailure(null, ex);
}
}

/*---------------------- RPC.java -------------------------*/
//RPC类中定义的方法invokeAndEncodeResponse,采用反射机制来调用具体的服务方法。
public static String invokeAndEncodeResponse(Object target,
Method serviceMethod, Object[] args,
SerializationPolicy serializationPolicy, int flags)
throws SerializationException {
if (serviceMethod == null) {
throw new NullPointerException("serviceMethod");
}

if (serializationPolicy == null) {
throw new NullPointerException("serializationPolicy");
}

String responsePayload;
try {
Object result = serviceMethod.invoke(target, args);

responsePayload = encodeResponseForSuccess(serviceMethod, result,
serializationPolicy, flags);
} catch (IllegalAccessException e) {
SecurityException securityException = new SecurityException(
formatIllegalAccessErrorMessage(target, serviceMethod));
securityException.initCause(e);
throw securityException;
} catch (IllegalArgumentException e) {
SecurityException securityException = new SecurityException(
formatIllegalArgumentErrorMessage(target, serviceMethod, args));
securityException.initCause(e);
throw securityException;
} catch (InvocationTargetException e) {
// Try to encode the caught exception
//
Throwable cause = e.getCause();

responsePayload = encodeResponseForFailure(serviceMethod, cause,
serializationPolicy, flags);
}

return responsePayload;
}


onBeforeRequestDeserialized()和onAfterResponseSerialized()由实现类继承,以做定制化处理。

以下是从客户端获取的请求数据包及请求属性:
[quote]
[INFO] Received Serialized Request Diagram: 5|0|4|http://localhost:1947/gmap/|A16261BA4B0DD6867308FEA211E4BEC2|com.wipro.gmap.client.AddressService|getAddresses|1|2|3|4|0|
[INFO] ContentType: text/x-gwt-rpc; charset=utf-8 ServletPath: /gmap/address ContextPath: Protocol: HTTP/1.1 Remote Address: 127.0.0.1 Remote Host: 127.0.0.1:1949 Remote User: null

[Header Information]:
[Host]: localhost:1947 [Connection]: keep-alive [User-Agent]: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2 [Referer]: http://localhost:1947/gmap/hosted.html?gmap [Accept]: */* [Accept-Encoding]: gzip,deflate [Accept-Language]: en-US,en;q=0.8 [Accept-Charset]: UTF-8,*;q=0.5 [Content-Length]: 127 [Origin]: http://localhost:1947 [X-GWT-Module-Base]: http://localhost:1947/gmap/ [Content-Type]: text/x-gwt-rpc; charset=utf-8 [X-GWT-Permutation]: HostedMode

[/quote]

以下为处理后即将返回客户端的数据包(JSON数据格式):
[quote]
[INFO] Serialized Response Diagrm to be sent: //OK[7,13,0.0,3.0,5,12,11,2,7,10,0.0,2.0,5,9,8,2,7,6,0.0,1.0,5,4,3,2,3,1,["java.util.ArrayList/3821976829","com.wipro.gmap.client.Address/1028263900","Golden Gate Bridge","San Francisco","","california","611756","Main St","Oxford","New York","Zhonghe","Chengdu","Sichuan"],0,5]
[/quote]

该数据返回客户端后将触发回调方法,回调方法定义在入口类(EntryPoint)中:

//final ArrayList<Address> addressList = new ArrayList<Address>();
//final ListBox addresses = new ListBox();

AddressServiceAsync as = (AddressServiceAsync) GWT
.create(AddressService.class);
as.getAddresses(new AsyncCallback<List<Address>>() {
@Override
public void onSuccess(List<Address> result) {
Window.alert("[INFO] Succeed to retrieve data from db!");
Iterator<Address> it = result.iterator();
Address address = null;
while(it.hasNext()){
address = new Address();
address = it.next();
System.out.println("[INFO] Address: " + address.getAddress());
addresses.addItem(address.getAddress());
addressList.add(address);
}
System.out.println("[INFO] Data in addresses ListBox:");
addresses.setVisibleItemCount(result.size());
addressGrid.setAddress(addressList.get(0));
System.out.println("[INFO] [GMap.getAddresse()] Retrieved addresses already be populated into the ListBox!");
}

@Override
public void onFailure(Throwable caught) {
String msg = "[ERROR] Failed to retrieve data from database!";
Window.alert(msg);
System.out.println(msg);
GWT.log(msg, caught);
}
});


服务接口及相应的异步接口由GWT编译至客户端Javascript中,由[url=http://gwt.google.com/missing-plugin]GWT Plugin[/url]根据服务器地址及端口,模块(Module),服务调用的页面来对服务器进行RPC调用。参考编译后的客户端JS代码(GWT Plugin负责连接服务器,调用服务端RPC服务):

if (!plugin) {
// try searching for a v1 plugin for backwards compatibility
var found = false;
for (var i = 0; i < pluginFinders.length; ++i) {
try {
plugin = pluginFinders[i]();
if (plugin != null && plugin.connect($hosted, $moduleName, window)) {
return;
}
} catch (e) {
}
}
loadIframe("http://gwt.google.com/missing-plugin");
} else {
if (plugin.connect(url, topWin.__gwt_SessionID, $hosted, $moduleName,
$hostedHtmlVersion)) {
window.onUnload = function() {
try {
// wrap in try/catch since plugins are not required to supply this
plugin.disconnect();
} catch (e) {
}
};
} else {
if (errFn) {
errFn(modName);
} else {
alert("Plugin failed to connect to hosted mode server at " + $hosted);
loadIframe("http://code.google.com/p/google-web-toolkit/wiki/TroubleshootingOOPHM");
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值