Creating the Forecaster JSP

In this project, the first task is to get the user's ZIP Code. You're going to need the ZIP Code in order to get the National Weather Service's high and low temperature forecasts, so that's the first order of business. After getting that ZIP Code, the code will concentrate on getting the forecast and graphing it.

Getting the ZIP Code

In the JSP, there are two ways to get the ZIP Code. First, you can supply a ZIP Code as part of the URL encoded as ?zip=nnnnn, this way, which sets the URL to 94707:

http://www.yourjsphost/username/forecast.jsp?zip=94707

This way is handy in case you want to embed the JPEG image this JSP creates in a web page, as you see in Figure 10.1. You can use the URL to the JSP, with the encoded ZIP Code, in an HTML <IMG> tag's SRC attribute to display the forecast in a web page like this (this is the page you see displayed in Figure 10.1—forecast.jsp just sends a JPEG back to the browser, but by using an HTML <IMG> tag, you can neatly embed that JEPG in a web page):

<HTML> tag;SRC attribute>;SRC attribute> tag> tag>
    <HEAD>
        <TITLE>
            The Forecaster
        </TITLE>
    </HEAD>

    <BODY>
        <CENTER>
            <H1>
                Forecasting the weather
            </H1>
            <IMG SRC="forecast.jsp?zip=94707">
        </CENTER>
    </BODY>
</HTML>
<IMG SRC="" />

Here's the second way the code can get the ZIP Code: If you don't supply a URL this way, forecast.jsp will ask you for your URL with an HTML text field, and it'll store that URL in a cookie on your machine.

NOTE

If you want to use the Forecast.java standalone application, on the other hand, it'll ask you the first time you run it what your ZIP Code is and then store it in a file named zip.txt, which it uses from then on.


The forecast.jsp page begins with the JSP page directive, which it uses both to import the needed Java classes and to set the type of the content it will send back to the browser. Setting the content type is particularly important, because this is how you tell the browser that you're not sending it HTML, but a JPEG image instead.

Here is the syntax for the JSP page directive:

<%@ page
          [ language="java" ]
          [ extends="package.class" ]
          [ import="{package.class | package.*}, ..." ]
          [ session="true | false" ]
          [ buffer="none | 8kb | sizekb" ]
          [ autoFlush="true | false" ]
          [ isThreadSafe="true | false" ]
          [ info="text" ]
          [ errorPage="relativeURL" ]
          [ contentType="mimeType [ ;charset=characterSet ]"
              |   "text/html ; charset=ISO-8859-1" ]
          [ isErrorPage="true | false" ] 
%>

Here are the attributes that are important in this case:

  • import="{package.class | package.* }, ..." You assign this attribute a comma-separated list of Java packages that you want to import. Once imported, the packages, and their contents, will be available to scriptlets, expressions, and declarations in the JSP.

  • contentType="mimeType [ ;charset=characterSet ]" | "text/html;charset=ISO-8859-1" You assign this attribute the MIME type (including, if you want to, the character encoding) the JSP code will use in the response it sends to the browser. Note that the default MIME type is “text/html,” and the default character encoding is “ISO-8859-1.”

In this case, the code needs to import the java.io.*, java.awt.*, java.awt.image.*, java.net.*, and com.sun.image.codec.jpeg.* packages, which looks like this in the page directive:

<%@ page import="java.io.*, java.awt.*, java.awt.image.*,java.net.*,com.sun.image.codec
.jpeg.*" %>
        .
        .
        .

To tell the browser that you're sending back a binary image in JPEG format, you can set the contentType attribute to the “image/jpeg” MIME type:

<%@ page contentType="image/jpeg" import="java.io.*, java.awt.*, java.awt.image.*,java.net
.*,com.sun.image.codec.jpeg.*" %>
        .
        .
        .

How do you recover the ZIP Code if the user has supplied one? He might have URL-encoded a ZIP Code, like this:

http://www.yourjsphost/username/forecast.jsp?zip=94707.com

Encoding data this way is often how the data in HTML controls is sent back to the server, so you can recover the ZIP Code by pretending that it was entered by the user into an HTML control named “zip.” To recover that data, you'd use this code in forecast.jsp:

<%@ page contentType="image/jpeg" import="java.io.*, java.awt.*, java.awt.image.*,java.net
.*,com.sun.image.codec.jpeg.*" %>

<%
    String zip = "";
						if(request.getParameter("zip") != null){
						zip = request.getParameter("zip");
						.
						.
						.
						}
    .
    .
    .
}

If the ZIP Code was found this way, the code only needs to call the drawImage method (covered in a few pages) that will perform the data gathering and drawing:

<%@ page contentType="image/jpeg" import="java.io.*, java.awt.*, java.awt.image.*,java.net
.*,com.sun.image.codec.jpeg.*" %>

<%
    if(request.getParameter("zip") != null){ 
        zip = request.getParameter("zip");
        drawImage(zip, response);
						return;
    }
    .
    .
    .
}

So far, so good. But what if the ZIP Code hasn't been URL-encoded? In that case, perhaps it was already encoded in a cookie set by forecast.jsp earlier. The cookie that forecast.jsp sets is called zipCookie, and the code checks if it already exists and sets a Boolean variable named foundCookie to true if it does.

Here's how that works. First, the code uses the request object's getCookies method to get the current cookies passed to the code by the browser as an array of Cookie objects:

<%@ page contentType="image/jpeg" import="java.io.*, java.awt.*, java.awt.image.*,java.net
.*,com.sun.image.codec.jpeg.*" %>

<%
    Cookie[] cookies = request.getCookies();
						boolean foundCookie = false; 
    String zip = "";
    .
    .
    .
}

This is the first time code in this book has worked with cookies, and you can find the significant methods of the Java Cookie class in Table 10.1. Being able to use cookies is a powerful technique, and JSP is up to the task.

Table 10.1. Significant Methods of the javax.servlet.http.Cookie Class
MethodDoes This
java.lang.String getComment()Returns a comment that describes the purpose of this cookie
java.lang.String getDomain()Returns the domain name for the cookie
int getMaxAge()Returns the maximum age of the cookie (given in seconds). The default value is -1, which means the cookie should be deleted when the browser is closed
java.lang.String getName()Returns the name of the cookie as text
boolean getSecure()Returns a value of true if the browser is sending cookies using only a secure protocol
java.lang.String getValue()Returns the value of the cookie as text
int getVersion()Returns the version of the protocol this cookie uses
void setComment (java.lang.String purpose)Sets a comment that describes this cookie and/or its purpose
void setDomain(java.lang.String pattern)Sets the domain associated with this cookie
void setMaxAge(int expiry)Sets the maximum age of the cookie (given in seconds)
void setSecure(boolean flag)Specifies to the browser whether or not the cookie should only be returned to the server using a secure protocol
void setValueAssigns a text value to the cookie (java.lang.String newValue)
void setVersion(int v)Sets the version of the cookie protocol this cookie uses

Now the code loops over the cookie array in order to search for the cookie named zipCookie:

<%@ page contentType="image/jpeg" import="java.io.*, java.awt.*, java.awt.image.*,java.net
.*,com.sun.image.codec.jpeg.*" %>

<%
    Cookie[] cookies = request.getCookies();
    boolean foundCookie = false;
    String zip = "";

    if(request.getParameter("zip") != null){
        zip = request.getParameter("zip");
        drawImage(zip, response);
        return;
    }

    if(cookies != null){
						for(int loopIndex = 0; loopIndex < cookies.length; loopIndex++)
						{
						.
						.
						.
						}
						}
    .
    .
    .
}

It can determine if the current cookie in the loop is named zipCookie by using the cookie's getName method:

    if(cookies != null){
        for(int loopIndex = 0; loopIndex < cookies.length; loopIndex++)
        {
            Cookie cookie1 = cookies[loopIndex];
						if (cookie1.getName().equals("zipCookie")) {
						.
						.
						.
						}
        }
    }
    .
    .
    .
}

If the zipCookie object was found, you can get the ZIP Code from it with the Cookie object's getValue method, and you can also set the foundCookie Boolean to true:

    if(cookies != null){
        for(int loopIndex = 0; loopIndex < cookies.length; loopIndex++)
        {
            Cookie cookie1 = cookies[loopIndex];
            if (cookie1.getName().equals("zipCookie")) {
                zip = cookie1.getValue();
						foundCookie = true;
            }
        }
    }
    .
    .
    .
}

If you've found the cookie, you've also got the ZIP Code, so you're ready to go. But what if the cookie didn't exist? In that case, you've got to ask the user for his ZIP Code and store it in the cookie. The Forecaster project does that with the web page you see in Figure 10.3.

Figure 10.3. Asking for the user's ZIP Code.


So how do you handle the case where the user enters his ZIP Code this way? The text field you see in Figure 10.3 is named “textField,” and you can check if the user has filled his ZIP Code in that text field this way, if the cookie doesn't exist:

    if(cookies != null){
        for(int loopIndex = 0; loopIndex < cookies.length; loopIndex++)
        {
            Cookie cookie1 = cookies[loopIndex];
            if (cookie1.getName().equals("zipCookie")) {
                zip = cookie1.getValue();
                foundCookie = true;
            }
        }
    }

    if (!foundCookie) {
						if(request.getParameter("textField") != null){
						.
						.
						.
						}
        .
        .
        .
}

If there's text waiting for you in the textField parameter, that text is the user's ZIP Code. The code stores that ZIP Code in the zipCookie cookie first:

if (!foundCookie) {
    if(request.getParameter("textField") != null){
        Cookie cookie1 = new Cookie("zipCookie",
						request.getParameter("textField"));
						.
						.
						.
        .
        .
        .
    }

Then you can set the maximum age of the cookie—in this case, the code sets it to 365 * 24 * 60 * 60 seconds, or one year, before it expires. To set that cookie in the user's machine, you can use the response object's addCookie method this way:

if (!foundCookie) {
    if(request.getParameter("textField") != null){
        Cookie cookie1 = new Cookie("zipCookie",
            request.getParameter("textField"));
        cookie1.setMaxAge(365 * 24 * 60 * 60);
						response.addCookie(cookie1);
        .
        .
        .
    }

At this point, you can also display the forecast using the ZIP Code the user has entered into the text field in Figure 10.3 by using the drawImage method, which will be created in a few pages:

if (!foundCookie) {
    if(request.getParameter("textField") != null){
        Cookie cookie1 = new Cookie("zipCookie",
            request.getParameter("textField"));
        cookie1.setMaxAge(365 * 24 * 60 * 60);
        response.addCookie(cookie1);
        drawImage(request.getParameter("textField"), response);
    }
    .
    .
    .
}

On the other hand, what if there is no text waiting for you in the textField parameter? In that case, the user hasn't seen the input page that appears in Figure 10.3 yet, and you should display it. Here's how that looks: (Note that this is where the input text field named “textField” is created.)

    if (!foundCookie) {
        if(request.getParameter("textField") != null){
            Cookie cookie1 = new Cookie("zipCookie",
                request.getParameter("textField"));
            cookie1.setMaxAge(365 * 24 * 60 * 60);
            response.addCookie(cookie1);
            drawImage(request.getParameter("textField"), response);
        }
        else{
						%>
						<HTML>
						<HEAD>
						<META HTTP-EQUIV="Expires" CONTENT="-1">
						<TITLE>
						Forecaster
						</TITLE>
						</HEAD>
						<BODY>
						<H1>
						Forecaster
						</H1>
						<FORM NAME="form1" METHOD="POST">
						Please enter your five-digit zip code:
						<INPUT TYPE="TEXT" NAME="textField"></INPUT>
						<BR>
						<BR>
						<INPUT TYPE="SUBMIT" VALUE="Submit">
						</FORM>
						</BODY>
						</HTML>
        .
        .
        .

If the user doesn't need to see the input page, you already have his ZIP Code at this point, so you're ready to call the method that does all the work, drawImage:

    if (!foundCookie) {
        if(request.getParameter("textField") != null){
        .
        .
        .
        }
        else{

%>
    <HTML>
        <HEAD>
            <META HTTP-EQUIV="Expires" CONTENT="-1">
            <TITLE>
                Forecaster
            </TITLE>
        </HEAD>
        .
        .
        .
    </HTML>
<%
        }
						}
						else{
						drawImage(zip, response);
						}
%>

Excellent, you've gotten the user's ZIP Code, whether it's from URL-encoding, a cookie, or the text field. Now it's time to start the real business of the Forecast project—displaying the forecast.

Gathering the Weather Data

Next on the list is writing the drawImage method, which does the actual work, now that you have the user's ZIP Code at hand. You pass this method the ZIP Code and response object (you need the response object so the code can send the JPEG back to the browser). Because this is a new method, you need to declare it in a JSP declaration (not a scriplet, which can't support method declarations). This is created with the markup <%! and %>:

<%!
public void drawImage(String zip, HttpServletResponse response) 
{
    .
    .
    .
}
%>

The drawImage method starts by getting the weather data from the National Weather Service. The National Weather Service has a web page at http://www.srh.noaa.gov that allows you to enter the ZIP Code you're interested in. By taking a look at the web page, you find that the ZIP Code is sent to a PHP file named zipcity.php and that the text field containing the ZIP Code is called “inputstring.” To automate the submission process to include the user's ZIP Code, then, you can create a Java URL object like this:

public void drawImage(String zip, HttpServletResponse response)
{
    try {
						URL url = new URL
						("http://www.srh.noaa.gov/zipcity.php?inputstring="
						+ zip);
        .
        .
        .
}

To connect to the National Weather Service's website, you can use a URLConnection object this way:

public void drawImage(String zip, HttpServletResponse response)
{

    try {

        URL url = new URL
            ("http://www.srh.noaa.gov/zipcity.php?inputstring="
            + zip);

        URLConnection urlconnection = url.openConnection();
        .
        .
        .
}

You can find the significant methods of the Java URLConnection class, which is terrific for downloading web pages in code, in Table 10.2.

Table 10.2. Significant Methods of the java.net.URLConnection Class
MethodDoes This
void addRequestProperty (String key, String value)Sets a request property as given by this key-value pair
abstract void connect()Connects to the resource referenced by this URL
int getConnectTimeout()Returns the value of the connection timeout (given in seconds)
Object getContent()Returns the contents of this URL connection as an object
String getContentEncoding()Returns the current value of the content-encoding header
int getContentLength()Returns the current value of the content-length header
String getContentType()Returns the current value of the content-type header
long getDate()Returns the current value of the date header
boolean getDoInput()Returns the current value of this URLConnection object's doInput setting
boolean getDoOutput()Returns the current value of this URLConnection object's doOutput setting
long getExpiration()Returns the current value of the expires header field
String getHeaderField(int n)Returns the value for a given header
String getHeaderField (String name)Returns the value of a header specified by name
long getHeaderFieldDate (String name, long Default)Returns the value of the named field treated as a date
int getHeaderFieldInt (String name, int Default)Returns the value of the named field treated as an int
String getHeaderFieldKey(int n)Returns the key for a given header field
Map<String,List<String>> getHeaderFields()Returns a Java map containing the headers
InputStream getInputStream()Returns an input stream that lets you read from this connection
long getLastModified()Returns the value of the last-modified header
OutputStream getOutputStream()Returns an output stream that you can use to write to this URL connection
Permission getPermission()Returns an object giving the permission you need to connect using this object
int getReadTimeout()Returns the current setting for the timeout length (given in milliseconds)
Map<String,List<String>> getRequestProperties()Returns a Java map containing request properties for this connection
String getRequestProperty (String key)Returns the value of the named request property
URL getURL()Returns the value of this URLConnection object's URL
void setConnectTimeout (int timeout)Sets a timeout value, in milliseconds, used when opening a connection
void setDoInput(boolean doinput)Sets the value of the doInput setting for this URLConnection object to the given value
void setDoOutput (boolean dooutput)Sets the value of the doOutput setting for this URLConnection object to the given value
static void setFileNameMap (FileNameMap map)Sets the FileNameMap setting for this connection
void setReadTimeout(int timeout)Sets the timeout for reading operations to the given value (given in milliseconds)
void setRequestProperty (String key, String value)Sets a request property to the given value

How do you read the National Weather Service page that's returned by this URL, letting you recover the weather data? You can open an InputStream object to the URL using the URLConnection class's getInputStream method this way in the code:

public void drawImage(String zip, HttpServletResponse response)
{

    try {

        URL url = new URL
            ("http://www.srh.noaa.gov/zipcity.php?inputstring="
            + zip);

        URLConnection urlconnection = url.openConnection();

        InputStream in = urlconnection.getInputStream();
        .
        .
        .
}

So far, so good. Now that you have an InputStream object connected to the National Weather Service's web server, you can read in the target web page to a temporary buffer, which in this case is a String object named input:

public void drawImage(String zip, HttpServletResponse response)
{

    try {

        URL url = new URL
            ("http://www.srh.noaa.gov/zipcity.php?inputstring="
            + zip);

        URLConnection urlconnection = url.openConnection();

        InputStream in = urlconnection.getInputStream();

        String input = "";
						String inchar;
						char[] cc = new char[1];
						while ((character = in.read()) != -1) {
						char z = (char)character;
						cc[0] = z;
						inchar = new String(cc);
						input += inchar;
						}
						in.close();
        .
        .
        .
}

At this point, you've got the weather data, and you can start searching it for high and low temperature predictions. You can see a representative National Weather Service forecast in Figure 10.4; notice the “Hi” numbers (these are displayed in red) and the “Lo” numbers (these are displayed in blue) at the bottom of the page. These numbers give the forecast.

Figure 10.4. A representative National Weather Service forecast.


To retrieve the high temperature forecasts for the next four days, you can search for the formatting text that immediately precedes the high temperatures. The National Weather Service pages use either the HTML Hi <font color="#FF0000"> or Hi: <span class="red"> right in front of the high temperature forecast values. You can determine which HTML is used in the page you've retrieved by searching for these HTML strings and then storing named hiSearch:

public void drawImage(String zip, HttpServletResponse response)
{
    try {

        URL url = new URL
            ("http://www.srh.noaa.gov/zipcity.php?inputstring="
            + zip);

        URLConnection urlconnection = url.openConnection();
        .
        .
        .
        in.close();

        if(input.indexOf("Hi <font color="#FF0000">") >= 0){
						hiSearch = "Hi <font color="#FF0000">";
						}
						else{
						hiSearch= "Hi: <span class="red">";
						}
        .
        .
        .
}

Now that you know what HTML to search for, you can retrieve the four-day high temperature forecasts and store them in an array named hiTemperature this way:

public void drawImage(String zip, HttpServletResponse response)
{
    String hiTemperature[] = new String[4]; 

    try {

        URL url = new URL
            ("http://www.srh.noaa.gov/zipcity.php?inputstring="
            + zip);

        URLConnection urlconnection = url.openConnection();
        .
        .
        .
        if(input.indexOf("Hi <font color="#FF0000">") >= 0){
            hiSearch = "Hi <font color="#FF0000">";
        }
        else{
            hiSearch= "Hi: <span class="red">";
        }

        int currentPosition = 0;
						for(int loopIndex = 0; loopIndex < 4; loopIndex++){
						int location = input.indexOf(hiSearch,
						currentPosition);
						int end = input.indexOf("&deg;", location);
						hiTemperature[loopIndex] = input.substring(location +
						hiSearch.length(), end);
						currentPosition = end + 1;
						}
    .
    .
    .
}

That fills the hiTemperature array with the highs. Now what about the lows? The National Weather Service pages use either Lo <font color="#0033CC"> or Lo: <span class="blue"> as the HTML to format the low temperature forecasts, so you can determine which format the current page uses and fill an array named loTemperature with the forecasted low temperatures this way:

public void drawImage(String zip, HttpServletResponse response) 
{
    String hiTemperature[] = new String[4];
    String loTemperature[] = new String[4];
        .
        .
        .
        if(input.indexOf("Lo <font color="#0033CC">") >= 0){
            loSearch = "Lo <font color="#0033CC">";
        }
        else{
            loSearch= "Lo: <span class="blue">";
        }

        currentPosition = 0;

        for(int loopIndex = 0; loopIndex < 4; loopIndex++){
            int location = input.indexOf(loSearch,
                currentPosition);
            int end = input.indexOf("&deg;", location);
            loTemperature[loopIndex] = input.substring(location +
                loSearch.length(), end);
            currentPosition = end + 1; 
        }
        .
        .
        .
}

Excellent. You've now stored the high temperature forecasts in the hiTemperature array and the low temperature forecasts in the loTemperature array. You've got your data—the next step is to graph that data and send it back to the user.

Graphing the Data

The problem now breaks down to graphing the data in the hiTemperature and loTemperature arrays and then sending it back to the browser.

The code in the drawImage method continues by drawing the image that will be sent back to the browser, using a BufferedImage object, and creating a Graphics2D object, called g, to draw in that image:

BufferedImage image = new BufferedImage(225, 201, 
     BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
.
.
.

You can start by filling the image with a white background (when first created, it's black):

g.setColor(Color.white);
g.fillRect(0, 0, 224, 200);
.
.
.

Here's how to draw the grid lines you see in Figure 10.1, in gray:

g.setColor(Color.gray);

for(int loopIndex = 0; loopIndex < 21; loopIndex++){
    g.drawLine(25, loopIndex * 10, 224, loopIndex * 10);
    g.drawLine(loopIndex * 10 + 25, 0, loopIndex * 10
        + 25, 199);
}
.
.
.

Next come the temperature labels, 10 to 90 degrees, you see along the left in Figure 10.1. Here's how to draw them in blue:

g.setColor(Color.blue); 
Font font = new Font("Courier", Font.PLAIN, 18);
g.setFont(font);

for(int loopIndex = 20; loopIndex < 200; loopIndex += 20){
    g.drawString(String.valueOf(100 - loopIndex / 2), 0,
        loopIndex + 5);
}
.
.
.

Okay, what about drawing the high and low temperature forecasts? First the highs, which are displayed in red. As you can see in Figure 10.1, each temperature forecast is drawn with a circle, and then the circles are connected. In the evening, the National Weather Service's forecasts omit the day's high, starting instead with the evening's low. For that reason, the code determines if it's evening by checking whether the low or high forecast appears first:

boolean evening = false;

if(input.indexOf(loSearch) < input.indexOf(hiSearch)){
    evening = true;
    hiTemperature[3] = hiTemperature[2];
    hiTemperature[2] = hiTemperature[1];
    hiTemperature[1] = hiTemperature[0];
}
.
.
.

After you determine whether this is the evening, here's how to draw those circles, each corresponding to a high temperature forecast (note that the code checks if it's evening in order to determine whether today's high value is no longer available):

g.setColor(Color.red);

if(!evening){
    g.drawOval(65 - 4, 200 - (Integer.parseInt(
        hiTemperature[0]) * 2) - 4, 8, 8);
}
g.drawOval(105 - 4, 200 - (Integer.parseInt(hiTemperature[1]) *
    2) - 4, 8, 8);
g.drawOval(145 - 4, 200 - (Integer.parseInt(hiTemperature[2]) *
    2) - 4, 8, 8);
g.drawOval(185 - 4, 200 - (Integer.parseInt(hiTemperature[3]) *
    2) - 4, 8, 8);
.
.
.

Then you connect those circles with lines to get the upper figure you see in Figure 10.1:

if(!evening){
    g.drawLine(65, 200 - (Integer.parseInt(
        hiTemperature[0]) * 2), 105, 200 -
        (Integer.parseInt(hiTemperature[1]) * 2));
}
g.drawLine(105, 200 - (Integer.parseInt(hiTemperature[1]) * 2),
    145, 200 - (Integer.parseInt(hiTemperature[2]) * 2));
g.drawLine(145, 200 - (Integer.parseInt(hiTemperature[2]) * 2),
    185, 200 - (Integer.parseInt(hiTemperature[3]) * 2));
.
.
.

Okay, now what about the low temperature forecasts? Here's how that works:

g.setColor(Color.blue);

g.drawOval(65 - 4, 200 - (Integer.parseInt(loTemperature[0]) *
    2) - 4, 8, 8);
g.drawOval(105 - 4, 200 - (Integer.parseInt(loTemperature[1]) *
    2) - 4, 8, 8);
g.drawOval(145 - 4, 200 - (Integer.parseInt(loTemperature[2]) *
    2) - 4, 8, 8);
g.drawOval(185 - 4, 200 - (Integer.parseInt(loTemperature[3]) *
    2) - 4, 8, 8);

g.drawLine(65, 200 - (Integer.parseInt(loTemperature[0]) * 2),
    105, 200 - (Integer.parseInt(loTemperature[1]) * 2));
g.drawLine(105, 200 - (Integer.parseInt(loTemperature[1]) * 2),
    145, 200 - (Integer.parseInt(loTemperature[2]) * 2));
g.drawLine(145, 200 - (Integer.parseInt(loTemperature[2]) * 2),
    185, 200 - (Integer.parseInt(loTemperature[3]) * 2));
.
.
.

Finally, here's how to create the label box with the text “Four-Day Forecast” and the attribution “Source: Nat. Weather Srvce.” that you see in Figure 10.1:

g.setColor(Color.white);
g.fillRect(55, 160, 140, 30);
g.setColor(Color.blue);
g.drawRect(55, 160, 140, 30);

font = new Font("Courier", Font.PLAIN, 12);
g.setFont(font);
g.drawString("Four-Day Forecast", 65, 172);

font = new Font("Courier", Font.PLAIN, 9);
g.setFont(font);
g.drawString("Source: Nat. Weather Srvce.", 58, 185);
.
.
.

That completes the image. However, all this work will have gone for nothing if you can't send the image back to the browser. You've already indicated in the JSP page's page directive that you're going to be sending back a JPEG image, but how do you actually do that?

This turns out to be simpler than you might imagine. You can create a com.sun.image.codec.jpeg.JPEGImageEncoder object that will encode the image and send it back to the browser. To create that object, you use the createJPEGEncoder method of com.sun.image.codec.jpeg.JPEGCodec, passing it the OutputStream object you want to send the data to.

You can get an OutputStream object that will send data back to the browser using the JSP response object's getOutputStream method. Here's what that looks like (note that this code opens a binary output stream to the browser):

        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder
            (response.getOutputStream());
        .
        .
        .
}

You can find the significant methods of the Java JPEGCodec class in Table 10.3 and the significant methods of the Java JPEGImageEncoder class in Table 10.4. Both these classes come bundled with Java, no extra downloads needed.

Table 10.3. Significant Methods of the com.sun.image.codec.jpeg.JPEGCodec Class
MethodDoes This
static JPEGImageDecoder createJPEGDecoder(InputStream src)Creates a JPEGImageDecoder object that you can use to decode JPEG data
static JPEGImageDecoder createJPEGDecoder (InputStream src, JPEGDecodeParam jdp)Creates a parameterized JPEGImageDecoder) object that you can use to decode JPEG data
static JPEGImageEncoder createJPEGEncoder (OutputStream dest)Creates a JPEGImageEncoder object that you can use to encode image data as JPEG data
static JPEGImageEncoder createJPEGEncoder (OutputStream dest, JPEGEncodeParam jep)Creates a parameterized JPEGImageEncoder object that you can use to encode image data as JPEG data
static JPEGEncodeParam getDefaultJPEGEncodeParam (BufferedImage bi)Lets you create JPEGEncodeParam objects given a buffered image to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (int numBands, int colorID)Lets you create JPEGEncodeParam objects, given the number of bands and a color ID to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (JPEGDecodeParam jdp)Lets you create JPEGEncodeParam objects, given a JPEGDecodeParam object to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (Raster ras, int colorID)Lets you create JPEGEncodeParam objects, given raster and color ID to hold parameter settings

Table 10.4. Significant Methods of the com.sun.image.codec.jpeg.JPEGImageEncoder Class
MethodDoes This
void encode(BufferedImage bi)Encodes a buffered image into JPEG data
void encode(BufferedImage bi, JPEGEncodeParam jep)Encodes a buffered image as JPEG data, using a JPEGEncodeParam object
void encode(Raster ras)Encodes a Raster object as a JPEG data stream
void encode(Raster ras, JPEGEncodeParam jep)Encodes a Raster object as a JPEG data stream using a JPEGEncodeParam object
static JPEGEncodeParam getDefaultJPEGEncodeParam (BufferedImage bi)Lets you create JPEGEncodeParam objects given a buffered image to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (int numBands, int colorID)Lets you create JPEGEncodeParam objects, given the number of bands and a color ID to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (JPEGDecodeParam jdp)Lets you create JPEGEncodeParam objects, given a JPEGDecodeParam object to hold parameter settings
static JPEGEncodeParam getDefaultJPEGEncodeParam (Raster ras, int colorID)Lets you create JPEGEncodeParam objects, given raster and color ID to hold parameter settings
JPEGEncodeParam getJPEGEncodeParam()Returns a copy of the current JPEGEncodeParam object
OutputStream getOutputStream()Returns the output stream the encoder is associated with
void setJPEGEncodeParam (JPEGEncodeParam jep)Sets the JPEGEncodeParam object used for encoding operations

REAL-WORLD SCENARIO: INTERACTIVE ONLINE IMAGES

Creating interactive online images and sending them to the browser in real time is one of the exciting things you can do with Java. But most Java programmers don't know that they can do this—let alone that it's so easy, just two lines of code.

All the functionality you need is packed into the com.sun.image.codec.jpeg packages, and more programmers have been putting this technology to work over the years. However, the fact that these classes are not in the standard Java packages has made adoption of these auxiliary classes slower than it should have been.

On the other hand, developers who know how to make this work know that the potential is great. You can put together real-time stock tickers, financial reports, system resource usage charts, and, as shown in this chapter, up-to-date weather forecasts. You can add your picture to your data or charts, display photos of current users, and even interact with the users if you track their mouse movements using JavaScript event handling to let them draw JPEG images.


Now that you've created a JPEGImageEncoder object, you can encode and send the image to the browser as a JPEG image this way:

        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder
            (response.getOutputStream());

        encoder.encode(image);
        .
        .
        .
}

That's all it takes. Now you've created the forecast graph and have sent it back to the browser. If you want to embed the image in an HTML page, use an HTML <IMG> element like this (change this to supply your correct ZIP Code):

<IMG SRC="forecast.jsp?zip=94707">

Congratulations, you've written your own online daily temperature forecaster in Java, forecast.jsp. This JSP is ready to go. Just store it in the webapps directory of a JSP-enabled Internet server.

What about writing the standalone desktop version? That's coming up next.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.176.155