ACET




REST tutorial

The GET verb

We start simply, with a Java servlet which presents the data in the database (all one record of it!) in a simple tag-value format. Create a file in CATALINA_HOME/webapps/auction/WEB-INF/classes called AuctionServer.java. The following section will build up the code, piece by piece, resulting in a complete servlet which services GET requests.

Libraries

First, import the necessary libraries.

// Standard Java utilities
import java.io.*;
import java.util.*;

// The sections of the servlet API we need
import javax.servlet.*;
import javax.servlet.http.*;

// The JDBC database library, used to access Hypersonic
import java.sql.*;

The servlet class

The AuctionServer class is the main body of the servlet. In fact, for the purposes of this tutorial, it will be the whole of the servlet.

public class AuctionServer extends HttpServlet {
}

Initialising the class

Within this class, we will need a persistent property to represent the database, plus some code to initialise it. The init(ServletConfig) method is called by Tomcat when the servlet is initialised. This will happen once for each instance of the servlet that Tomcat creates.

private Connection db_connection;
private PreparedStatement flush_db;

public void init(ServletConfig config)
	throws ServletException
{
	// Call the init method for the parent class.
	super.init(config);
	try {
		// Load the database driver
		Class.forName("org.hsqldb.jdbcDriver");

		// Connect to the database.
		// These parameters should match those
		// supplied in the hsql.rc file created earlier.
		db_connection = DriverManager.getConnection(
			"jdbc:hsqldb:/path/to/tutorial/auction",
			"sa", // Username
			""    // Password
			);

		// Hypersonic requires a "COMMIT" statement to 
		// flush the contents of memory to disk, and make 
		// the storage permanent. We will need to do this
		// frequently, so we prepare the statement once,
		// here.
		flush_db = db_connection.prepareStatement("COMMIT");
	} catch(ClassNotFoundException ex) {
		throw new ServletException(ex);
	} catch(SQLException ex) {
		throw new ServletException(ex);
	}
}

Receiving an HTTP request

The core function of a servlet is to respond to HTTP requests. The servlet interface provides for a number of ways of doing this. We will be using the service() method. Every HTTP request that is sent to the servlet will be seen by the service() method – so we will see all of the GET, POST, PUT and DELETE requests. Doing it this way allows us to use the same chunk of code to parse the URL of the request in a common way for all requests, and then despatch it to the relevant code to handle the HTTP verb.

  public void service(HttpServletRequest request,
		HttpServletResponse response)
	throws IOException, ServletException
  {
	// This method handles all incoming requests, for all HTTP
	// actions. It allows us to do all of the URL validation and
	// despatch in common across all of the actions.

	// Work out what we've been asked for.
	String path = request.getServletPath();
	String[] path_components = path.split("/");

We need to perform some sanity checks on the resource being requested.

	// Check that we've got something in the path
	if(path_components.length <= 1) {
		sendNotFoundError(response, "Path was empty");
		return;
	}

	// Check that the first part is "/item"
	if(!path_components[1].equals("item")) {
		sendNotFoundError(response, "Path did not start with /item");
		return;
	}

	// If we were asked for a specific item, get the item ID
	int item_id = getItemID(path_components);

	// If the item ID was less than -1, there was an
	// error, so return it (If it was equal to -1, then
	// there was no ID)

	if(item_id < -1) {
		sendNotFoundError(
			response,
			"Item ID " + path_components[2] + " not an integer"
			);
		return;
	}

Having checked that the resource being requested is of a form that we expect (/item/1729), we can now check if we’ve got a GET request, and call an appropriate function to handle the request.

	// Work out what the HTTP action was
	String verb = request.getMethod();

	// For the first version, we only handle HTTP GET
	if(verb.equals("GET")) {
		handleGetItem(item_id, response);
	}
  }

Parse the resource URI

We also need a function that parses the URI and looks for an item ID in it.

private int getItemID(String[] path_components) {
	// Get the ID part of an item ID request. Return -1 if no ID
	// was requested. Return a value less than -1 if there was an
	// error.

	int requested_id = -1;

	if(path_components.length >= 3) {
		try {
			requested_id = Integer.parseInt(path_components[2]);
		} catch(NumberFormatException nfex) {
			requested_id = -2;
		}
	}

	return requested_id;
}

Respond to the request

We will eventually have a handler function for each type of object and each action that could act on that object. This next function is the first of those. It handles GET requests for /item/itemid.

public void handleGetItem(int item_id, HttpServletResponse response)
	throws IOException, ServletException
{
	// The GET handler queries the database for information on the
	// object requested, and returns it in a simple tag-keyword
	// form, similar to RFC 2822.
	try {
		// Construct a query to get data out of the database
		String sql = "SELECT id, title, description, reserve, expiry"
			+ " FROM item";

		// If we were asked for /item/123, limit the query to that
		// particular ID.
		if(item_id >= 0) {
			sql += " WHERE id = ?";
		}

		// Create the query statement
		PreparedStatement stmt = db_connection.prepareStatement(sql);

		// If we were asked for /item/123, set the query parameter
		if(item_id >= 0) {
			stmt.setInt(1, item_id);
		}

		// Run the query
		ResultSet results = stmt.executeQuery();
			
		// Set up the response to send plain text output,
		response.setContentType("text/plain");
		// and get an object we can write to for the output.
		PrintWriter out = response.getWriter();

		// Go through all the results, and output them in a simple
		// tag-value format
		boolean found = false;
		while(results.next()) {
			found = true;
			out.print("id:" + results.getInt(1) + "\n");
			out.print("title:" + results.getString(2) + "\n");
			out.print("description:" + results.getString(3) + "\n");
			out.print("reserve:" + results.getFloat(4) + "\n");
			out.print("expiry:" + results.getTimestamp(5) + "\n"); 
			out.print("\n");
		}
		if(!found) {
			sendNotFoundError(response, "Item not found");
			return;
		}
	} catch(SQLException ex) {
		ex.printStackTrace(System.err);
		sendInternalError(response, "SQL Exception");
		return;
	}
}

Error handling

Finally, we will need some boring error-handling functions to return suitable error messages. There is a huge variety of HTTP error responses, many of which are never (or very rarely) seen in traditional web applications. However, some of those are useful for REST systems.

private void sendNotFoundError(HttpServletResponse response,
	String msg)
	throws IOException
{
	// Set the status to 404 -- Not found
	response.setStatus(HttpServletResponse.SC_NOT_FOUND);		

	// Get a writer for the output
	PrintWriter out = response.getWriter();
	out.println("error-code: NOT_FOUND");
	out.println("error-message: " + msg);
}

private void sendNotAllowedError(HttpServletResponse response,
	String msg)
	throws IOException
{
	// Set the status to 405 -- Not allowed
	response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);		

	// Get a writer for the output
	PrintWriter out = response.getWriter();
	out.println("error-code: METHOD_NOT_ALLOWED");
	out.println("error-message: " + msg);
}

private void sendInternalError(HttpServletResponse response,
	String msg)
	throws IOException
{
	// Set the status to 500 -- Internal server error
	response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);		

	// Get a writer for the output
	PrintWriter out = response.getWriter();
	out.println("error-code: INTERNAL_ERROR");
	out.println("error-message: " + msg);
}

That completes the first version of our service.

Compile and test

Change to the $CATALINA_HOME/webapps/auction/WEB-INF/classes directory, where the AuctionServer.java code is, and build your servlet:

(In Linux) javac -cp $CATALINA_HOME/lib/servlet-api.jar:../lib/hsqldb.jar AuctionServer.java

(In Windows) javac -cp %CATALINA_HOME%\lib\servlet-api.jar;..\lib\hsqldb.jar AuctionServer.java

Test it by going to http://localhost:8080/auction/item/0. You should be able to see the data for the item in your browser. You can also try http://localhost:8080/auction/item to get a list of all the items in the database.

In the next section, we will write a basic web client to use this service.

Valid XHTML | Copyright | Last Modified: 1/Apr/2009 |