 |  |  | | Contents: |  | |
 | | Related content: |  | |
 | | Subscriptions: |  | |
| How to successfully apply traditional HTTP development to the pervasive computing environment
Lin Ma (mallin@cn.ibm.com), Software Engineer, IBM Yu Chen Zhou (zhouyuc@cn.ibm.com), Advisory Software Engineer, IBM Jian Lin (jianlin@cn.ibm.com), Manager, IBM
01 Mar 2005 This article helps you apply your knowledge of developing applications with an HTTP service under Java™ 2 Platform, Standard Edition (J2SE) and Java 2 Platform, Enterprise Edition (J2EE) environments to the Java 2 Platform, Micro Edition (J2ME) environment. The authors have illustrated and resolved some of the setbacks specific to the J2ME environment by practical example in an open standard platform: the Service Management Framework (SMF). Today, with the advent of pervasive computing technology, more J2ME applications are transforming from stand-alone J2ME applications, which only run locally and never communicate with other applications, to interactive J2ME applications, which communicate with remote servers and applications on other devices. One of the most popular approaches for this type of communication is HTTP. The Service Management Framework (SMF), IBM's major embedded platform for the J2ME environment, provides a service called the HTTP service for HTTP development. However, when using traditional HTTP development models in an SMF environment, developers will experience some setbacks. This article illustrates these setbacks and gives practical solutions for transforming traditional HTTP development models and applying them to an SMF/J2ME environment. We will first introduce SMF, then summarize the setbacks when using traditional HTTP development models in an SMF environment. Finally, we will provide two examples to illustrate HTTP buffering and multiple threading issues when applying traditional HTTP development models to an SMF/J2ME environment. You will also be provided with related solutions and source code. The Service Management Framework IBM's SMF is an implementation of the Open Service Gateway initiative (OSGi) Service Platform Release 3 specification. OSGi is an industry organization that defines and promotes an open standard for the networked delivery of managed services to local networks and devices. An open standard, meaning a standard that manufacturers create and voluntarily adhere to, has the advantage of minimizing the number of incompatible products and, therefore, exclusive to other standards. The Open Services Gateway standard is intended to complement other residential standards and is open to almost any protocol, transport, or device layer -- making it inclusive of other standards. With SMF's HTTP service, you can easily develop HTTP-enabled applications. Listing 1 shows how to develop a servlet using the HTTP service on a mobile device, and you can follow the same approach for complex HTTP-based applications. You can download the complete source code for this simple example in the Resources section. Here, we will only show the major source code. In the first step, you will have to implement a class that implements the BundleActivator interface, which tracks an SMF's HTTP service, then registers a servlet into the HTTP service. Here is the source code for that task: Listing 1. Tracking an HTTP service
httpContext = new HttpContext()
{
public boolean handleSecurity (HttpServletRequest request, HttpServletResponse response)
{
return true;
}
public URL getResource(final String name)
{
return (URL) AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
String resource = name;
if (resource.charAt(0) != '/')
{
resource = "/".concat(resource);
}
return getClass().getResource(resource);
}
});
}
public String getMimeType(String name)
{
return null;
}
};
Filter filter = context.createFilter(
"("+Constants.OBJECTCLASS+"="+HttpService.class.getName()+")");
System.out.println ("Start service tracker, filter="+filter);
tracker = new ServiceTracker(context, filter, this);
tracker.open();
|
Then you'll have to implement a method called addingService(), which is invoked automatically when you track the HTTP service. When you track an HTTP service, a servlet registers to the service, as Listing 2 shows: Listing 2. Servlet registration
public Object addingService(ServiceReference reference)
{
Object retVal=null;
Object prop=reference.getProperty(Constants.OBJECTCLASS);
String[] classArray = (String[]) prop;
String className = classArray[0];
System.out.println ("Info: adding service reference=" + className);
if (className.equals(HttpService.class.getName())) {
httpServiceRef = reference;
httpService = (HttpService) context.getService(httpServiceRef);
if (httpService != null) {
System.out.print("Info: Got httpService ");
Object portString = (Object) reference.getProperty("http.port");
System.out.println("\nport=" + portString);
try {
if (null == servlet) {
System.out.println(
"Panic: servlet is null in startServlet function.");
}
httpService.registerServlet(
servletURI,
servlet,
null,
httpContext);
} catch (ServletException e) {
Throwable n = e.getRootCause();
if (n == null) {
n = e;
}
context.ungetService(httpServiceRef);
httpService = null;
} catch (NamespaceException e) {
Throwable n = e.getException();
if (n == null) {
n = e;
}
context.ungetService(httpServiceRef);
httpService = null;
}
}
retVal = httpService;
}
return retVal;
}
|
The second step is to develop a standard Java™ servlet. In our example, we will simply print out a string "Hello developerWorks!" when the URL http://localhost/SampleHttpServlet accesses the servlet. This example can run in IBM WebSphere® Device Developer V5.7.1 and Service Management Framework (SMF) V5.7.1. Setbacks for applying a traditional HTTP development model to J2ME As you can see, developing an HTTP-based application in an SMF/J2ME environment follows the same model in a J2SE/J2EE environment, except that we need a BundleActivator to register servlets. However, if we simply copy the same model for the J2ME environment, we will face some setbacks. When we develop HTTP-based applications in a J2SE or J2EE environment, we run and host them on HTTP servers. But when we develop HTTP-based applications in an SMF/J2ME environment, the applications will run on capability-limited devices (short of computing power and memory), such as PDAs and set-top boxes (STBs). Obviously, the running environments differ greatly. We won't experience major setbacks if we simply copy the same model to perform demos or prototypes that are not mission-critical. If you use these techniques to make real-world applications under a very strict testing environment, you will encounter some critical issues not found in a J2SE/J2EE environment. This is especially true when running applications on a capability-limited device for a relatively long period of time, or in an environment where network connections are not very stable. From our experience, most of the issues are caused by HTTP buffering and multiple threading approaches. In the following section, we will illustrate the issues in a simple approach, along with practical samples. Then we will describe how to resolve the issues for SMF/J2ME application development. Issues related to HTTP buffering Put simply, HTTP buffering means that even if a developer uses APIs at the application level to send data, the data still remains in the buffer of an HTTP stack locally -- that is, until there is enough data ready to transfer. This approach will enhance the performance of HTTP communication because data is transferred in relatively large blocks, which saves the time in setup and release network connections. But the buffering approach is not always perfect. Sometimes data is not delivered instantly. If you design an application based on the logic that requested data will be retrieved instantly, the approach may fail in that regard. In Listing 3, a "client" servlet running on a device will request a large amount of data from a "server" servlet running on another device. We will divide the response data into two parts: textual data and binary data. The textual data transfers in an HTTP response header field called "Custom," and the binary data transfers into an HTTP response body. Normally, the source code for Listing 3 will achieve the target in traditional HTTP application development. Method dataRequest() runs on the client servlet to send a request to a server servlet. From this method, we can see when the servlet opens the connection to another servlet; it will try to retrieve the response header field "Custom." If the servlet cannot retrieve this field, it will return and print out an error message. Method dataResponse() runs on the server servlet to send a response to a client servlet. From this method, we can see when the servlet receives the connection from another servlet. It will try to set the value of the response header field "Custom" and stuff a response body with binary data reading from a file. Listing 3. Code affected by HTTP buffering
private void dataRequest(String urlString) {
URL url=null;
StringBuffer sb = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
int contentLength = -1;
int bs = 0;
try {
url = new URL (urlString);
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
try {
URLConnection conn = url.openConnection();
if (null == conn.getHeaderField ("Custom))
{
System.out.println ("Can not get Custom header field!");
return;
}
InputStream is = conn.getInputStream();
// deals with is in the following logics
}
public void dataResponse(HttpServletResponse res)
throws ServletException, IOException {
OutputStream os = res.getOutputStream();
byte[] buffer = null;
InputStream is = null;
try {
res.setHeader("Custom","sample.dat");
buffer = new byte [1024];
int count;
is = new FileInputStream ("c:\\temp\\sample.dat");
while( true ) {
count = is.read( buffer );
if ( count > 0 ) {
os.write(buffer, 0, count);
try {
Thread.yield();
} catch(Exception e) {
}
} else break;
}
} catch( NullPointerException e ) {
throw new IOException("got a NullPointer: "+e.toString());
} finally {
is.close();
}
}
}
|
When running the above example frequently -- and for an extended period of time -- on a capability-limited device, the error message "Cannot get customer header field" may print out. Because of HTTP buffering, the header field is buffered on the server side and sent out only after the application reads all the file content and closes the InputStream. At that time, the client servlet fails to retrieve the header field because it is not transported instantly. How can we apply a stable HTTP development model to an SMF/J2ME environment? The solution is very simple: We only need to flush HTTP header data (force data transfer irrespective of buffering) before transferring the HTTP body. In that case, the HTTP response header will transfer instantly. Here is the updated method dataResponse(): Listing 4. Code free of HTTP buffering
public void dataResponse(HttpServletResponse res) throws ServletException, IOException {
OutputStream os = res.getOutputStream();
byte[] buffer = null;
InputStream is = null;
try {
res.setHeader("Custom","sample.dat");
os.flush();
buffer = new byte [1024];
int count;
is = new FileInputStream ("c:\\temp\\sample.dat");
while( true ) {
count = is.read( buffer );
if ( count > 0 ) {
os.write(buffer, 0, count);
try {
Thread.yield();
} catch(Exception e) {
}
} else break;
}
} catch( NullPointerException e ) {
throw new IOException("got a NullPointer: "+e.toString());
} finally {
is.close();
}
}
}
|
For similar issues dealing with HTTP buffering, you can apply this flush approach to the appropriate logic. Issues related to multiple threading When a servlet in an SMF HTTP service receives a request, SMF creates an individual thread to handle the request and give a response. This thread invokes a related request handle method. If the method's execution is blocked (for any number of reasons) and does not return in a limited interval, the thread may be terminated relatively quickly (faster than in traditional HTTP application development). This is especially true when capability-limited devices are receiving multiple HTTP requests in a short interval. In this approach, a capability-limited device will be protected because the resources occupied by blocked methods will be released in a timely manner. But when the handle method takes up time (for example, to do some complex computation), SMF may wrongly treat it as a "blocked" method and terminate it, causing the termination of our processing task and the retrieval of no result. We've illustrated this problem and a related solution on how to write effective time-consuming methods in an SMF environment. In the following example, when SMF invokes the doGet() method, it also invokes a time-consuming method called task_timeconsuming. SMF may terminate the processing of task_timeconsuming, especially when we run the following method on a capability-limited device: Listing 5. Code affected by multiple threading
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// preparation task
// time consuming task
task_timeconsuming();
// clean up task
}
|
To solve this issue, we can create another thread to process the time-consuming method. In Listing 6, we create a class TimeConsumingThread, which SMF executes as a separate thread and wraps around the HTTP request handle and HTTP response handle. When the HTTP service receives an HTTP request and invokes the doGet() method, it creates a TimeConsumingThread object that processes the time-consuming task in a separate thread. Therefore, the doGet() method returns immediately. In this approach, even if SMF terminates the doGet() method, the time-consuming task will proceed in another thread. Listing 6 illustrates the whole framework for this solution: Listing 6. Code free of multiple threading issues
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// preparation task
// time consuming task
Thread timeConsumingProcess = new Thread (new TimeConsumingThread (req, res));
timeConsumingProcess.start();
// clean up task
}
public class TimeConsumingThread implements Runnable {
HttpServletRequest req = null;
HttpServletResponse res = null;
public timeConsumingThread(HttpServletRequest req, HttpServletResponse res) {
this.req = req;
this.res = res;
}
public void run() {
// processing time-consuming task
}
|
Conclusion Developing J2ME applications with an HTTP service can give mobile applications more attractive features. However, at the same time, you may encounter some setbacks you wouldn't find in a J2SE/J2EE environment. This article has illustrated these setbacks and offered solutions through a simple and practical approach in an open standard environment -- that of SMF. With your knowledge of traditional HTTP development and some critical concerns under a J2ME environment, you can now easily transform your previous knowledge to J2ME applications. Resources Download
| Name | |  | | Size | |  | | Download method | |  | | wi-httpsource.zip | |  | | 11.4 KB | |  | | FTP | |  |  |  |  |  |  |  |  |  |  |  |  |  |
About the authors Lin Ma is a software engineer at the IBM China Software Development Lab. He is also a winner of the developerWorks Internal Author Award. His work focuses on pervasive computing technologies and services. | Yu Chen Zhou is an Advisory Software Engineer at the IBM China Software Development Lab. He is also a winner of the developerWorks Internal Author Award. His work focuses on pervasive solutions.
| Jian Lin is the manager of Pervasive Computing Device Software Development and Services at IBM China Software Development Lab. He is also a winner of the developerWorks Internal Author Award.
|

|  |