|
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 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 MimeMessage mess = new MimeMessage(Session.getDefaultInstance(new Properties())); MimeMultipart multipart = new MimeMultipart( "related"); We initialize the
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 Now that we have completed the construction of the
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 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 |
