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

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.
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

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.
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

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.
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:
- 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.
- 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.
- 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.
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

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.
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!
Download | Name | Size | Download method |
|---|
| j-jspdwj.zip | | HTTP |
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
|  |