JavaEnterprise JavaServing it with Style Part II

Serving it with Style Part II

In this second part of  our tutorial on writing Java servlets, we’ll examine how servlets can receive input in the form of parameters from a CGI request, such as the output of an HTML form. This allows our servlets to become truly interactive and to solicit information from the user. As an example, we’ll look at a simple voting servlet, that keeps a tally of a yes/no response to a question and displays the results online.

The need for dynamic servlets

A great way to spice up a Web site composed of static, unchanging pages is to add dynamic content powered by Java servlets. Dynamic pages that respond to interaction with the user, or that change over time, offer big advantages for Web developers :

  • Gives Web site visitors greater control over interaction with a site
  • Reduces Web development work, as some content becomes automated and updates without human intervention.

Rather than using a series of static pages, Java servlets can augment an existing site, by providing forms that respond to user queries or record data. For example, a search form can display a list of relevant pages for a search query, giving the user greater control over his or her interaction with the Web site. Not all pages need to respond to an HTML form on a Web page though — a “what’s new” page or table of contents can display dynamic content that changes over time, based on changes to the Web site. By automating some of the human effort involved with creating and maintaining pages, significant time savings can be made.

Writing interactive servlets

In order to create interactive servlets, we must first be capable of providing the servlets with a source of input. In most cases this will be the actual Web visitor, but it could just as easily be another source of data (such as a database for a list of news items or a local file). The most important types of interaction, however, occur between people and servlets; so for the purpose of this tutorial, we’ll concentrate primarily on Web visitors. To respond to users’ requests, and establish interaction, a servlet must be capable of reading parameters.

The most important parameters that servlets have access to are those sent by the browser in a GET or POST request. When implementing a GET or POST request handler, servlets are passed an HttpServletRequest object, which contains these parameters. We can fetch parameters via the getParameter(String) method, or for multi-value parameters, getParameterValues(String) (see Listing 1).

Listing 1: Implementing a GET request

// Implement a "GET" request
public void doGet(<b>HttpServletRequest  request</b>,
                  HttpServletResponse response)
      throws ServletException, IOException
{
    // Set the content type to text/html
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();

    // Get the parameter 'username' from request
    String username = request.getParameter("username");

    if (username != null)
        out.println ("<H2>Hello there, " + username +
"</H2>);
    else
        out.println ("<H2>Who are you?</H2>");
}

Note that you should always check for the presence of parameters, rather than presuming they exist. A null value will be returned if the parameter does not exist, and any attempt at comparison (using String.equals() or String.compareTo() or string manipulation will generate a NullPointerException if the parameter was not present.

Getting initialization parameters

Another type of parameter that is useful for servlet development is an initialization parameter. This allows the webmaster to customize the functionality of a servlet, without recompilation of the Java servlet. This makes servlets more flexible and makes it less tempting for developers to "hard-wire" configuration details into the servlet. All servlets inherit a method, getInitParameter(String), which will return a configuration parameter in string format (see Listing 2).

Listing 2: Getting initialization parameters

public void doGet(HttpServletRequest  request,
                  HttpServletResponse response)
                  throws ServletException, IOException
{
    // Get initialization parameter
    String format = getInitParameter ("format");

    // Check to see if it is not null, and then compare
    if ( format == null ) format = "HTML";
    if ( format.equals ("HTML") )
    {
        // Set MIME content-type for response
        response.setContentType("text/html");
    }
    else
    {
        // Set MIME content-type for response
        response.setContentType("text/plain");
    }

    // ................         
}

Putting it all together with a practical example

To demonstrate the reading of parameters and dynamic page generation, I’ve written a simple voting servlet, that displays an online survey and maintains a tally of the results. Though the example itself is simplistic, it does demonstrate several key points

  • reading of CGI and initialization parameters
  • generation of a dynamic page that changes based on user actions
  • multi-threaded servlets.

As discussed earlier, input parameters make it possible to create dynamic pages that change in response to users. However, an important consideration that must be addressed by servlet developers is that multiple users will be interacting with a servlet concurrently. Like any multi-threaded application, there is a vital need to take care when multiple servlet instances access and modify data concurrently. Why? Because unless care is taken to restrict updates, two or more instances can overwrite each other’s modifications, and counters can become inaccurate.

One strategy for dealing with multiple threads of execution accessing and modifying data concurrently is to restrict read and write access to data to only one thread at any point in time. This strategy is used in our voting example, where access to vote tallies are provided by accessor methods. Accessor methods provide a controlled access to member variables and, in this case, are marked with the synchronized keyword to prevent concurrent access (see Listing 3).

Listing 3: Restricting read and write access to data to one thread at a time

// Static variables shared by all instances of VotingServlet
private static int VOTE_YES = 0;
private static int VOTE_NO  = 0;
private static int VOTE_UND = 0;

// Accessor methods to access member variables
public synchronized int getYes()
{
    return VOTE_YES;
}

// Acccessor methods to modify member variables
public synchronized void incYes()
{
    VOTE_YES++;
}

Our voting servlet implements both GET and POST methods. When first executed via a GET method, the servlet outputs an HTML form, showing the voting question (controlled by an initialization parameter) and a list of three radio-button choices. Figure 1 shows the output of the GET method.

Figure 1

Figure 1. GET method of voting servlet displays form

The output of this HTML form could be sent to a GET or a POST method. However, to distinguish between the action of displaying the voting question and displaying the results, I’ve chosen to redirect output to the POST method and have the GET method be responsible for outputting the form. Another alternative is to embed a hidden field parameter in a form and to display a form if the parameter isn’t present. This allows developers to implement only a single method, rather than GET and POST. Figure 2 shows a sample view of the voting servlet, after some votes have been cast.

Figure 2

Figure 2. POST method of voting servlet displays tally

Source code for VotingServlet

Listing 4: Source code for voting servlet

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

//
//
// VotingServlet
//
//
public class VotingServlet extends HttpServlet
{
        // Static variables shared by all instances of VotingServlet
        private static int VOTE_YES = 0;
        private static int VOTE_NO  = 0;
        private static int VOTE_UND = 0;

        public void doGet(HttpServletRequest  request,
                              HttpServletResponse response)
                                          throws ServletException, IOException
        {
                // Set MIME content-type for response
                response.setContentType("text/html");

                // Obtain a print writer to output our HTML form
                PrintWriter out = response.getWriter();

                // Output header
                out.println ("<H4 ALIGN=CENTER><FONT
COLOR='GREEN'>Online Voting Servlet</FONT></H4><HR
COLOR='GREEN'>");
                
                // Get initialization parameter
                String question = getInitParameter ("question");
        
                // Output question
                out.println ("<H3> " + question + "
</H3>");

                // Output HTML form
                out.println ("<FORM ACTION='" +
request.getServletPath() + "' METHOD=POST>");
                out.println ("<PRE>");
                out.println ("     <B> <input type=radio
name='vote' value='yes'> YES");
                out.println ("     <B> <input type=radio
name='vote' value='no' > NO");
                out.println ("     <B> <input type=radio
name='vote' value='und' checked> UNDECIDED");
                out.println ("<input type=submit>");
                out.println ("</PRE></FORM>");

                // Output footer
                out.println ("<HR COLOR='GREEN'>");

                out.close();
        }

        public void doPost(HttpServletRequest  request,
                               HttpServletResponse response)
                                          throws ServletException, IOException
        {
                // Set MIME content-type for response
                response.setContentType("text/html");

                // Obtain a print writer to output our HTML form
                PrintWriter out = response.getWriter();

                // Output header
                out.println ("<HEAD><TITLE>Voting
Results</TITLE></HEAD>");
                out.println ("<H4 ALIGN=CENTER><FONT
COLOR='GREEN'>Online Voting Servlet</FONT></H4><HR
COLOR='GREEN'>");
                
                // Get initialization parameter
                String question = getInitParameter ("question");
        
                // Output question
                out.println ("<H3> " + question + "
</H3>");

                // Get vote from HTML form
                String vote = request.getParameter ("vote");

                // Default to undecided if no vote cast
                if (vote == null) vote="und";

                // Increment appropriate vote counter
                if (vote.equals ("yes")) incYes();
                if (vote.equals ("no"))  incNo();
                if (vote.equals ("und")) incUndecided();

                // Output table of results
                out.println ("<TABLE BORDER=1 WIDTH=40%>");
                out.println ("<TR> <TD> <H4> Results
</H4> </TD> </TR>");
                out.println ("<TR> <TD> YES </TD>
<TD> " + getYes() + " </TD> </TR>");
                out.println ("<TR> <TD> NO </TD>
<TD> " + getNo() + " </TD> </TR>");
                out.println ("<TR> <TD> UNDECIDED
</TD> <TD> " + getUndecided() + " </TD>
</TR>");
                out.println ("</TABLE>");


                // Output footer
                out.println ("<HR COLOR='GREEN'>");

                out.close();

        }

        // Accessor methods to access member variables
        public synchronized int getYes()
        {
                return VOTE_YES;
        }

        // Accessor methods to access member variables
        public synchronized int getNo()
        {
                return VOTE_NO;
        }

        // Accessor methods to access member variables
        public synchronized int getUndecided()
        {
                return VOTE_UND;
        }

        // Acccessor methods to modify member variables
        public synchronized void incYes()
        {
                VOTE_YES++;
        }

        // Acccessor methods to modify member variables
        public synchronized void incNo()
        {
                VOTE_NO++;
        }

        // Acccessor methods to modify member variables
        public synchronized void incUndecided()
        {
                VOTE_UND++;
        }

        // Return name of servlet
        public String getServletInfo() 
        {
                return "VotingServlet";
        }
}

Configuring initialization parameters

The voting servlet uses an initialization parameter to specify the question to be voted on. Setting initialization parameters like this varies, depending on the type of servlet engine or Web server used. However, if you’re using the servletrunner engine, it’s a relatively straightforward process to configure.

  1. Edit the servlet.properties file and add the following entries

    servlet.voting.code=VotingServlet
    servlet.voting.initArgs=question="Are servlets cool or what?"

  2. Compile VotingServlet.java and copy it across to the JSDK/examples directory.
  3. Start servletrunner, and execute the servlet.

    eg – http://localhost:8080/servlet/voting  

Summary

Creating dynamic servlets that respond to user interaction is really easy and often far simpler than alternatives offered by scripting languages (many of which require you to write or use a third-party CGI parser). Additionally, servlets support initialization parameters, which allow servlets to be easily customized, without modifying any source code or recompiling the software. This offers a substantial advantage over Perl CGI scripts, which almost always need customization to remove hard-coded path information or configuration details.

Code for this article

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

//
//
// VotingServlet
//
//
public class VotingServlet extends HttpServlet
{
	// Static variables shared by all instances of VotingServlet
	private static int VOTE_YES = 0;
	private static int VOTE_NO  = 0;
	private static int VOTE_UND = 0;

	public void doGet(HttpServletRequest  request,
		              HttpServletResponse response)
					  throws ServletException, IOException
	{
		// Set MIME content-type for response
		response.setContentType("text/html");

		// Obtain a print writer to output our HTML form
		PrintWriter out = response.getWriter();

		// Output header
		out.println ("<H4 ALIGN=CENTER><FONT COLOR='GREEN'>Online Voting Servlet</FONT></H4><HR COLOR='GREEN'>");
		
		// Get initialization parameter
		String question = getInitParameter ("question");
	
		// Output question
		out.println ("<H3> " + question + " </H3>");

		// Output HTML form
		out.println ("<FORM ACTION='" + request.getServletPath() + "' METHOD=POST>");
		out.println ("<PRE>");
		out.println ("     <B> <input type=radio name='vote' value='yes'> YES");
		out.println ("     <B> <input type=radio name='vote' value='no' > NO");
		out.println ("     <B> <input type=radio name='vote' value='und' checked> UNDECIDED");
		out.println ("<input type=submit>");
		out.println ("</PRE></FORM>");

		// Output footer
		out.println ("<HR COLOR='GREEN'>");

		out.close();
	}

	public void doPost(HttpServletRequest  request,
		               HttpServletResponse response)
					  throws ServletException, IOException
	{
		// Set MIME content-type for response
		response.setContentType("text/html");

		// Obtain a print writer to output our HTML form
		PrintWriter out = response.getWriter();

		// Output header
		out.println ("<HEAD><TITLE>Voting Results</TITLE></HEAD>");
		out.println ("<H4 ALIGN=CENTER><FONT COLOR='GREEN'>Online Voting Servlet</FONT></H4><HR COLOR='GREEN'>");
		
		// Get initialization parameter
		String question = getInitParameter ("question");
	
		// Output question
		out.println ("<H3> " + question + " </H3>");

		// Get vote from HTML form
		String vote = request.getParameter ("vote");

		// Default to undecided if no vote cast
		if (vote == null) vote="und";

		// Increment appropriate vote counter
		if (vote.equals ("yes")) incYes();
		if (vote.equals ("no"))  incNo();
		if (vote.equals ("und")) incUndecided();

		// Output table of results
		out.println ("<TABLE BORDER=1 WIDTH=40%>");
		out.println ("<TR> <TD> <H4> Results </H4> </TD> </TR>");
		out.println ("<TR> <TD> YES </TD> <TD> " + getYes() + " </TD> </TR>");
		out.println ("<TR> <TD> NO </TD> <TD> " + getNo() + " </TD> </TR>");
		out.println ("<TR> <TD> UNDECIDED </TD> <TD> " + getUndecided() + " </TD> </TR>");
		out.println ("</TABLE>");


		// Output footer
		out.println ("<HR COLOR='GREEN'>");

		out.close();

	}

	// Accessor methods to access member variables
	public synchronized int getYes()
	{
		return VOTE_YES;
	}

	// Accessor methods to access member variables
	public synchronized int getNo()
	{
		return VOTE_NO;
	}

	// Accessor methods to access member variables
	public synchronized int getUndecided()
	{
		return VOTE_UND;
	}

	// Acccessor methods to modify member variables
	public synchronized void incYes()
	{
		VOTE_YES++;
	}

	// Acccessor methods to modify member variables
	public synchronized void incNo()
	{
		VOTE_NO++;
	}

	// Acccessor methods to modify member variables
	public synchronized void incUndecided()
	{
		VOTE_UND++;
	}

    // Return name of servlet
	public String getServletInfo() 
	{
		return "VotingServlet";
	}
}


About the author

David Reilly is a software engineer and freelance technical writer living in Australia. A Sun Certified Java 1.1 Programmer, his research interests includes the Java programming language and networking & distributed systems. He can be reached via email at java@davidreilly.com.


Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories