SolutionsTools & SDKSupportForums Register



Quick Links
 
April 2004
 
 
Jack's Hack for the month of April, 2004:

Optimizing Image Delivery in the Openwave Mobile Browser using Java Servlets

Exactly a year ago now (April 2003), we looked at delivering mime-multipart/related content using an updated version of the Digest library in Perl. This month we're going to take a look at that exact same topic again, but this time instead of using Perl, we're going to do it using Java Servlets.

As a reminder of why this is an important enough topic to revisit again we need to keep in mind that with faster networks, and more powerful devices, minimizing wait time in your application becomes even more critical. In the past users might have been willing to dismiss the slow performance of a mobile app to the 9600bps data rates, or the slow processer in their phone, but expectations are being set differently for 2.5G and 3G networks. If your site does not perform quickly, you are going to loose users and revenue, so let's get started!

When an application uses a large number of images, performance can be degraded if each image needs to be fetched individually. Even though the image size maybe small, each image request will contain communication overhead that can slow down the overall transaction. Fortunately, the Openwave Mobile Browser supports MIME Multipart content, in accordance with the WAP 2.0 Wireless Application Environment speficication, and every image that a page, or even an entire application, uses can be delivered in a singe HTTP Response. While the over all size of the content delivered remains the same, there is a significant savings in terms of network overhead.

To build a servlet that delivers content in a multipart (instead of seperate responses), we can fortunatley leverage a suite of existing classes from Java Mail (the javax.mail package that is part of the J2EE platform available from sun here) and the Java Activation Framework (available here).

The overall structure of how the multipart will be constructed is represented in the image below:

We need to use a MimeMessage to hold the multipart so that we can add the headers we need to the entire Multipart before outputting it to a ServletOutputStream. Without using the MimeMessage as a container, we cannot gain insight into the boundary that is goign to be used to seperate the obejcts in the mulipart.

	MimeMessage mess = new MimeMessage(Session.getDefaultInstance(new Properties()));
	MimeMultipart multipart = new MimeMultipart( "related");

We initialize the MimeMessage with the default Properties to get an empty message that we can send (rather than initialize it with a connection to a message server as normal when constructing a mail message). Once we have these objects ready for use, we can iterate across the files we want to add to the MimeMultipart with the following loop.

	for (int i = 0; i < parts.length; i++){
	    String filename = parts[i];
	    MimeBodyPart part = new MimeBodyPart();
	    DataSource source = new FileDataSource(filepath + filename);
	    part.setDataHandler( new DataHandler( source ) );
	    File file = new File(filepath + filename);
	    part.setHeader("Content-location",file.getName());

	    //Make sure that your mime.types file has all the file types you will deliver
	    part.setHeader("Content-type", source.getContentType());
	    part.setHeader("Content-length", Integer.toString((int)file.length()));
	    multipart.addBodyPart( part );
	    if (i == 0){
		multipart.setParent(part);
	    }
		

In the code above, we are using an array of file names that will each be added as a new body part to our mime/multipart. For each file, we add the file, and set the headers of Content-Type (from the mime.types), Content-location (so the browser knows where to store it in the cache), and CONFILTERED=length to the BodyPart, and then add the Body Part to the MimeMultpart. We also set the first file as the parent of the multipart, so it is important that the markup file be the first object in the multipart.

Now that we have completed the construction of the MimeMultipart object, we can add it to the MimeMessage which we will then use to output to the ServletOutputStream. However, before we simply write to the output stream we need to clean up and modify the headers a bit (since we're not really sending a message...)

 
	mess.setContent(multipart);
	mess.saveChanges();
	String[] contentType = mess.getHeader( "content-type" );
	Enumeration headers = mess.getAllHeaders();
	
	//Yank off all the headers and write the multipart to an output stream
	//so that the size can be calculated correctly
	while( headers.hasMoreElements() ) {
	    Header header = ( Header ) headers.nextElement();
	    mess.removeHeader( header.getName() );
	}
	ByteArrayOutputStream baos = new ByteArrayOutputStream( 4096 );
	mess.writeTo( baos );
	response.setHeader("Content-Type",  contentType[0] + "\r\n");
	sout.write( ("Content-Length: " + (baos.size()-2) + "\r\n" ).getBytes() );
	
	//Write the output to the servlet output stream
	baos.writeTo(sout);
	sout.flush();

What's going on here is that we're stripping off all of the headers that the creation of a MimeMessage automatically adds for us, and applying only the ones that we really need. Instead of setting the Content-Type manually, we do need to pull it out of the object first, as it will contain the automatically created boundary that is being used between each part in the multipart.

The complete servlet code can be found in the DemoDigest class, and can be viewed using the Openwave PhoneSimulator 6.2.2 by visiting http://demo.openwave.com:8080/tool/servlet/DemoDigest. To see the difference in performance between using a digest and individual images, also view http://demo.phone.com:8080/tool/static (be sure to flush the cache on the browser by hitting the F12 key before you load either page so you can get an accurate representation of the time needed to fetch the images...)

 
Copyright © 2000-2008 Openwave Systems Inc.    About Us  |  Openwave  |  Terms & Conditions  |  Privacy Policy  |  Update Profile