Skip to main content

skip to main content

developerWorks  >  Java technology  >

Govern your images with JSP code

Bring dynamic servlet imaging under your reign

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Introductory

Dan Becker (beckerdo@us.ibm.com), Software developer, IBM Software Group

01 Nov 2002

If you code and support a Web site or have a page on the Internet, you know how difficult it can be to serve images that meet the needs of all your readers. This article proposes one solution that uses JavaServer Pages (JSP) tags to manage the images for you.

This article was published in the November 2002 issue of the IBM developerWorks journal.

If you code and support a Web site or have a page on the Internet, you know how difficult it can be to serve images for your readers. Casual readers and those with small displays or slow dial-up connections may prefer small images, perhaps 320 pixels wide by 240 pixels high or smaller. Others with large displays or fast, high-bandwidth connections may desire larger images and greater detail.

Rule the world -- or at least your images

As a site developer or page author, it is difficult to cater to all these preferences. To do this manually, you must convert each image on the site to each image size supported by your Web site. Then, you need to adjust the image tags on each page on the site so that each tag properly reflects the size of the image. You cannot simply change the HTML img tag width and height without changing the image: this causes the low bandwidth users to download the large image and resize it on the client side. If you offer eight common image sizes, it is easy to see how each image would require eight sizings and each page would require eight versions of image tags. This sort of image management becomes tedious and error prone, and it is easy to see why most Web sites do not offer multiple image sizes.

The problem is not one of technology: it is easy to use Java programming to convert images to various sizes or formats. The problem also is not one of service: it is common to use Web servers to customize pages to meet the needs of individual readers. The problem is, however, one of combining technology and service in a form that is easy to deploy and manage.

This article proposes one solution that uses JavaServer Pages (JSP) tags to manage the images for you. For instance, instead of coding image tags in HTML and having multiple versions for each image size, such as this:

<img src="images/LazyDog.jpg" width="800" height="600" >

It would make more sense to have one tag that automatically sizes the image based on the reader's preferences, such as this:

<util:imagesizer src="images/LazyDog.jpg"/>

It is possible to let the reader choose from many sizes and have the preference affect all the images on the site, as shown in the sample browser image in Figure 1. It also is possible to insert width and height attributes and remove the drudgery of editing these tags manually.


Figure 1. Example JSP page with image preferences (give us a smile)
Figure 1. Example JSP page with image preferences (give us a smile)

In case you have never seen a JSP custom tag before, let us briefly examine the syntax in the example. A JSP custom tag looks very similar to an HTML tag with the following differences:

  • There is the tag name imagesizer created by the tag developer.

  • The tag has a prefix util, which groups sets of tags into libraries much like a package name in Java programming. You can create a new prefix or use the default name that comes with the library.

  • The tag has the new XML way of ending tags, which is "/>".

Like HTML tags, JSP tags can have any number of attributes, such as the src attribute shown here, they can have bodies, and they can have other tags within their bodies. Because we are mimicking the HTML img tag, there will be no body for our JSP image sizing tag.

When a JSP page uses the custom image-sizer tag, the Java implementation of the tag finds the image file, converts it to the proper size (perhaps adding a copyright or watermark logo on the way), and serves the image to the reader. This tag saves the site manager from converting images before publishing. It also simplifies the job of writing Web pages, because only one page is required to handle many image size preferences. Finally, and most importantly, offering this type of flexibility on all your site images will be a big hit with your readers.



Back to top


What happens on the Web server

This section provides a high-level view of what happens behind the scenes when a client (a reader with a Web browser) visits a site that serves a JSP page. There are three common interactions, as shown in Figure 2:


Figure 2. Interaction between a Web client and server
Figure 2. Interaction between a Web client and server

In the first case, assume the browser simply requests a static document, such as an HTML file or an image file. The server locates the resource in its file space and serves the file back to the browser. Requesting a document and responding to a request are defined in the HTTP, which forms the basis of client-server interactions on the Internet. The Web server completely handles the request and there is no need for interaction with a servlet container or Web application server.

In the second case, assume the browser requests a Web resource containing a Java servlet. Java servlets enable a Web server to perform a task on the server using the Java programming language. Servlets are efficient and use less memory and processing power than older technologies, such as common gateway interface (CGI) and server-side JavaScript. Servlets are more portable than other technologies in that many Web servers, such as IBM WebSphere Application Server (Application Server) and Apache Tomcat, support a servlet container that can run the same servlet on many different platforms. Finally, because of the inherent safety of the Java language (such as robust exception handling and fine grain security), it is rare that an errant servlet affects the Web server. As Figure 2 shows, the Web server searches for the appropriate servlet, compiles the servlet source code if necessary, and returns the results of the servlet processing to the requester. Commonly requested servlets are cached in the server's memory.

In the third case, assume the browser requests a Web page containing a JSP page. JSP pages help ease the task of displaying information and help separate dynamic content (generated on the fly) from static pages. Web page designers use the JSP tags as if they were any other tag in the HTML library. JSP programmers follow the JSP programming specification and implement the tag according to its conventions.

The following sections explain how to implement the image-sizing JSP tag and how to write JSP pages. From the view of the Web container, a JSP page is closely related to a Java servlet. A text-based JSP page is translated by the Web container (once per page) to its Java implementation. The Web container looks for the Java implementation, treats the implementation like a Java servlet, runs the code, and returns the processing results to the client. These may seem like many layers and redirects, but the dispatching is quick and transparent to the user. As with servlets, JSP pages that are commonly requested are cached in the server's memory.



Back to top


Write custom JSP tags

Now that you see how a Web server deals with requests for JSP pages, let us look at how to implement a custom JSP tag. Note that JSP tags come both in standard libraries, such as the Java Standard Template Library (JSTL), and in libraries that you write yourself, also known as custom tags. Typically, custom tags deal with particular problem domains. For this article, we are dealing with how to manage images. Currently, Java 2 Extended Edition (J2EE) Versions 1.2 and 1.3 use the JSP specification Version 1.2. As of this writing, Sun has released a Version 2.0 of the JSP specification. This new specification has not significantly changed the way that you implement custom tags.

You can import standard and custom tag libraries into a JSP page through a taglib directive, such as this:

<%@ taglib uri='imagesizer.tld' prefix='util' %>

This directive names the location of the tag library descriptor file, here named imagesizer.tld, and a prefix for its use in the page, here named util. As shown previously in the tag example, you use a tag with its prefix and its name:

<util:imagesizer src="images/LazyDog.jpg"/>

The tag library descriptor tells the Web container what tags are available and how they function. Listing 1 shows an example of this. The file is in XML format and is easy to read, but application development platforms, such as IBM WebSphere Studio Application Developer (Application Developer), can help you fill in the fields and validate the file. The most important information is the tag element: it defines the name of the custom JSP tag and the Java class that implements the tag. It also shows any attributes and body content that the tag accepts.


Listing 1. A Tag Library Descriptor (TLD) excerpt

<taglib >
  <tlibversion> 1.0 </tlibversion>
  <jspversion> 1.1 </jspversion>
  <tag>
    <name>imagesizer</name>
    <tagclass>tags.ImageSizerTag</tagclass>
    <bodycontent>empty</bodycontent>
    <attribute>
      <name>src</name>
      <required>required</required>
    </attribute>
    <attribute>
      <name>alt</name>
    </attribute>
    <attribute>
      <name>quality</name>
    </attribute>
  </tag>
</taglib>

In this example, the tag has three attributes of which only the src attribute is required. The optional alt attribute mimics the HTML img alt attribute. As an exercise, you may expand this JSP tag to include other optional img attributes. (There are approximately 12 of them.) Finally, the implementation provides an optional quality attribute to give the page writer control of the graininess and size of the resized image.

The next step in writing a custom JSP tag is to implement the Java code for the tag. In this article, the tag imagesizer is implemented in the tags.ImageSizerTag Java class. Most of the J2EE custom tag support is located in the javax.servlet.jsp.tagext package. The imagesizer class extends the standard TagSupport, which implements tags without bodies. Its descendant class is BodyTagSupport, which also implements tags with bodies. Both classes implement the Tag interface, of which the methods doStartTag and doEndTag are called when the tag is first read and again after the tag is fully read by the Web container. The ImageSizer tag only implements the doEndTag because it needs to act once all attribute information is available.

In the TagSupport class, the PageContext class provides access to important information concerning the JSP page. For instance, the PageContext provides access to the HttpRequest and HttpResponse objects. These objects are essential to reading form values and writing responses. The request also provides access to the HttpSession in case you want to track user preferences and carry form values from page to page. PageContext also provides access to the ServletContext, which helps locate the servlet path, name, and other information. In the ImageSizer code, shown in Listing 2, there are many references to a PageContext object and the information that it provides. Figure 3 shows the relationship of these classes. As with any standard class diagram, solid boxes represent classes and dashed boxes represent interfaces. Inheritance is modeled as a line from the derived class or interface to the parent.


Listing 2. ImageSizerTag doEndTag implementation

// Implement the tag once the complete tag has been read.
public int doEndTag() throws JspException {

  // Move request data to session.
  int outputSize = 0;
  String sizeVal = request.getParameter( REQUESTSIZEKEY );
  if ( sizeVal != null ) {
    session.setAttribute( REQUESTSIZEKEY, sizeVal );
    sizeVal = (String) session.getAttribute( REQUESTSIZEKEY );
    if ( sizeVal != null ) {
      outputSize = Integer.parseInt( sizeVal );
    }
  }
  
  // Get specified image locally.
  String contextPath = getContextPath( request );
  Image image = Toolkit.getDefaultToolkit().getImage(contextPath + src );
  ImageSizer.waitForImage( image );
  int imageWidth = image.getWidth( null );
  int imageHeight = image.getHeight( null );

  if (( imageWidth > 0 ) && ( imageHeight > 0 )) {
    if (( outputSize > 0 ) && ( outputSize != imageWidth )) {
      // Convert image to new size.
      Image outputImage = ImageSizer.setSize( image, outputSize, -1 );
      ImageSizer.waitForImage( outputImage );
      int outputWidth = outputImage.getWidth( null );
      int outputHeight = outputImage.getHeight( null );
 
      if ( outputWidth > 0 && outputHeight > 0 ) {
        // Change image file name to xxxx.size.jpg
        String originalSrc = src;
        int lastDot = src.lastIndexOf( '.' );
        if ( lastDot > -1 ) {
          src = src.substring( 0, lastDot + 1 );
        }
        setSrc( src + outputSize + ".jpg" );

        // Write new size image to JPEG file.
        File file = new File( contextPath + src );
        if ( !file.exists() ) {
          out.println( "" );
          FileOutputStream fos = new FileOutputStream( contextPath + src );
          ImageSizer.encodeJPEG( fos, outputImage, quality );
          fos.close( ) ;
        }
        
        imageWidth = outputWidth;
        imageHeight = outputHeight;
      }
    } // if outputSize
  } // if image found

  // Produce output tag.
  out.print( "<img src=\"" + src + "\"" );
  // Add alt text, if any
  if ((alt != null ) && ( alt.length() > 0 )) {
    out.print( " alt=\"" + alt + "\"" );
  }
  
  // Add proper width, height.
  out.print( " width=\"" + imageWidth + "\" height=\"" + 
    imageHeight + "\"" );
  out.println( ">" );

  return EVAL_PAGE;
} // doEndTag


Figure 3. Important javax.servlet.jsp.tagext classes
Figure 3. Important javax.servlet.jsp.tagext classes

The doEndTag method of the ImageSizerTag class is shown in Listing 2. This is nearly all the Java code that is required to implement the custom JSP tag. It is a large block, but it will help you understand to see the method in its entirety. First, any HTTP request parameters are saved in the HTTP session. This takes an attribute, such as the user preference for image size, and saves it in the session so that it follows the user from page to page. To expand the capabilities of this tag, you can extend it to save the preference in a cookie so that the preference is available the next time the user visits the site.

The next step is to load a default image, which the JSP page uses as a base image from which all other images are sized. Here the java.awt.Toolkit requests the image, it is loaded with ImageSizer.waitForImage, and it is checked for proper sizing. The pause for loading is required because Java images are loaded asynchronously and are not always fully available when requested. The ImageSizer auxiliary class performs the entire image processing in this example and the following section covers this further. If width and height match, the image does not need to be resized, the large if block is skipped, and the HTML image tag is written using the image name and current size. This is all the JSP implementation needs to do to mimic the HTML image tag.

The ImageSizer auxiliary class resizes the image if the user requests a new image size. The image file is given a new name using the file size, and the file is JPEG encoded and written to the file system. Subsequently, the new resized file is used in the HTML image tag output. An alternate implementation of this tag might save the file to a GIF or PNG format or even serve the image from memory to save disk space. However, Listing 2 caches the resized file to disk for future use. Thus, the first resize requires some server processing time, but subsequent requests for the image size require no processing at all. An extension to the example could check available disk space to help balance the trade-off between limited file space and the immediacy of information that you wish to provide to your clients.



Back to top


Size an image

The previous section examined the steps in writing a custom JSP tag. The ImageSizerTag class automatically resizes images to match the user preferences. This section provides more detail on how you can resize and save the image as a JPEG file using the ImageSizer class. Resizing an image in Java code is easy with the getScaledInstance method on the java.awt.Image class. You call this method with a new width and height, or provide a value of -1 to one of the parameters to preserve the aspect ratio, and you get a new resized image. However, as with any Java image, the image is not immediately available, and so you must use a java.awt.MediaTracker to wait for the image to be fully loaded. The waitForImage method of ImageSizer encapsulates this code.

The most difficult design point in this example is deciding how to save the image. There are many choices for encoding and saving an image in Java programming, and all have various trade-offs.

  • com.sun.image.codec. This package is available in Java 2 SDK 1.2 and 1.3 implementations, but it is in a private package, which may go away in future Java 2 versions. The package is limited to JPEG encoding.

  • Java Image I/O API. This package is public and standard in Java 2 SDK 1.4. However, as of this writing, no J2EE version uses the SDK 1.4. The package offers good image manipulation capability and encoding options.

  • Java Advanced Imaging API. This API is a standard extension, but using it requires installing the package -- something your Webmaster might not support.

  • ACME GIF Encoder. This and a host of other third-party image packages are useful and you can incorporate them into the example code, but there is the question of cost and maintainability. Unlike the other options, this software is not free and it does not fully support the GIF standard.

For Listing 3, we use the com.sun.image.codec package because it is available in all J2EE 1.2 and 1.3 Web server containers, such as IBM WebSphere and Apache Tomcat. The encoders are simple and 100 percent pure Java code, but they are in the com.sun package. However, for the long term, the Java Image I/O package may be the way to go. It is rich in image transformation features and in abilities to save to many file formats. The Java Image I/O package was not standard until Java 2 version 1.4.

Having decided which imaging package to use, the code to save the JPEG file is straightforward. The encodeJPEG method of ImageSizer encapsulates the procedure:

  1. A java.awt.image.BufferedImage object, an enhanced Java Image descendant, is created from the resized output image. A comment marks the point in the code at which you can expand the example to add logos, watermarks, timestamps, or copyright information to the image.

  2. After converting the Image to a BufferedImage, create a JPEGImageEncoder object on the output stream. The output encoding quality ranges from 0.0 (worst) to 1.0 (best). 0.75 is the default, but 0.95 will produce a larger file size with a more detailed image. As an extension to this example, you might consider making the quality dependent on the image size -- smaller images require higher quality settings and larger images require lower settings.

  3. The image is encoded to the output stream, and the stream is flushed to ensure that all information shows up in the image file.

Listing 3. ImageSizer encodeJPEG implementation

// Encodes the given image at the given
// quality to the output stream.
public static void encodeJPEG
  ( OutputStream outputStream,
  Image outputImage, float outputQuality )
  throws java.io.IOException {

  // Get a buffered image from the image.
  BufferedImage bi = new BufferedImage
    ( outputWidth, outputHeight,
      BufferedImage.TYPE_INT_RGB );
  Graphics2D biContext =
    bi.createGraphics( );
  biContext.drawImage
    ( outputImage, 0, 0, null );
  // Additional drawing code, such as
  // watermarks or logos can be placed here.

  // com.sun.image.codec.jpeg package
  // is included in Sun and IBM sdk 1.3.
  JPEGImageEncoder encoder =
    JPEGCodec.createJPEGEncoder
    ( outputStream );
  // The default quality is 0.75.
  JPEGEncodeParam jep =
    JPEGCodec.getDefaultJPEGEncodeParam
    ( bi );
  jep.setQuality( outputQuality, true );
  encoder.encode( bi, jep );
  outputStream.flush();
} // encodeImage

That is all that is required to resize and save the image.



Back to top


Package and deploy on WebSphere or Tomcat

This section explains how to package and deploy the ImageSizer JSP tag on Application Server version 4.0 or Apache Tomcat version 4.0. Figure 4 shows a screen shot of Application Developer. The Navigator windowpane on the top of the left column shows the directory structure of a Web application and how a custom JSP tag must be packaged according to the J2EE specification. Because it is required in the J2EE specification, this directory structure is common to all Web applications. Once the structure is archived, it becomes a Web Archive (WAR) file and can be transported easily to WebSphere, Tomcat, or any other compliant Web container. A good development environment, such as Application Developer, helps developers follow these specifications and produce a valid application.


Figure 4. Package the ImageSizer in WebSphere Studio Application Developer
Figure 4. Package the ImageSizer in WebSphere Studio Application Developer

Under the ImageSizer project, there is a directory for source code; it is the developer's choice to include or not include this directory in the final WAR file. The webApplication directory contains the actual program code. The example project includes a test JSP page called PickASize.jsp and a great test image called LazyDog.jpg. Normally, these would not be included with a library version of the ImageSizer custom tag. The implementation of the tag is within the WEB-INF directory. The Java classes are within WEB-INF/classes, and the Tag Library Descriptor file is within WEB-INF/tlds. These are standard directory placements for all Web applications. Other files in this tree help set server options but otherwise are not mandatory to the WAR file. Use Application Developer or the Java SDK to create the WAR file for this application.

To deploy the Web application on a Web Application Server, such as To m c a t , place the file in the ROOT/webapps directory and let the server expand the WAR file into a directory structure. For Application Server, you can use the Web Application wizard in the Administrators Console to install the application. Once deployed, run the JSP page by visiting http://yourhostname:port/ImageSizer/PickASize.jsp.



Back to top


Conclusion

Now you have created a JSP custom tag that automatically manages image sizing. The custom tag saves you the work of resizing images and enables users to specify their preferences when visiting your Web site. You easily can expand this example tag to perform all sorts of image manipulation: copyright text, timestamps, logos, or watermarks. You can experiment with the code by deploying it to Application Server or Apache Tomcat and writing a few image-based JSP pages or using the given examples. I hope that this article provides a JSP tag that is useful right out of the box, but also provides the code that enables you to expand on the capability to suit your needs. Happy image browsing!




Back to top


Download

NameSizeDownload method
j-jspdwj.zipHTTP
Information about download methods


Resources

  • Visit the IBM WebSphere Developer Domain to learn more about the WebSphere family of products, including the WebSphere Application Server mentioned here.



  • Find out about Apache Tomcat and other projects at the Apache Software Foundation Web site.



  • In Advanced JavaServer Pages (Prentice Hall, 2001), David M. Geary presents advanced server-side techniques that can help you fully exploit the power of JSP technology.


About the author

Dan Becker works in the Software Group of IBM Corporation in Austin, Texas. Currently, he is working on online Web banking systems in the Denali and Interactive Financial Services projects. Before that, he was responsible for the audio subsystem in IBM Java 2 version 1.3 releases for AIX, Linux, Operating System/2, System/390, and Windows. Dan also worked on the multimedia plug-ins for Netscape Navigator for OS/2 and the multimedia parts for OS/2 Warp Version 4.0.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top