|
HTTP 上的二进制远程过程调用
在模型层,RemoteModelProxy类中的HTTPCommunicationHandler类通过HTTP连接上的二进制RPC协议调用服务器端的远程过程。该协议定义如下:
所有从客户端到服务器端的RPC请求都遵循同一基本模式。数据流的第一个字节指定服务器端的界面会话bean必须执行的动作方法,余下的字节编码为UTF数据串序列,它表示传递给远程方法的参数。HTTP数据流包含RPC返回值。请求和响应的格式对每一种方法来说都是唯一的,所以,你必须查看每一种方法的源代码,以确定准确的格式。
进入请求数据流第一个字节的RPC编码在MessageConstants类中进行定义:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>package com.sun.j2me.blueprints.smartticket.shared.midp;
public final class MessageConstants {
public static final byte OPERATION_LOGIN_USER = 0;
public static final byte OPERATION_CREATE_ACCOUNT = 1;
public static final byte OPERATION_UPDATE_ACCOUNT = 2;
public static final byte OPERATION_GET_THEATERS = 3;
public static final byte OPERATION_GET_THEATER_SCHEDULE = 4;
public static final byte OPERATION_GET_MOVIE = 5;
public static final byte OPERATION_GET_MOVIE_POSTER = 6;
public static final byte OPERATION_GET_MOVIE_SHOWTIMES = 7;
public static final byte OPERATION_GET_SEATING_PLAN = 8;
public static final byte OPERATION_RESERVE_SEATS = 9;
public static final byte OPERATION_PURCHASE_TICKETS = 10;
public static final byte OPERATION_CANCEL_SEAT_RESERVATION = 11;
public static final byte OPERATION_GET_LOCALES = 12;
public static final byte OPERATION_GET_RESOURCE_BUNDLE = 13;
public static final byte OPERATION_INITIATE_SYNCHRONIZATION = 14;
public static final byte OPERATION_SYNCHRONIZE_MOVIE_RATINGS = 15;
public static final byte OPERATION_COMMIT_MOVIE_RATINGS = 16;
public static final byte ERROR_NONE = 0;
public static final byte ERROR_UNKNOWN_OPERATION = 1;
public static final byte ERROR_SERVER_ERROR = 2;
public static final byte ERROR_MODEL_EXCEPTION = 3;
public static final byte ERROR_REQUEST_FORMAT = 4;
private MessageConstants() {}
}
|
下面的两个类解释了一个RPC往返过程,HTTPCommunicationHandler类的动作方法请求指定电影的信息,并调用Movie类的一个方法来提取响应流中的返回值。
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %> package com.sun.j2me.blueprints.smartticket.client.midp.model;
public class HTTPCommunicationHandler
extends RemoteModelRequestHandler {
// ...
public Movie getMovie(String movieKey)
throws ModelException,
ApplicationException {
HttpConnection connection = null;
DataOutputStream outputStream = null;
DataInputStream inputStream = null;
try {
connection = openConnection();
updateProgress();
outputStream = openConnectionOutputStream(connection);
// The RPC request
outputStream.writeByte(MessageConstants.OPERATION_GET_MOVIE);
outputStream.writeUTF(movieKey);
outputStream.close();
updateProgress();
// unmarshal the return values
inputStream = openConnectionInputStream(connection);
Movie movie = Movie.deserialize(inputStream);
updateProgress();
return movie;
} catch (IOException ioe) {
throw new
ApplicationException(ErrorMessageCodes.ERROR_CANNOT_CONNECT);
} finally {
closeConnection(connection, outputStream, inputStream);
}
}
// Other action methods ...
}
package com.sun.j2me.blueprints.smartticket.shared.midp.model;
public class Movie {
private String primaryKey;
private String title;
private String summary;
private String rating;
private boolean alreadySeen = false;
transient private byte[] poster = null;
public static Movie deserialize(DataInputStream
dataStream) throws ApplicationException {
try {
Movie movie = new Movie();
// Read the RPC response stream
movie.primaryKey = dataStream.readUTF();
movie.title = dataStream.readUTF();
movie.summary = dataStream.readUTF();
movie.rating = dataStream.readUTF();
try {
movie.alreadySeen = dataStream.readBoolean();
} catch (IOException ioe) {
movie.alreadySeen = false;
}
try {
return
ModelObjectLoader.getInstance().getMovie(movie);
} catch (ModelException me) {
throw new ApplicationException();
}
} catch (IOException ioe) {
throw new ApplicationException(ioe);
}
}
// Other methods ...
} |
在服务器端,SmartTicketServlet首先确定在请求数据流中的第一个字节编码所表达的动作,然后立即通过界面将请求分派合适的动作方法,并传递保留在数据流中的所有RPC参数。
在Smart Ticket程序中,客户机和服务器紧密联系。这种方式可提高网络性能,因为,每次RPC交换都可以经过特别的设计和优化。然而,要在开发速度和健壮性之间进行权衡。 即使服务器端的微小改变也很可能逼使客户端的协议和解析码进行改变,还有很多潜在的可能因素。开发人员需要对所有可能影响的代码保持跟踪,并在必要时更新它。 他们也需要经常重新编译和重新分发客户端程序,否则将可能导致错误。
客户端线程模型
Smart Ticket应用程序在客户端采用一个复杂的线程模型,有两个重要方面:
◆MIDP规范请求CommandListener.commandAction()方法“立即返回”以避免阻塞UI,因此任何长时间的操作都必须放入其他线程。
◆正运行的线程能够显示一个指示长时操作进度的动态进度条,特别是涉及远程网络操作的线程。进度条屏幕为缺乏耐心的用户提供了一个取消按钮,可终止太长的运作。
也许你早就注意到UIController类中的动作方法只是runWithProgress()方法的简单包装,该方法设置屏幕为ProgressObserverUI并启动EventDispatcher线程。 ProgressObserverUI屏幕显示一个进度条和一个Stop按钮,通过主MIDlet系统UI线程来监控它。如前所述,EventDispatcher线程最终委托到模型层方法的请求动作。 这些方法中的每一个都在其执行的某个阶段调用 ProgressObserverUI.updateProgress() ,以告知用户的进度情况。
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %> public class UIController {
// Action methods ...
public void chooseMovieRequested() {
runWithProgress(
new EventDispatcher(
EventIds.EVENT_ID_CHOOSEMOVIEREQUESTED,
mainMenuUI),
getString(UIConstants.PROCESSING), false);
}
// Action methods ...
public void runWithProgress(Thread thread, String title,
boolean stoppable) {
progressObserverUI.init(title, stoppable);
getDisplay().setCurrent(progressObserverUI);
thread.start();
}
class EventDispatcher extends Thread {
// ...
public void run() {
// Switch -- case statements to delegate
// actions to the model layer
}
}
}
public class ProgressObserverUI extends Form
implements ProgressObserver,
CommandListener {
private UIController uiController;
private static final int GAUGE_MAX = 8;
private static final int GAUGE_LEVELS = 4;
int current = 0;
Gauge gauge;
Command stopCommand;
boolean stoppable;
boolean stopped;
public ProgressObserverUI(UIController uiController) {
super("");
gauge = new Gauge("", false, GAUGE_MAX, 0);
stopCommand = new
Command(uiController.getString(UIConstants.STOP),
Command.STOP, 10);
append(gauge);
setCommandListener(this);
}
public void init(String note, boolean stoppable) {
gauge.setValue(0);
setNote(note);
setStoppable(stoppable);
stopped = false;
}
public void setNote(String note) {
setTitle(note);
}
public boolean isStoppable() {
return stoppable;
}
public void setStoppable(boolean stoppable) {
this.stoppable = stoppable;
if (stoppable) {
addCommand(stopCommand);
} else {
removeCommand(stopCommand);
}
}
/**
* Indicates whether the user has stopped the progress.
* This message should be called before calling update.
*/
public boolean isStopped() {
return stopped;
}
public void updateProgress() {
current = (current + 1) % GAUGE_LEVELS;
gauge.setValue(current * GAUGE_MAX / GAUGE_LEVELS);
}
public void commandAction(Command c, Displayable d) {
if (c == stopCommand) {
stopped = true;
}
}
} |
结束语
本文介绍了全新的Smart Ticket v2.0 蓝图。几个针对早期版本的重大改进都利用了智能客户端丰富的功能。Smart Ticket向你展示了如何用几个我们刚才简要讲述的重要设计模式来实现高级功能。我们希望我们本文讲述的内容能让你在端到端设计模式领域快速起步。
参考资料
Sun Java Wireless Blueprints
“Developing an End to End Wireless Application Using Java Smart Ticket Demo”,作者:Eric Larson (涵盖了Smart Ticket v1.1)
关于作者
Norman Richards是美国得克萨斯州奥斯汀市Zilliant的一名工程师。他是XDoclet in Action (Manning Publications,2003 年夏)一书的合著者。
Michael Yuan是奥斯汀市得克萨斯大学的一名在读博士。他定期为JavaWorld的“Wireless Java”专栏撰稿。他当前正在为Prentice Hall写一本关于J2ME的图书,Enterprise J2ME: Developing Java Mobile Applications (2003 年秋)。
(责任编辑 火凤凰 sunsj@51cto.com TEL:(010)68476636-8007)
|