Enterprise J2ME Developing Mobile Java Applications [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Enterprise J2ME Developing Mobile Java Applications [Electronic resources] - نسخه متنی

Michael Juntao Yuan

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



5.3 Important Architectural Patterns


Smart Ticket utilizes several architectural design patterns, which are commonly used by enterprise architects. It is important for enterprise mobile developers to understand those patterns.


5.3.1 The Overall MVC Pattern


The overall architecture of the Smart Ticket application follows the Model-View-Controller pattern. According to Martin Fowler in his Patterns of Enterprise Application Architecture, the MVC pattern "splits user interface interaction into three distinct roles." In an MVC application, the view and controller components work together as the UI, which primarily concerns how to present the information to the user through a series of displays and interactions. The model component represents the domain model. The model's primary concern is business logic and machine-to-machine interactions (e.g., database access). The use of the MVC patterns brings some important benefits:

Since the presentation and the underlying data model are separated, we can employ different experts to work on different parts of the code. For example, a UI expert can design the views while a database expert optimizes the database connections at the same time.

MVC allows us to develop multiple views for the same model. For example, the Smart Ticket v2.0 EA and v1.2 applications have the exact same model layer. The MVC pattern allows Sun developers to rewrite the UI for MIDP v2.0 in a short period of time.

Nonvisual objects in the model layer are easier to test using automatic tools than are the UI components.


In the Smart Ticket application, the MVC pattern is implemented as follows:

Model: Classes in the model layer contain all the business logic. In fact, the entire J2EE server component, on-device caches, and communication classes all belong to the model layer. The most notable design pattern in the model layer is the facades, which we discuss in the next sections.

View: Each interactive screen is represented by a view class. There are 17 view classes in the Smart Ticket v2.0 EA client. Once the user generates a UI event (e.g., by pressing a button or selecting an item from a list), the view class's event handler captures the event and passes it to the controller class. Listing 5.1 demonstrates the UI classes for the screen to confirm ticket purchase.

Controller: The controller class knows all the possible interactions between the user and the program. In the Smart Ticket application, the controller is the UIController class (Listing 5.2). It has one method for each possible action (e.g., purchaseRequested()). The action method often starts two new threads: one to perform the action in the background and the other to display a progress bar for the user. The background action thread is represented by the EventDispatcher class. The EventDispatcher.run() method contains a long list of switch statements that invoke the corresponding methods in the model layer to perform the action. When the model method returns, the controller displays the next UI screen using the appropriate view class.


Listing 5.1. The ConfirmTicketUI class represents the screen for confirming the ticket purchase


package com.sun.j2me.blueprints.smartticket.client.midp.ui
public class ConfirmTicketUI extends Form
implements CommandListener, ItemCommandListener {
private UIController uiController;
private Command cancelCommand;
private Command confirmCommand;
private StringItem theater, movie, showTimeStr, seatsStr;
private StringItem cost, totalCost, placeOrderBtn;
public ConfirmTicketUI(UIController uiController) {
super(uiController.getString(UIConstants.CONFIRM_TITLE));
this.uiController = uiController;
createItems();
append(theater); append(movie); append(showTimeStr);
append(seatsStr); append(cost); append(totalCost);
append(placeOrderBtn);
confirmCommand =
new Command(uiController.getString(UIConstants.CONFIRM),
Command.OK, 5);
cancelCommand =
new Command(uiController.getString(UIConstants.CANCEL),
Command.EXIT, 5);
addCommand(confirmCommand);
addCommand(cancelCommand);
setCommandListener(this);
placeOrderBtn.setDefaultCommand(confirmCommand);
placeOrderBtn.setItemCommandListener(this);
}
public void init(String theaterName, String movieName,
int[] showTime, Seat[] seats) {
// Set the display strings to the correct values
}
// Command callback for UI events for text button "placeOrderBtn"
public void commandAction(Command command, Item item) {
if (command == confirmCommand) {
uiController.purchaseRequested();
}
}
// Command callback for UI events on the command buttons
public void commandAction(Command command, Displayable displayable) {
if (command == cancelCommand) {
uiController.mainMenuRequested();
} else if (command == confirmCommand) {
uiController.purchaseRequested();
}
}
}

Listing 5.2. Process the purchaseTickets action in the UIController class in the controller layer


package com.sun.j2me.blueprints.smartticket.client.midp.ui;
public class UIController {
// references to all UI classes
// ... ...
public UIController(MIDlet midlet, ModelFacade model) {
this.display = Display.getDisplay(midlet);
this.model = model;
}
// ... ...
public void purchaseRequested() {
runWithProgress(
new EventDispatcher(EventIds.EVENT_ID_PURCHASEREQUESTED,
mainMenuUI),
getString(UIConstants.PROCESSING), false);
}
class EventDispatcher extends Thread {
private int taskId;
private Displayable fallbackUI;
EventDispatcher(int taskId, Displayable fallbackUI) {
this.taskId = taskId;
this.fallbackUI = fallbackUI;
return;
}
public void run() {
try {
switch (taskId) {
// cases ... ...
case EventIds.EVENT_ID_PURCHASEREQUESTED: {
model.purchaseTickets(reservation);
purchaseCompleteUI.init(reservation.getId(),
selectedTheater.getName(),
selectedMovie.getTitle(),
selectedShowTime);
display.setCurrent(purchaseCompleteUI);
break;
}
// Other cases ... ...
}
} catch (Exception exception) {
// handle exceptions
}
} // end of run() method
} // end of the EventDispatcher class
}

The overall end-to-end architecture of the Smart Ticket application is illustrated in Figure 5.5). The model layer is by far the most sophisticated. Now, let's study how the model is assembled around a series of facades.


Figure 5.5. The overall architecture of the Smart Ticket application.



5.3.2 The Clientside Facade


The facade pattern is a structural pattern that provides a simple interface for complex subsystems. In the Smart Ticket application, the clientside subsystems in the model layer, such as the LocalModel, RemoteModelProxy, and SynchronizationAgent classes, are behind the facade class ModelFacade (Listing 5.3), which is the entry point from the controller to the model. The ModelFacade class contains one method for each action in the model layer. It delegates the actions to the subsystems as follows.

The LocalModel class handles actions that access the local on-device storage. For example, the purchaseTickets() method adds the purchased movie to the on-device rating list. The addMovieRating() action method in the LocalModel class is called.

The RemoteModelProxy class, which implements the RemoteModel interface, handles actions that require access to the remote J2EE server. For example, if the user decides to purchase tickets (reserveSeats() and purchaseTickets()), the transaction has to be done on the server side and be persisted to the database through the RemoteModelProxy. Action methods in the RemoteModelProxy class invoke remote procedure calls (RPC) to the remote facade on the server side. The details of the remote facade and the RPC format are discussed later in this chapter.

The SynchronizationAgent class handles all synchronization actions from the local data storage to the remote server. In the case of the Smart Ticket application, it handles only the movie ratings synchronization. It has two action methods: The synchronizeMovieRatings() method synchronizes the ratings; the commitMovieRatings() method commits the resolved synchronization requests to the back end and updates the content of the local store.


The three interactions are illustrated in the following code snippet (Listing 5.3).

Listing 5.3. The ModelFacade class


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());
// Purchased movies are eligible for rating.
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) {
// ... ...
}
}
// ... ...
}


5.3.3 The Serverside Facade


One of the most important benefits of the facade pattern is that it reduces network round trips between remote systems. A properly designed facade allows us to use fine-grained objects in the subsystems yet still have a coarsegrained, simple network interface. It is especially important for mobile applications, since the wireless network is very slow.

When an RPC is made from the RemoteModelProxy to the server side, the HTTP servlet SmartTicketServlet (Listing 5.4) invokes the corresponding action method in Session EJB SmartTicketFacadeBean (Listing 5.6) through a business delegate object SmartTicketBD (Listing 5.5). Depending on the nature of the action, it is further delegated to either TicketingBean or SynchronizingBean, both of which are session EJBs too. The application data on the server side is persisted to the relational database through an array of Container Managed Persistence (CMP) v2.0 entity EJBs.

Listing 5.4. The gateway servlet SmartTicketServlet


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 encode the URL for
// session tracking
}
public int handleCall(SmartTicketBD smartTicketBD, InputStream in,
OutputStream out) throws IOException,
ApplicationException {
// Identifies the requested action method
// Execute the method through a list of switch -- case statements
switch (method) {
// ... ...
case MessageConstants.OPERATION_GET_MOVIE:
getMovie(smartTicketBD, call, successfulResult);
break;
// ... ...
}
}
}

Listing 5.5. The business delegate class SmartTicketBD


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
}

Listing 5.6. The facade session bean SmartTicketFacadeBean


package com.sun.j2me.blueprints.smartticket.server.ejb;
public class SmartTicketFacadeBean implements SessionBean {
// ... ...
public void ejbCreate() throws CreateException {
// ... ...
Context context =
(Context) new InitialContext().lookup("java:comp/env");
ticketingHome =
(TicketingLocalHome) context.lookup(EJB_REF_TICKETING);
synchronizingHome =
(SynchronizingLocalHome) context.lookup(EJB_REF_SYNCHRONIZING);
// ... ...
}
public MovieLocal getMovie(String movieId)
throws SmartTicketFacadeException {
try {
return movieHome.findByPrimaryKey(movieId);
} catch (FinderException fe) {
throw new SmartTicketFacadeException("No matching movie.");
}
}
public void purchaseTickets(String reservationId)
throws SmartTicketFacadeException {
if (ticketing != null) {
ticketing.purchaseTickets(reservationId);
return;
}
throw new SmartTicketFacadeException("User not logged in.");
}
public MovieRatingData[] synchronizeMovieRatings(
MovieRatingData[] movieRatings,
int conflictResolutionStrategyId)
throws SmartTicketFacadeException {
if (synchronizing != null) {
return synchronizing.synchronizeMovieRatings(movieRatings,
conflictResolutionStrategyId);
}
throw new SmartTicketFacadeException("User not logged in.");
}
// ... ...
}


/ 204