Ask a question
Wednesday, 16 September 2009 11:38
The Smart Client Paradigm: iFeedBack
-
font size
decrease font size
increase font size
The Smart Client Paradigm: iFeedBack
3.1 Benefits of Smart Clients
Microbrowser-based thin client technologies (e.g., Wireless Application Protocol, WAP) were instrumental in bringing mobile Internet to masses in the early days of mobile commerce. But WAP-based mobile commerce has never been very popular due to the lack of usability on the client side. The new generation of smart client mobile technology (e.g., J2ME, and Microsoft's .NET Compact Framework) promises to bring feature-rich clients to mobile applications. The benefits of smart clients over thin clients include the following.
· Smart clients have richer and more pervasive user interfaces. In particular, the judicial use of threads can drastically improve user perception of the application performance.
· Smart clients can be more easily personalized. Extreme personalization is one of the most touted benefits of the freedom (mobile) economy.
· On-device data storage reduces network traffic, especially unnecessary round trips; enables transactions; supports the "offline" mode when the network is temporarily unavailable; and hence improves overall performance, reliability, and availability of mobile applications.
· Smart clients can leverage device extensions. For example, a smart client program can talk with the device's built-in (or attached) GPS module and bar-code scanners. A smart client can also integrate with device-specific software (e.g., email and messaging clients) to improve the user's workflow.
· Smart clients support more powerful and flexible security schemes, such as content-based security and distributed single sign-on.
· Smart clients support advanced integration technologies. They are easy to plug into existing corporate infrastructure. Supports for asynchronous messaging and XML Web Services are crucial for reliable and maintainable mobile solutions.
The focus of this entire book is to introduce technologies, architectures, and tools that maximize those benefits. The iFeedBack example in this chapter presents a bird's eye view of the smart client paradigm through a concrete application.
3.2 Introducing iFeedBack
In order to tap J2ME smart clients' potential in emerging markets and seek new ideas of killer applications, NexTel, Sun Microsystems, and Motorola jointly sponsored the University Wireless Developer Contest in the United States. My contest entry "iFeedBack: A Single Sign-on Mobile Survey Tool for University Students" won the grand prize of $20,000. In the rest of this chapter, I use iFeedBack as an example to illustrate what can be done with managed smart clients. The complete source code of the iFeedBack application can be downloaded from this book's Web site (see "Resources").
3.2.1 The Problems to Solve
A big "business problem" we had for a long time in the higher education enterprise (i.e., universities) was the lack of communication channels between knowledge producers (professors) and consumers (students). College students in large universities usually have little interaction with professors. The lack of feedback on course advances and teaching techniques is one of the major factors that impede effective learning. Course evaluation surveys at the end of the semester often are the only way a student can convey his or her opinions of the teaching to the professor. But there are two problems with such delayed instructor surveys: They are too late for the students to remember earlier incidents, and they are too late for the professor to take corrective measures.
An open, real-time, and convenient communication channel between professors and students will benefit both parties. iFeedBack is a mobile survey application that allows questionnaires to be presented and answered anytime, anywhere. It is not only suitable for frequent instructor surveys, but can also be used in in-class quizzes and homework assignments.
3.2.2 Architecture
The overall architecture of iFeedBack is illustrated in Figure 3.1.

There are three major components in an iFeedBack deployment:
· Each class has its own survey site that feeds current questions to mobile clients and receives time-stamped answers over the HTTP. The professor and teaching assistants have administrative access to that site.
· The university provides a single sign-on service. This server authenticates students and issues time-limited access tokens to survey sites. The student uses tokens to access class survey sites. Class sites also connect to the authentication server to verify the tokens. To ensure interoperability with potentially large numbers of different clients, this single sign-on service is provided via an XML Web Services interface.
· The smart MIDP client provides user interface and temporary storage for time-stamped answers. It also manages authentication credentials and maintains a list of endpoint URLs for classes the student is attending.
Why Single Sign-On Tokens
The single sign-on server provides authentication services to all class survey sites. Single sign-on and on-device credential management are crucial features to make this application usable. Imagine the hassle if the student has to manually enter a username and password for every class survey she uses. Another benefit of the single sign-on scheme is that it separates the user identity from the surveys she completes. The professor sees only anonymous survey results. That encourages students to tell the truth.
3.2.3 Real-World Deployment
iFeedBack is a prototype application that is designed to sell to universities rather than to individual students. The backend services are deployed into the university IT infrastructure: The single sign-on services should be linked with the existing electronic ID systems; the class surveys could be integrated with existing campus-wide course Web sites, such as the blackboard system. The two serverside components I provided with the sample application are for proof-of-concept demonstration only.
3.3 iFeedBack Usage Scenarios
Before we dive into the code, let's see how iFeedBack works from a user's perspective. To test run the application, we have to build and deploy it first.
3.3.1 Build and Deploy
The application download package has three subdirectories, each containing an application component. You can use the ANT build.xml script in each directory to build and deploy the component.
Directory surveyserver contains a sample survey server application. After running ANT, a file bin/iFeedBackSurvey.ear is generated. It can be deployed to any J2EE-compliant server (J2EE v1.3 RI is tested). If this is the first time you deploy it, you need to run the sql/Init.sql script against your default database.
Directory singlesignon contains an example single sign-on token service. Currently, all usernames and passwords are hardcoded into the source code for simplicity. Obviously, in real-world deployment, it must be hooked up with campus identity databases. File deployable.war is produced by the ANT script and can be deployed to the Java Web Services Developer Pack v1.0 engine.
Directory wireless contains the MIDP mobile client. Command ant builds the JAR file in the bin directory. Command ant run runs the client in the J2ME Wireless Toolkit device emulator. In the default setting, the MIDP client assumes that both the class survey server and single sign-on server reside on a computer at IP address localhost.
Note
Once you build the JAR files for the J2ME and J2EE components, you can deploy them to any compatible emulators, devices and servers. For details on how to set up your device emulator and server environment, please refer to your product manual.
3.3.2 Try It Out!
- Start up the MIDlet.
- Choose Update Token and put in username/password pair test01/pass01. Then click UPDATE. The MIDlet will contact the single sign-on server and obtain a new authentication token for future use.
- Choose Add Course. Add in a new class survey nickname and endpoint URL. You can just accept the suggested values.
- Now you should see CS 301 available for selection under the Choose Course menu item. If you want to add more class surveys, you can repeat the last step.
- Choose a course. iFeedBack will fetch the current question and display it for you.
- Choose the appropriate answer and put in any comments. Then click SAVE to save the time-stamped answer to the on-device persistent cache.
- You can repeat the last two steps multiple times for different questions from different courses.
- From the main menu, click Submit Answers to submit all cached answers to their corresponding endpoints. All successfully submitted answers will be deleted from the cache.
- Now you can view the uploaded answers from the class survey server. Just point your HTML browser to the URL
3.4 Implementation Walk Through
In this section, we use sample code snippets to illustrate the overall structure of the implementation. We focus on the J2ME smart client.
3.4.1 The Call Model
The MIDP application contains multiple screens interconnected by menu selection and soft button actions. I decided to make each screen an independent functional unit with its own event handlers, transparent data models, and UI view generator. Each screen is represented by a class derived from abstract class MVCComponent (Listing 3.1). The relationship of classes in the UI model is illustrated in Figure 3.3.

Listing 3.1. The MVCComponent abstract class
public abstract class MVCComponent implements CommandListener {
// Set from outside at beginning
public static Display display;
// Returns the screen object from the derived class
public abstract Displayable getScreen();
public Displayable prepareScreen () throws Exception {
if ( getScreen() == null ) {
initModel();
createView();
} else {
updateView();
}
getScreen().setCommandListener ( (CommandListener) this );
return getScreen ();
}
public void showScreen() {
try {
display.setCurrent( prepareScreen() );
} catch (Exception e) {
e.printStackTrace();
Alert a = new Alert("Error");
a.setTimeout(Alert.FOREVER);
display.setCurrent(a);
}
}
// Initialize. If a data member is not backed by RMS,
// make sure it is null before you put in values.
protected abstract void initModel () throws Exception;
protected abstract void createView () throws Exception;
protected abstract void updateView () throws Exception;
public abstract void commandAction(Command c, Displayable s);
}
Using the UpdateToken object as an example, code Listing 3.2 illustrates how to make a new screen object from the MVCComponent class.
Listing 3.2. Example implementation of MVCComponent
public class UpdateToken extends MVCComponent {
// MIDP UI components: Commands, TextFields etc.
// ... ...
// Model parameters
private static String username;
private static String password;
private static String token;
private static String endPointURL;
// Bean-style methods to access data members
// Event handler
public void commandAction(Command c, Displayable s) {
try {
if (c == backCommand) {
(new MainMenu()).showScreen();
} else if (c == updateCommand) {
username = usernameField.getString();
password = passwordField.getString();
UpdateTokenTask t =
new UpdateTokenTask(endPointURL, display);
t.go();
}
} catch (Exception e) {
// Show some alerts
}
}
public Displayable getScreen () {
return screen;
}
// In initModel(), first check whether the
// the data model is persistently saved.
//
// The use of persistent store is transparent
// to this class's users.
protected void initModel() throws Exception {
try {
securityInfoStore =
RecordStore.openRecordStore("securityInfo", true);
RecordEnumeration re =
securityInfoStore.enumerateRecords(null, null, false);
ByteArrayInputStream bais =
new ByteArrayInputStream( re.nextRecord() );
DataInputStream din = new DataInputStream(bais);
username = din.readUTF();
password = din.readUTF();
token = din.readUTF();
din.close();
bais.close();
securityInfoStore.closeRecordStore();
} catch (Exception e) {
username = Start.UsernameSugg;
password = Start.PasswordSugg;
token = "-1:-1";
}
}
// createView() creates a view from the current
// model into the screen object
protected void createView() throws Exception {
backCommand = new Command("MAIN", Command.SCREEN, 2);
updateCommand = new Command("UPDATE", Command.SCREEN, 1);
usernameField = new TextField("Username:",
username, 40, TextField.ANY);
passwordField = new TextField("Password:",
password, 40, TextField.ANY);
screen = new Form("Security Info");
((Form) screen).append( usernameField );
((Form) screen).append( passwordField );
((Form) screen).append( "Current Token is " + token );
screen.addCommand( backCommand );
screen.addCommand( updateCommand );
}
// updateView() updates the screen object when
// the model changes.
protected void updateView() throws Exception {
// display updated username and password
usernameField.setString( username );
passwordField.setString( password );
// Deletes the old token display
((Form) screen).delete(2);
if ( "-1:-1".equals(token) ) {
((Form) screen).append("The current token is not valid");
} else {
((Form) screen).append("Current Token is token");
}
}
}
Using the UpdateToken screen is very simple. All you need to do is set up the model data and then call the showScreen() method.
3.4.2 The Threading Model
An important feature of iFeedBack is its use of worker threads during lengthy network operations to improve the perceived performance. The threading model of iFeedBack includes a worker thread, an animation thread, and a UI frontend thread (see Figure 3.4).


Listing 3.3. The BackgroundTask is the base for all thread tasks
public abstract class BackgroundTask extends TimerTask {
private Thread th;
private boolean stopped;
// Could be set in derived class
// If the task is successfully completed
protected Displayable nextScreen;
// If the task is aborted
protected Displayable prevScreen;
// Display to draw-on
protected Display display;
// The gauge screen title
protected String title;
// Do we need to display an alert before
// moving to the nextScreen?
protected boolean needAlert = false;
protected Alert alertScreen;
public BackgroundTask (Display d) {
display = d;
th = new Thread(this);
}
public void go () {
stopped = false;
th.start();
}
public void stop () {
stopped = true;
th.setPriority(Thread.MIN_PRIORITY);
}
public void run() {
ProgressGauge pg = null;
try {
pg = new ProgressGauge(this, title, display, prevScreen);
runTask ();
} catch (Exception e) {
// elaborate error handling using alerts
} finally {
// Since pg could callback and reset
// "stopped" when its
// Cancel key is pressed on Gauge.
if (!stopped) {
if ( needAlert ) {
pg.setNextScreen(alertScreen, nextScreen);
} else {
pg.setNextScreen(nextScreen);
}
pg.stop(); // notify progress guage to quit
}
}
}
// template method.
public abstract void runTask () throws Exception;
}
The ProgressGauge object starts a foreground thread that displays an animated guage. It features a Cancel button. If the Cancel button is pressed, the ProgressGuage displays the prevScreen screen, instructs the BackgroundTask not to update the display when finished, and stops. For more information, please refer to the ProgressGauge source code.
In your derived class for a specific task, you only need to implement the runTask(). Upon completion of the task, the runTask() updates the nextScreen variable. Listing 3.4 shows the runTask() method in the Update_ Token Task class.
Listing 3.4. The UpdateTokenTask implementation
public class UpdateTokenTask
extends BackgroundTask {
private String endPointURL;
public UpdateTokenTask (String url, Display d) throws Exception {
super (d);
endPointURL = url;
prevScreen =
(new UpdateToken()).prepareScreen();
}
public void runTask () throws Exception {
String username = UpdateToken.getUsername ();
String password = UpdateToken.getPassword ();
String token = getToken(username, password);
if ( "-1:-1".equals(token) ) {
needAlert = true;
alertScreen = new Alert("Cannot update token");
alertScreen.setString(
"Authentication failed!");
alertScreen.setTimeout(Alert.FOREVER);
nextScreen = prevScreen;
} else {
UpdateToken.setAll( username, password, token );
needAlert = true;
alertScreen = new Alert("Success!");
alertScreen.setString("The new token is token");
alertScreen.setTimeout(Alert.FOREVER);
nextScreen = (new MainMenu()).prepareScreen();
}
}
// ... ...
}
To invoke the worker thread, you only need to call its go() method (Listing 3.2). The threading model presented here is rather simplistic. For more advanced threading control examples, please refer to the Smart Ticket example (Chapter 5).
3.4.3 Data Exchange
The MIDP client communicates with the class survey server via a pre-agreed binary format over the HTTP. In the wired world, generic binary data are passed through TCP/IP sockets. Higher-level protocols such as HTTP are used to transport application specific data, which require additional semantic structures (e.g., HTTP headers). However, in the J2ME world, access to raw TCP/IP sockets is not ubiquitous. In fact, HTTP is the only protocol mandated by the MIDP v1.0 specification. Therefore, J2ME developers often use HTTP to transport generic binary data.
Convenience I/O methods in the DataInputStream and DataOutputStream classes are extensively used to pack or unpack application data to or from the stream. For example,
1. The writeUTF() method writes a data string with leading bytes indicating the string length and encoding into the stream.
2. The readUTF() method at the other end of the stream reads the length and encoding bytes first, and then reads the specified number of bytes from the stream and reconstructs the correctly encoded string.
There is a pair of convenience read/write methods for every primitive Java data type. Those methods have greatly simplified our work. Listing 3.5 shows the relevant code segments from the SubmitAllTask class, which retrieves the cached answers from the RMS store and submits them to the remote HTTP server. As we can see, the I/O convenience methods are also extensively used to access unstructured RMS store record data. Interested readers can refer to Chapter 12 for advanced tools and techniques that build structured and easy-to-manage storage structures on top of the RMS.
Listing 3.5. The binary I/O in the SubmitAllTask class
Public class SubmitAllTask extends BackgroundTask {
public SubmitAllTask (Display d) throws Exception {
// Set up screens
}
public void runTask () throws Exception {
RecordStore answerStore =
RecordStore.openRecordStore("answer", true);
RecordEnumeration re =
answerStore.enumerateRecords(null, null, false);
int recordNum = 0;
while ( re.hasNextElement() ) {
recordNum++;
int recordid = re.nextRecordId();
ByteArrayInputStream bais =
new ByteArrayInputStream( answerStore.getRecord(recordid) );
DataInputStream din = new DataInputStream(bais);
String url = din.readUTF();
int qid = din.readInt();
long timestamp = din.readLong();
String answer = din.readUTF();
String comment = din.readUTF();
HttpConnection conn = null;
DataInputStream hdin = null;
DataOutputStream hdout = null;
try {
conn = (HttpConnection) Connector.open( url );
conn.setRequestMethod(HttpConnection.POST);
hdout = conn.openDataOutputStream();
hdout.writeInt(1); // Submit opcode
hdout.writeUTF( UpdateToken.getToken () );
hdout.writeLong( timestamp );
hdout.writeInt( qid );
hdout.writeUTF( answer );
hdout.writeUTF( comment );
hdout.flush();
hdin = conn.openDataInputStream();
boolean authsucc = hdin.readBoolean();
if (authsucc) {
boolean updatesucc = hdin.readBoolean();
if ( updatesucc ) {
answerStore.deleteRecord( recordid );
} else {
// Server error. Show alert
}
nextScreen = prevScreen;
} else {
// Auth error. Show Auth token screen
}
} finally {
// Close all connections
}
}
re.destroy();
answerStore.closeRecordStore();
// Setup the next screen and show the number of
// records submitted
}
}
The binary data exchange formats are efficient, but they are proprietary and result in tightly coupled data producers and consumers. For services that require open interfaces, such as the authentication server, binary protocols are not sufficient.
We implemented the iFeedBack authentication server using Sun's Java Web Service Developer Pack (v1.0). The server exposes its services through the SOAP (Simple Object Access Protocol) XML API and publishes the API specification in an open WSDL (Web Services Definition Language) document. On the mobile client side, we use the kSOAP library to assemble and parse the SOAP data. For more information on kSOAP and mobile Web Services
We implemented the iFeedBack authentication server using Sun's Java Web Service Developer Pack (v1.0). The server exposes its services through the SOAP (Simple Object Access Protocol) XML API and publishes the API specification in an open WSDL (Web Services Definition Language) document. On the mobile client side, we use the kSOAP library to assemble and parse the SOAP data. For more information on kSOAP and mobile Web Services
Read 390 times
Published in
Java Mobile Development
