Vicky

Vicky

E-mail: This e-mail address is being protected from spambots. You need JavaScript enabled to view it

Thursday, 17 September 2009 13:32

End-to-End Best Practices

7.1 Limited Device Hardware

The most visible difference between the mobile and PC platforms is the difference in computing hardware. Today's PCs have much faster CPUs and far more memory and storage spaces than any mobile computing devices. Desktop and server developers can afford the luxury to write applications with bloated features (e.g., Microsoft Office); they also have access to rich productivity features provided by large, all-in-one frameworks (such as the J2SE platform itself). However, on mobile devices, it is a completely different story. With CPUs as slow as 20MHz and RAM as little as 100KB, we must carefully evaluate the features we need, thoroughly optimize our code, and live with limited framework support. In this section, we discuss how to cope with those challenges.

7.1.1 Lightweight Libraries

The most common mistake beginners make is the "golden hammer" anti-pattern: choosing the wrong technology for the task. In the Java world, software tools are often available as reusable objects in standard or third-party libraries. To choose the best libraries that support required application features at the minimum hardware cost is essential.

J2ME Foundation and Personal Profiles (as well as PersonalJava) are compatible with J2SE at the bytecode level and inherit a large subset of the J2SE core API. In theory, we can port J2SE libraries (e.g., XML processing, cryptography, messaging, and UI) directly to mobile devices. However, to do so would defeat the purpose of J2ME and result in slow and bloated applications that can be deployed only to the most expensive devices. In most cases, we should choose from lightweight library alternatives that are specifically designed for the mobile platform. Multiple vendors often compete in the same market. Each vendor offers a slightly different lightweight product with an emphasis on different features.

CLDC and MIDP standard libraries are designed from the ground up as lightweight components. However, the need to select the right tools also applies to MIDP projects when it comes to third-party libraries. For a specific library, vendors often offer a version with J2SE-compatible APIs for larger MIDP devices (e.g., Symbian OS devices) and another extremely lightweight version that uses proprietary APIs. The latter often has a smaller memory footprint and better performance, but requires extra developer training and results in less portable applications. Examples of MIDP lightweight libraries include the PointBase MIDP relational database APIs and iBus//Mobile JMS client APIs ).

7.1.2 Reduce Application Footprint

Pervasive mobile devices have extremely limited memory and storage spaces, requiring us to minimize both the storage and runtime footprints of the application. Specific suggestions are as follows.

  • Optimize the packaging process: Even after carefully choosing the best lightweight library, we may still find that the application utilizes only part of the library. In the packaging process, we should include only the classes we actually use. We can do this manually for smaller libraries or use automatic tools bundled with some J2ME IDEs (such as the IBM WebSphere Studio Device Developer) for large libraries. If you want to further reduce the binary application size, you can use a bytecode obfuscator to replace long variable names and class names with shorter, cryptic ones.

  • Partition the application: Since the MIDP runtime loads classes only as needed, we can partition the application into separate parts to reduce the runtime footprint. For MIDP applications, the MIDlet suite can contain several relatively independent MIDlets.

Note

Although the standard MIDP specification does not support shared libraries, some vendor-specific implementations do. An example is the BlackBerry Java Development Environment (JDE) for BlackBerry handheld devices. A shared library further reduces the overall footprint, since the library no longer needs to be duplicated and packaged in each application.


7.1.3 Minimize the Garbage Collector

One great advantage of Java is the built-in garbage collector that automatically frees memory space used by stale objects. This allows developers to focus on the core logic rather than on mundane details of memory management. As a result, Java developers are usually unconcerned about object creation. In fact, many popular Java design patterns promote the idea of creating more objects in exchange of more maintainable code. For example, in the Sun Smart Ticket sample application, the use of the MVC and facade patterns results in many objects that simply delegate the action to the next layer. To get a feel for this problem, just look into the numerous classes that implement the RemoteModel interface.

But on mobile devices, due to the small amount of available memory, the garbage collector must run more often. When the garbage collector runs, its thread takes up precious CPU cycles and slows down all other application processes. For effective J2ME applications, we need to minimize object creation and quickly dispose of objects that are no longer in use. Specific suggestions are as follows:

  • Carefully examine design patterns in early stages of the development cycle. For example, the screen flow-based approach demonstrated in the iFeedBack sample  results in many fewer objects than a traditional MVC implementation.

  • Concisely reuse existing objects at the implementation level. For example, if a same button (e.g., the DONE button) appears in many screens, we should create it once and reuse it.

  • Use arrays and StringBuffers. Arrays are much faster and more memory efficient than collection objects. When we modify or concatenate strings, the immutable String objects result in a lot of intermediate objects. The StringBuffer is much more efficient.

  • Close network connections, file handlers, and Record Management System (RMS) record stores quickly after use. We need to look over the documentation carefully to find out all the close(), destroy(), and dispose() methods and use them judiciously. It is usually considered a best practice to place those methods in the finally block to make sure that the resources are released even if runtime exceptions are thrown.

    try {
    
      HttpConnection c =
    
        (HttpConnection) Connector.open("http://someurl");
    
      InputStream is = c.openInputStream ();
    
      // do something with the data
    
    } catch (Exception e) {
    
      // handle exceptions
    
    } finally {
    
      try {
    
        if ( c != null ) c.close();
    
        if ( is != null ) is.close();
    
      } catch (IOException ioe) { }
    
    }
    
    
  • Free resources when using native libraries. In smart mobile applications, we sometimes need to access native libraries for better performance, restricted functionalities (e.g., to make a phone call), or simply native UI look and feel (e.g., the IBM SWT library for PocketPC). Native resources are not subject to garbage collection. It is important to follow proper instructions of the native libraries (and their Java wrapper classes) to free resources after use.

7.1.4 Use Mobile Portals

Smart mobile devices are getting more powerful every day. However, in complex enterprise environments, many tasks are still too resource-intensive for most mobile devices. In this case, a commonly used approach is to set up portal servers to which the mobile devices can delegate complex tasks. Mobile middleware portals bridge mobile clients to enterprise backend servers. The smart portal is much more than a proxy or a surrogate for mobile devices. The uses of mobile portals include the following.

  • Allow mobile clients to utilize multiple communication and messaging protocols.

  • Aggregate backend services and enable bundled services. For example, the Oracle9iAS Wireless server provides J2ME SDKs for Oracle's SQL database, push-based messaging, and location-based services. The BlackBerry Enterprise Server supports unified access to Microsoft Exchange-based or IBM Lotus Domino-based corporate information systems from BlackBerry MIDP devices .

  • Provide simple mobile interfaces for powerful and sophisticated backend services. There are several notable examples:

    - The MapPoint facade, shows how to build an easy-to-access interface for a complex backend Web service.

    - Database synchronization servers synchronize J2ME mobile databases with backend enterprise data sources using complex conflict resolution logic.

    - The Simplicity Enterprise Mobile Server supports simple, visual ways to build J2ME clients for legacy (mainframe) applications.

7.1.5 Use Design Patterns Judiciously

No design pattern is the silver bullet for every situation. For example, the powerful MVC and Facade patterns demonstrated in the Smart Ticket blueprint  require several abstraction layers and are probably too heavy for simple applications. For simple applications, we can design the entire logic around screens, as we did in the iFeedBack example .
 

7.2 Slow, Unreliable Networks

Unlike always-on broadband networks for desktop and server computers, wireless networks have proven to be very slow, unreliable, and insecure. Developers from the PC world, especially those who used to develop server-based with thin client solutions, tend to make excessive use of the network. In this section, we discuss ways to make the best use of network resources.


7.2.1 Support the Offline Mode

As we discussed in , one of the most important advantages of the smart client paradigm is the ability to support offline operations when the network connection is temporarily unavailable. The key enabling technology is on-device persistence storage (cache). Other advantages of the on-device cache include reduced network round trips and improved performance.

Offline operations require careful design of the data model. On-device cache can be used explicitly by the application developer or can be built into the framework and become transparently available to applications. Examples of both approaches are illustrated in the iFeedBack  and Smart Ticket sample applications. For simple caches, the application-managed MIDP RMS stores, plain files, or XML documents are adequate. For more sophisticated data management solutions, we can use on-device relational data stores. For backend powered applications, we also need to keep the cache synchronized with backend data sources. Simple synchronization logic can be programmed into the application itself. Commercial mobile databases often come with advanced synchronization solutions.

7.2.2 Use Remote Facades

Remote facade is an effective pattern to have the best of two worlds: fine-grained object model at the server side and coarse-grained access interface for improved network efficiency. Another excellent example of remote facade is the Axis-based MapPoint facade gateway

7.2.3 Place Portals Locally

Mobile portals are essential components in enterprise mobile architectures. However, fixed portals residing in remote data centers are often not accessible from the national mobile networks due to limited coverage and unreliable connections.

Compared with wide area networks, local wireless networks often have better coverage, lower bandwidth cost, higher speed and better security. Mobile portals that reside on the local wireless networks boost the performance and availability of the client devices. Examples of such mobile portals include OSGi service gateways and IBM WebSphere MQe .In practice, we can build a mobile application architecture that contains a hierarchical structure of hubs and portals. Each portal handles part of the logic and delegates the rest to the next layer. That allows us to build an enterprise mobile architecture that continues to function with limited capabilities in different levels of network failures. Figure 7.1 illustrates the mobile portal network architecture discussed in this chapter.
 

Figure 7.1. Mobile portal networks for small clients.

 

7.2.4 Buffered I/O

Reading network data byte by byte is very slow. We should always read and write data in chunks. In Personal Profile applications, we can use the JDK's standard BufferedReader and BufferedWriter. In MIDP applications, we need to buffer the I/O ourselves (Listing 7.1).

Listing 7.1. The buffered input in MIDP
HttpConnection conn = (HttpConnection) Connector.open(url);

conn.setRequestMethod(HttpConnection.GET);

DataInputStream din = conn.openDataInputStream();

ByteArrayOutputStream bos = new ByteArrayOutputStream();

byte[] buf = new byte[256];

while (true) {

  int rd = din.read(buf, 0, 256);

  if (rd == -1) break;

  bos.write(buf, 0, rd);

}

bos.flush();

buf = bos.toByteArray();

// byte array buf now contains the downloaded data

 

7.2.5 Encrypt Your Data

Wireless networks broadcast data traffic into the air. Anyone can drop in and intercept the traffic. Built-in security for most wireless networks is not adequate for enterprise applications. The use of HTTPS for confidential communication is strongly recommended. Having said that, we must also understand that cryptography tasks are often CPU-intensive. Security comes at the cost of performance. For small devices, we must carefully evaluate the security requirements and come up with balanced solutions.

 

7.2.6 Obtain Server Status Efficiently

Many enterprise mobile applications, such as an Instant Messaging client or a database monitoring client, need to be updated with real-time server status at all times. Since the HTTP protocol is ubiquitously supported in all J2ME devices, inexperienced developers sometimes program the device to initiate periodic HTTP connections to poll the server for its status. The polling frequency must be much faster than the expected server status-change frequency to keep the device updated. The constant polling results in a lot of redundant data. It is a waste of bandwidth, server resources, and time. There are several ways to deal with this problem:

  • Use HTTP conditional GET: The HTTP conditional GET operation allows the server to return data only when the data source has been updated since the last query. An excellent description of the HTTP conditional GET and its usage can be found in a blog entry from Charles Miller (see the "Resources" section). This method reduces the amount of network data but does not reduce the frequency of the polling operation. In a long latency network, it could still be a performance bottleneck.

Use PUSH-based protocols: To completely fix the "excessive poll" problem, let the server notify the client when the server status is updated. We cannot use the HTTP protocol for this purpose, since HTTP is designed to be a stateless request/response protocol. HTTP connections cannot be kept alive over an extended period of time. That requires us to explore other PUSH-based communication protocols on devices.

- SMS messages can be pushed to devices and handled by the J2ME Wireless Messaging API library or the MIDP v2 PUSH Registry.

- SIP is a protocol specially designed for signaling in a PUSH-based network. The SIP API for J2ME has already been finalized 

7.3 Pervasive Devices

Pervasive mobile devices are at the core of the mobile enterprise solution's value proposition. However, unlike PCs, which can be centrally administrated, managing a large number of small devices that people carry around all the time is an IT nightmare. Many of the device management issues are both social and technical in nature. In this section, we discuss what the problems are and the technical tools that can help IT managers and users.

 

Note

The successful use of the technologies described in this section rely on proper user education and corporate policies.

7.3.1 Protect On-Device Data

Small devices are very easy to lose. Stolen enterprise devices that contain sensitive business data, user credentials, or even company private keys could pose a real security risk. The only way to guard against this is to use strong encryption to protect on-device data.

7.3.2 Synchronize Often

Today's battery technology lags far behind the device technology. A smart mobile device with a fast CPU; a large, backlit LCD; and multimedia features could drain its battery in a matter of hours. Most smart phone or high-end PDA devices require the user to recharge every day. If the user forgets, she will probably end up with drained batteries in the middle of the next day. Drained batteries could result in lost data. One way to cope with this is to synchronize the device periodically with backend data sources.

7.3.3 Optimize for Many Devices

Because pervasive devices are cheap and easy to carry around, there tends to be many of them in a company. Each worker could carry multiple interconnected devices. Enterprise solutions need to support all devices in use in the company. J2ME provides a device-independent platform to develop applications. But applications still need to be optimized for the specific target UI and other device characteristics. The use of the MVC pattern could ease the pain of customizing applications: Only the view layer needs to be modified. For example, when the Sun Smart Ticket blueprint teams decide to port the application to MIDP v2.0 devices, they need to recode only the view layer in a matter of days. Another way to implement an MVC solution is to use the clientside container.

7.3.4 Centralized Provisioning

Mobile enterprise users need to have the latest patched software and up-to-date application data. However, it just does not fit the mobile worker's busy life and work style to sit down, hook the devices to PCs, and follow detailed update instructions from the IT department every day. As a result, those instructions are often ignored. To manage and update software and data on a large number of mobile devices is a challenging task. The following are several tools that automate the device management process for both mobile users and IT administrators.

J2EE provisioning server: The JSR 124 develops a specification for J2EE client provisioning servers. The server allows operators to plug in adapters for any client provision scheme. For example, the MIDP Over-the-Air (OTA) support is provided by a bundled adaptor in the reference implementation. When the device requests a client software, the provisioning server matches the device with clients in the repository and deploys the client using the appropriate adaptor. The provisioning server also provides hooks for backend billing, tracking, and CRM applications. Figure 7.2 shows the overall design of the J2EE client provisioning server.

 

Figure 7.2. The J2EE client provisioning server.

  • OGSi bundles: OSGi bundles are self-contained mobile applications with managed life cycles. For devices running OSGi services, OSGi bundles could be the ideal way to deploy applications and contents.

  • Synchronization server: Database synchronization can also be used to provision and update contents

7.4 Ubiquitous Integration

Enterprise mobile clients need to integrate with many different back end or middleware systems. In this section, we introduce several common integration technologies and discuss how to use them judiciously. Table 7.1 is a brief layout of the pros and cons of each approach. Figure 7.3 illustrates the characteristics of each integration scheme.

Figure 7.3. J2ME smart client and J2EE backend integration schemes.


 Table 7.1. Integration Comparison Chart

 

7.4.1 Proprietary Binary Protocols

Since HTTP support is mandatory on all J2ME devices, it is the basis for most other approaches. HTTP can transport text as well as any arbitrary binary content. Our examples iFeedBack  and Smart Ticket  both demonstrate the use of custom-designed binary protocols over HTTP. The binary protocols are designed to tailor the application needs and minimize the number of bytes needed to be sent over the network.

However, this approach results in tight coupling between the servers and clients. We have to develop both serverside and clientside components to interface with the custom protocol. If the design changes in the future, we have to change the application on both sides. If we do not have control over the server, we cannot take this approach. For applications that require frequent updates, the custom protocols are also not optimal.

7.4.2 Use Mobile RPC Frameworks

A more standardized integration approach is to use commercially available RPC frameworks. Such examples include the Open Source kCommand toolkit and the Simplicity transaction engine. The kCommand toolkit defines a set of open APIs that both the client and server can call to pass generic RPC parameters. Please refer to the link in the "Resources" section to find out more about its use. The Simplicity transaction engine is a proprietary solution tightly bundled with the Simplicity IDE. Using Simplicity RAD tools ,you can drag and drop your remote transaction components on an application composer and let the IDE generate the code for you. It is very easy to use for simple applications. However, the auto-generated source code can be hard to customize.

With the mobile RPC frameworks, we save the time to develop proprietary and hard-to-maintain interface components. But the server and client remain tightly coupled.

7.4.3 Messaging Is Our Friend

Messaging solutions, especially asynchronous messaging, decouple the client and the server through the messaging middleware. Properly designed messaging solutions could greatly improve the reliability and scalability of the system because resources can be allocated to respond to requests on a priority basis rather than a first-come-first-served basis. 

7.4.4 XML and Web Services

XML Web Services advocate platform-agnostic open interfaces. It supports both RPC style and messaging style integration. However, since XML Web Services pose large bandwidth and CPU overheads, we have to use them carefully. I suggest the use of XML Web Service only when the mobile client is interfacing external components or multiple client interoperability is required.

7.5 The Impatient User

The "anytime, anywhere" convenience is the biggest strength of mobile applications. However, it is a major challenge to implement a truly convenient solution for human users. Users treat mobile devices as personal belongings and have high expectations for their devices.

In this section, we discuss efficient and responsive UI designs, which are crucial to the adoption of mobile applications. Another aspect of personal devices is that users would like to customize them to fit their individual style. We discuss preference management as well in this section. Most issues we discuss in this section are covered in the Smart Ticket sample application .

7.5.1 Take Advantage of the Rich UI

Rich UI is one of the great appeals of smart clients. We should make judicious use of advanced UI components, such as direct draw on canvas and animation sprites. In the Smart Ticket application, the use of raw canvas to draw seating maps is an excellent example of appropriate UI usage. Advanced UI widgets are supported in the MIDP v2.0 specification. Device vendors also often provide their own UI enhancement APIs.

As described in Section 7.3.3, the MVC pattern is a powerful tool to support multiple optimized UIs for different devices while reusing the same business logic components.

7.5.2 Use Threads Judiciously

UI lock-up is one of the most annoying problems users can experience. On PCs, users are used to crashes in a certain popular operating system, and they can just hit the reboot button. But the user's tolerance for malfunctioning mobile devices is much lower. We expect our cell phones to work out of the box like any other electronic household appliance. The best practice to avoid hang-ups in the main UI thread is to put all lengthy or potentially blocking operations in separate threads. In fact, the MIDP specification clearly states that the UI event handler (i.e., the CommandListener. commandAction() method) must "return immediately," which implies that proper UI threading is actually mandated by the specification. Listing 7.2 shows the use of threads.

Listing 7.2. The use of threads
public class DemoMIDlet extends MIDlet implements CommandListener {



  // other methods



  public void commandAction(Command command, Displayable screen) {

    if (command == exit) {

      // handle exit

    } else if (command == action) {

      WorkerThread t = new WorkerThread ();

      t.start();

    }

  }



  class WorkerThread extends Thread {



    void run () {

      // Do the work

    }

  }

}

In the Smart Ticket sample application, the use of threads is pushed one step further: Each worker thread also has a helper thread that displays an animated gauge to indicate the progress of the worker thread. This is especially useful to keep the user informed during lengthy network operations.

7.5.3 One Screen at a Time

Mobile users have relatively short attention spans. We should break up lengthy operations into small pieces to show one screen at a time and offer users options to pause or abort in the middle of the process. Smart clients are especially well equipped to handle the screen flow process, since on-device storage could cache information between screens. A good example of screen flow is the "buy a ticket" action in the Smart Ticket application.

7.5.4 Store User Preferences

Mobile devices become more personal and hence have more value if they are customized to fit the user's personal preferences. Advanced mobile applications should store its owner's preference data on device. As we see in the Smart Ticket application, the stored preferences also allow users to have smoother workflow experiences. For example, the user does not need to stop and enter her credit card information in the middle of the purchasing flow.

7.5.5 Use Deployment Descriptors

The mobile application can also be customized at the back end before the user downloads it. For example, when a user signs up on a Web site, the site automatically customizes the download package with the profile derived from the submitted forms. We can customize the application without rebuilding it through the deployment descriptors. The MIDP specification defined the format and usage of Java Application Descriptor (JAD) files. But for other J2ME platforms, we still need to embed property files and/or other nonstandard configuration files in the custom-generated JAR package.

Thursday, 17 September 2009 13:08

Advanced HTTP Techniques

6.1 The Decorator Approach

One way to enhance the MIDP standard HTTP I/O is to provide a decorator class (CustomConnection) that wraps around the default MIDP HttpConnection implementation but overrides some methods to handle custom headers. Since the decorator class also implements the HttpConnection interface, it is transparent to existing MIDP applications that use HttpConnection.

6.1.1 The CustomConnector Factory Class

In order to instantiate the CustomConnection decorator, we need to write a new connection factory class CustomConnector (Listing 6.1). The custom request headers are set in the CustomConnector.open() method when a new connection is established.

Listing 6.1. The CustomConnector factory class
// In CustomConnector class

public static HttpConnection open(String url) throws IOException {

  HttpConnection c = (HttpConnection) Connector.open(url);

  setRequestHeaders(c);

  c.setRequestProperty("User-Agent",

    "Profile/MIDP-1.0, Configuration/CLDC-1.0");

  c.setRequestProperty("Content-Language", "en-US");

  CustomConnection sc = new CustomConnection(c);

  return sc;

}



private static void setRequestHeaders(HttpConnection c) {

  // Generate custom header for the request and

  // set the headers to the connection object.

}



private static void getResponseHeaders(HttpConnection c) {

  // Retrieve headers from the response stream

  // and process it.

}

6.1.2 The CustomConnection Class

Now, let's have a closer look at class CustomConnection (Listing 6.2). It overrides only two methods, openInputStream() and openDataInputStream(), which process custom headers when the response data is retrieved.

Listing 6.2. The CustomConnection class
class CustomConnection implements HttpConnection {

  private HttpConnection c;



  public CustomConnection(HttpConnection c) {

    this.c = c;

  }



  public String getURL() {

    return c.getURL();

  }



  public String getProtocol() {

    return c.getProtocol();

  }



  public String getHost() {

    return c.getHost();

  }



  // More HttpConnection methods

  public InputStream openInputStream() throws IOException {

    CustomConnector.getResponseHeaders(c);

    return c.openInputStream();

  }



  public DataInputStream openDataInputStream() throws IOException {

    CustomConnector.getResponseHeaders(c);

    return c.openDataInputStream();

  }

}

6.1.3 Decorator Pros and Cons

The decorator solution is elegant and transparent to existing applications. However, it has several weaknesses.

  • It is not scalable. For each task involving custom HTTP headers, we need to write a pair of decorator and connector factory classes.

  • The decorator solution does not work correctly with HTTP tasks that require automatic header resubmission from the client side. An example of such tasks is the HTTP Digest Authentication (see Section 6.5).

For general-purpose HTTP headers handling, we need a new framework that is more powerful than simple decorators.

 

6.2 The Process-Chain Approach

In this section, I present a process-chain-based HTTP transport framework that works on all J2ME platforms. For simplicity, the new transport class treats all requests and responses as byte arrays rather than streams. If your application requires stream I/O (e.g., a SAX XML parser), you can easily wrap a ByteArrayInputStream or ByteArrayOutputStream around those arrays. The key components in the new framework are listed in Table 6.1.

Table 6.1. The HttpClient Framework

6.2.1 The HttpClient Source Code

The source code of the HttpClient class is shown in Listing 6.3. Notice how we walk through the handlers chain twice to process both the request and response headers in the query() method. The maxIteration property is used to prevent infinite loops in case of failed challenge-response cycles.

Listing 6.3. The HttpClient class
public class HttpClient {



  private String url;

  private String requestMethod;

  private Vector handlers = new Vector ();

  // Max number of challenge/response cycles.

  private int maxIteration = 3;



  public HttpClient() {}



  public void setUrl (String url) {

    this.url = url;

  }



  public void setRequestMethod (String method) {

    this.requestMethod = method;

  }



  public void setMaxIteration (int n) {

    maxIteration = n;

  }



  public void addHandler (Handler h) throws Exception {

    handlers.addElement(h);

  }



  public void removeAllHandlers () throws Exception {

    handlers = new Vector ();

  }



  public byte [] query (byte [] req) throws Exception {

    boolean needConnect = true;

    HttpConnection c = null;

    int currentIteration = 0;

    while (needConnect) {

      currentIteration++;

      if (currentIteration > maxIteration)

        throw new Exception("Too many Iterations");

      needConnect = false;



      if ( c != null ) {

        try {

          c.close();

        } catch (Exception ignore) {

        }

      }

      c = (HttpConnection) Connector.open (url);

      c.setRequestMethod( requestMethod );

      for (int i = 0; i < handlers.size(); i++)

((Handler) handlers.elementAt(i)).prepareHeaders(c);

      c.setRequestProperty("User-Agent",

       "Profile/MIDP-1.0, Configuration/CLDC-1.0");

      c.setRequestProperty("Content-Language", "en-US");



      if ( req != null ) {

        OutputStream os = c.openOutputStream ();

        os.write(req);

        os.close();

      }

      for (int i = 0; i < handlers.size(); i++) {

        needConnect =

    ((Handler) handlers.elementAt(i)).processHeaders(c) | | needConnect;

      }

    }

    InputStream is = c.openInputStream ();

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    byte[] buf = new byte[256];

    while (true) {

      int rd = is.read(buf, 0, 256);

      if (rd == -1) break;

      bos.write(buf, 0, rd);

    }

    buf = bos.toByteArray();

    is.close();

    c.close();

    return buf;

  }

}

Now, let's look at how to use those two frameworks to handle HTTP headers in the real world.


6.3 Session Tracking via HTTP Cookies

Cookies are pieces of NAME=VALUE formatted text embedded in HTTP headers. They are used to track client states. Since cookies reside in HTTP headers, they are transparent to applications and users. The server assigns new cookies to the client through the HTTP header set-cookie. The set-cookie header takes the following format:

set-cookie: NAME=VALUE; expires=DATE; path=PATH;

domain=DOMAIN_NAME; secure

The first NAME=VALUE is the cookie itself and is required. All the following attributes, such as expiration time, domain, and path, are optional. When the client makes subsequent requests, it sends the cookies back in the cookies header to identify itself.

cookie: NAME1=VALUE1; NAME1=VALUE2; ...

Note

The server can send out multiple cookies in one connection using multiple set-cookie headers. The client can send back multiple cookies in one header by delimiting them using semicolons.


6.3.1 Handle Cookies via Decorator Classes

Sun Microsystems' Smart Ticket blueprint v1.1 provides a class SessionConnector that utilizes the decorator pattern to add cookie support into the standard MIDP HTTP framework. The source code of this class is available from this book's Web site. The following snippet demonstrates how to use this class.

HttpConnection c =

  (HttpConnection) SessionConnector.open(url);

// You can use "c" as a normal HttpConnection

// but it is session aware now.

6.3.2 Handle Cookies via HttpClient Handlers

To support cookie headers in the HttpClient framework, we need to write the handler class. The source code of handler class (CookieHandler) is shown in Listing 6.4. Method getCookie() parses the response header and stores cookies in a static data member cookies. Method addCookie() matches stored cookies with the current request URL to determine which cookies to send out. Please refer to this book's Web site for complete source code.

Listing 6.4. The CookieHandler class
public class CookieHandler implements Handler {



  private static Vector cookies;

  private static Vector domains;



  public CookieHandler() {

    cookies = new Vector ();

    domains = new Vector ();

  }



  public void prepareHeaders(HttpConnection c) throws Exception {

    String url = c.getURL ();

    addCookie(c, url);

  }



  public boolean processHeaders (HttpConnection c) throws Exception {

    getCookie(c);

    return false;

  }



  // Remove all cookies.

  public void removeCookies() throws Exception {

    cookies = new Vector ();

    domains = new Vector ();

    return;

  }



  // Retrieve cookies from the connection header

  // and save them with domain information

  private void getCookie(HttpConnection c) throws Exception {

    // Parse the incoming cookies and store them in

    // cookies and domains vectors.

  }



  private void addCookie(HttpConnection c,

                String url) throws Exception {

    // Match the url domain with existing cookies

    // in the cookies vector. If a match is found,

    // set it into the connection header.

  }

}

The use of CookieHandler is illustrated in Listing 6.5.

Listing 6.5. The CookieHandler usage
HttpClient client = new HttpClient ();

Handler h = new CookieHandler();

client.addHandler( h );

client.setUrl( url );

client.setRequestMethod( HttpConnection.GET );

byte [] result = client.query(null);

6.4 HTTP Basic Authentication

Some HTTP headers can carry client credential information. Those credentials are used by servers to determine the client's identity and then grant or deny access to the requested resources. In the HTTP basic authentication scheme, the client sends its username and password in plain text with every request. The procedure is the following:

  1. Use the Base64 algorithm to encode a username : password string

  2. Send the encoded string and string Basic in the HTTP header Authorization

For example, if the username is Aladdin and password is open sesame, the HTTP authentication header is the following.

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

6.4.1 Code Example

To enable HTTP basic authentication in the HttpClient class, we need to plug in a handler (BasicAuthHandler). We can easily use BasicAuthHandler together with CookieHandler to make the HttpClient object keep track of a client session over an authentication connection (Listing 6.6).

Listing 6.6. Use cookies with HTTP basic authentication
HttpClient client = new HttpClient ();

Handler h1 = new CookieHandler();

Handler h2 = new BasicAuthHandler(user, pass);

client.addHandler( h1 );

client.addHandler( h2 );

client.setUrl( url );

client.setRequestMethod( HttpConnection.GET );

byte [] result = client.query(null);

Sample source code for the BasicAuthHandler class is shown in Listing 6.7.

Listing 6.7. The BasicAuthHandler class
public class BasicAuthHandler implements Handler {



  private String username;

  private String password;



  public BasicAuthHandler (String u, String p) {

    username = u;

    password = p;

  }



  public void prepareHeaders(HttpConnection c) throws Exception {

    String s = encode(username + ":" + password);

    c.setRequestProperty("Authorization", "Basic " + s);

  }



  public boolean processHeaders(HttpConnection c) throws Exception {

    // Do nothing.

    return false;

  }



  // Base64 encoding.

  //

  // This implementation is adopted from

  // Kenneth Ballard's HttpClient package.

  // Released under LGPL.

  private String encode(String d) {

   // Implementation details skipped

  }

}

6.5 HTTP Digest Authentication

Basic authentication can be used in a secure network environment. However, in an insecure network, such as the Internet, the problem for basic authentication is obvious: A cracker can easily intercept the clear text username and password, and forge the user's identity. A more secure scheme is to use one-way hashes (digests) to carry user credentials. The HTTP Digest Authentication works as follows.

  1. The client contacts the server and requests a restricted resource.

  2. The server sends a challenge to the client, including a randomly generated nonce value in predefined HTTP headers.

  3. The client calculates a hash using its username, password, and the nonce value according to an algorithm defined in the specification.

  4. The client resends its request with the new authentication header.

  5. The server compares hashes with its own calculations. If the authentication is successful, the client will continue to use the same hash until the server changes the nonce value or the user changes its username and password.

Besides eliminating the clear text username and password, the digest authentication scheme has other important benefits. The server knows only the hash of the password but not the password itself. This prevents insider abuses. The server nonce value is embedded in the hash and therefore cannot be forged. This allows the server to have better control over the authentication process.

6.5.1 Code Example

We can use the DigestAuthHandler class to make the HttpClient object aware of digest authentication. The implementation of DigestAuthHandler is based on Kenneth Ballard's Open Source package "HttpClient." Code snippet from the DigestAuthHandler class is shown in Listing 6.8. Please note that the processHeaders() method returns false when the authentication fails, causing the HttpClient to recalculate the digest headers and resubmit its request. The digest value is generated using code adopted from the Bouncy Castle package (see Chapter 20).

Listing 6.8. The DigestAuthHandler class
public class DigestAuthHandler implements Handler {



  public DigestAuthHandler (String u, String p) {

    username = u;

    password = p;

  }



  public void prepareHeaders(HttpConnection c) throws Exception {

    String h = "Digest ";



    if(username != null)

      h = h + "username=\"" + username + "\", ";

    if(realm != null)

      h = h + "realm=\"" + realm + "\", ";

    if(nonce != null)

      h = h + "nonce=\"" + nonce + "\", ";

    if(uri != null)

      h = h + "uri=\"" + uri + "\", ";

    if(opaque != null)

      h = h + "opaque=\"" + opaque + "\", ";



    if(qop != null) {

      h = h + "qop=\"" + qop + "\", ";

      // cnonce is a random number generated by the

      // client. You should use your device build-in

      // random number generator to produce it.

      cnonce = "0123456789";

      h = h + "cnonce=\"" + cnonce + "\", ";



      h = h + "nc=" + count + ", ";



      // Increase counter by one. The counter will

      // be reset when a new nonce comes in.

      ncount++;

      String nc = Integer.toHexString(ncount);

      count = new String("00000000").substring(nc.length()) + nc;

    }

    h = h + "algorithm=\"MD5\", ";

    h = h + "response=\"" + getDigest() + "\"";

    c.setRequestProperty("Authorization", h);

  }



  public boolean processHeaders (HttpConnection c)

                                  throws Exception {

    if ( c.getResponseCode() == 401 ) {

      httpMethod = c.getRequestMethod();

      uri = c.getFile();

      parse (c.getHeaderField("WWW-Authenticate"));



      // need to re-send request

      return true;

    } else {

      return false;

    }

  }



  // Other utility methods

}

6.6 Secure HTTP

Both basic and digest HTTP authentication schemes discussed above are weak security measures. They only authenticate users but do not protect the
communication content. They do not prevent crackers from intercepting or even tampering with the communication data. For complete point-to-point HTTP
security, we need the HTTPS protocol that is based on secure underlying transport protocols such as the Secure Socket Layer (SSL) and the Transport Layer
Security (TLS). Compared with thin client solutions where security is provided by the fixed infrastructure, direct HTTPS connections allow more flexible security schemes. For example, the communication parties can decide what to encrypt, the level of encryption and how often the session key should be changed
based on their business needs. In addition, by eliminating the middleman, HTTPS smart clients avoid the single point of failure and hence they are not affected
by infrastructure level security holes. The discovery of security weaknesses in WAP gateways and WiFi access points has made this an important concern.
Figure 6.1 illustrates the difference between HTTPS end-to-end solutions and WAP thin client solutions.





 

Figure 6.1. HTTPS end-to-end security versus WAP security.
 



 

6.6.1 HTTPS Support in the MIDP

Support for HTTPS is mandatory in the MIDP v2.0 but optional in the MIDP v1.0. To establish an HTTPS connection, all you need to do is pass
an https://-style URL string to the Connector.open() factory method.

 

  •  
  • On an HTTPS-enabled MIDP v1.0 device, a normal HttpConnection object will be returned. You can open input and output streams as

usual. But the underlying data are properly encrypted. The entire process is transparent to developers.

  • On a MIDP v2.0 device, an HttpsConnection object will be returned. Interface HttpsConnection extends HttpConnection with two
    more methods: getPort() and getSecurityInfo(). The getSecurityInfo() method returns a SecurityInfo object, which can be used to
    obtain further information on cipher and server certificate.









 

 

Wednesday, 16 September 2009 16:23

Mobile Design Patterns: The Smart Ticket Blueprint

5.1 Getting Started

The Smart Ticket application is available from Sun Microsystems' blueprints Web site (see "Resources"). The zip package contains source code; ANT build scripts; and prebuilt, deployable application binaries.


The Smart Ticket sample contains a J2EE component for the enterprise back end and a J2ME component for the mobile front end. Running the application requires a J2EE application server and a MIDP v2.0-compatible device (or emulator) with Internet connectivity. For learning and testing purposes, we can run both the J2EE server and MIDP emulator on the same computer as follows.

  1. Set up the following environment variables:

    • JAVA_HOME: The JDK installation directory (JDK v1.4.1 and above required)

    • J2EE_HOME: The J2EE reference implementation (RI) installation directory (J2EE v1.3.1 and above required)

    • J2MEWTK_HOME: The J2ME Wireless ToolKit installation directory (J2ME WTK v2.0 and above required)

  2. Start the J2EE server using the following two commands:

    J2EE_HOME\bin\cloudscape -start
    
    J2EE_HOME\bin\j2ee -verbose
    
  3. Deploy the J2EE application using the following script. The setup script in turn invokes the corresponding ANT task in setup.xml:

    setup deploy
  4. Point your browser to http://localhost:8000/smartticket and click on the populate database link to import the mock theater and movie data to the Smart Ticket database. This could be a very slow process on older computers, so be patient! The mock data set contains theater information in two zip code areas: 95054 and 95130.

  5. Start the J2ME WTK v2.0 and run MIDlet smart_ticket-client.jad.

Now, you are ready to order some movie tickets from the phone emulator!

Note

If you want to run the MIDP client on an actual smart phone device, the J2EE server must run on an Internet-accessible computer and you must configure the MIDP client for the correct server IP address.

5.2 Smart Ticket in Action

The Smart Ticket client allows the user to manage user preferences, browse movie schedules, order tickets, rate watched movies, and pre-download the schedule. We discuss those features one by one in this section.

Note

The Smart Ticket blueprint is designed for educational purposes. It demonstrates the use of design patterns. As developers, we should judicially use patterns learned from the Smart Ticket. The overuse of design patterns results in unnecessary abstraction layers and produces slow and large applications.

5.2.1 Manage User Preferences

When the user starts the MIDP client for the first time, she will be asked to create a profile. The profile includes two types of information:

  • Account credentials: The username, password, and optional credit card numbers.

User preferences: The theater search zip codes, favorite day of the week, and preferred seating. Figure 5.1 shows how to manage user preferences.
 

Figure 5.1. Manage user preferences.
 

After the user submits the profile, a corresponding user account is created on the J2EE server. The preference information is cached on the device. The user could configure the MIDP client to cache the account credentials so that she does not need to manually sign in every time she wants to purchase tickets or submit movie ratings. User preferences can be modified at any time through the MIDP UI.

Note

The Smart Ticket client does not encrypt the account and preference information stored on the device. That could create problems if the device is lost.

5.2.2 Search and Purchase Tickets

Once logged in, the user can browse theaters, movies, and show times in her zip code areas. This process involves a series of real-time queries to the J2EE server. Once she selects a show, she will be asked to select currently available seats from an interactive seating map to make reservations. The reservation is persisted to the server database.

5.2.3 Rate Movies

The user can rate movies she has seen . The ratings are not immediately submitted to the server. They are cached on device and can be synchronized to the server upon the user request. That allows the user to rate movies even when the phone is out of network range (for example, in a shielded cinema building!). The synchronization agent is smart: When the same user rates the same movie multiple times, it resolves the issue by keeping only the most recent rating in the backend database.

5.2.4 Cache Theater Schedules

Smart Ticket allows the user to download a theater's schedule to the mobile client. The cached schedule enables offline browsing and improves the performance by reducing network round trips. The user can delete or re-download the schedule as needed 

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

}

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.");

  }



  // ... ...

}

5.4 Implementation Techniques

The MVC and facade patterns define the overall architecture of the application. In addition, Smart Ticket showcases some important behavioral patterns and implementation techniques that could greatly improve developer productivity.

5.4.1 Chain of Handlers

On the J2ME device side, the RemoteModelProxy class (see Listing 5.3) further delegates the action to a chain of handler classes that transparently work out the dirty plumbing of the RMS and HTTP serialization. The chained handlers are based on the RequestHandler interface and the RemoteModelRequestHandler abstract class (Listing 5.7), which implements the former:

Listing 5.7. The RemoteModelRequestHandler class
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

  // ... ...

}

Concrete handler classes extend the RemoteModelRequestHandler class. A chain of handlers is established through nested constructors. Two handler classes are available in the Smart Ticket application: the RMSCacheHandler and HTTPCommunicationHandler classes. Listing 5.8 illustrates how the chain is assembled and used (e.g., getMovie()) in the RemoteModelProxy class.

Listing 5.8. Assemble the handler chain in class RemoteModelProxy
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;

  }



  // ... ...



  // get a movie from the chain of handlers

  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 action methods etc.



}

A handler can selectively implement any action methods in the RemoteModel interface. There are two possibilities:

  • If a RemoteModelProxy class calls an action method not implemented by the first handler class in the chain, the default implementation in the base class RemoteModelRequestHandler ensures that the call is passed to the next handler in the chain.

  • If a handler in a chain decides that it has finished processing an action, it returns directly. Otherwise, it can invoke the same action method in the base class to pass it to the next handler in the chain.

The following code snippets (Listing 5.9 and 5.10) illustrate how to implement the getMovie() method in the two handlers. The RMSCacheHandler looks up the on-device cache for the requested movie. If the requested movie is not cached, RMSCacheHandler calls its base class's getMovie() method, which passes the control to the next handler in the chain: the HTTPCommunicationHandler class. The getMovie() method in HTTPCommunicationHandler performs some network tasks to retrieve the movie object from the J2EE back end. To understand the inner workings of the HTTPCommunicationHandler class, you need to read on to the next section.

Listing 5.9. The getMovie() method in RMSCacheHandler
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);

  }



  // ... ...

}
Listing 5.10. The getMovie() method in HTTPCommunicationHandler
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);

      outputStream.writeByte(MessageConstants.OPERATION_GET_MOVIE);

      outputStream.writeUTF(movieKey);

      outputStream.close();

      updateProgress();



      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);

    }

  }



  // ... ...

}

5.4.2 Binary RPC over HTTP

In the model layer, the HTTPCommunicationHandler class in the RemoteModelProxy class invokes remote procedures on the J2EE server side through a binary RPC protocol over the HTTP.

All RPC requests from the client to the server follow the same basic pattern: The first byte in the HTTP request data stream specifies the action method to be executed on the serverside session facade EJB. The RPC request code constants are defined in the MessageConstants class (Listing 5.11).

Listing 5.11. The RPC action codes in MessageConstants
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() {}

}

The second byte to the end of the request stream encodes a sequence of UTF strings that represent the parameters to be passed to the remote method. The response HTTP stream contains the RPC return value. The format is unique to each method, and you have to look at the source code for each method to figure out the exact format. The two code snippets below demonstrate the entire RPC round trip to get a list of theaters using a zip code. The RPC request is assembled in HTTPCommunicationHandler's action method getTheaters() (Listing 5.12), and the response array is unmarshaled by the shared model object Theater (Listing 5.13).

Listing 5.12. The HTTPCommunicationHandler class generates the RPC request in the handler chain
package com.sun.j2me.blueprints.smartticket.client.midp.model;



public class HTTPCommunicationHandler

             extends RemoteModelRequestHandler {

  // ... ...



 public Theater[] getTheaters(String zipCode)

            throws ModelException, ApplicationException {

    HttpConnection connection = null;

    DataOutputStream outputStream = null;

    DataInputStream inputStream = null;



    try {

      connection = openConnection();

      updateProgress();

      outputStream = openConnectionOutputStream(connection);



      outputStream.writeByte(MessageConstants.OPERATION_GET_THEATERS);

      outputStream.writeUTF(zipCode);

      outputStream.close();

      updateProgress();



      inputStream = openConnectionInputStream(connection);



      // The first number in the response stream indicates

      // the number of theater objects to follow.

      Theater[] theaters = new Theater[inputStream.readInt()];



      // Iterate to unmarshal all theater objects in the response.

      for (int i = 0; i < theaters.length; i++) {

        theaters[i] = Theater.deserialize(inputStream);

      }

      updateProgress();

      return theaters;

    } catch (IOException ioe) {

      throw new

        ApplicationException(ErrorMessageCodes.ERROR_CANNOT_CONNECT);

    } finally {

        closeConnection(connection, outputStream, inputStream);

    }

  }



  // Other action methods

}

Listing 5.13. The Theater class in the J2ME model layer unmarshals the RPC response
package com.sun.j2me.blueprints.smartticket.shared.midp.model;



public class Theater {

  private String primaryKey;

  private String name;

  private String address;

  private String zipCode;



  // ... ...



  public static Theater deserialize(DataInputStream dataStream)

                                     throws ApplicationException {

    try {

      Theater theater = new Theater();

      theater.zipCode = dataStream.readUTF();

      theater.primaryKey = dataStream.readUTF();

      theater.name = dataStream.readUTF();

      theater.address = dataStream.readUTF();

      return theater;

    } catch (IOException ioe) {

      throw new ApplicationException(ioe);

    }

  }

  // ... ...

}

The SmartTicketServlet first determines the RPC action code from the first byte in the request stream. It then dispatches the RPC to the corresponding action method through the facade and passes all the RPC parameters remaining in the stream. In the Smart Ticket application, the client and server are tightly coupled. This approach can improve network efficiency, since each RPC exchange can be specially designed and optimized. However, the trade-off is development speed and robustness. If we make small changes to the server, the protocol and the parsing code on the client are likely to need to change too. We need to keep track of and update the code in multiple places, which could prove error prone. We also often need to recompile and redistribute clients.

5.4.3 The Clientside Thread Model

The Smart Ticket application uses a sophisticated threading model on the mobile client side. During a prolonged background task, another thread displays a moving gauge to the user indicating the progress (Figure 5.6). The gauge screen could also provide a button for the user to cancel the long action if she does not want to wait.

Figure 5.6. The progress gauge.
 
 

As we have seen, action methods in the UIController class are simply wrappers of the runWithProgress() method (Listing 5.14), which sets the display to ProgressObserverUI and starts the EventDispatcher thread. The ProgressObserverUI screen displays a gauge and an optional Stop button, which is monitored by the main MIDlet system UI thread. As we described in Section 5.3.1, the EventDispatcher thread eventually delegates the action to methods in the model layer. The model action method calls the ProgressObserverUI's updateProgress() method (Listing 5.15) at certain stages over the execution to update the gauge and inform the user of the progress (see Listing 5.12).

Listing 5.14. The runWithProgress() method in the UIController class
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

    }

  }

}

Listing 5.15. The ProgressObserverUI class
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;

    }

  }

}
Wednesday, 16 September 2009 11:38

The Smart Client Paradigm: iFeedBack

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.
Figure 3.1. The iFeedBack architecture.
 
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!
Now, you can follow these steps to test and run the application. Figure 3.2 shows the screen shots.
  1. Start up the MIDlet.
  2. 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.
  3. Choose Add Course. Add in a new class survey nickname and endpoint URL. You can just accept the suggested values.
  4. 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.
  5. Choose a course. iFeedBack will fetch the current question and display it for you.
  6. Choose the appropriate answer and put in any comments. Then click SAVE to save the time-stamped answer to the on-device persistent cache.
  7. You can repeat the last two steps multiple times for different questions from different courses.
  8. 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.
  9. 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.

Figure 3.3. UML model for the UI component.
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).
 
Figure 3.4. Execution flow: iFeedBack's worker threads
 
All worker threads are derived from abstract class BackgroundTask (Listing 3.3; see Figure 3.5).
 
Figure 3.5. Classes in the iFeedBack thread model.
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
 
Wednesday, 16 September 2009 12:14

Managed Smart Clients

Managed Smart Clients

4.1 Container-Managed Applications

In the field of software engineering, the term container refers to specialized software that runs other software. For example,

  • The MIDP Application Management Software (AMS) is a container that installs, starts, pauses, stops, updates, and deletes MIDlet applications. In the CDC Personal Basis Profile, the Xlet programming model also features container-managed life-cycle methods.

  • A Java servlet engine is a container that invokes servlets and provides access to the HTTP context.

  • The Java Virtual Machine (JVM) itself is a container. It monitors Java applications for proper memory usage (garbage collector) and security.

In the next two sections, we will discuss the features and benefits of mobile containers.

4.1.1 Container Features

As mobile enterprise applications become mainstream, the complexity of smart clients grows. For example, fully commercial applications often require features such as user login, logging, transaction, and transparent data access. Without proper tools for code and service reuse, mobile developers have to duplicate those functionalities for every smart client. Wasting time reinventing the wheel is not only inefficient but also causes error-prone code.


Hence, it makes sense to make those common features available as services in software containers that run on mobile devices. An advanced container usually provides the following functionalities.

  • Self-contained applications: Applications run inside the container are self-contained with portable code and necessary configuration files. The interdependence of applications and library components could be managed by the container. Examples are the WAR files for servlet containers and EAR files for EJB containers.

  • Life-cycle management: By calling the life-cycle methods defined in the container framework and implemented by all applications, the container can install, start, stop, update, and delete any application programmatically or through an interactive console.

  • Application services: The container provides services that are common to all applications. For example, an authentication module in the container could allow all applications to authenticate against a single password database.

  • Custom services: The container should also allow its applications to offer services to each other. That encourages code reuse and prompts architectures for layered and modularized applications.

In this book, we use the term container rather loosely. Our containers do not impose arbitrary boundaries for API usages. Applications installed inside the container can transparently access any Java or native API available on the device. These containers are often known as frameworks. The container architecture on J2ME mobile devices is illustrated in Figure 4.1Figure 4.1. The container architecture for J2ME smart clients. 

4.1.2 Benefits of Containers

The above container features translate to real benefits in mobile development projects:

  • Reduced code redundancy: Since the common services are not repeatedly implemented, we can reduce overall footprint and potential number of errors while improving the developer productivity.

  • Managed update: When we fix a bug or add a new feature in a service, all applications that use it automatically get the update. Some containers support service versioning for more refined controls.

  • Support for multitiered application models: Services in a container offer natural separations between application tiers (e.g., the presentation and business layers).

  • Simplified application provisioning: Self-contained applications can be easily deployed to any container. That enhances Java's value proposition of "write once, run anywhere."

Given these benefits, containers or frameworks are widely used in mobile Java application development. In the next section, we introduce a standard container specification for lightweight mobile devices: the OSGi specification.

4.2 OSGi Containers

The OSGi Alliance is an industry consortium that creates open standard specifications for network-delivered services. Founded in March 1999, OSGi is a nonprofit organization with open membership. Its board of directors includes Acunia, BMW, Deutsche Telekom, Echelon, Gatespace, IBM, Motorola, Oracle, Philips, ProSyst, Samsung, Sun and Telcordia. The OSGi specification defines the mobile container framework and standard container services as Java APIs that span from J2ME to J2SE to J2EE.

The OSGi Service Platform Release 2 specification was released in October 2001. It has been widely adopted by vendors and has many implementations. The OSGi Service Platform Release 3 specification was made available in March 2003. The IBM Services Management Framework (SMF) v3.5 is targeted to be OSGi R3 compatible. It is available for free evaluation from the IBM Web site (see "Resources"). We discuss both Release 2 and 3 in this chapter. However, all examples are written for and tested on Release 2 containers.

Note

Despite the term Gateway in its name, the OSGi specification does not define any particular kind of gateway servers. It defines a framework for service components delivery and execution. The OSGi container provides the runtime environment for those services. Gateway server is only one of OSGi's application areas.

4.2.1 Bundles

OSGi applications are packaged as bundles, which are just standard JAR files. The OSGi bundle is completely self-contained with all the necessary metadata in its manifest file. The OSGi container completely manages the bundle life cycle:

  • Install, update, and uninstall the bundle.

  • Start and stop the bundle.

  • Register, unregister, and track services in the bundle.

The bundle management interfaces are defined in the org.osgi.framework package. Since the bundles can be deployed to the container dynamically without restarting the container, the OSGi platform is an ideal choice for mobile application provisioning clients. It allows applications to be managed, tracked, and updated throughout its lifetime.

4.2.2 Standard Services

The OSGi container provides common crosscutting services such as device drivers, user preferences, and logging to all its bundles. Table 4.1 lists the OSGi services defined in OSGi Service Platform Release 2 specification. The new OSGi Service Platform Release 3 specification defines more services, some of which are of great importance to mobile applications. Those new services are listed in Table 4.2.

OSIG Services Table
 

4.2.3 Bundle Interaction and Custom Services

The OSGi framework provides powerful ways for bundles to interact with each other. This encourages code reuse and makes it easier to architect complex multilayer applications. For example, the OSGi container on a stock trader's PDA might be provisioned with services bundles from major exchanges. Each bundle knows how to run real-time queries and execute trades in a specific exchange market, and it makes those functionalities available to other bundles. The trader can then deploy the actual trading client bundle, which provides a user interface, supports custom trade logic, and executes the query/trade through the individual service bundles. The possible interactions among bundles are as follows.

  • Static sharing: The OSGi container runs on a single JVM instance but has a different classloader for each bundle. That means bundle namespaces are separate. We cannot directly access objects or classes in another bundle by default. However, a bundle can explicitly export some of its Java packages through the Export-Package attribute in its manifest file. It can also import Java packages exported by others using the Import-Package manifest attribute. The export and import features allow direct sharing of Java packages.

  • Dynamic services: In addition to standard services provided by the container, any bundle can consume and provide services from/to other bundles at the same time:

    1. A bundle can dynamically register (or unregister) services with the container. The bundle needs to register the service interface with a concrete implementation class. Any change to the service (register, modify, unregister) will result in framework events that could be captured and processed.

    2. Another bundle finds the service reference through a lookup API in the framework. It calls a framework method to obtain the service implementation object from the service provider bundle. The service object is now ready to use.

The interacting bundles allow us to deliver reusable services to any OSGi node, from the high function grid to pervasive devices.

4.2.4 OSGi Runtime Requirements

Currently, different OSGi vendors have different requirements for their products. The required execution environments range from PersonalJava v1.1 to J2SE. This has created considerable confusion in the developer community. In an effort to standardize the runtime requirements, the OSGi Service Platform Release 3 specification formally defines the following runtime environments:

  • The Java 2 Micro Edition: All OSGi implementations should run under the CDC v1.0 plus Foundation Profile v1.0 runtime environment.

  • The OSGi minimum execution environment: The specification also defines a subset of CDC/FP APIs, which allows devices not powerful enough for the CDC/FP (e.g., Palm PDAs) to run the OSGi framework. The OSGi minimum execution environment is defined to be a proper subset of CDC/FP and J2SE.

The standard execution environments make it easier for developers, especially resource-conscious mobile developers, to choose the right OSGi product.

 

4.3 A Simple Echo Service Example

In this section, we first introduce a J2ME-compatible OSGi implementation from IBM. Using a simple echo example, we demonstrate how to implement bundles and share services among them.

 

4.3.1 The IBM Service Management Framework

The IBM SMF is a readily available OSGi implementation. It has a memory footprint of 3 MB and runs on both execution environments defined in the OSGi Service Platform Release 3 specification. IBM supports the J2ME environments through WME (WebSphere Micro Environment JVM) and the minimum execution environment through WCE (WebSphere Custom Environment JVM) products. It can be tightly integrated into IBM's WebSphere Studio Device Developer IDE. The SMF product versions we cover in this book are v3.1 for OSGi R2 and v3.5 for OSGi R3.

The SMF installation process varies among devices. It generally involves the following steps.

 

  1. Download and unpack the SMF toolkit from IBM.

  2. Copy the following directories and files to the target device (or to a local execution directory, if you want to run SMF on a desktop computer). For my PocketPC device, I put all four items under the device root directory.

    • The jarbundles directory contains installed bundles.

    • The smf.jar file provides implementation classes for the OSGi specification.

    • The smfconsole.jar file provides a command-line management console for the container.

    • The smf.properties file specifies the runtime configuration.

  3. Make sure that the com.ibm.osg.smf.bundledir property in the smf.properties file points to the correct bundle directory. For example,

    com.ibm.osg.smf.bundledir=jarbundles
    
    
  4. Now we can start the SMF console using the following command (in one line) or its equivalent on the device platform.

    java -classpath "smf.jar:smfconsole.jar"
    
    com.ibm.osg.smf.SMFLauncher -console "launch"
    
    

    For my PocketPC device with IBM WebSphere Micro Environment preinstalled, I use the following command (in one line). Please refer to the Appendix B for the steps to install the IBM J2ME runtimes on PDA devices.

    "\WSDD\j9.exe" -jcl:foun
    
    "-Xbootclasspath:\WSDD\lib\jclFoundation\classes.zip;
    
    \smf.jar;\smfconsole.jar" "com.ibm.osg.smf.SMFLauncher"
    
    -console "Launch"
    

After the SMF console is started, it loads all currently installed bundles into the container and presents the user a command-line interface for management tasks. For a complete list of management commands, please refer to the SMF manual. Table 4.3 lists some of the most frequently used commands.

Table 4.3. Common Commands Available in the SMF Console


In the next two sections, we describe how to create and deploy two OSGi bundles: The EchoService bundle exposes an echo service; the EchoUIConsumer bundle presents a simple GUI client and uses the EchoService in the container to echo user input. Figures 4.3 and 4.4 show the two bundles in action in J2SE and J2ME OSGi containers. Figure 4.5 shows user interactive OSGi bundles.
 

Figure 4.3. The echo bundles in action in J2SE.


Figure 4.4. The echo bundles in action in J2ME. 



4.3.2 The EchoService Bundle

The EchoService bundle demonstrates how to implement and register a service in an OSGi bundle. The service itself is extremely simple: It only defines one method that does nothing more than echo a string input. The steps to create the bundle are as follows.

Define the service interface as a Java interface (Interface EchoService, Listing 4.1).

 


    Listing 4.1. The EchoService interface
    package com.enterprisej2me.osgi.echoservice;
    
    
    
    public interface EchoService {
    
      public String echo (String s);
    
    }
    
    
    Listing 4.2. The EchoServiceImpI class
    package com.enterprisej2me.osgi.echoserviceimpl;
    
    
    
    import com.enterprisej2me.osgi.echoservice.*;
    
    
    
    public class EchoServiceImpl implements EchoService {
    
    
    
      EchoServiceImpl () { }
    
    
    
      public String echo (String s) {
    
        return s;
    
      }
    
    
    
    }
    
    
    Listing 4.3. The EchoActivator class
    package com.enterprisej2me.osgi.echoserviceimpl;
    
    
    
    import org.osgi.framework.*;
    
    import com.enterprisej2me.osgi.echoservice.*;
    
    
    
    
    
    public class EchoActivator implements BundleActivator {
    
    
    
      private ServiceRegistration reg;
    
    
    
      public EchoActivator () { }
    
    
    
      public void start (BundleContext context) throws Exception {
    
        EchoServiceImpl impl = new EchoServiceImpl ();
    
        reg = context.registerService (
    
            EchoService.class.getName(), impl, null);
    
      }
    
    
    
      public void stop (BundleContext context) throws Exception {
    
        reg.unregister ();
    
      }
    
    }
    
    
    Listing 4.4. The JAR manifest for the echo service bundle
    Manifest-Version: 1.0
    
    Bundle-Name: Echo service
    
    Bundle-Description: Echo the input
    
    Bundle-Activator: com.enterprisej2me.osgi.echoserviceimpl.EchoActivator
    
    Import-Package: org.osgi.framework; specification-version=1.1
    
    Export-Package: com.enterprisej2me.osgi.echoservice
    
    Export-Service: com.enterprisej2me.osgi.echoservice.EchoSerivce
    
    

    Now, we can install and start the package in our SMF console.

    4.3.3 The EchoUIConsumer Bundle

    The EchoUIConsumer bundle is created to demonstrate how to use the echo service through the framework:

    1. Create a BundleActivator implementation as the entry point to the bundle (Class EchoUIConsumer, Listing 4.5).

    2. In the EchoUIConsumer.start() method, create and open a ServiceTracker object to track the echo service we started.

    3. Create the UI frame class EchoFrame (Listing 4.6) and pass the ServiceTracker object and the current bundle (i.e., the echo consumer bundle) to EchoFrame.

    4. The EchoFrame object obtains the EchoService object from the ServiceTracker and uses the EchoService to echo any user input. When we hit the Exit button in the UI frame, the AWT event handler calls the bundle's stop() method and triggers the container to invoke the EchoUIConsumer.stop() method. For more details, refer to method actionPerformed() in Listing 4.6.

    5. In the EchoUIConsumer.stop() method, dispose the UI frame and close the ServiceTracker (Listing 4.5).

    6. Create the manifest file (Listing 4.7). We import the package containing the EchoService interface here.

    7. Package and deploy the JAR bundle.

    Tracking the Services

    In the service consumer bundles, we could manually look up the service objects from the framework. Then, we would have to register event listener and callback methods to handle situations when other bundles or the container itself changes those services (e.g., removes the service). This could be a tedious task. Instead, we take a shortcut and use a pair of ServiceTracker objects to automatically track those services.

    The ServiceTracker object tracks a list of services meeting certain criteria passed to it in the constructor. It provides default event handlers for the services it tracks.

    The ServiceTracker object can be instantiated with a ServiceTrackerCustomizer object. When a service in the tracker is added, modified, or deleted, the appropriate method in its associated ServiceTrackerCustomizer is called. For more usage examples of the ServiceTracker class, please refer to Section 4.4.


    Listing 4.5. The EchoUIConsumer class
    package com.enterprisej2me.osgi.echouiconsumer;
    
    
    
    import org.osgi.framework.*;
    
    import org.osgi.util.tracker.*;
    
    import com.enterprisej2me.osgi.echoservice.*;
    
    
    
    public class EchoUIConsumer implements BundleActivator {
    
    
    
      ServiceTracker echoTracker;
    
      EchoFrame frame;
    
    
    
      public EchoUIConsumer () { }
    
    
    
      public void start (BundleContext context) {
    
        echoTracker = new ServiceTracker (context,
    
                   EchoService.class.getName(), null );
    
        echoTracker.open ();
    
        frame = new EchoFrame(250, 250, echoTracker, context.getBundle());
    
      }
    
    
    
      public void stop (BundleContext context) {
    
        frame.dispose ();
    
        echoTracker.close();
    
      }
    
    }
    
    
    Listing 4.6. The EchoFrame class
    package com.enterprisej2me.osgi.echouiconsumer;
    
    
    
    import java.awt.*;
    
    import java.awt.event.*;
    
    import org.osgi.framework.*;
    
    import org.osgi.util.tracker.*;
    
    import com.enterprisej2me.osgi.echoservice.*;
    
    
    
    public class EchoFrame extends Frame
    
        implements WindowListener, ActionListener {
    
    
    
      private TextField entryText;
    
      private Label echoedText;
    
      private Button submit;
    
      private Button exit;
    
      private Panel content, top, bottom, middle;
    
      private ServiceTracker echoTracker;
    
      private Bundle echoUIConsumerBundle;
    
    
    
      public EchoFrame (int width, int height,
    
                        ServiceTracker t, Bundle b) {
    
        super ("Echo UI");
    
        setBounds(0, 0, width, height);
    
        echoTracker = t;
    
        echoUIConsumerBundle = b;
    
    
    
        entryText = new TextField (20);
    
    
    
        echoedText = new Label (" ");
    
        submit = new Button ("Echo");
    
        exit = new Button ("Exit");
    
        submit.addActionListener (this);
    
        exit.addActionListener (this);
    
    
    
        top = new Panel ();
    
        middle = new Panel ();
    
        bottom = new Panel ();
    
        top.setLayout(new FlowLayout(FlowLayout.LEFT));
    
        top.add(new Label("Echo: "));
    
        top.add(echoedText);
    
        middle.setLayout(new FlowLayout(FlowLayout.LEFT));
    
        middle.add(new Label("Input: "));
    
        middle.add(entryText);
    
        bottom.setLayout(new FlowLayout(FlowLayout.CENTER));
    
        bottom.add(submit);
    
        bottom.add(exit);
    
    
    
        content = new Panel ();
    
        content.setLayout(new GridLayout(3, 1));
    
        content.add(top);
    
        content.add(middle);
    
        content.add(bottom);
    
    
    
        add (content);
    
        addWindowListener(this);
    
        pack ();
    
        setVisible (true);
    
      }
    
    
    
      public void actionPerformed (ActionEvent e) {
    
        if ( e.getSource() == submit ) {
    
          top.remove (echoedText);
    
    
    
          // Obtain the echo service object
    
          EchoService echoObj = (EchoService) echoTracker.getService();
    
          // Use the echo service to echo a string
    
          echoedText = new Label ( echoObj.echo(entryText.getText()) );
    
    
    
          top.add (echoedText);
    
          entryText.setText("");
    
          setVisible (true);
    
        } else if ( e.getSource() == exit ) {
    
          // see note
    
    
    
          // echoUIConsumerBundle.stop();
    
          dispose ();
    
        }
    
      }
    
    
    
      public void windowClosing(WindowEvent e) {}
    
      public void windowOpened(WindowEvent e) {}
    
      public void windowClosed(WindowEvent e) {}
    
      public void windowIconified(WindowEvent e) {}
    
      public void windowDeiconified(WindowEvent e) {}
    
      public void windowActivated(WindowEvent e) {}
    
      public void windowDeactivated(WindowEvent e) {}
    
    
    
    }
    
    
    Listing 4.7. The manifest file for the echo consumer bundle
    Manifest-Version: 1.0
    
    Bundle-Name: Echo UI consumer
    
    Bundle-Description: Consume the echo service
    
    Bundle-Activator: com.enterprisej2me.osgi.echouiconsumer.EchoUIConsumer
    
    Import-Package: com.enterprisej2me.osgi.echoservice,
    
     org.osgi.framework; specification-version=1.1,
    
     org.osgi.util.tracker; specification-version=1.1
    
    Import-Service: com.enterprisej2me.osgi.echoservice.EchoSerivce
    

 

4.4 Smart Client with HTTP Front End

The managed GUI bundle uses only a fraction of the power provided by the OSGi container. Through its services, the OSGi container supports external applications and devices over the network. To use a separate program to render the UI, we can more effectively separate the business and presentation layers.

The "pizza order" example application distributed with the SMF illustrates the use of HTTP services in the OSGi framework. After starting the bundle from the SMF console, we can launch the device's built-in HTML browser (Internet Explorer for PocketPC or the Opera browser for Embedded Linux) and point the URL to http://localhost/pizza. An HTML page of a dummy pizza store appears. We can fill out the pizza order form, submit the form, and get response from a servlet running inside the bundle.

This design allows us to build a simple UI very quickly, using HTML without messaging with complex event handlers in AWT code. It also allows the vast majority of serverside Java developers to transfer their skills and make use of their familiar patterns, such as the Model-Viewer-Controller pattern. The overall architecture of the smart client with HTTP front end is illustrated in Figure 4.7.


Figure 4.7. The smart client with HTTP front end.

4.4.1 The Pizza Order Bundle

The PizzaBundle class (Listing 4.8) implements the BundleActivator interface. This bundle does not register or provide any new services. It customizes the container HTTP service to serve pizza order HTML content at a specified URL. It also uses the container logging services to record activities inside the bundle. If no logging service has been registered for this bundle, it logs to the standard output.

  1. The bundle start() method instantiates trackers for LogService and HttpService. The bundle then invokes their open() methods.

  2. When the ServiceTracker for the HttpService is opened, it obtains all registered HttpService references from the container, adds them into the tracker, and invokes the corresponding ServiceTrackerCustomizer's addingService() method for each added reference.

  3. The addingService() method obtains the HTTP service object from the container and customizes it with the pizza order servlet and other resources.

  4. When the bundle stops, its stop() method calls the close() methods of the two ServiceTrackers. The HttpService tracker in turn calls the removedService() method, which unplugs the servlet from the HTTP service.

Listing 4.8. The PizzaBundle class
public class PizzaBundle implements

  BundleActivator, ServiceTrackerCustomizer {



  /** BundleContext for this bundle */

  protected BundleContext context;



  /** Log Service wrapper object */

  protected LogTracker log;



  /** Http Service tracker object */

  protected ServiceTracker tracker;



  /** HttpContext for HTTP registrations */

  protected HttpContext httpContext;



  // ... ...



  public PizzaBundle() { }



  // Methods in BundleActivator

  public void start(BundleContext context) throws Exception {

    this.context = context;

    httpContext = new HttpContext() { ... ... };

    log = new LogTracker(context, System.err);

    tracker = new ServiceTracker(context,

        HttpService.class.getName(), this);

    tracker.open();

  }



  public void stop(BundleContext context) throws Exception {

    tracker.close();

    log.close();



  }



  // Methods for ServiceTrackerCustomizer

  public Object addingService(ServiceReference reference) {

    HttpService http = (HttpService)context.getService(reference);

    if (http != null) {

      try {

        http.registerServlet(servletURI,

            new Pizza(), null, httpContext);

        http.registerResources(servletURI+imagesURI,

                           imagesURI, httpContext);

        log.log(log.LOG_INFO, "Pizza Servlet registered");

      } catch (Exception e) {

        // handle the exception

      }

    }

    return http;

  }



  public void modifiedService(ServiceReference

                    reference, Object service) {

  }



  public void removedService(ServiceReference

                    reference, Object service) {

    HttpService http = (HttpService) service;



    http.unregister(servletURI);

    http.unregister(servletURI+imagesURI);



    context.ungetService(reference);

    log.log(log.LOG_INFO, "Pizza Servlet unregistered");

  }

}

4.4.2 The Pizza Order Servlet

In the addingService() method, we use a servlet Pizza (Listing 4.9) to provide the custom HTTP service (the application logic). This is just a standard Java servlet that reads from HttpRequest and writes HTML data to HttpResponse objects. Those HTTP context objects are provided by the container.

Listing 4.9. The Pizza servlet
import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.util.*;



public class Pizza extends HttpServlet {

  protected void doGet(HttpServletRequest req,

                       HttpServletResponse res)

            throws ServletException, IOException {

    // Generate some output

    res.setContentType("text/html;" + "charset=iso-8859-1");

    PrintWriter out = res.getWriter();

    out.print(" ... ... ");



    // Get query parameters

    String queryString = req.getQueryString();



    // any pizza order logic

    // ... ...



    out.println("
Wednesday, 16 September 2009 11:14

Is Mobile Java Ready for Enterprise?

2.1 Why Java?

Before Java was called Java, it was Oak–a programming language designed for TV set-top boxes and other devices (see "Resources"). Considering its deep roots in devices, there is no surprise that many philosophies and designs behind the

Java technology are perfectly suited for mobile applications. The advantages of the Java technology are as follows.
 
·         Cross platform: This is very important in the diversified mobile device market. In a heterogeneous enterprise environment, the ability to develop and maintain a single client for all devices results in huge savings.
·         Robust: Since Java applications are completely managed, the bytecode is verified before execution, and memory leaks are reclaimed by garbage collectors. Even if a Java application does crash, it is contained within the virtual machine. It will not affect other sensitive applications or data on the device.
·         Secure: The Java runtime provides advanced security features through a domain-based security manager and standard security APIs.
·         Object oriented: The Java language is a well-designed, object-oriented language with vast library support. There is a vast pool of existing Java developers.
·         Wide adoption at the back end: It is relatively easy to make Java clients work with Java application servers and messaging servers. Due to the wide adoption of Java 2 Enterprise Edition (J2EE) on the server side, mobile Java is the leading candidate for enterprise frontend applications.

However, technical merits are not the only factors that determine the success of a technology. The business values are just as important. In particular, the vendor's ability to address the concerns of the developer and user community is key to the technology's adoption. Now, let's have a look at how the Java community influences the evolution of the technology.

2.2 The Java Community Process

The concept of open interfaces is core to the Java technology. It works as follows: For a given computing task, a set of standard APIs is defined by a standards committee. Individual vendors then provide competing libraries that implement those APIs. The application code using the API is completely decoupled from the specific implementation provider. That approach minimizes the developer's learning cost and improves code portability. Yet, it also protects the freedom of choosing vendors. The Java Community Process (JCP) is an effort to develop standard Java API specifications.

JCP Executive Committees) consist of industry leading companies. Anyone in the general public can submit a new Java Specification Request (JSR) for a new API. The appropriate EC decides whether to accept this new JSR. Once approved, the JSR lead can recruit more companies or individuals to develop the API specification together. Every specification goes through multiple stages of community and public reviews before it becomes an official Java standard.

The JCP ensures that all interested parties can express their concerns. As a result, the final APIs are supported by industry consensus. All standard mobile Java APIs are developed democratically through the JCP.

2.3 Java Everywhere

The conference theme of the 8th JavaOne conference in 2003 is "Java Everywhere." Beyond the buzz and the hype, the Java Everywhere vision signals a departure from Java's traditional focus on "Write Once Run Anywhere" (WORA). Instead, it promotes a single consistent architecture, compatible APIs and easy skill migration for developers. Java Everywhere will have profound impacts on Java-based mobility solutions.

2.3.1 The Single Architecture Solution

Although cross-platform compatibility is a key concept behind the Java philosophy, Java designers also realize that portability has its limits. Portability is only meaningful among similar OSs and hardware platforms. Today, the Java platform is partitioned into three editions, all of which have significant roles in mobility. The overall architecture of the Java platform is shown in Figure 2.1.
 
·         The Java 2 Standard Edition (J2SE): The J2SE is the basis of the Java platform. It defines the JVMs and libraries that run on standard PCs and workstations. In the mobility world, J2SE is the ideal choice for wireless laptop-based applications.
·         The Java 2 Enterprise Edition (J2EE): The J2EE is J2SE plus a large number of serverside APIs, containers and tools. It aims to implement complex application servers. J2EE application servers can drive browser-based (e.g., WML and xHTML) mobile applications and become service end-points for smart mobile clients.
·         The Java 2 Micro Edition (J2ME): J2ME is a Java platform that is designed for small devices. It contains specially designed, lightweight virtual machines; a bare minimum of core-class libraries; and lightweight substitutes for standard Java libraries. J2ME is the ideal mobile client platform for wireless PDAs and enhanced mobile phones.
 
Figure 2.1. The Java 2 platform architecture.
 
Applications for each of the Java editions can follow similar architectures. That allows companies to leverage existing developer talent, cut cost and build more maintainable products.

2.3.2 Opportunities for J2EE Developers

The single architecture approach benefits existing Java developers the most. As the serverside technologies reach maturity, consolidation and outsourcing become the trend. According to a U.S. Department of Commerce study, in year 2002, the IT employment in the United States fell for the first time in history. Many J2EE development projects have been moved to cheaper offshore locations.

Enterprise developers have to keep up with the need of their customers and stay on top of the innovation value chain to stay employed. As more companies look for IT solutions to improve mobile workers' productivity, enterprise mobility is a natural next step for developers. Fortunately, with the Java Everywhere architecture, it is easy for experienced J2EE developers to migrate their skills to the mobile arena. For example,
 
·         Common J2EE design patterns can be applied to end-to-end applications with little change.
·         Mobile enterprise messaging, data access, integration and security solutions are parallel to their serverside counterparts.
·         Lightweight editions of popular J2EE frameworks are already available on J2ME.

2.4 Java 2 Micro Edition Explained

Although enterprise mobility solutions can be implemented over J2SE laptops and J2ME devices, the J2ME solutions have significantly lower costs. A Gartner 2002 study concluded that the TCO (Total Cost of Ownership) of smart phones is only one-tenth of wireless laptops and one-fourth of wireless PDAs. The sheer number of J2ME devices makes J2ME the most important enterprise mobility platform. We will focus on J2ME in this book.

2.4.1 J2ME Architecture

Merely distinguishing J2ME from J2SE does not solve all our compatibility problems. There is a huge variety of mobile devices, designed for different purposes and with different features. The "lowest common denominator" does not work. For example, applications on an automobile mounted system are much more complex than those on a cell phone. Even among similar devices, such as high-end and low-end cell phones, portability can cause underutilization of resources on one device and strain on another.

To balance portability with performance and feasibility in the real world, J2ME contains several components known as configurations, profiles, and optional packages (Figure 2.2). Each valid combination of a configuration and a profile targets a specific kind of device. The configurations provide the most basic and generic language functionalities. The profiles sit on top of configurations and support more advanced APIs, such as a graphical user interface (GUI), persistent storage, security, and network connectivity. The optional packages can be bundled with standard profiles to support specific application needs.


Figure 2.2. J2ME components.
2.4.2 J2ME Components
The two most important J2ME configurations are as follows.
 
·         The Connected Limited Device Configuration (CLDC) is for the smallest wireless devices with 160 KB or more memory and slow 16/32-bit processors. The CLDC has limited math, string, and I/O functionalities and lacks features such as the JNI (Java Native Interface) and custom class loaders. Only a small subset of J2SE core libraries is supported by the CLDC virtual machines (known as KVMs or Kilobyte Virtual Machines). The most recent version of the CLDC is version 1.1. It was developed by the JSR 139 and released in March 2003.
 
·         The connected Device Configuration (CDC) is for more capable wireless devices with at least 2 MB of memory and 32-bit processors. Unlike the CLDC, the CDC supports a fully featured Java 2 VM and therefore can take advantage of most J2SE libraries. The CDC v1.0 was developed by the JSR 36 and it became available in March 2001. The new CDC v1.1 is currently being developed by the JSR 218 and is expected before the end of year 2003.
 
·         The most important and successful J2ME profile is the Mobile Information Device Profile (MIDP), based on the CLDC. The MIDP targets the smallest devices, such as cell phones. It is already deployed on millions of handsets. However, the MIDP v1.0 lacks some important features, such as security and advanced UI controls. As a result, device vendors often supply their own MIDP extensions to provide advanced custom features. Vendor-specific extensions undermine the protability of J2ME applications. Many problems with the MIDP v1.0 have been fixed in the MIDP v2.0, which came out of JCP in August 2002. Table 2.2 lists MIDP-compatible optional packages. Most of those optional packages run on CDC profiles as well. The profiles and optional packages based on the CDC are listed in Tables 2.3 and 2.4.
 
Table 2.2. MIDP Optional Packages
 


CDC Package Table
2.5 Competing Technologies
Finally, it is worth noting that J2ME is not the only technology that enables mobile commerce. Leading competing technologies are as follows.
  • WAP/WML: WAP/WML is a platform for thin client applications (i.e. microbrowser-based applications). The thin client paradigm is completely different from the smart client paradigm enabled by the J2ME (Chapters 3 and 4). J2ME smart clients are likely to replace WAP/XML applications in the future.
  • BREW: Qualcomm's Binary Runtime Environment for Wireless (BREW) is a technology that supports rich client development and provisioning. BREW applications are written in C/C++ and run natively on BREW-enabled phones. However, only a limited number of phones support BREW. Although BREW native applications can be heavily optimized, they are not executed in managed environments and therefore are prone to programming errors. J2ME applications could run on BREW devices through a J2ME runtime for BREW (e.g., the BREW MIDP runtimes from IBM and Insignia).
  • NET Compact Framework (.NET CF): Microsoft's .NET CF is the closest competition to the J2ME. Like J2ME, it targets smart-managed mobile clients development. It has a strong focus on enterprise applications. However, the .NET CF runs only on high-end Windows CE and PocketPC devices. For a detailed analysis of the .NET CF and J2ME, please refer to the articles in the "Resources" section.
 
Wednesday, 16 September 2009 11:05

Mobile Commerce

Mobile Commerce: Visions, Realities, and Opportunities
1.1 Mobile Commerce Value Propositions

A lesson we learned from the bust of dot-com bubbles is that a cool technology itself does not automatically translate into business success. Successful business models leverage new technology to create values and profits from ultimate customer satisfaction. So, what new values or cost savings can mobile technology create?

Unlike the PC-centric electronic commerce, mobile commerce is focused on personal experiences. A person carries a pervasive mobile device and gets information anytime, anywhere, from anyone. For the first time in history, a person's information access can be disassociated from her environment. For example, a traveler does not need to be in her office at a specific time to get ticket information. That unprecedented freedom of information could fundamentally affect all business categories. The next several sections provide a brief overview with examples.

1.1.1 Business to Consumer (B2C)

From a consumer's perspective, mobile commerce provides extreme convenience, speed, and personalization to access information services. Let's use a hypothetical stock trader example to illustrate how mobile commerce can change the way we consume information. A mobile stock trader can access markets around the world anytime, anywhere. He can take advantage of the 24/7 continuous global markets and never miss any profit opportunities. Mobile services store the trader's portfolio and target price information. Relevant news and price alerts are pushed to the trader at real time regardless of his location. Using personalized smart mobile agents, the trader can focus on stock researches and use his time more efficiently. As a result, he now spends more time with his family and enjoys life. In this case, mobile commerce creates value by saving time, improving efficiency, reducing opportunity loss, and improving the quality of life for its consumer. In fact, the financial services industry has been a major adopter of mobile commerce technologies. Almost all major banks, credit card companies, and trading firms have offered mobile interfaces to their informational and even transactional services. According to IDC, mobile banking has grown tenfold in western Europe from 1999 to 2001 and is expected to reach $334 million during 2003 in western Europe alone.

In addition to improving existing services, the freedom of information enables new breeds of consumer information services. For example, with smart in-home monitoring devices, patients may now stay with their families instead of in hospital observation rooms. Phase Forward is one of many firms in this emerging market. Another example is customized marketing application. Marketing firms can take advantage of the human desire for instant gratification and design more effective product promotion schemes based on consumers' real-time experiences. Avantgo is a leading company that provides mobile marketing solutions. Those new applications improve our lives directly and represent huge opportunities of profits for mobile commerce firms.

1.1.2 Business to Business (B2B)

From a business's perspective, mobile commerce could create value by improving efficiency. A good example is mobile supply chain management. Today's business supply chains consist of multiple suppliers and sellers at multiple levels from multiple countries. A poorly managed supply chain can create redundant inventories or insufficient supplies (and hence market share losses). In highly competitive business sectors, such as the PC business, efficient supply chain management can determine the survival of a firm. PC business leader Dell excels in supply chain management. As a result, Dell can make profits even when competitors are taking huge losses.

In a mobile supply chain management system, warehouse workers and truck loaders use mobile devices to track inventory and shipment data. The data is uploaded into enterprise backend systems at real time. Managers make timely decisions based on the most up-to-date supply chain information. Real-time information also allows the management team to quickly identify and correct bottlenecks in supply chains. Purchase authorization, billing, and payment are also completed by field agents at real time, streamlining the whole process and reducing turnaround time for both goods and information. Supply chain management innovation is often custom done inhouse. A commercial product that enables real-time supply chain management using mobile technology is Savi Technology's Smart Chain solution.

Better managed and more transparent supply chains can ultimately benefit a business's bottom line by increasing customer satisfaction at the end of the supply chain. Mobile technology enables vendors to ship goods faster and to better predict the availability or arrival dates. UPS and FedEx's package-tracking services have became hugely popular. In a world of mobile commerce, real-time order tracking would be the norm of retail business. Highly visible supply chains allow customers to adjust their schedules to meet the product delivery time.

1.1.3 Business to Employee (B2E)

Mobile commerce allows firms to reduce operational costs for their mobile employees, including sales force, field agents, and factory floor workers.

Pharmaceutical companies rely on physicians to sell new medicines. Pharmaceutical sales representatives and doctors often meet at lunchtime outside of the doctor's office—it is easier to make personal connections during an informal lunch. However, it is difficult to hook up a networked computer on a dining table. If the doctor wants to make a purchase, the sales representative has to come back later with price and inventory quotes— lengthy and costly process. Companies like AvantGo and XcelleNet develop mobile sales management and automation suites for big pharmaceutical companies. With the help of mobile commerce, the salesperson can quote prices and close the deal right on the dining table, and the doctor can then track the shipment. Of course, mobile sales automation can go far beyond the pharmaceutical industry. Combined with leading CRM (customer relationship management) software, companies of all sizes, such as IBM, NexTel, SAP, and Numeric Computer Systems, offer a range of mobile sales solutions.

Like sales representatives, field agents also need to access their company's enterprise information system on the run. Endurable equipment vendors from Xerox to Otis equip their field service technicians with mobile devices. They can check technical information as well as conduct asset management on customer sites.

Even for factory workers who do not work outside the company, mobile information access can still be very useful. Boeing has huge plants to build commercial jet airliners. It is impossible to wire the plant with Ethernet cables, since there are so many moving parts. Technicians working inside a plane often need to make little trips to a nearby computer terminal to check digital blueprints. That is not only inefficient but also error prone—humans can recall wrong details even after a short walk. Mobile information devices make it possible for technicians to check blueprints inside the plane right at the problem spot, improving efficiency, reducing error, and hence saving operational costs.

1.1.4 Public Services and Safety

Government sectors are among the first to adopt sophisticated mobile applications. Police officers need to check driver's license, license plate, and vehicle identification numbers whenever they stop a driver. Emergency medical workers at an accident scene need to check drug conflicts and other life-critical information, and emergency response systems have to be coordinated wirelessly. The military requires real-time updates from soldiers and commanders in the battlefield.

The events of September 11, 2001, in the United States revealed successes and failures in government mobile information systems. When all the fixed-voice and data lines were knocked off by the terrorist attacks, the cell network was still functional. Cell phone calls and wireless email messages became the last words we heard from many people in the World Trade Center and in the hijacked planes. However, the emergency response wireless networks proved fatally flawed.

Many firefighters in the Towers never received the evacuation orders. The information gathered by police helicopter pilots about the imminent danger of building collapse never reached the fire department information system. Reliable mobile information systems are crucial to public safety in the 21st century.

1.2 Mobile Technology Adoption

To take advantage of mobile commerce, businesses and consumers must adopt state-of-the-art mobile technology. The diffusion of innovations usually goes through five stages: visionary, missionary, ordinary, commodity, and maturity.
 
·         Visionary: The technology has just come out. Few people see its business value. The technology proponents in the visionary stage base their arguments on advocacy rather than on solid value propositions. A famous advocacy slogan is "You need this. You just do not know it yet." In this stage, few companies except infrastructure builders can make money.
·         Missionary: Business practitioners start to see the value of the innovation. Pioneer companies or employees become early adopters of the new technology and start to profit from it.
·         Ordinary: The value of the technology is well accepted by the main stream business executives, and most companies have developed plans to implement solutions based on the new technology. In this stage, the developers and enablers of the new technology make the bulk of profits.
·         Commodity: In this stage, the adoption of the technology becomes common practice. The technology has started to generate profits industrywide. However, since implementations have been standardized, the barrier of market entry becomes substantially lower, which results in intense competition in the enablers sector.
·         Maturity: In a mature market, most commodity technology suppliers are consolidated to a few dominant players.

On a typical innovation diffusion curve, the transition period between missionary and ordinary stages are associated with explosive growth of adoptions and a limited number of technology firms who have the expertise to implement viable solutions. The unbalanced demand and supply creates golden opportunities for developers and technology firms to make money. We have seen this pattern repeated throughout history.

At the time of writing (Fall 2003), the value of mobile commerce has been well accepted by industry leaders and business executives. Leading companies in financial services, information services, transportation, and manufacturing sectors have already started to implement their mobile commerce strategies. Other companies will soon follow suit. All these signs indicate that mobile commerce is currently at late missionary stage and is moving toward the ordinary stage.

Although mobile commerce poses to bring tremendous opportunities, we have to be cautious and understand the risks. Historically, technology adoption was never a smooth or linear process. As we have seen in the recent rise and fall of dotcoms, the expectations of technology adoption are often exaggerated; the relationships between the new and old business models are often distorted. Those unrealistic expectations have resulted in severe consequences for those failed dotcom companies and their employees. Nevertheless, adoption of e-commerce as a whole is steadily moving on. I expect the mobile commerce adoption will experience similar up and downs. Many of the heavily hyped, first-generation mobile companies may not ultimately survive. The final winners might still be steady and effectively managed blue chip companies.

1.3 The Search for Killer Mobile Applications

The value of mobile commerce is ultimately realized through successful applications. A popular application can jump-start the technology adoption process and make a lot of profit for its inventor. The search for mobile "killer applications" has started from the beginning days of mobile commerce. In this section, we will discuss application ideas and trends.

1.3.1 Mobile Entertainment

The most mature consumer markets for mobile commerce are in the Asia-Pacific region and Northern/Western Europe. In those markets, mobile entertainment applications are hugely successful. A good example is Japan's DoCoMo. However, in the United States, mobile entertainment has yet to take off. U.S. consumers are less subject to crowded public transportation systems and have easy access to superior wired voice and data networks. The individualistic culture of the U.S. society makes community and messagingbased games less attractive to American consumers. Mobile entertainment might never be a killer application in the United States.

However, mobile entertainment represents only a small part of the consumer commerce. Mobile commerce's application is much broader than personal entertainment.

1.3.2 From Toys to Tools

Although mobile entertainment can grow huge businesses, it provides nonessential toys to consumers. Enterprise mobile applications that enable essential business tools are more likely to be killer applications for future mobile commerce. For example, mobile games allow travelers to kill time when waiting in long lines in airports. In contrast, a mobile ticketing and scheduling application allows travelers to go directly to gates and avoid the lines altogether.

According to a study released by the IDC, the number of mobile workers in the United States will reach 105 million by year 2006. That is almost twothirds of the total U.S. work force. Those mobile workers perform complex and essential tasks. Their workflows must be fully integrated into the IT infrastructure through enterprise mobility solutions.

In reality, enterprise mobile applications are quickly gaining momentum, especially in the United States. Although the public wireless network in the United States is not as advanced as many Asian and European countries, U.S. companies have seen the value of enterprise mobile applications and have invested a lot of money to build high-speed corporate wireless networks using technologies such as WiFi. The leading mobile commerce applications in the United States are not games but business applications.

1.3.3 The Enterprise Mobility Eco-system

The mobile commerce revolution goes much deeper than a single killer application. It is about the freedom of information access. For example, mobile workers can access email and synchronize with calendar applications; securely read and write company files and databases; get price and inventory quotes from live application servers; and respond to customer service requests anytime, anywhere. Mobile enterprise applications change the ways consumers, employees, and companies conduct their businesses.

Enterprise mobility will become a driving force behind the mobile commerce revolution and form an eco-system nobody can live without. This collection of killer mobile business tools and applications provides great opportunities for businesses and developers.

1.4 Mobile Commerce Landscape

The previous sections explained the rationales and values behind enterprise mobile applications. After a company develops a mobile strategy, the natural next step is to implement it. Given the complexity of a mobile commerce solution, it requires collaborations from many different application and infrastructure service providers. Mobile commerce provider firms can generate revenues from hardware, software, or services sales.

1.4.1 Mobile Device Manufacturers

There is a wide range of mobile devices, including many kinds of cell phones, PDAs, consoles, and auto-mounted devices. Since mobile devices are to become pervasive personal belongings, they pose some unique design and technical challenges to manufacture. Successful mobile devices should have the following features.
·         Small size
·         Rich multimedia presentation capabilities
·         Fast response time
·         Large memory for data and applications
·         Long battery life
·         Fashionable and personalizable

Billions of dollars have been invested in mobile hardware research by leading companies such as Intel, Nokia, Motorola, and Qualcomm; they have produced many competing chipsets and handset designs.

1.4.2 Mobile Internet Service Providers

Mobile commerce requires mobile devices to be connected to data networks. Mobile Internet Service Providers (MISPs) are often wireless network carriers such as SprintPCS and AT&T. Those carriers build radio towers across the country and buy expensive radio spectrum licenses. They provide national cell phone voice and data services and often bundle Internet services in wireless access packages. Companies can also partner with wireless carriers to provide mobile Internet access to their customers under their own brand name. An example of an independent MISP is Palm.net.

If your mobile application is internal to your company, you may not need a national MISP. You can set up your own local corporate wireless network. Provision stations around your building or campus connect your wireless network to your internal network and then connect to the general Internet through your company firewalls. This way, you act as your own MISP.

1.4.3 Mobile Software Platform Providers

Given the diversity of mobile hardware, there are many mobile device operation systems. Examples include PalmOS, Symbian OS, Windows CE, and Embedded Linux. The OS SDKs (Software Developer Kits) often lack advanced programming language support and important libraries for business functions.

So, on top of operating systems, there are also application software platforms. Those platforms run on a variety of mobile devices and provide advanced sets of development tools and features. Examples of such platforms include WAP microbrowsers, Java, and Microsoft .NET Compact Framework. Java mobile application platform is one of the major focuses of this book.

1.4.4 Mobile Application Service Providers

Since mobile devices have very limited processing power and poor network connections, they often rely on backend servers to conduct sophisticated tasks. Mobile Application Service Providers (MASP) provide middleware and backend services. Examples of MASPs are 247 Solutions, JP Mobile, AGEA, Avantgo, and many others.

Companies looking for customized inhouse enterprise solutions can become their own MASPs. This book is targeted to MASP developers in both outsource and inhouse firms. We discuss mobile commerce-specific backend and middleware solutions in detail in the rest of this book.
 
Wednesday, 16 September 2009 09:05

J2EE DesingPatterns - J2EE Antipatterns

J2EE Antipatterns
In this chapter, we present a few of the most common antipatterns in the J2EE world. The list is by no means complete. Just like the active community collecting design patterns, there is an equally active community cataloguing antipatterns and their solutions. For a general introduction, the text by William J. Brown, et al, AntiPatterns:Refactoring Software, Architectures, and Projects in Crisis (Wiley & Sons), is the book on antipatterns; it has a decidedly process-based focus. The current seminal work on Java enterprise antipatterns, which we've relied on heavily, is Bruce A. Tate's excellent resource, BitterJava (Manning).
We will look at the following antipatterns:
Excessive Layering and Leak Collection
Cover repeated architectural errors that affect performance and extensibility.
Magic Servlet, Monolithic JSP, Compound JSP, and Overstuffed Session
Cover breakdowns in Model-View-Controller separation.
Everything Is an EJB, Round-Tripping, and Stateful When Stateless Will Do
Help determine how and how not to use EJBs.
Some of these antipatterns are closely related to the regular patterns we've discussed elsewhere in this book. Since antipatterns are recurring mistakes, and design patterns are recurring solutions, it make sense that most antipatterns have a corresponding pattern or two.
12.1 Causes of Antipatterns

Every program, big or small, contains shortcuts, mistakes, and code that could have been thought out a little better. What distinguishes an antipattern from these errors is that, like a design pattern, it is repeated. When we explore the causes of antipatterns, we don't need to look at why they exist—we need to look at why they persist and propagate. The main reasons that antipatterns spread are:
 
·         Inexperience
·         Unreadable code
·         Cut-and-paste development

Inexperienced developers are the major cause of antipatterns. Obviously, newer developers are less likely to have come across these common mistakes previously, and are less like to recognize when they are making them. The danger of antipatterns is subtler when experienced developers use new technology. This danger is especially pronounced in J2EE technology, which evolves quickly, and in which many of the standard tutorials and code generation tools are full of antipatterns. To combat the problem, most organizations use training as well as code reviews with senior developers. But the best defense against antipatterns is to know your enemy: understanding and recognizing antipatterns is the key to avoiding them.

Unreadable code is another fertile breeding ground for antipatterns. Often, the existence of a well-known antipattern will be hidden because readers have to spend all their mental cycles trying to figure out what the code does, not why it does it. Developers sometimes favor conciseness and optimization over readability without taking into account the maintenance costs. Every shortcut that saves a few keystrokes or a few bytes of memory should be balanced against the costs in terms of developer hours and lost business when a hard-to-find bug is discovered. Unreadable code is best fought with consistency: publishing and enforcing code guidelines, relentless commenting, and aggressive review.

Cut-and-paste development refers to code that is taken from one place and pasted directly into another. Teams sometimes assume that cut-and-paste is a form of reuse: because the code has been used before, it is often considered more robust. Unfortunately, cut-and-paste actually makes code less reliable, since the pasted portion is used out of context. Even worse, bug changes don't propagate: changes to cut-and-paste code must be propagated manually. Cut-and-paste should only be necessary when entire objects cannot be reused. If you find yourself copying a particular piece of code, consider abstracting it and making it available to many classes through inheritance or as part of a separate utility class.

12.2 Architectural Antipatterns

The first class of antipatterns we will look at is architectural in nature. These antipatterns are not J2EE-specific: they affect many Java applications and the Java APIs themselves. They are included in a book on J2EE because they affect highly scalable, long-running applications—meaning they are of particular relevance to J2EE developers.

12.2.1 Excessive Layering

If you've read the preceding chapters, you've probably noticed that design patterns tend to suggest adding layers. Façades, caches, controllers, and commands all add flexibility and even improve performance, but also require more layers.
Figure 12-1 shows a rough sketch of the "typical" J2EE application. Even in this simplified view, each request requires processing by at least seven different layers. And this picture doesn't even show the details of each individual layer, which may themselves contain multiple objects.
 
Figure 12-1. A standard J2EE application
 
Unfortunately, in a high-level, object-oriented environment like J2EE, the layers we add are not the only layers that exist. We've already talked about containers—the servlet and EJB containers are the main ones—and how they provide advanced services, generally through layers of their own. The underlying containers are themselves built on top of Java's APIs; the APIs are still a few layers away from the JVM, which interact through layered system libraries with the operating system—which finally talks to the actual hardware. If you look at stack traces from a running JVM, it is not surprising to see a Java application with a call stack well over 100 methods deep!

It's easy to see how a CPU that can execute billions of instructions a second can get bogged down running the J2EE environment. It's also easy to think that with all these layers, adding a few of our own can't possibly make any difference.
That's not the case, however. Think of the structure as a pyramid: every call to a container method requires two or four calls to the underlying Java API, which requires eight Java instructions, and so forth and so on. The layers we add are far more expensive than many of the preexisting layers.

An example of the Excessive Layering antipattern is a common scenario that we call the "Persistence Layer of Doom." While abstracting database access away from business logic has so many benefits that we hesitate to say anything negative about the process, hiding SQL from other components has one serious problem: expensive activities (such as accessing a network or filesystem for a database query) start to look like cheap activities (such as reading a field from a JavaBean). Developers working on the presentation tier will inevitably call the expensive functions frequently, and end up assuming the entire business tier is horribly slow. We'll talk more about this problem later in this chapter when we discuss the Round-Tripping antipattern.
12.2.1.1 Reducing layers

Because it's easy to add layers to a J2EE application, it's important to understand which ones are necessary and which are excessive. Unfortunately, there's no generic answer, since the correct number of layers depends on the type and expected use of an application.

When deciding whether to add layers, we have to balance the costs with the benefits they provide. The cost of layers can be expressed in terms of design time, code complexity, and speed. The benefits are twofold. Layers that provide abstract interfaces to more specific code allow cleaner, more extensible code. Layers such as caches, which optimize data access, can often benefit an application's scalability.

While we can't provide a generic solution to layering problems, we can offer a few hints as to what level of layering is appropriate for generic types of J2EE applications:

All-in-one application
For small applications where the entire model, view, and controller always live on the same server (like a single-function intranet application), reduce layers as much as possible. Generally, this will mean condensing the business tier, often using DAO and business delegate objects that interact directly with an underlying database through JDBC. In the presentation tier, you should stick to a simple servlet/JSP model.
Front end
Often, medium-size applications provide a simple web or similar frontend to a shared data model, usually a legacy application. Examples include airline ticketing systems and online inventory tools. In these applications, the presentation tier is generally the focus of development. The presentation tier should scale across multiple servers, and should be made efficient and extensible with liberal use of layering. Most functions of the business tier will probably be supported by the underlying legacy application and should not be duplicated in a large, deeply layered business tier.

Internet scale application
The last type of application is the ultimate in J2EE: a large application spread over numerous servers meant to handle thousands of users and millions of requests per day. In these large environments, communication overhead between the many servers, as well as the cost of maintaining a large code base, dwarf the cost of layering. Using all the standard layers and multiple layers of caching in both the presentation and business tier can help optimize network transactions, while layers of abstraction keep the code manageable and extensible.

Communication and documentation are our best weapons. When layers are added for performance, document which calls are expensive, and, where possible, provide alternative methods that batch requests together or provide timeouts. When layers are added for abstraction, document what the layer abstracts and why, especially when using vendor-specific methods (see the "Vendor Lock-In" sidebar). These steps assure that our layers enhance extensibility or improve performance instead of becoming an expensive black hole.
Vendor Lock-In

Many J2EE vendors offer enhancements to the core J2EE functionality, such as optimized database access methods or APIs for fine-grained control of clustering capabilities. Using these functions, however, ties your application to that vendor's implementation, whether it's a database, MOM, or application server. The more your application depends on a particular vendor's APIs, the harder it is for you to change vendors, effectively locking you into the vendor you started with.

J2EE purists will tell you why vendor lock-in is a bad thing. If your vendor decides to raise their prices, you are generally stuck paying what they ask or rebuilding your application. If you sell software to a customer, they too must buy your vendor's product, regardless of their own preference. And if the vendor goes out of business, you could be stuck with unsupported technology.

From a practical standpoint, however, vendor's enhancements are often just that: enhancements. Using vendor-specific APIs can often make your application easier to build, more efficient, and more robust. So is there a happy middle ground? There is. While using vendor-specific APIs is not an antipattern, vendor lock-in is.

The most important step in avoiding lock-in is understanding which APIs are generic and which are vendor-specific. At the minimum, clearly document all vendor dependencies. Also, make your best effort to avoid letting the structure of the API influence overall design too much, particularly if you think you might have to eventually abandon the vendor.

A better solution is to hide the vendor complexities by defining an interface with an abstract definition of the vendor's methods and then implementing that interface for the particular vendor you have chosen. If you need to support a new vendor, you should be able to simply reimplement the interface using the new vendor's methods or generic ones if necessary.

12.2.2 Leak Collection

Automated memory management is one of Java's most important features. It is also somewhat of an Achilles's heel. While a developer is free to create objects at will, she does not control when or how the garbage collector reclaims them. In
some situations, objects that are no longer being used may be kept in memory for much longer than necessary. In a large application, using excess memory in this way is a serious scalability bottleneck.

Fortunately, by taking into account how the garbage collector actually works, we can recognize common mistakes that cause extra objects. The Java Virtual Machine uses the concept of reachability to determine when an object can be garbage-collected. Each time an object stores a reference to another object, with code like this.intValue = new Integer(7), the referent (the Integer) is said to be reachable from the object referring to it (this). We can manually break the reference, for example by assigning this.intValue = null.

To determine which objects can be garbage-collected, the JVM periodically builds a graph of all the objects in the application. It does this by recursively walking from a root node to all the reachable objects, marking each one. When the walk is done, all unmarked objects can be cleaned up. This two-phase process is called mark and sweep. If you think of references as strings that attach two objects together, the mark and sweep process is roughly analogous to picking up and shaking the main object in an application. Since every object that is in use will be attached somehow, they will all be lifted up together in a giant, messy ball. The objects ready for garbage collection will fall to the floor, where they can be swept away.
So how does this knowledge help us avoid memory leaks? A memory leak occurs when a string attaches an object that is no longer in use to an object that is still in use. This connection wouldn't be so bad, except that the misattached object could itself be connected to a whole hairball of objects that should otherwise be discarded. Usually, the culprit is a long-lived object with a reference to a shorter-lived object, a common case when using collections.

A collection is an object that does nothing more than organize references to other objects. The collection itself (a cache, for example) usually has a long lifespan, but the objects it refers to (the contents of the cache) do not. If items are not removed from the cache when they are no longer needed, a memory leak will result. This type of memory leak in a collection is an instance of the Leak Collection antipattern.

In Chapter 5, we saw several instances of caches, including the Caching Filter pattern. Unfortunately, a cache with no policy for expiring data constitutes a memory leak. Consider Example 12-1, a simplified version of our caching filter.
Example 12-1. A simplified CacheFilter
public class CacheFilter implements Filter {
 // a very simple cache
 private Map cache;
  
 public void doFilter(ServletRequest request,
   ServletResponse response, FilterChain chain)
 throws IOException, ServletException {
   ...
 
 if (!cache.containsKey(key)) {
    cache.put(key, data);
 }
   // fulfill the response from the cache      
   if (cache.containsKey(key)) {
          ...         
   }
 }
 
 public void init(FilterConfig filterConfig) {
   ...
   cache = new HashMap( );
 }
}
Nowhere in this code is there a remove( ) call to match the put( ) call that adds data to the cache. Without a cache expiration policy, the data in this cache will potentially use all the available memory, killing the application.
12.2.2.1 Reclaiming lost memory

The hardest part of dealing with Java memory leaks is discovering them in the first place. Since the JVM only collects garbage periodically, watching the size of the application in memory isn't very reliable. Obviously, if you're seeing frequent out-of-memory type errors, it's probably too late. Often, commercial profiling tools are your best bet to help keep an eye on the number of objects in use at any one time.

In our trivial example, it should be clear that adding data to the cache and never removing it is a potential memory leak. The obvious solution is to add a simple timer that cleans out the cache at some periodic interval. While this may be effective, it is not a guarantee: if too much data is cached in too short a time, we could still have memory problems. A better solution is to use a Java feature called a soft reference, which maintains a reference to an object but allows the cached data to be garbage-collected at the collector's discretion. Typically, the least-recently used objects are collected when the system is running out of memory. Simply changing the way we put data in the cache will accomplish this:
cache.put(key, new SoftReference(data));
There are a number of caveats, the most important being that when we retrieve data, we have to manually follow the soft reference (which might return null if the object has been garbage-collected):
if (cache.containsKey(key)) {
 SoftReference ref = (SoftReference) cache.get(key);
 Object result = ref.get( );
 
 if (result == null) {
    cache.remove(key);
 }
}
Of course, we could still run out of memory if we add too many keys to the cache. A more robust solution uses a reference queue and a thread to automatically remove entries as they are garbage-collected.

In general, the most effective way to fight memory leaks is to recognize where they are likely to be. Collections, as we have mentioned, are a frequent source of leaks. Many common features—such as attribute lists and listeners—use collections internally. When using these features, pay extra attention to when objects are added and removed from the collection. Often, it is good practice to code the removal at the same time as the addition. And, of course, make sure to document pairs of adds and removes so that other developers can easily figure out what you did.

12.3 Presentation Tier Antipatterns

The Model-View-Controller pattern, covered in Chapter 3, is the fundamental organizing principle for the presentation tier. The building blocks of the MVC pattern create an overall map of the application, making it easier to understand and extend. Preserving the separation of model, view, and controller is essential to maintaining these advantages.

It should be no surprise, then, that the presentation tier antipatterns have to do with the breakdown of model-view-controller separation. While these antipatterns are specific to the presentation tier, be aware that other antipatterns, such as Leak Collection and Excessive Layering, can affect the presentation tier as well.

12.3.1 The Magic Servlet

When servlets were first introduced, developers immediately saw the potential of combining the robust Java environment with an efficient mechanism for serving dynamic content. Server-side Java's killer app was JDBC, a powerful and high-level mechanism for communicating with databases. Over time, technologies like JNDI and JMS were added, allowing J2EE to talk easily and directly to a large number of enterprise information systems.

Because it was suddenly so easy to talk to databases, directory servers, and messaging systems, many developers were tricked into thinking that their applications would be simple, too. Who needed a complicated design when reading a row from a database was a one-line operation? Unfortunately, as applications grew in scope, complexity crept back in—but the design to handle it did not.

The typical symptom of complexity outpacing design is the Magic Servlet antipattern, in which a single servlet handles all aspects of a given request. On the surface, the magic servlet seems like a reasonable encapsulation. It captures all the logic needed to handle a request in a single, convenient class. But a magic servlet is also large, complex, difficult to maintain, and impossible to reuse. Typically, these servlets have a huge amount of code in a single doGet( ) or similar method.

Imagine you are given the task of putting an LDAP-based corporate address book on the web. Example 12-2 shows a typical magic servlet solution, using the servlet to read all the names and phone numbers in the LDAP directory.
Example 12-2. A magic servlet
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;
 
public class MagicServlet extends HttpServlet {
 private DirContext peopleContext;
  
  // setup LDAP access
 public void init(ServletConfig config)
 throws ServletException {
    super.init(config);
    
    Properties env = new Properties( );
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://localhost/o=jndiTest");
    env.put(Context.SECURITY_PRINCIPAL, "cn=Manager, o=jndiTest");
    env.put(Context.SECURITY_CREDENTIALS, "secret");
    
    try {
      DirContext initalContext = new InitialDirContext(env);
      peopleContext = (DirContext)initalContext.lookup("ou=people");
    } catch (NamingException ne) 2{
      ne.printStackTrace( );
      throw new UnavailableException("Error inializing LDAP", ne);
    }
 }
  
  // close LDAP 
  public void destroy( ) {
    try {
      peopleContext.close( );
    } catch(NamingException ne) {
      ne.printStackTrace( );
    }
 }
  
  // "magic" function is model, view and controller
 protected void doGet(HttpServletRequest request, 
             HttpServletResponse response)
 throws ServletException, IOException {
    // view logic
    response.setContentType("text/html");
    java.io.PrintWriter out = response.getWriter( );
    out.println("<html>");
    out.println("<head>");
    out.println("<title>
Servlet</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<table>");2
  
    // model logic
    try {
      NamingEnumeration people = peopleContext.list("");
      while(people.hasMore( )) {
        NameClassPair personName = (NameClassPair)people.next( );
        Attributes personAttrs = peopleContext.getAttributes(personName.getName( ));
        Attribute cn = personAttrs.get("cn");
        Attribute sn = personAttrs.get("sn");
        Attribute phone = personAttrs.get("telephoneNumber");
        
        out.println("<tr><
td>" + cn.get( ) + " " + 
              sn.get( ) + "</td>" +
              "<td>" + phone.get( ) + 
              "</td></
tr>");
      }
    } catch(Exception ex) {
      out.println("Error " + ex + " getting data!");
    }
  
    // back to view logic
    out.println("</table>");
    out.println("</body>");
    out.println("</html>");
 }
}
The doGet( ) method talks directly to the data source, LDAP. It also writes directly to the output. This example is relatively simple, since it doesn't handle input validation or any kind of error handling, for starters. Adding those functions would make the servlet even more complicated and specialized.

The problem with a magic servlet is not a lack of encapsulation, but rather improper encapsulation. The first rule of objects is that they should perform a single function well. The scratch test is whether you can describe what an object does in one phrase. For our magic servlet, the best we could say is "it reads phone numbers from a database and formats the results as HTML." Two phrases at least, and awkward ones at that.
12.3.1.1 Refactoring the magic servlet

To fix the Magic Servlet antipattern, we need to break our servlet into multiple objects, each with a specific task. Fortunately, we already have a pattern for solving just this type of problem: the Model-View-Controller.  The salient point is the separation of the interaction into (at least) three pieces.

The model is responsible for all interactions with persistent storage. We will create a simple JavaBean, Person, with accessor methods for fields firstName, lastName, and phoneNumber. Then we create a class, LdapPersonCommand, to read from LDAP and expose the values as a collection of class Person. The command implements a generic PersonCommand interface:
public interface PersonCommand {
 // initialize the command
 public void initialize(HttpSession session) throws NamingException; 
  
  // execute the query
 public void runCommand( );
  
  // get the result of the query as a List of class Person
 public List getPeople( );        
}
The view, a simple JSP page, uses an instance of class PersonCommand placed in request scope and the Java Standard Tag Libraries to generate a table of names and phone numbers. PersonView.jsp is shown in Example 12-3.
Example 12-3. PersonView.jsp
<%@page contentType="text/html"%>
<%@taglib uri="/jstl/core" prefix="c"%>
<html>
<head><
title>Addresses</title>
</head>
<body>
<jsp:useBean id="personCommand" scope="request"
       class="PersonCommand" />
<table>
 <c:forEach var="person" items="${personCommand.people}"
 <tr><
td><c:out value="${person.firstName}"/
></td>
    <td><c:out 
value="${person.lastName}"/></
td>
    <td><c:out 
value="${person.phoneNumber}"/></
td>
 </tr>
 </c:forEach>
</table>
</body>
</html>
As you can see, our JSP is entirely focused on view management. It contains only the minimal amount of JSTL code needed to handle looping and output.
All this abstraction leaves us with a very simple servlet, as shown in Example 12-4.
Example 12-4. PersonServlet.java
import javax.servlet.*;
import javax.servlet.http.*;
import java.IO.*
import javax.naming.*;
 
public class PersonServlet extends HttpServlet {
 protected void doGet(HttpServletRequest request,
             HttpServletResponse response)
 throws ServletException, IOException {
    try {
      PersonCommand personCommand = new LdapPersonCommand( );
      personCommand.initialize( );
      personCommand.runCommand( );
      
      request.setAttribute("personCommand", personCommand);
    } catch(NamingException ne) {
      throw new ServletException("Error executing " + "command", ne);
    }
    
    RequestDispatcher dispatch =
      getServletContext( ).getRequestDispatcher("/PersonView.jsp");
    dispatch.forward(request, response);
 }
}
This servlet is responsible for coordinating the response but not for performing the dirty work of reading LDAP or generating HTML. The major advantages of this solution are in extensibility. We could easily replace LdapPersonCommand with a class that reads from a database or legacy system. Writing a separate JSP to output to WAP or WML or a different language would also be easy and require no modification of the servlet. All of this should be familiar from previous chapters. The point is that the problem can be solved. You can resolve this antipattern a piece at a time. In this case, you might want to isolate the view first, and come back and separate the model and controller when you have time.

12.3.2 Monolithic/Compound JSPs

When JSPs were first introduced, some developers recognized their potential to replace the awkwardness of embedding HTML output in a servlet with the convenience of scripting. Many decided that since JSPs were parsed into servlets internally, they were just a new syntax for servlets. So they built servlets—including access to models, control logic, and output—as JSPs. Example 12-5 shows a worst-case JSP design.
Example 12-5. LoginPage.jsp
<%@page contentType="text/html"%>
<%@page import="antipatterns.LoginManager" %>
<html>
<% 
  LoginManager lm = new LoginManager( );
 ServletRequest req = pageContext.getRequest( );
 if (!lm.doLogin(req.getParameter("username"), 
          req.getParameter("password"))) {
%>
<head><
title>Login Page</title>
</head>
<body>
 <form action="/LoginPage.jsp" method="post">
 User name: <input type="text" name="username">
<br>
 Password: <input type="password" name="password"
><br>
 <input type="submit">
 </form>
</body>
<% } else { %>
<head><
title>First Page</title>
</head>
<body>
 Welcome to the page!
<% } %>
</body>
</html>
This example has the same problems as a magic servlet. It performs operations belonging to the model, view, and controller. The example above combines the functionality of JSP and servlets, but represents the worst of both worlds from the point of view of maintenance. The HTML is confusing, often duplicated, and spread throughout the file in an opaque way. The Java code is embedded awkwardly—again, hard to find and follow.

There are actually two common antipatterns in Example 12-5. The Monolithic JSP antipattern occurs when JSP pages do more than their fair share of the work, often acting as both view and controller. While this example doesn't interact directly with the model via JNDI or JDBC, it would be a logical extension, further destroying the separation of model and controller.

A closely related antipattern is the Compound JSP antipattern, in which multiple JSP files are crammed into one using Java conditionals. In the example above, our code tests the return value of a call to doLogin( ) and displays different pages based on the result.
12.3.2.1 Fixing monolithic and compound JSPs

JSP's advantage is its simplicity. As a tag-based language, it should be familiar to web designers who no longer need to understand the complexities of Java development in order to build web pages. It is also meant to integrate easily into existing tools, such as WYSIWYG HTML editors.

Monolithic and compound JSPs take away those advantages. It would take a pretty sophisticated editor to separate the two pages contained in Example 12-5. And web designers working with it would have to understand the embedded code, at least enough so as not to break it.

Since monolithic JSPs are really just another type of magic servlet, the solution is pretty much the same. The JSP should be refactored into three separate pieces, with a servlet acting as the controller, the JSP as the view and JavaBeans as the model. We won't go through the whole exercise of converting our monolithic JSP to MVC, since the result is so similar to the previous example and those in Chapter 3.

Compound JSPs present a slightly different problem, but again a similar solution. In Example 12-5 we effectively have two pages: the login page and the welcome page. We should separate these into two separate JSP pages (in this case, HTML pages would work as well). The selection logic should go in the controller servlet, which contains code like:
if (lm.doLogin(username, password)) {
        nextPage = "/welcome.jsp";
} else {
        nextPage = "/login.jsp";
}
 
RequestDispatcher dispatch = 
  getServletContext( ).getRequestDispatcher(nextPage);
dispatch.forward(request, response);
12.3.3 Overstuffed Session

The session, which tracks users as they navigate a web site, is a major feature of the J2EE presentation tier. Objects like the user's security permissions can be stored in the session when a user first logs in, and used several pages later to determine what the user can access. While maintaining session information is quite convenient, it is not without its pitfalls. Putting too much data or the wrong kind of data into the session leads to the Overstuffed Session antipattern.

The first danger of this antipattern is from data with a short lifespan. Since the session is implemented as a collection, we have to be on the lookout for a variant of the Leak Collection antipattern. Putting objects in the wrong scope, as we have done in Example 12-6, can lead to a pseudo memory leak.
Example 12-6. The LeakyServlet's doGet( ) method
protected void doGet(HttpServletRequest request,
           HttpServletResponse response)
throws ServletException, IOException {
 HttpSession session = request.getSession( );
    
  // create the bean
 AddressBean address = new AddressBean( );
 address.setFirst(request.getParameter("first"));
 address.setLast(request.getParameter("last"));
    
  // pass the bean to the view
 session.setAttribute("antipatterns.address", address);
  
  // instantiate the view
 RequestDispatcher dispatcher = 
    getServletContext( ).getRequestDispatcher("/View.jsp");
 dispatcher.forward(request, response);
}
As the snippet shows, we have placed a bean called AddressBean (which contains state data that is only used in this request) in session scope. This bean, however, will be kept around in the user's session for as long as the user is connected.

It's hard to see this kind of scenario as a real memory leak. The user's session expires when they log out, and the memory is reclaimed. But each object adds up, and if lots of users store lots of irrelevant beans in session scope, the total number of users that can connect at any one time will be reduced.[1]

If you use Struts or a similar web application framework, pay particular attention to this issue. The default Form Bean scoping in Struts is to the session, rather than to the request, so it's easy to end up with a large number of extra objects in memory for each active user.

The second danger is from data with a long lifespan. It is very common to store complex state data—such as shopping carts or user preferences—in the user's session, assuming the session will stay alive for as long as the user is using the site. Unfortunately, this is not always the case: if the user takes a break to respond to email or read another web page, or if the web server crashes, the user may unexpectedly lose all the data. This can be a real problem: just ask anyone who has painstakingly selected all the components for his new computer system only to have them disappear while he is looking for his credit card.
12.3.3.1 Unstuffing the session

Fortunately, as far as antipatterns go, fixing an overstuffed session isn't too difficult. For short-lived data, make sure you default to request scope for all your objects, and only use session scope when it is explicitly required. Also, remember that it is possible to remove items from the session if you know they will no longer be used.

Long-lived data should generally be migrated to the business tier. This precaution has a number of advantages, including persistence between logins and across server restarts and transactions. Recently, there has been a trend toward applications with a database solely used by the presentation tier. While it may be overkill, this method provides a convenient (and usually high-speed) place to store user and session information without affecting the design of the business tier.

12.4 EJB Antipatterns

Our final set of antipatterns deal with Enterprise JavaBeans. EJBs are a powerful technology, but can also be complicated and heavyweight. Two of our antipatterns deal with the complexity of EJBs: the Everything Is an EJB antipattern describes when EJBs are appropriate to use at all, while the Stateful When Stateless Will Do antipattern describes when stateful session EJBs should be used. The Round-Tripping antipattern covers common performance problems in client-server applications, and often turns up when you're using remote EJBs.

12.4.1 Everything Is an EJB

There is a common antipattern called the golden hammer. A golden hammer starts life as a good solution to a recurring problem—the kind of solution that design patterns are made from. Eventually, however, the golden hammer starts getting used because it is the most familiar to developers, not because it's the best solution. Like a carpenter with only one saw, the golden hammer may get the job done, but it doesn't give the cleanest or easiest cut.

In many cases, EJBs are just such a golden hammer. Developers—especially developers with a history of database development—tend to see entity EJBs as the solution to every problem. Need security? Create a username and password bean. Need an address? Create an address bean.

Unfortunately, EJBs are not the solution to every problem. Like any other technology (and EJBs are a complex technology, at that), EJBs have both costs and benefits. EJBs should only be used when their benefits outweigh the costs in solving the problem at hand. This is an important concept, so let's look at each aspect separately.

The first part of applying this concept is to understand the benefits of EJBs. The central idea behind Enterprise JavaBeans, particularly entity beans, is to create an "abstract persistence mechanism." EJBs provide a generic, object-oriented way to manage data without worrying about the details of what's underneath. So we describe the data we want to store, along with transaction characteristics, security mechanisms, and so on, and the EJB container worries about translating it into whatever database or other storage mechanism we happen to use. On top of that, the container makes the beans available remotely, enforces the security constraints, and optimizes access to the beans using pools and other mechanisms. It seems like a pretty good deal.

The other significant aspect of the benefit/cost concept involves understanding that there are also substantial costs to using EJBs. EJBs add significant complexity to an application. EJBs must be created, looked up, and referenced using JNDI, and a dizzying array of home, remote, and local interfaces. While containers use caching and pooling to help, EJBs are often a memory and performance bottleneck. And, of course, you often need to negotiate with the purchasing department to buy the container.

Choosing whether to use EJBs comes down to one key issue: solving the problem at hand. EJBs are a great solution when their benefits line up with the features you need. If you don't need to use an existing database, using container-managed persistence can be quite efficient.[2] If you need to add transactions to otherwise transactionless storage, or add secure remote access to your data, EJBs are also a good idea. Too frequently, however, EJBs are used when none of these features are needed.

Contrary to popular belief, CMP can be far more efficient than BMP in recent EJB implementations, due to built-in pooling and caching. Advanced database concepts like views and indexes, however, are still not fully matched with CMP, and mapping existing schemas to CMP can be painful, particularly when there's a lot of data normalization involved.

For example, let's go back to our web-based address book application based on an LDAP database. At the under-designed extreme, we have the magic servlet we saw earlier. While the magic servlet is not robust or extensible enough, solving the problem with entity EJBs is overkill. Figure 12-2 shows a sketch of the EJB solution: an address command in the presentation tier uses an address EJB, which in turn communicates with the underlying LDAP directory.
 
Figure 12-2. A poor use of entity beans

What are the EJBs adding? Chances are, the LDAP directory already provides some of the basic EJB features, such as remote access to the data, security, and maybe even transactions. If the AddressCommand is well defined, it will provide a level of abstraction between the controller and the underlying LDAP. Since we're stuck with the existing LDAP database anyway, the EJBs are not really protecting us from changes to the underlying storage. In the end, there are no real advantages to using EJBs in this case, and the costs in terms of development and performance outweigh the benefits.
12.4.1.1 Escaping EJBs

What are our options, if EJBs are out of the picture? If it's entity beans we gave up, usually the answer is just plain JavaBeans. A command interface looks a whole lot like an EJB, anyway: there are usually setter methods to describe the arguments to the command, business methods to perform the actual command, and getter methods to read the results. We simply build a Command like we would a BMP entity bean, by communicating directly with a database. Since the fundamental bean structure is preserved, it's usually a simple task to switch back to EJBs if we need the features later.

There are, of course, other alternatives. For instance, storing data in XML files or as serialized objects works for simple applications. In more complex cases, Java Data Objects (JDO) provide a simple way to persist arbitrary Java objects without many of the complexities of EJBs.[3] A flexible but more complicated option is to use a variation of the Service Locator pattern in conjunction with façades to hide whether beans are local or remote at all.

In fact, some developers have suggested that using JDO in conjunction with session EJBs provides a better architecture than BMP entity EJBs, but we'll leave that debate for a different book.

On the system logic side, consider turning stateful session beans to stateless session beans (see Section 12.4.3") and evaluate your stateless session beans to determine whether they should be EJBs. If not, think about replacing them with business delegates.

The following criteria can help determine whether you can safely convert your session beans into business delegates:
 
·         Transaction management is locally controlled, such as with a transaction wrapper.
·         Entity beans are not being used.
·         The session bean is just a gateway to an additional remote service (such as a DAO or PAO).
·         A small number of clients (such as a presentation tier) will be using the business component.
·         Business processes all have to run in the same environment.

If most or all of these conditions are true, there won't necessarily be much of a benefit to using session beans. Transaction management, in particular, is often a simpler problem than it is sometimes portrayed as: EJBs make it easy to distribute transactions across multiple resources, including databases and MOM middleware, but if all your transaction management needs are focused on simple database calls (or invoking DAOs), a transaction wrappe
Tuesday, 15 September 2009 13:11

J2EE Design Pattern - Enterprise Concurrency

Enterprise Concurrency

The most dramatic difference between an enterprise system and a "regular" application is the number of users it must serve. As a result, enterprise applications face problems that don't often occur on the desktop. A word processor, for example, doesn't have to handle two users making changes to one paragraph at the same time—at the most, it might have to notify the second user that the document is already in use.

Of course, all data-centric applications, whether they serve one user or 10,000, need to concern themselves with the validity of their data. An action that seems simple to the user ("Order a book") may require complex, choreographed activities within the application ("Check inventory, reserve stock, charge credit cards, arrange shipping, etc."). The more operations an application performs, the more likely one of those activities will fail—and when it does, the system must be able to carry on. Providing for this capability is easy in standalone applications: just don't save your changes if the mail merge fails. But if other users have been modifying the same databases at the same time, the brute-force approach to data integrity won't pan out.

A lot of ink has been spilled on these issues. Since we're always eager to help, we've coined a new term for an old concept: enterprise concurrency. Enterprise concurrency has two elements: transaction management and concurrency management. Transaction management, abstracted to a high level, controls the implementation of the individual use cases within a system, ensuring that changes to underlying data are performed consistently and that the system is always in a valid state. Concurrency management controls how different users access a system.

The two concepts might not seem linked at first, but they are. Concurrency problems can often be stated in terms of transaction boundaries. A transaction boundary defines a set of actions that must be performed together and without interference. Concurrent applications face transaction management problems that differ from their single-user brethren's in degree more than in kind.

10.1 Transaction Management

Here's a common situation that arises in enterprise development: you need to perform a set of actions as if they were a single action.[1] In real life, the simplest transactions take place within one resource, such as a database, and within a very short period of time. A bank transaction implemented by changing values for a particular field in two different rows of a table, via two update statements executed one after the other, is a simple transaction. You don't want to record the value of one update without the other. Even if the chance of something interfering—money automatically being deducted to pay a bill between the two updates, for instance—is small, it's a chance that can't be taken. To make life even more exciting, the odds of something going wrong increase dramatically when you start coordinating the transaction across space (multiple databases, messaging systems, EJBs, etc.) and time (multiple user requests to a web application).

This may, in fact, be the ur-pattern of enterprise development, fundamentally underlying everything else. We'd speculate more on this front, but suspect that the discussion would be less a matter of engineering than of theology.

Complex activities call for complex transactions. Let's take the ubiquitous stock-trading system example: for a brokerage firm to execute a buy order, it must charge a user's account, execute the trade, and send various confirmation messages. The confirmation messages shouldn't be sent without the trade, the account shouldn't be charged without the trade, and the trade shouldn't be executed without confirmations being sent. Each activity in the chain might involve a different system or technology, yet the entire process needs to be treated as one concrete business activity. Each activity might also consist of a subtransaction; for example, charging an account involves checking for sufficient funds and then transferring funds to a different account.

10.1.1 About Transactions

A transaction is an activity that changes the state of a system in a particular way. The purpose of formalized transaction management is to make sure that a system is altered in a manner that ensures the underlying state of the system is always valid. A transaction, therefore, is made up of a series of operations that result in a valid change of a system's state.

The simplest use for transactions is for preventing incomplete operations. If an activity calls for two SQL statements to run, enclosing them in a transaction ensures that either both run, or neither; if the second statement fails, the entire transaction will be rolled back, undoing the effects of the first statement. If each SQL statement succeeds, the transaction will be committed, permanently updating the state of the systems.

The other benefit of using a transaction is to ensure that the overall view of the system is always in a consistent state, even when multiple transactions are in process. Without proper transactions, even if every activity completes successfully, an application faces three subtle threats: dirty reads, nonrepeatable reads, and phantom reads. A dirty read occurs when two or more processes attempt to work on the same data at the same time. Imagine a transaction that reads two properties, A and B, from an object, performs a calculation, and updates a third property, C. If another transaction modifies A and B after the first transaction has read A but before it has read B, the computed value of C will not only be out-of-date, it may be entirely invalid! Figure 10-1 shows what can happen.

When the transactions are complete, the value of the C property on the data object has been computed based on the original A and the new value for B. This was never a valid value for C! The same problem can arise if you're using database tables rather than objects (and, in fact, that version of the problem happens more often; if nothing else, you can synchronize on an object).

A nonrepeatable read is similar to a dirty read, in that the problem comes from two transactions working with the same data at the same time. In this case, the first transaction might read a table that is subsequently modified by another transaction. If the first transaction reads the table again, it finds a new value. The original read, therefore, is nonrepeatable: the transaction can execute the same query multiple times and receive different answers.

The difference between a nonrepeatable read and a dirty read is that in a nonrepeatable read, the transaction that changes the data has completed. Systems can therefore allow nonrepeatable reads without also allowing dirty reads (in which the change becomes visible as soon as it is made within the transaction, rather than when the transaction is committed).

Phantom reads occur when one transaction is reading data while another transaction is modifying the same data. For example, imagine a program runs an SQL SELECT statement to retrieve a set of rows from the database, and then runs the same statement again a few seconds later. If another process has subsequently inserted rows into the table, the second query will return rows that weren't present in the first query. Conversely, if rows are deleted by the other transaction, they won't show up on the second query. This is sometimes acceptable, but not always: since the same query returns different results, the application doesn't have access to a consistent view of the database in order to do its work. Phantom reads differ from nonrepeatable reads in that they deal with the presence of data altogether rather than the contents of the data being read.

10.1.2 ACID Transaction Pattern

We've discussed two requirements of transaction management: performing multiple actions as one and ensuring the consistency of an underlying resource (generally a database). The ACID Transaction pattern defines four properties that can be used to implement a transaction. These four properties create a framework of operations that meets the needs we discussed in the previous section.[2]

So, is this a pattern? If you've spent time doing transactional programming, you're probably familiar with the ACID properties as part of the infrastructure of an enterprise system. However, a surprising number of programmers haven't been formally exposed to them; since they're fundamental, we're promoting them to pattern level.

A transaction that meets the ACID requirements is:

Atomic

Consistent

Isolated

Durable

Transactions must be atomic because we want every action within the transaction to succeed or fail as a unit. If a transaction reserves inventory, charges a credit card, and ships the inventory, we want to make sure the reserved inventory is still available if the credit card charge fails.

Transactions must be consistent because we need the state of the system to be correct after each activity. A system that has inventory reserved but no plans to ship it isn't in the correct state. The business rules within the domain model must be satisfied at the beginning and end of every transaction, regardless of whether the transaction succeeds.

Saying a transaction is isolated means that the transaction in progress is invisible to other transactions until it has been committed. This property, in particular, ensures that the overall view of the database remains consistent. Each transaction is shielded from dirty, nonrepeatable, and phantom reads, ensuring that any data loaded from the underlying resources was valid at least at one point in time. Keep in mind that there's no guarantee that the data is still valid: if another transaction started and finished while the first transaction was still in effect, the first transaction may be working from an older, though possibly consistent, set of data. We'll look at strategies for dealing with this issue in the next section of this chapter.

Finally, the durability property ensures that after a transaction is completed it will persist in the system. Of course, there's no absolute guarantee—somebody could take a sledgehammer to the database server—but the result of the transaction will be visible to all subsequent transactions.

10.1.2.1 Transaction isolation

Perfect transaction isolation is expensive. By allowing dirty reads, nonrepeatable reads, phantom reads, or some combination of the three, applications can reap greatly improved performance. It's not difficult to see why: avoiding these problems requires either keeping track of multiple versions of data, or requiring the system to delay some transactions until others have finished, or some combination of both. Some applications require perfect transaction isolation, but many, possibly most, don't. For performance reasons, applications often want to break the perfect isolation that a pure ACID transaction would enjoy.

JDBC supports four transaction isolation modes (five if you count turning it off). They are, in order from least stringent to most stringent:

TRANSACTION_NONE

No transactions supported.

TRANSACTION_READ_UNCOMMITTED

Data is visible as soon as it is written to a table. This mode provides basic transaction support, but dirty reads, nonrepeatable reads, and phantom reads are all possible.

TRANSACTION_READ_COMMITTED

Data is visible after commit( ) is called. No dirty reads allowed, but nonrepeatable and phantom reads are possible.

TRANSACTION_REPEATABLE_READ

The state of each row in each affected table is kept for the duration of the transaction. Rows are locked as soon as they are read. Dirty and nonrepeatable reads are both prevented. Phantom reads can still occur.

TRANSACTION_SERIALIZABLE

Perfect isolation is maintained. Each transaction will effectively lock each table that it accesses.

Of course, not all databases support each level. Oracle, for example, does not support the READ_UNCOMMITTED or REPEATABLE_READ isolation levels. Since isolation levels are set on a per connection level, it's possible to use different isolation levels across an application. Read-only SQL can use TRANSACTION_NONE; applications doing multipart updates that aren't likely to affect other transactions can use READ_COMMITTED; and data analysis activities that demand a consistent view can use SERIALIZABLE.

10.1.2.2 System and business transactions

Virtually any interaction with the domain model underlying an application can be represented as a transaction. Complex operations can be treated as a single transaction or split into several smaller transactions. When we write about transactions in design documentation, we often split them into system transactions and business transactions: the former approach is for the implementation details, and the latter is for the business modeling activity.

Business transactions will be instantly recognizable to the end user: they include actions such as withdrawing money from an account, finalizing a purchase, or submitting an order. These transactions have fundamental constraints your application must abide by. Business transaction boundaries flow logically from your use cases and requirement specifications. Each business transaction is made up of one or more system transactions.

A system transaction is implemented by the underlying resource layer, whether it's a database, EJB, messaging system, or something else. The tools for handling system transactions are provided by the Java APIs that access the particular infrastructure. Most programmers who've worked with database backends have been exposed to JDBC's transaction management API, which implements database level transactions.

The following code shows how to use JDBC's transaction management support. This code fragment starts a transaction (by turning off automatic commits on the Connection), attempts to run two SQL statements, each of which modifies the database, and then commits the transaction after both statements have run. If either statement fails (as indicated by an SQLException), the transaction is rolled back. A finally block cleans up database objects and closes the connection, regardless of whether the transaction succeeded.

Connection con = null;
Statement stmt = null;
PreparedStatement pstmt = null;
 
try {
  con = dataSource.getConnection(  ); // retrieve from a javax.sql.DataSource
  con.setAutoCommit(false);
 
  stmt = con.createStatement(  );
  stmt.executeUpdate("INSERT INTO VALUABLE_DATE (NUMVAL) VALUES (42)");
  pstmt = con.prepareStatement("UPDATE VITAL_DATA SET DATA = ? WHERE ID=33");
  pstmt.setString(1, "This is really important"");
  pstmt.executeUpdate(  );
  // commit the transaction
  con.commit(  );
 
} catch (SQLException e) {
 
  try { 
    con.rollback(  );  
  } catch (SQLException se) {} // report this in severe cases
    e.printStackTrace(  ); // handle it better
 
} finally {
 
  if(stmt != null)
    try { stmt.close(  ); } catch (SQLException ignored) {}
  if(pstmt != null)
    try { pstmt.close(  ); } catch (SQLException ignored) {}
  if (con != null)
    try { con.close(  ); } catch (SQLException e) {}
 
}

The details of the system transactions may be opaque to the end users. This is because many complex applications make a set of changes to a variety of resources, including multiple databases, messaging systems, and proprietary tools, in the process of fulfilling one user request. A business transaction, which maps much closer to a use case, is more recognizable to the end user, and may consist of several system transactions linked in various ways.

One of the primary purposes of the EJB API was to blur the line between system and business transactions. EJBs make it easier to link system transactions involving multiple resources; in fact, the process is largely transparent to the programmer. A single call to a method on a session façade can start a system transaction that encompasses all of the activities within a business transaction, and includes multiple databases, messaging systems, and other resources.

If we aren't using an EJB environment, we'll need to build business transactions out of multiple system transactions. If we're using two databases, we'll need a system transaction for each one. We'll also need system transaction resources for anything else we want to include in the transaction. The JMS API, for instance, includes a transaction management idiom that's similar to the one in JDBC: the JMS Session interface, which is analogous to the JDBC Connection interface, provides a commit( ) method and a rollback( ) method.

A business delegate implementing a business transaction can start as many system transactions as it needs. If every transaction completes successfully, each system transaction is committed. If any transaction fails, your code can roll back all of them.

10.1.3 Transactions and Objects

Graphs of objects are not naturally amenable to transactions. Unless transaction capability is baked into an environment at a very low level, capturing the web of relations between the different objects is very difficult. Modifying an object in a meaningful way often involves creating or destroying references to a variety of other objects. A single business transaction often creates or modify customers, orders, line items, stock records, and so on. Transaction control across a single object, however, is relatively easy. When you don't have to worry about dynamically including external objects in a transaction, it's a (relatively!) simple matter to record changes to objects.

Databases don't face this problem because the relationships between tables are defined by key relationships (if you're not familiar with primary keys, turn back to Chapter 8). A transaction might include edits to five or six different tables in a database and those edits might affect the relations between entities, but as far as commits and rollbacks are concerned, the relationships don't matter.

In the end, the application is ultimately responsible for maintaining the consistency of the data. Consistency can be maintained by declaring "constraints" on the database that disallow updates and inserts that don't meet a particular set of conditions, or by enforcing the business rules at the application logic level by forbidding entry of invalid or inappropriate data.

The EJB specification provides transaction support at the object level by assigning a primary key to each entity bean. References between objects are handled not by direct Java references, but by the application server, using the keys. This method allows the application server to monitor each object for changes in internal state and changes in its relationship with other objects. Entity beans can be included in a running transaction as they are touched upon by application code.

For example, an EJB stateless session bean can be declared as TX_REQUIRED, which requires the EJB container to create a transaction (or join an existing transaction) whenever a method is invoked on the bean. Initially, the transaction will not encompass any objects on the server. As code execution goes forward, any entity beans requested by the session bean will be included in the transaction. If the session bean method executes successfully, all of the changes will be committed to the system. If the method fails, any changes made to any transaction-controlled objects will be rolled right back.

10.1.4 Transactional Context Pattern

In an EJB environment, transaction management is taken care of transparently. By setting transaction levels on session and entity beans and accessing database connections and other resources through the application server, you get most of your transaction support automatically. If a method on a session façade fails in mid-execution, all of the entity bean changes it made are undone—any JMS messages are unsent, and so on.

If you're not running in an environment that handles transactions for you, you need to build them yourself. Since 95% or more of enterprise transaction management code involves the database, the standard approach is to use the transaction functionality that is built into the JDBC API and the underlying databases. Transactions can then be handled at the DAO level: each DAO method starts a new transaction, updates the database, and commits the transaction.

Handling transactions at the DAO or PAO level, though, can be limiting. For one thing, the transaction boundary generally does not lie at the DAO level. Instead, transactions usually sit at the business delegate/session façade level. This level is, of course, the use case level. Since a business delegate might call several DAOs in the course of completing its activities, and might even call other business delegates, handling transactions at this level is not going to give us the degree of control we want: if the last DAO fails, the changes made by the first DAOs remain in the database, leaving the application in an inconsistent and incorrect state.

The Transactional Context pattern allows us to spread a transaction across multiple, otherwise unrelated objects. A transactional context is an environment that business logic and data access components execute inside. It controls access to transaction control resources such as database connections, and is responsible for starting, stopping, committing, and rolling back transactions. As we stated, the EJB environment itself provides a transactional context, but if you're not using an application server, it's possible to write your own.

For simple applications, the business delegate itself can serve as the transactional context for the DAO objects it uses. All the software really needs to do is decouple the DAO objects from the code to retrieve database connections from the application's connection pool. The business delegate can retrieve the connection itself, set up the transaction, and pass the same connection into a series of DAOs or other business delegates. As long as the DAOs don't call any transaction management code themselves (the JDBC setAutoCommit( ), commit( ), and rollback( ) methods), all activities against the database will be included in the same transaction. Failures at any point can be used to roll back the transaction for all the affected components.

This approach works fine for simple activities and within DAOs, but it breaks down for larger scale applications. For one thing, the business delegate object is now aware of transaction management in all its gory (well, not too gory) details. It's perfectly appropriate for the business delegate to be aware of the business constraints surrounding a transaction, but less appropriate for it to be responsible for implementing them. If nothing else, the ability to easily switch back and forth between local business delegates and remote EJB session façades vanishes: the EJB specification specifically disallows independent transaction control.

A more elegant implementation involves placing control of the transaction context with an outside object that is known to the business delegates, DAOs, and other components. Figure 10-2 shows how it works.

In this example, the business delegate informs the context control object that it wishes to start a transaction. The context object obtains a connection and starts the database transaction. It then passes the connection to any other object, such as the two DAOs, that have access to it. The DAOs use the connection, and their activities are included in the transaction. After calling all the DAOs it needs, the business delegate commits the transaction.

10.1.4.1 Implementing transactional context with business delegates

Many applications are small enough to run in a simple web container such as Jakarta Tomcat. In these cases, where business logic is embedded in business delegates and DAOs, we need to manage our own transactions within business delegates.

Our solution is to associate a database connection with a web application's request scope. When a request comes into the web server, we assign a single JDBC Connection object to support every database-related activity from the start of the request to the end of the request. This allows us to declare a transaction that spans multiple objects, which is similar to the behavior an EJB server would provide.

This approach requires one decision: which level of the application should have knowledge and control of the transaction boundary? Earlier we placed this responsibility at the DAO level when dealing with DAOs in non-EJB environments. In this case, we need to put the responsibility at the business delegate or even the business delegate factory level.

The first step is to provide an idiom for sharing a connection across multiple objects. This procedure might seem daunting, since it's easy to imagine having to pass connection references from object to object. In fact, it's pretty simple, at least in the servlet world. Since each servlet request takes place on its own thread, we can use Java's ThreadLocal class to build a static ConnectionManager object that can be associated with a particular request.[3] The connection manager provides part of our transactional context. Example 10-1 shows an implementation, which includes methods for setting and retrieving the current connection.

There's a caveat: while ThreadLocal has been around since JDK 1.2, the original implementation was sluggish at best. JDK 1.3 introduced some performance improvements, but it wasn't until JDK 1.4 that overhead became reasonable.

Example 10-1. ConnectionManager.java
import java.sql.Connection;
 
public final class ConnectionManager
{
    private static final ThreadLocal currentConnection = new ThreadLocal(  );
     
    static Connection setConnection( Connection connection ) {
        Connection priorConnection = (Connection)currentConnection.get(  );
        currentConnection.set( connection );
        // We return the prior connection, if any, to give the application
        // the opportunity to deal with it, if so desired. It's important
        // that all database connections be properly closed.
        return priorConnection;
    }
    
    public static Connection getConnection(  ) {
        return (Connection)currentConnection.get(  );
    }
}

Once the connection has been set up, using the connection manager is easy: just call the getConnection( ) method when you need a connection. Unlike a JNDI connection pool, you don't call the close( ) method when you're done with the connection; the system does it for you (Example 10-2), so closing it prematurely just makes trouble for the next block of code that needs to use it. Here's a simple example of correct usage:[4]

We could write a wrapper class, which would allow us to safely ignore calls to close( ); when we're retrofitting existing code, this can sometimes save time and reduce the potential for unforeseen bugs.

Connection con = ConnectionManager.getConnection(  );
try {
  con.setAutoCommit(false);
  // Do SQL
  con.commit(  );
  con.setAutoCommit(true);
} catch (SQLException e) {
  // Report somehow  
  // Rollback exception will be thrown by the invoke method
  con.rollback(  ); 
  con.setAutoCommit(true);             
}

The only rule is that you have to finish with the connection in the same scope you retrieved it: if you stash a reference to the connection in a place where it might be accessed by another thread, the other thread has no guarantee of the connection's transactional state or even validity. For example, if you retrieve the connection within a persistence method attached to a domain object, you can't cache that Connection object within the domain object for use later: if other methods need it, they must retrieve it from the ConnectionManager itself.

The safest possible version of this strategy would wrap the connection in a wrapper object that simply passed all of the methods in the Connection interface through to the actual connection object, with the exception of the close( ) method.

Now that we have the connection manager, we need to associate a connection with our thread. We do this via a servlet filter, shown in Example 10-2. The filter is responsible for interacting with a JNDI data source and opening and closing connections.

Example 10-2. ConnectionFilter.java
import java.io.IOException;
 
import javax.servlet.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
 
public class ConnectionFilter implements Filter {
 
    private DataSource dataSource = null;
    
    /**
     * Create a datasource from a parameter defined in web.xml.
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        try {
            InitialContext iCtx = new InitialContext(  );
            Context ctx = (Context) iCtx.lookup("java:comp/env");
            dataSource = (DataSource) 
              ctx.lookup(filterConfig.getInitParameter("JNDI_datasource"));
        } catch (Exception e) {
            ServletException se = new ServletException(  );
            // Uncomment in JDK 1.4 for easier troubleshooting.
            // se.initCause(e); 
            throw se;
        }
    }
 
    public void destroy(  ) {
    }
 
    /** Retrieve a connection, run the filter chain, and return the connection. 
     * */
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
 
        Connection con = null;
        
        try {
          con = dataSource.getConnection(  );
          
          // Set the connection, and retrieve the previous connection for 
          // disposal
          Connection previousCon = ConnectionManager.setConnection(con);
          if(previousCon != null) 
            try { previousCon.close(  ); } catch (SQLException e) {}
 
          // Run the rest of the filter chain.  
          chain.doFilter(request, response);
          
          // Make sure we disassociate the connection, just in case.
          ConnectionManager.setConnection(null);
        } catch (SQLException e) {
          ServletException se = new ServletException(e);
          throw se;
        } finally {
          if (con != null)
            try { con.close(  ); } catch (SQLException e) {}
        }
    }
}

This approach has a side benefit, by the way: if your application needs a connection in several places during the course of a single request, using the ConnectionManager instead of repeated JNDI requests means less code and a slight performance boost.

We could stop now that we have a connection manager associated with our threads. Each business delegate that wants a transaction that spans multiple DAOs or transactionally ignorant subdelegates can retrieve the connection, set the transaction status, run its code, and commit or roll back as appropriate. However, we've placed a lot of responsibility for implementing transactions on the business delegates, which is probably not a good thing. Instead, we can push responsibility all the way up to the business delegate factory. The TransactionWrapper class shown in Example 10-3 decorates an object with an InvocationHandler that wraps each method call in a transaction. Coupled with the ConnectionManager, we now have a complete transactional context implementation: a way of sharing the transaction environment and a way of controlling the transaction status.

Example 10-3. TransactionWrapper.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
import java.sql.Connection;
 
final class TransactionWrapper {
 
    /**
     * Decorates a business delegate object with a wrapper. The
     * object returned by this method will implement all of the interfaces
     * originally implemented by the target.
     *
     * @param The Business Delegate to wrap
     * @return The business delegate wrapped in this wrapper
     */
    static Object decorate(Object delegate) {
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(  ),
            delegate.getClass().getInterfaces(  ),
            new XAWrapperHandler(delegate));
    }
 
    static final class XAWrapperHandler implements InvocationHandler {
        private final Object delegate;
 
        XAWrapperHandler(Object delegate) {
            // Cache the wrapped delegate, so we can pass method invocations
            // to it.
            this.delegate = delegate;
        }
 
        /** Invoke the method within a transaction. We retrieve a connection,
         * set auto commit to false (starting the transaction), run the original
         * method, commit the transaction, and return the result. If any 
         * exceptions are thrown (SQLException or not) we roll the transaction
         * back. 
         * 
         * Note that we don't use a finally block to reset the connection to
         * autocommit mode. This approach gives us a better idea of the root
         * cause of any error. In JDK 1.4, we might modify the catch block
         * to attach the original Throwable as the root cause for any exception
         * thrown by the rollback() and setAutoCommit(  ) methods. */
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
            Object result = null;
            Connection con = ConnectionManager.getConnection(  );
            try {
              con.setAutoCommit(false);
              result = method.invoke(delegate, args);
              con.commit(  );
              con.setAutoCommit(true);
            } catch (Throwable t) {
              // Rollback exception will be thrown by the invoke method
              con.rollback(  ); 
              con.setAutoCommit(true);
              throw t;
            } 
            
            return result;
        }
    }
}

The final step is to modify our business delegate factory to wrap an object instance in a transaction wrapper. If you've read the code closely, you'll see that we commit the database transaction after every method invocation. Proxy handlers wrap the object from the view of application, but not from within the object itself (unless you do some complicated passing of self-references, which is pretty hard to manage). So you don't need to worry about your methods spawning a new transaction for each internal method call. Figure 10-3 shows the chain of method invocations for a simple example: the application calls the getCustomer( ) method on the proxy object; the proxy object calls the invoke( ) method of our InvocationHandler, which in turn calls the original object. If the original object calls methods on itself, they won't be passed through the invocation handler.

This behavior has two implications. First, we don't have to worry about the business delegate creating new transactions by calling its own methods, since those calls don't go through the proxy. Second, if we want to nest business delegates we must ensure that the nested delegates are not wrapped in the proxy when the parent delegate retrieves them. This may involve providing two different object creation methods on the business delegate factory: one to create a wrapped object that generates a new transaction with each method invocation, and one to create an unwrapped version that joins existing transactions.

Nesting business delegates only comes up sometimes, but many, if not most, business delegates will call multiple DAO methods in the course of a single business delegate method. That's why we wrap at the business delegate level rather than the DAO level.

 

Tuesday, 15 September 2009 11:48

J2EE Design Pattern - Business Tier Interfaces

Business Tier Interfaces
Business tier interfaces connect the domain model with the client (in thick-client applications) or the server-side presentation layer (in web applications). They also implement some of the business logic for an application, controlling and limiting the actions that the presentation tier can perform on the domain model.
This chapter focuses on patterns for packaging and presenting the business logic of an application. The Business Delegate pattern and the Session Façade pattern address the organization of process logic and the division between the domain logic and the process logic. As an added bonus, both of these patterns provide performance enhancements in addition to their organizational benefits. We'll also discuss the Business Delegate Factory pattern, which allows us to build more effective non-EJB applications by creating our own versions of some of the services provided by a full application server.
We also cover two patterns for connecting to remote business services. The Service Adapter pattern, which is a variation on the original Gang of Four Adapter pattern, can be used to provide a useful Java-based interface to non-EJB business tier resources. The Service Locator pattern provides centralized management of connections to Enterprise JavaBeans and other remote resources.

9.1 Abstracting Business Logic

The last few chapters explored ways to keep the presentation tier and the business tier separate. We accomplished this by wrapping the domain logic for the system in both regular and Enterprise JavaBeans and introducing various controller patterns, including the MVC and Service to Worker patterns, in order to mediate the interaction between the tiers. This decoupling makes the overall system easier to understand and maintain. But without a good way to communicate between the tiers, the advantages can quickly be lost. The Business Delegate and Business Delegate Factory patterns provide client-side objects that flexibly connect the presentation and business tiers.

9.1.1 Business Delegate

In an MVC design, the controller still has to interact directly with the business tier (the model). The view will do so as well, although in a read-only fashion and mediated through the controller. This exposes the underlying implementation details of the business tier to the presentation tier. The application needs to know enough about the business tier to perform the operations requested by the client, and enough about the networking infrastructure between the presentation tier and the business tier to locate the business objects in the first place. The result is a tightly coupled application.

The Business Delegate pattern defines a client-side object that handles communications with the business objects. It fulfills the model role in an MVC framework. The business delegate doesn't necessarily contain any business logic itself, but encapsulates knowledge about how to locate, connect to, and interact with the business objects that make up the application. These objects might be in the same JVM, or they might be remote Enterprise JavaBeans, CORBA components, web services, or other resources. The business delegate is also responsible for returning enough data to the controller to allow the controller to instantiate an appropriate view.

The business delegate wraps access to a series of business objects and business services into a single, easy-to-implement application call. It can also incorporate all of the networking code required to access those resources, including any retries, caching, or exception handling that might be required. Networking or database access exceptions can be converted into application exceptions for consistent and user-friendly handling.
 
The UML diagram in Figure 9-1 shows a basic set of object relationships for an application using business delegates. The client interacts with the business delegate object, which in turn interacts with a set of business services. Some of these services are instantiated directly, and others are retrieved via a service locator class. The service locator provides a directory interface to locate the various business services used by the business delegate. In EJB environments, this is often a JNDI server. We cover service locators later in this chapter.
 
Figure 9-1. Business delegate

The major difference between our definition of Business Delegate and others is that we don't consider them to be pure EJB front ends. Rather, we consider them the client-side equivalent of an EJB session bean: an object that contains a front end to the process logic underlying an application. It doesn't matter whether the business delegate object implements that logic itself, parcels it out to other components, or does a little of both. The presentation tier delegates business process responsibilities to the business delegate, and that's more or less the end of that.

Our version varies from the version presented in Sun's J2EE Patterns collection.[1] For one thing, we make the Service Locator component optional. While Service Locators are often a good idea, we don't consider them vital to a business delegate implementation.
9.1.1.1 Organizing business logic into business delegates

If none of the external resources available to the business delegate are capable of incorporating all of the business logic, then the business delegate's own implementation should provide the missing elements. For example, consider a use case that involves retrieving stock information from a web service, computing a shipping charge, and submitting stock updates and shipping information to two additional web services. Because business delegates can be shared, we don't consider it bad practice to include the code for computing shipping charges in the business delegate implementation. Of course, it does make sense to consider ways to move that functionality into other areas that can be reused more easily, but that's almost always true. Incorporating the code directly into the presentation tier (having a servlet compute the charges before submitting the order), on the other hand, would easily prove problematic.

The quantity of business logic included in the business delegate depends on the application's persistence model. If the business tier consists exclusively of EJB entity beans that represent the data model, the business delegate will, of necessity, have to incorporate a substantial amount of domain knowledge. It must know which entity beans to create, remove, or update to perform a particular activity. A delegate for a legacy interface, on the other hand, may simply relay calls to an existing API that handles object management. In both cases, we consider the business delegate to be part of the business logic tier, even if the object itself is instantiated at the client tier.

We'll talk more about the division of business logic between business delegates and EJBs later in this chapter, when we discuss the Session Façade pattern.
9.1.1.2 Business delegate implementation

In an iterative or distributed environment, the business delegate can be stubbed out first, allowing development to proceed on the presentation tier while the ultimate business logic implementation is worked out. For applications that include complex relationships with external systems, this approach substantially accelerates development.

Example 9-1
shows a very simple business delegate. It performs a data validity check and then uses a DAO object to update the domain objects. In a real application, the business delegate doesn't need to be much more complex. It should be easy to see how we would convert this class to an EJB session bean.
Example 9-1. A business delegate
public class PatientManagementDelegate {
 
 public static PatientDTO createNewPatient(PatientDTO patient) 
    throws InvalidPatientException{
 
    if(patient == null|| patient.getFirstName( ) == null 
        || patient.getLastName( ) == null) 
          throw new InvalidPatientException(
            "Patient Records Require Full Name");
    
    PatientDAO pDAO = PatientDAOFactory.getPatientDAO( );
    PatientDTO newPatientRecord = pDAO.createPatient(patient);
 
    return newPatientRecord;
 }
  
}
The implementation of the business delegate can include other patterns as well, further hiding the business logic's complexity from the client. For example, a business delegate can use the Gang of Four Strategy pattern to retrieve data from different sources depending on runtime requirements or changing infrastructure (we touched on the Strategy pattern in Chapter 2).

In an EJB environment, business delegates usually work as a front end to a session bean (specifically, to a session façade, which we'll discuss later in this chapter). Implementing the presentation tier-to-EJB tier connection this way insulates the presentation tier programmers from the EJB environment, and simplifies error handling, since the various EJB exceptions can be caught, handled, and either resolved within the delegate or reported to the presentation layer in a consistent, simplified manner. A business delegate can throw exceptions related to the business process rather than exceptions related to the EJB environment.
9.1.1.3 Nesting business delegates

Since a business delegate is an access point to business logic and the domain model, there's no reason it can't use the resources of other business delegates. Business delegates can be nested arbitrarily deep, particularly when you're not in an EJB environment. This ability allows you to create separate business delegates for each use case and combine them as needed to implement the master use cases for your application.

For example, Figure 9-2 shows how a servlet, accessing a business delegate object to submit an order to a purchasing system, might actually invoke three different delegates, even though the servlet only sees one of them.
Figure 9-2. Nested business delegates
 
Nesting business delegates means that we have to keep track of the resources each delegate uses. For example, if each call to a business delegate retrieves a database connection and doesn't return it until all processing is completed (whether by instantiating DAO objects or retrieving the connection directly), a call stack containing five business delegate instances might hold onto five separate database connections. This means that a pool of 25 database connections can only support 5 concurrent users.

Using nested business delegates in an EJB application could result in inappropriate connections between different EJBs. If a session bean uses a business delegate to access another session bean, the business delegate may have to make a remote connection to the second bean, even if it's located in the same EJB server. If the session bean connected directly, it could easily use local interfaces instead. We'll address this issue later in this chapter with the Session Façade pattern.

The other major issue raised by nested business delegates is transaction control. A presentation tier programmer will, absent indications to the contrary, expect that each call to a business delegate method takes part in one transaction. If each business delegate creates its own transaction, the nested delegates will handle their transactions separately from the parent delegate. At best, this process results in incomplete rollbacks when the parent delegate fails; at worst, it will result in database resource locks and the application slowing or grinding to a halt. Since this isn't a simple issue, we'll look at transaction issues in enterprise applications in much more detail in the next chapter, including a pattern, Transactional Context, which addresses the problem specifically.
9.1.1.4 Stateful business delegates

Business delegates can be stateful or stateless. To use a stateful business delegate, generate an instance of the business delegate class. To create a stateless business delegate, declare the business delegate methods as static, and call them accordingly. You can also make the business delegate class a singleton. Either way, all classes running in the same Java VM and class loader will share a stateless business delegate. For web applications, this means that all the code will share the delegate.

The tradeoffs between stateful and stateless business delegates are almost identical to the tradeoffs between stateful and stateless session beans in EJB. Stateful delegates require more resources and can be marginally slower, due to the overhead of creating the additional objects. On the other hand, they allow you to configure a particular instance to your needs, making the delegates themselves easier to reuse. Stateless delegates require less memory and present a simpler interface to the programmer.

Here's a rule of thumb: if a business delegate requires a resource that is both expensive to create and that must be created specifically for the current user (such as a JAAS security principal), and if that resource will be used multiple times within the same delegate by the same thread, use a stateful delegate. You should also use a stateful delegate if the delegate needs complex configuration specifically for the current invocation, rather than trying to pass in all configuration information with each method call. Otherwise, look for opportunities to use a stateless delegate.

9.1.2 Business Delegate Factory Pattern

Since the simplest business delegates are just objects with static methods, we don't need to worry about creating or configuring them. But once we start using stateful business delegates, things get more complicated—even with stateless delegates we can eventually accumulate so many of them that it becomes hard to keep track. Architectures using business delegates face the following problems:
 
·         No standard for instantiating business delegates, making it difficult to exchange one delegate for another
·         Instantiation code, which may include configuring the delegate to access runtime-specific resources, is spread across the application
·         No centralized directory of available business logic

We can address these points with the Business Delegate Factory pattern. The Business Delegate Factory pattern is specialization of the Gang of Four Factory pattern. A business delegate factory is a single object responsible for creating instances or one or more kinds of business delegates.
 
Depending on your application, the factory can produce a particular kind of business delegate (resulting in one factory per delegate class), or a set of delegates related by their configuration requirements or general functionality. Having a broad-spectrum factory makes it easier to find the various business delegates within an application, but also introduces additional compilation dependencies.
 
Like business delegates themselves, business delegate factories can be stateful or stateless. The same rules apply: if the factory needs to be configured for the current thread/user/block of code, or if creating a new delegate instance requires an expensive resource that cannot be shared between all users of the application, consider a stateful factory. Otherwise, consider a stateless factory.
 
Figure 9-3 shows the sequence diagram for a stateful factory creating a stateful business delegate. It's a simple process: the client application creates a new instance of the factory by calling a static newInstance( ) method. It then calls a setAttribute(...) method to configure the factory, and finally requests an instance of a particular business delegate type. The factory creates the delegate instance and returns it to the client. At the end of their useful lives, both the factory and delegate objects go out of scope and are garbage-collected.
 
Figure 9-3. Stateful business delegate factory
A stateless business delegate factory would look similar but could include all of the code executed in the newInstance( ) and setAttribute( ) methods in static initialization blocks or in the getBusinessDelegate( ) method, which would now be a static method on the delegate factory class. If we implement the delegate as a singleton, the initialization can still take place at startup.
9.1.2.1 Modifying business delegate behavior

The Business Delegate Factory pattern lets you add functionality to your business delegates at runtime. As a simple example, consider logging. An application might need to support logging calls to business delegate methods but require different levels of detail, or different outputs, depending on the current status of the application. The simplest way of implementing this functionality (and the road most often taken) is to implement the logging functions in each business delegate object, perhaps tagging them with a "Loggable" interface, and toggling logging on or off via a centralized control class or at creation-time within the factory.

Other solutions take better advantage of the object-oriented nature of Java and create wrapper classes for each business delegate. The factory can wrap each delegate in the appropriate logging wrapper when logging is required, and skip the step when it isn't. The problem is that we need to maintain a separate logging wrapper for each business delegate class, and update them at the same time we update the delegate. It's a lot of work, and a lot of class profusion.

Obviously, we're leading up to something! The Java reflection API (which we've already seen in Chapter 8) includes a class called Proxy, which allows you to dynamically create an object that implements a given set of interfaces. An InvocationHandler object handles calls to methods on those interfaces, and the entire assemblage is referred to as a proxy object. Most uses of this class involve taking an existing object, extracting its interfaces, and creating a new object that implements those same interfaces while providing some additional functionality. The new object can delegate method calls to the original object or handle method invocations itself. Figure 9-4 shows how this can work in practice.
The custom InvocationHandler includes a reference to the wrapped object. When the application calls methods on the proxy object, the invocation handler forwards the method on to the original object and returns the results to the application.
 
Figure 9-4. Wrapping an object with an InvocationHandler

Example 9-2
shows how we can apply this technique to the logging problem. The DelegateLoggingWrapper class includes a single method, decorate( ), and an inner class defining a LoggingWrapperHandler, which implements the InvocationHandler interface. The invoke( ) method prints out the name of the method and the arguments passed into it, invokes the method on the original object, prints another message confirming success, and returns the result of the method call. In the next chapter, we'll use this approach to handle transaction management within and across business delegates.
Example 9-2. DelegateLoggingWrapper.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
final class DelegateLoggingWrapper {
    /**
     * Decorates a business delegate object with a Logging Wrapper. 
     * The object returned by this method will implement all of the 
     * interfaces originally implemented by the target
     * and loaded by the same class loader as that of the target.
     * @param delegate The Business Delegate to wrap
     * @return The business delegate wrapped in this wrapper
     */
    static Object decorate(Object delegate) {
        return Proxy.newProxyInstance(
            delegate.getClass().getClassLoader( ),
            delegate.getClass().getInterfaces( ),
            new LoggingWrapperHandler(delegate));
    }
 
    static final class LoggingWrapperHandler 
                       implements InvocationHandler {
        private final Object delegate;
 
        LoggingWrapperHandler(Object delegate) {
            this.delegate = delegate;
        }
 
        /** Invoke the target method, but display logging 
            information first. */
        public Object invoke(Object proxy, Method method, 
                             Object[] args)
            throws Throwable {
            System.out.println("Invoked Business Delegate Method: " +
                method.getName( ));
 
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    System.out.print(args[i]); 
                    System.out.print( 
                      (i < (args.length - 1)) ? "," : "\n");
                }
            }
 
            Object result = method.invoke(delegate, args);
            System.out.println("Completed Without Exception");
 
            return result;
        }
    }
}
Example 9-3 shows how to use invocation proxies in the business delegate factory itself. We've created a static factory object that produces runtime instances of each delegate object (to save space, we've included only one delegate type). The user can call static methods on the class in order to start or stop logging. If logging is enabled, the DelegateLoggingWrapper is used to add logging capability. Note that we've shifted the logging/no-logging decision all the way down to the wrap( ) method, rather than placing it in the delegate creation methods. While this adjustment is marginally slower, it reduces the amount of code we need to write in each delegate creation method, and makes it easier to add support for additional wrappers later.
Example 9-3. BusinessDelegateFactory with logging
public class BusinessDelegateFactory {
 
 private static boolean useLogging = false;
 
 public static PatientManagementDelegate 
                 getPatientManagementDelegate( ) {
    PatientManagementDelegate delegate = 
        new PatientManagementDelegateImpl( );
    return (PatientManagementDelegate)wrap(delegate);   
  }
 
 private static Object wrap(Object o) {
    if(useLogging)
      return DelegateLoggingWrapper.decorate(o);
    return o;
 }
  
  public static void startLogging( ) {
    useLogging = true;
 }
 
 public static void stopLogging( ) {
    useLogging = false;
 }
 
 public static boolean getLogStatus( ) {
    return useLogging;
 }
}
You probably noticed that the proxy object requires interfaces, rather than concrete classes. Rather than maintain a single class for each business delegate we want to wrap, we now need to maintain an implementation class and an interface. This is simple enough to do (and still less work than maintaining separate wrapper classes for each delegate, since it's easy to update the interface at the same time a delegate changes: no additional code needs to be written). We assume you already know how to create an interface, but for completeness, Example 9-4 shows the interface for the patient management delegate.
Example 9-4. PatientManagementDelegate.java (interface)
public interface PatientManagementDelegate 
{
 public PatientDTO createNewPatient(PatientDTO patient) 
    throws InvalidPatientException;
}
Example 9-4 expects a PatientManagementDelegateImpl object that implements this interface. The code for this class is exactly the same as in Example 9-1, but renamed and with an implements clause.
This approach to modifying behavior on the fly does have performance implications. Each request for a business delegate now carries the overhead of creating the proxy object, which increases the time required to create a new business delegate instance. Each method call also incurs additional overhead, both for the reflection activity itself and whatever activities the wrapper performs. In absolute terms, the difference due to reflection is measured in microseconds, and we don't consider it significant, particularly given that the business delegates and the wrapper objects are likely to be participating in much more resource intensive activities.
Since the Factory pattern itself is fundamental to good object-oriented programming (out of everything in the original Gang of Four book, it may well be the pattern most frequently encountered),[2] it's worth keeping the proxy approach in mind when implementing other areas of your system.

9.2 Accessing Remote Services

Business delegates are great for providing local code with access to business resources, whether the resources themselves are local or not. For local resources the data lives on the same server—maybe even in the same JVM—as the business delegate. In these situations, connecting to the data is not a huge concern. J2EE, however, is designed to handle much more complicated scenarios. An enterprise J2EE deployment typically involves resources that are distributed between multiple servers. To fulfill requests, the servers hosting the presentation tier may need to communicate with application servers, messaging middleware servers, database servers, and legacy IS systems. Finding all these servers and maintaining the various connections can quickly complicate otherwise simple business delegates.

Since the work of connecting is not central to the business delegate, it is often beneficial to build a separate object that the delegates can use to access remote services. The Service Adapter and Service Locator patterns describe ways to do this, the former for non-J2EE business services and the latter primarily for EJBs. Figure 9-5 shows how the service locator and service adapter act as intermediaries between the business delegates and business services.
 
Figure 9-5. Using service adapters and locators

9.2.1 The Service Adapter Pattern

A business delegate might make use of several services to fulfill a particular use case. In order to keep the business logic code understandable and maintainable, we want to use the simplest interfaces possible. A service adapter encapsulates interactions with a remote service, hiding details of the implementation when they aren't central to fulfilling the business need.

The Service Adapter pattern is a variation on the Gang of Four Adapter pattern. The Adapter pattern wraps a class in a superclass that implements a standard interface. The Service Adapter pattern goes one step further by wrapping a remote service in a standard Java object that can be used without knowledge of the underlying access mechanism. The Java wrapper is responsible for:
 
·         Locating the Remote Service.
·         Translating Java method calls into a format that the remote service understands.
·         Accessing remote functionality.
·         Translating results into a pure Java representation.

The Service Adapter pattern is overkill for EJBs, which already map business methods to Java methods. The translation activities are not required, and the directory services abstracted by a service adapter can be better addressed in the EJB context with the Service Locator pattern, discussed later in this chapter.

However, if the native interface to the external service is not Java, a service adapter can bridge the gap. A good example is an application that needs to access a SOAP-based web service. The service might be delivered by SOAP over HTTP, SOAP over SMTP, or via specialized, routed protocols like ebXML. The service adapter hides the complexities of creating a SOAP or ebXML message, allowing the developer to substitute different transport mechanisms as the needs of the application evolve. A SOAP-based service adapter in a prototype system can be replaced with a JMS-based adapter before going to production. Figure 9-6 shows a service adapter for a SOAP web service.
 
Figure 9-6. Service adapter for a SOAP web service

In J2EE 1.4 or earlier versions with the appropriate add-on modules, the JAX-RPC API and support tools can be used to build service adapters for web services. This largely eliminates the need to hand-code service adapters for web services.
The most recent Java IDEs support building service adapters directly from WSDL definition files. Some tools do this in a more platform-independent way than others, so it's worth watching for portability issues when using such tools.

However, most of the generated code is either already reusable or can be easily modified to be so. Let's look at how this might work with a simple web service. Sticking with our health care theme, imagine a web service that returns a patient's medical records. Since complex data is more useful than simple data (and because we don't want to have to execute a few dozen web service requests for each use case), we've written the service to return a number of pieces of data with each request, in the form of a complex XML type. (If you're new to web services programming, now would be a good time to go read Java Web Services, by Dave Chappell and Tyler Jewell, also published by O'Reilly.)

A call to a web service can return a simple data type (numbers, strings, etc.) or a complex type. Complex types are defined within the WSDL file that specifies the web service, in the form of embedded XML Schema documents. In this case, the schema for the patient record type looks like this:
<schema
         targetNamespace="http://chapter9/IMedicalRecordService.xsd"
         xmlns="http://www.w3.org/2001/XMLSchema"
         xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
         <complexType name="chapter9_PatientRecord">
            <all>
               <element name="FirstName" type="string"/>
               <element name="LastName" type="string"/>
               <element name="MiddleName" type="string"/>
               <element name="Address1" type="string"/>
               <element name="Address2" type="string"/>
               <element name="City" type="string"/>
               <element name="State" type="string"/>
               <element name="Zip" type="string"/>
               <element name="Country" type="string"/>
               <element name="PatientID" type="long"/>
            </all>
         </complexType>
      </schema>
Within our application, we've defined a JavaBean, PatientRecord, which corresponds with this type. The JavaBean includes the same String and long properties, in traditional get/set form, and is otherwise indistinguishable from any other JavaBean.

Example 9-5
shows a class that uses the Apache SOAP toolset to identify a web service, calls its getMedicalRecord( ) method with a patient identifier as a parameter, and translates the return value from the XML defined in the WSDL file (shown above) into the JavaBean defined within our application. It then returns the JavaBean to the code that called it.

The code is pretty simple, and it gets even simpler once you realize that it's largely auto-generated: our IDE put most of the adapter code together based on the WSDL file and the JavaBean. All we did was clean it up and replace a vendor-provided SOAPHTTPConnection implementation with Apache's freely available version.
Example 9-5. MedicalRecordServiceAdapter.java
import org.apache.soap.transport.http.SOAPHTTPConnection;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.util.xml.QName;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
 
import java.net.URL;
import java.util.*;
 
public class MedicalRecordServiceAdapter {
 
 public MedicalRecordServiceAdapter( ) {
    m_httpConnection = new SOAPHTTPConnection( );
    m_smr = new SOAPMappingRegistry( );
 
    BeanSerializer beanSer = new BeanSerializer( );
   m_smr.mapTypes(Constants.NS_URI_SOAP_ENC, 
      new QName("http://chapter9/IMedicalRecordService.xsd", 
        "chapter9_PatientRecord"), chapter9.PatientRecord.class, 
        beanSer, beanSer);
 }
 
 public String endpoint = 
    "http://service.hospital.org/records/MedicalRecordService";
 private SOAPHTTPConnection m_httpConnection = null;
 private SOAPMappingRegistry m_smr = null;
 
 public PatientRecord getMedicalRecord(Long patientID) 
    throws Exception {
    PatientRecord returnVal = null;
 
    URL endpointURL = new URL(endpoint);
    Call call = new Call( );
    call.setSOAPTransport(m_httpConnection);
    call.setTargetObjectURI("chapter9.MedicalRecordService");
    call.setMethodName("getMedicalRecord");
    call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
 
    Vector params = new Vector( );
    params.addElement(new Parameter("patientID", 
      java.lang.Long.class, patientID, null));
    call.setParams(params);
 
    call.setSOAPMappingRegistry(m_smr);
 
    Response response = call.invoke(endpointURL, "");
 
    if (!response.generatedFault( )) {
      Parameter result = response.getReturnValue( );
      returnVal = (PatientRecord)result.getValue( );
    }
    else {
      Fault fault = response.getFault( );
      throw new SOAPException(fault.getFaultCode(), fault.getFaultString( ));
    }
 
    return returnVal;
 }
 
}
Using this service adapter in a regular application is easy. To print out a patient's first name, all you would need to do is:
try {
      MedicalRecordServiceAdapter adapter = 
         new MedicalRecordServiceAdapter( );
      System.out.println(adapter.getMedicalRecord(new Long(4)).getFirstName( ));
    }
    catch(Exception ex) {
      ex.printStackTrace( );
    }
9.2.2 Session Façade

Enterprise JavaBeans take much of the complexity of building a distributed system out of the hands of the developer. They do not, however, eliminate that complexity: they hide it under an easy-to-use implementation (or at least easier). Just because you, the programmer, don't have to write the code for transporting data across a TCP/IP connection, doesn't mean that the data doesn't have to get transported.
Performance can be a challenge in EJB architectures, particularly when you don't have a lot of computing muscle to put behind your application. If a client application needs to access multiple entity beans in order to perform a transaction, it must make a network call each time it retrieves a new entity bean or calls an EJB method. The performance consequences are potentially huge, since a single network invocation can take several hundred milliseconds or more, depending on the amount of work that has to be done on the other side and the state of the intervening network.

The Composite Entity Bean pattern reduces the number of entity beans in a system and makes them easier to manipulate but does not address the broader performance problem. To make matters worse, even the best entity bean designs usually involve more than one bean per use case. A client wishing to modify more than one entity bean in the course of a transaction must handle transaction control itself, via the JTA API or another mechanism. Alternately, the business logic for the specific use case can be built into one entity bean, but this approach results in a massively overcomplicated entity bean design, in which domain objects have far too much knowledge of the context in which they are used.
EJB entity beans also don't provide much help when it comes to structuring business logic. The Business Delegate pattern helps organize an application's business logic in a convenient, easy-to-use manner, but doesn't control the number of network calls or simplify transaction management (although the burden can be shifted to the Business Delegate from the client application itself, which can be of some advantage to the programmers involved).

The Session Façade pattern addresses these issues by grouping sets of activities into a session bean that can be invoked by a client to complete an entire use case at once. The façade is a business-tier object that hides the session beans, entity beans, and other business services from the presentation tier.

Using the session façade simplifies transaction management. Since transaction control is implemented at the first point of entry into a method on the EJB, a business method attached to a session façade can manipulate all of the entity and session beans required to fulfill the current use case within a single transaction.

Figure 9-8
shows a potential class diagram for an EJB session façade that provides two business methods and makes use of generic business objects, EJB session beans, and EJB entity beans (in one case directly, in another indirectly via the session bean). The session façade can also use service adapters to access non-EJB resources.
 
Figure 9-8. A session façade class structure

Session façades provide a performance boost because interactions between EJBs within a server are much more efficient than interactions between EJBs and remote clients: with EJB 2.0, local beans can use local interfaces to communicate with one another, cutting out the networking layer.
Not all session beans are session façades! Business rules usually should not be implemented within the façade itself. A mathematical formula assessing a customer's credit rating, for example, belongs either in the entity bean representing the customer or in a separate, "utility" session bean, depending on how tightly coupled it is to the entity and whether the rule needs to be reused in other contexts. Conversely, remote applications shouldn't access any EJBs except for session façades. That same credit rating assessment should be included in (or at least accessible via) a session façade when the use case reports a credit assessment back to the presentation tier.

Use cases can be divided between a set of façades, although splitting use cases between session façades can be as much an art as a science. Creating a separate session façade for each use case can rapidly lead to a profusion of session façades. At the same time, avoid creating a single session façade for the entire application, unless the application is extremely small. All-encompassing session façades are sometimes known as "God Façades" or "God Objects." A God façade inevitably becomes difficult to maintain as the number of included use cases increases.

When organizing logic into a session façade, try to locate groups of related use cases. An e-commerce system, for example, might use a single session façade for all activities related to processing sales: checking inventory, setting shipping, arranging handling, and billing customers. Another façade might support catalog activities, and a third could handle account management for individual customers.
It's helpful to organize your use cases in an outline format, using the highest-level items as your top-level session façades. This technique usually leads to a nice balance between class complexity and class profusion.
9.2.2.1 Implementing a session façade

The EJB 2.0 specification introduced local EJBs, which can boost the performance of session beans by avoiding excessive round trips to the same server (see the Round-Tripping antipattern in Chapter 12). By restricting local EJBs to the same JVM, much of the overhead associated with translating data to and from network formats can be avoided. Example 9-6 shows a simple session façade, which uses the local interfaces of a number of EJBs to process a sale.
Example 9-6. A session façade EJB
import javax.ejb.*;
import java.util.*;
import javax.naming.*;
 
public class SaleFacadeBean implements SessionBean {
    private SessionContext context;
    private LocalCustomerHome customerHome;
        private LocalItemHome itemHome;
        private LocalSalesRecordHome recordHome;
    
    public void setSessionContext(SessionContext aContext) {
        context=aContext;
    }
    
    public void ejbActivate( ) {}
    public void ejbPassivate( ) {}
    
    public void ejbRemove( ) {
        customerHome = null;
        itemHome = null;
        recordHome = null;
    }
 
    public void ejbCreate( ) {
        try {
            InitialContext ic = new InitialContext( );
            customerHome = 
               (LocalCustomerHome)
               ic.lookup("java:comp/env/ejb/local/Customer");
            itemHome = 
               (LocalItemHome)
               ic.lookup("java:comp/env/ejb/local/Item");
            recordHome = 
               (LocalSalesRecordHome)
             ic.lookup("java:comp/env/ejb/local/Record");
        } catch(Exception ex) {
            throw new EJBException(
           "Error looking up home object: " + ex, ex);
        }
    }
 
    public ReceiptDTO doSale(int itemNumbers[], int customerId) {    
        try {
           LocalCustomer cust =     
          customerHome.findByPrimaryKey(customerId);
      
           LocalItem items[] = new LocalItem[itemNumbers.length];
                for (int i = 0; i < itemNumbers.length; i++) {
               items[i] = itemHome.findByPrimaryKey(itemNumbers[i]);
           }
Sitelinkx -->			  
Page 4 of 12