|
客户端界面
对于大多数应用程序动作,指向模型层的控制器条目是ModelFacade类。为符合MVC模式,ModelFacade类包含一个响应模型层中每个事件的方法。根据动作的本质,界面将它委托给以下的一个或多个模型类:
◆LocalModel类处理需要对本地设备上存储的数据进行访问的动作。例如,如果一个动作需要读写首选数据,ModelFacade 调用 LocalModel中合适的动作方法。
◆RemoteModelProxy类,它实现了RemoteModel接口,处理需要对J2EE服务器进行访问的动作,比如购票。RemoteModelProxy中的方法对服务器端界面进行远程过程调用(remote procedure call,RPC) ,这种方式我们将在讨论后台时讲述。
◆SynchronizationAgent类用本地数据同步化远程服务器端数据。在Smart Ticket程序中,只有评级实现了同步。这个代理有两个动作方法:synchronizeMovieRatings()同步了评级;commitMovieRatings() 方法向后台提交已分析的同步请求,并更新本地存储的内容。
package com.sun.j2me.blueprints.smartticket.client.midp.model;
public class ModelFacade {
private SynchronizationAgent syncAgent;
private RemoteModelProxy remoteModel;
private LocalModel localModel;
// Action methods ...
public Reservation reserveSeats(String theaterKey,
String movieKey,
int[] showTime, Seat[] seats)
throws ApplicationException {
try {
return remoteModel.reserveSeats(theaterKey,
movieKey, showTime, seats);
} catch (ModelException me) {
// ...
}
}
public void purchaseTickets(Reservation reservation)
throws ApplicationException {
try {
remoteModel.purchaseTickets(reservation.getId());
localModel.addMovieRating(
new MovieRating(remoteModel.getMovie(reservation.getMovieId()),
reservation.getShowTime()));
} catch (ModelException me) {
// ...
}
return;
}
public void synchronizeMovieRatings(int
conflictResolutionStrategyId)
throws ApplicationException {
try {
syncAgent.synchronizeMovieRatings(conflictResolutionStrategyId);
return;
} catch (ModelException me) {
// ...
}
}
// ...
}
|
服务端界面
应用程序的服务端使用了很多Enterprise JavaBeans组件 (EJB) 来封装业务逻辑和管理与关系数据库的交互。当客户端的RemoteModelProxy向服务器端发出RPC调用时,HTTP servlet SmartTicketServlet通过业务代理对象SmartTicketBD调用会话EJB中合适的动作方法 SmartTicketFacadeBean。根据请求性质,它进一步委托两个其他会话bean中的一个,TicketingBean 或SynchronizingBean。一组实体bean在需要时使用EJB 2.0的容器托管的持久性来更新数据库。
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>package com.sun.j2me.blueprints.smartticket.server.web.midp;
public class SmartTicketServlet extends HttpServlet {
public static final String SESSION_ATTRIBUTE_SMART_TICKET_BD =
"com.sun.j2me.blueprints.smartticket.server.web.midp.SmartTicketBD";
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
SmartTicketBD smartTicketBD = (SmartTicketBD)
session.getAttribute(SESSION_ATTRIBUTE_SMART_TICKET_BD);
// Calls handleCall() method and encodes the URL for
// session tracking
}
public int handleCall(SmartTicketBD smartTicketBD,
InputStream in, OutputStream out)
throws IOException, ApplicationException {
// Identifies the requested action method
// Executes the method, as selected in a switch statement
switch (method) {
// cases ...
case MessageConstants.OPERATION_GET_MOVIE: {
getMovie(smartTicketBD, call, successfulResult);
break;
}
// more cases ...
}
}
}
package com.sun.j2me.blueprints.smartticket.server.web.midp;
public class SmartTicketBD implements RemoteModel {
public static final String EJB_REF_FACADE =
"ejb/SmartTicketFacade";
private SmartTicketFacadeLocal facade;
private ServletContext servletContext = null;
public SmartTicketBD(ServletContext servletContext)
throws ApplicationException {
this.servletContext = servletContext;
try {
Context context = (Context)
new InitialContext().lookup("java:comp/env");
facade = ((SmartTicketFacadeLocalHome)
context.lookup(EJB_REF_FACADE)).create();
return;
} catch (Exception e) {
throw new ApplicationException(e);
}
}
public Movie getMovie(String movieKey)
throws ModelException, ApplicationException {
try {
MovieLocal movieLocal = facade.getMovie(movieKey);
Movie movie = new Movie(movieLocal.getId(),
movieLocal.getTitle(),
movieLocal.getSummary(),
movieLocal.getRating());
return movie;
} catch (SmartTicketFacadeException stfe) {
throw new
ModelException(ModelException.CAUSE_MOVIE_NOT_FOUND);
} catch (Exception e) {
throw new ApplicationException(e);
}
}
// Other action methods in RemoteModel interface ...
}
package com.sun.j2me.blueprints.smartticket.server.ejb;
public class SmartTicketFacadeBean implements SessionBean {
// ...
public MovieLocal getMovie(String movieId)
throws SmartTicketFacadeException {
try {
return movieHome.findByPrimaryKey(movieId);
} catch (FinderException fe) {
throw new
SmartTicketFacadeException("No matching movie.");
}
}
// ...
}
|
下图展示了整体 MVC 和界面的体系结构:
实现模式
MVC和界面模式定义了应用程序的整体结构。另外,Smart Ticket也列出了一些重要的行为模式,这些模式能帮助开发人员提高效率。
处理程序链
The RemoteModelProxy类把每一个请求动作都委托给handler类链,以便透明地解决RMS串行化和 HTTP连接的异常管道。 链接的处理程序体系结构基于实现它的RequestHandler接口和RemoteModelRequestHandler抽象类:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %> public interface RequestHandler {
RequestHandler getNextHandler();
void init() throws ApplicationException;
void destroy() throws ApplicationException;
}
abstract public class RemoteModelRequestHandler
implements RequestHandler, RemoteModel {
private RemoteModelRequestHandler nextHandler;
private Preferences preferences;
protected static ProgressObserver progressObserver;
public RemoteModelRequestHandler(
RemoteModelRequestHandler nextHandler) {
this.nextHandler = nextHandler;
}
public RequestHandler getNextHandler() {
return nextHandler;
}
public void init() throws ApplicationException {
if (nextHandler != null) {
nextHandler.init();
}
return;
}
public void destroy() throws ApplicationException {
if (nextHandler != null) {
nextHandler.destroy();
}
return;
}
public void login(String userName, String password)
throws ModelException, ApplicationException {
getRemoteModelRequestHandler().login(userName, password);
return;
}
public void createAccount(AccountInfo accountInfo)
throws ModelException,
ApplicationException {
getRemoteModelRequestHandler().createAccount(accountInfo);
return;
}
// Other action methods declared in RemoteModel
// ...
} |
具体的handler类扩展了RemoteModelRequestHandler类。嵌套的构造函数建立一个处理程序链。Smart Ticket启用了两个处理程序类:RMSCacheHandler和 HTTPCommunicationHandler。因此链接装配方法如下:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %> public class RemoteModelProxy extends ModelObjectLoader
implements RemoteModel {
private RemoteModelRequestHandler requestHandlerChain;
private Preferences preferences = null;
private Hashtable movies = new Hashtable();
public RemoteModelProxy(String serviceURL)
throws ApplicationException {
requestHandlerChain =
new RMSCacheHandler(
new HTTPCommunicationHandler(null, serviceURL));
return;
}
// ...
public Movie getMovie(String movieKey)
throws ModelException,
ApplicationException {
Movie movie = (Movie) movies.get(movieKey);
if (movie == null) {
movie = requestHandlerChain.getMovie(movieKey);
movies.put(movieKey, movie);
}
return movie;
}
// Other methods ...
} |
一个处理程序能够选择性地实现任何RemoteModel接口中的动作方法 ,采用两种方法之一:
◆如果一个RemoteModelProxy类调用一个动作方法不能通过前面的handler类完成,那么基类RemoteModelRequestHandler类能够确保这个调用被传递给链中的下一处理程序。
◆如果链中的一个处理程序判定它已经完成一个动作的处理,它会直接返回。否则,它再调用在超类中的同一动作方法,并将它传递给链中的下一处理程序。
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>public class RMSCacheHandler extends RemoteModelRequestHandler {
// ...
public Movie getMovie(String movieKey)
throws ModelException,
ApplicationException {
IndexEntry indexEntry =
rmsAdapter.getIndexEntry(movieKey,
IndexEntry.TYPE_MOVIE, IndexEntry.MODE_ANY);
if (indexEntry != null) {
return rmsAdapter.loadMovie(indexEntry.getRecordId());
}
return super.getMovie(movieKey);
}
// ...
}
|
|