This week you will start to develop servlets. Servlets are to Servers what applets are to browsers. They are programs that extend the functionality of a server. They have no user interface, but often generate an HTML document in response to a user request. Like CGI scripts they can process data submitted using a HTML form. They are easier to write and give better performance than CGI scripts. You will see a servlet that allows a user to enter a new item in a database from a browser. Servlets can also deposit information the client side using cookies. You will see how to use cookies next week.
The Java Tutorial Continued on line http://java.sun.com/docs/books/tutorial/overview/index.html describes the following applications for servlets.
Here are a few more of the many applications for servlets:
- Allowing collaboration between people. A servlet can handle multiple requests concurrently, and can synchronize requests. This allows servlets to support systems such as on-line conferencing.
- Forwarding requests. Servlets can forward requests to other servers and servlets. Thus servlets can be used to balance load among several servers that mirror the same content, and to partition a single logical service over several servers, according to task type or organizational boundaries.
Get requests are used to retrieve specified information from a specified URL. The data is normally supplied as an HTML document (web page). You will create a simple get request servlet. The one problem with the doGet is that it displays the data that is transmitted in the location box of the browser. If passwords or other sensitive material is sent do not use doGet. The doPut does not transmit this information in the location.
| First create a servlet with the "Generate doGet() Method" checked. You will not have to add parameters for a doGet servlet, so click on finish. | |
| You will now have a project. The HTML document generated will have the appropriate form to allow the user to do the get. You need only add the text that describes the purpose of this page. In this case "Go to Servlet Land." | |
| The actual code for this page is shown here. | |
| The Java code for the servlet itself is mostly generated for you. Here is that code. Just add the one line pointed to by the red arrow. | |
| When you run the servlet, you will see the following message appear in the dos box. | |
| Next open the HTML document from your browser and click on the Submit button. You will see the web page at the right displayed. | |
Add a member variable count to the class and initialize it to zero. The add the first two lines below just above the /body line.
count++;
out.println("< P>You are viewer number " + count + " of this site < /P>" );
out.println("< body>" );
Now run the servlet and access it a few times from the web page to see how it works.
As you discovered above there are two parts to every servlet, the servlet itself and the html page that calls the servlet.
The central part of the HTML page that calls the servlet is a form. Forms are used to gather information from a user and pass that information to the servlet at the URL specified by the ACTION where it is processed by a METHOD specified. The action specifies the port used for the process. In this case the URL is localhost:8080 because you want to test this program on one machine.
< FORM ACTION = http://localhost:8080/servlet/getServlet.Servlet1 METHOD="GET">
Normally Web browsers use port 80 as a default and the URL is just the standard web address. The following form comes from the login page of the Java Devlopers site. The site specified here is different from the one on which the page resides.
< FORM NAME="seek1" METHOD="GET" ACTION="http://search.java.sun.com/query.html">
On the other hand the following form (also found on the Sun Java Developer login page) calls a servlet that resides at the same site as the web page, just in a subdirectory of the directory containing the Web page.
< FORM METHOD=POST ACTION="/servlet/SessionServlet">
Finally, the button that causes the request to be executed is created with this HTML code.
< input type=submit value="Submit">< input type=reset value="Reset">< /form>
The type "submit" generates a button. The "value" sets the name to appear on the button. When a user presses an input type submit the form information is submitted to the method named in the form. The other button is of input type "reset". Pressing this type of button causes the form to be cleared.
First notice that you have to import "import javax.servlet.*;" if you want to create a servlet. All servlet classes implement the interface Servlet. When a servlet accepts a call it receives two objects:
The class HttpServlet implements Servlet by overriding the service method so that these servlets handle Web browser requests. Normally HttpServlets implement "Post" and "Get" requests. The ServletRequest and ServletResponse classes are implemented by HttpServletRequest and HttpServletResponse .
To get a better understanding of what your code does look at the program you have just generated. The doGet has two paramerters the request and the response. Since this is a doGet you do not expect to receive information from the client, so you will not see any use for the request here. On the other hand the response is used throughout the function.
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
In the following line, the context type of the response is to a string that indicates the MIME type of the response to the browser. This indicates how to display the information sent back to the browser. You generated html code, so the type was "text/html".
response.setContentType("text/html" );
Once the type of the data is established, you have to open an output stream to the client. The getOutputStream function obtains a byte-based output stream that enables binary data to be sent to the client. PrintWriter implements functions necessary to write strings to a byte stream. Once you have initialized out as a PrintWriter, use println to write out the text that creates an html document. Notice when you are done, you should close the PrintWriter.
PrintWriter out = new PrintWriter (response.getOutputStream());
out.println("< html>" );
out.println("< head>< title>Servlet1< /title>< /head>" );
out.println("< H1>Welcome to Servlet Country< /H1>" );
count++;
out.println("< P>You are viewer number " + count + " of this site" );
out.println("< body>" );
out.println("< /body>< /html>" );
out.close();
}
Get the complete code for the get servlet by clicking here .
Repeat the steps above, this time checking "Generate doPost() Method" as shown in the servlet introduction. This time add the userName parameter and name just like the example on that page.
| JBuilder will create the following HTML document. | |
| The code generated by JBuilder is shown at the right. Add the lines highlighted with red arrows and a red bracket. | |
| Run this servlet and open the corresponding HTML file. You should see the window at the right. Enter your name in the text box and click on submit. | |
| The program will reply with the web page at the right. | |
Input elements are used to transmit information from the client to the server. In this doGet example, you not only used buttons, you used a textbox. You could also have used a radio button, or checkbox. There are some fairly obscur types such as "hidden" which does not appear in the page, but is used to transmit information from the client. The "file" element is used when the user is supposed to select a file so that the contents of that file may be submitted with the request.
You may see part of the page containing the login form by clicking here. Look at the source code for this page and try typing in each of the fields. Notice that the password field
< INPUT TYPE="PASSWORD" NAME="Password" VALUE="" SIZE="10" MAXLENGTH="30">
This code gives you the following text box. Type in it to check out how it works.
n addition to input elements, programmers can provide users with combo boxs created using the SELECT element and its attendent OPTION element. Value is the actual string returned by the getParameter call described below. If no value is specified, the string following the option is returned. For example if "VALUE = NE" is not included in the first option, "Northeast" is returned. The following code:
< FORM> < SELECT NAME = REGION> < OPTION VALUE = NE > Northeast < OPTION VALUE = MA > Middle Atlantic < OPTION VALUE = SE > Southeast < OPTION VALUE = MW >Midwest < OPTION VALUE = NW > Northwest < OPTION VALUE = W> West < OPTION VALUE = SW> Southwest < /SELECT>< /FORM>
gives you this listbox.
Check boxes are used when you want to retrieve zero or more values from a group of possible values. They differ from radio buttons since more than one may be checked. To get a check box you need only set the type of the input to checkbox. The results returned are of course strings, but there are two different ways that check boxes return strings. If you give all of the checkboxes in a group the same name, then the result will be a string with commas separating the values.
When you use check boxes that all have the same name, you have to parse the string returned to look for commas. The easiest way to do this is to use IndexOf(',') to find the locations of commas and substring to get each string. The following code suggests itself:
String check = "" ;
try { check = request.getParameter("check" ); } catch (Exception e) { e.printStackTrace(); }
String checkResults[]={"" ,"" ,"" };//There are three check boxes in this case
int howManyChecks=0;
if (check !=null ) {
int begin=0;
int end = check.indexOf(',' );
while (end!=-1){
checkResults[howManyChecks++]=check.substring(begin,end);
begin = end+1;
end = check.indexOf(',' ,end+1);
}
end = check.length();
checkResults[howManyChecks++]=check.substring(begin,end);
}
//latter in the page generation
if (int i = 0; i< howManyChecks;i++)
out.println("check " +i+" is " +checkResults[i]+"< BR>" );
This code can be retrieved by clicking here .
If you had given the check boxes below the same name, and check boxes 1 and 3 you would get 1,3 back as your results. If you give each checkbox a different name, you have to retrieve each value separately. In either case, the value may be null if no box is checked, so you must first check for null before doing anything to the string.
The checkboxes above were generated by the following code. In this case the program was designed to compute the sum of the values returned (similar to the type of computation needed to get the style of a font).
Check 'til your heart's content < BR>< BR>check 1 < input type = checkbox name = "check1" value = 1> < BR>< BR>check 2 < input type = checkbox name = "check2" value = 2> < BR>< BR>check 3 < input type = checkbox name = "check3" value = 3>
The following code is used to process this set of check boxes. Notice that in each case you must check for null (not checked) before calling on parseInt to converte the string returned to an integer.
String check1 = "" ;
try { check1 = request.getParameter("check1" ); } catch (Exception e) { e.printStackTrace(); }
String check2 = "" ;
try { check2 = request.getParameter("check2" ); } catch (Exception e) { e.printStackTrace(); }
String check3 = "" ;
try { check3 = request.getParameter("check3" ); } catch (Exception e) { e.printStackTrace(); }
int sum =0;
if (check1!=null )
sum+= Integer.parseInt(check1);
if (check2 !=null )
sum+=Integer.parseInt(check2);
if (check3 !=null )
sum += Integer.parseInt(check3);
Radio buttons are used when you want the user to respond with exactly one value from a group of related choices. The radio boxes are grouped by giving them all the same name and different values. The values are returned as a string. You can make sure that one of the boxes are checked by adding the worded "checked" to the one you want as the default. In this example the first button is the default.
Check one only please < BR>< BR>radio 1 < input type = radio name = "radio" value = 0 checked> < BR>< BR>radio 2 < input type = radio name = "radio" value = 1> < BR>< BR>radio 3 < input type = radio name = "radio" value = 2>
This is how this set of radio buttons looks in a browser.
You can get this code and the last checkbox code from above by clicking here .
Get the complete code for this Post servlet by clicking here .
Most of the Java code is similar to the first example. One difference is that since this is a doPost method, you expect to get information back. The program created the variable userName to hold the value returned from the client. The HttpServletRequest, request, which retrieves information from the client is used here. The function getParameter takes a parameter that is a string that corresponds to the name of the input (or select) element the data is comming from and returns the value submitted. Notice that there must be something submitted or there is an exception thrown.
//The user's name
String userName = "" ;
userCount++;
try { userName = request.getParameter("userName" ); }
catch (Exception e) { e.printStackTrace(); }
Today you will create a servlet that updates a database. Servlets don't have user interfaces, so the techniques you learned last week cannot be used in this program. You must instead go back to some of the basic java commands contained in java.sql.*.
Build a servlet with a doPost method just as you did in day one of this week. Create two parmeters profName and profRank. Now you are ready to add the database code.
Create a database with one table ProfTable. You can get such a database by clicking here. Next create the alias "ProfDatabase" using ODBC (through BDE) like you did last week. Now add the variables connection, url, userName and password as show below. These variables will be used to connect to the database.
You are now ready to add the code that will initialize you database. First you must import java.squ.* since all of the database functions are in this package. The class definition for the database driver must be loaded before the program can connect to the database. In this case you will use the sun JdbcOdbcDriver which is a java language bridge from jdbc to odbc. The class is loaded by the line
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver" );
Once the driver is loaded, the database is connected by the line:
connection = DriverManager.getConnection(url,userName,password);
In this case the user name and password are blank, but the url is complicated. It starts with the communications protocol, "jdbc" with a colon separator, then the communications subprotocol, "odbc" and finally the database alias ProfDatabase. This whole statement means that the program will use jdbc to connect to a Microsoft ODBC data source. ODBC is a technology that allows generic access to disparate database systems on the Windows Platform and some UNIX platforms.
public class ProfServlet extends HttpServlet {
private Connection connection;
private String url = "jdbc:odbc:ProfDatabase" ;
private String userName ="" ;
private String password ="" ;
//Initialize global variables
public void init(ServletConfig config) throws ServletException {
super.init(config);
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver" );
connection = DriverManager.getConnection(url,userName,password);
}catch (ClassNotFoundException cnfex){
System.err.println("Failed to load JDBC/ODBC driver." );
}
catch (SQLException sqlex){
System.err.println("Unable to Connect" );
}
}
The doPost function will allow the client page to post new members for the database, then show a page with the entire database. The first part of this function retrieves the two values from the client and sets up the response so you can create a Web page to send back. The database is open, so you must create a query. Java has the class Statement that can execute queries. There are two queries in this program. The insertQuery inserts the new professor into the table. You construct this query by a concatenation of constant strings along with the variable values returned from the client. Notice the format of this query is:
INSERT INTO(list of columns with values following in the order they are listed in the values section) VALUES (list of values separated by commas. Each value enclosed in single quotes)
Once the query string is created, ask the connection to create a statement then use the executeUpdate to execute the insert query and executeQuery to execute the select all query (like the ones you saw last week).
String insertQuery = "INSERT INTO profTable (" +
"profName, profRank" +
") VALUES (" +
"'" +profName+"', '" +
profRank + "')" ;
Statement statement1 = connection.createStatement();
int result = statement1.executeUpdate(insertQuery);
if (result != 1)
throw new Exception("Bad Insert Query" );
statement1.close();
String query = "SELECT * FROM profTable" ;
Statement statement=connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
The update query returns a number 1 if successful, while the select query returns a ResultSet. Result sets act like arrays in that you can access items with an iterator like function "next". When you build your response page, you want to add a table like display of the professor table. You can use a function like getNextRow given here to retrieve on record from the database. The ResultSetMetaData class contains detailed information about the result set such as number of columns and names of each column. Here the number of columns is used to make the data retrieval flexable.
String getNextRow(ResultSet rs, ResultSetMetaData rsmd)throws Exception
{
String row="" ;
for (int i = 1; i< = rsmd.getColumnCount();i++)
row += rs.getString(i)+" " ;
return row;
}
The ResultMetaData is used in the doPost function to retrieve the column names for a header.
boolean moreRecords = resultSet.next();
if (!moreRecords)
out.println("< P>No professors in out records< /P>" );
else {
ResultSetMetaData rsmd = resultSet.getMetaData();
out.print("< H3>" );
for (int i = 1; i< = rsmd.getColumnCount();i++)
out.print(rsmd.getColumnName(i)+" " );
out.print("< /H3>< BR>" );
do {
out.print(getNextRow(resultSet,rsmd)+"< BR>" );
}while (resultSet.next());
statement.close();
When you run this program and open you html page using a browser, you will see the following.
After entering the new item data, and pressing submit, the updated database appears in the window.
//Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//Professors name
String profName = "" ;
try { profName = request.getParameter("profName" ); } catch (Exception e) { e.printStackTrace(); }
//Professors rank
String profRank;
profRank = request.getParameter("profRank" );
if (profRank == null)
profRank = "Assistant ";
try {
response.setContentType("text/html" );
String insertQuery = "INSERT INTO profTable (" +
"profName, profRank" +
") VALUES (" +
"'" +profName+"', '" +
profRank + "')" ;
Statement statement1 = connection.createStatement();
int result = statement1.executeUpdate(insertQuery);
if (result != 1)
throw new Exception("Bad Insert Query" );
statement1.close();
String query = "SELECT * FROM profTable" ;
Statement statement=connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
PrintWriter out = new PrintWriter (response.getOutputStream());
out.println("< html>" );
out.println("< head>< title>ProfServlet< /title>< /head>" );
out.println("< body>" );
out.println("< H1>Current Professor List< /H1>" );
boolean moreRecords = resultSet.next(); //Load the first result from the query
if (!moreRecords)
out.println("< P>No professors in out records< /P>" );
else {
ResultSetMetaData rsmd = resultSet.getMetaData();
out.print("< H3>" );
for (int i = 1; i< = rsmd.getColumnCount();i++)
out.print(rsmd.getColumnName(i)+" " );
out.print("< /H3>< BR>" );
do {
out.print(getNextRow(resultSet,rsmd)+"< BR>" );
}while (resultSet.next());
statement.close();
}
out.println("< /body>< /html>" );
out.close();
} catch (Exception e) { e.printStackTrace(); }
}
The SQL update statement is relatively simple. It takes the form:
UPDATE tableName SETfieldName1 = value1, fieldName2 = value2... WHERE criteria
Remeber to place single quotes (') around string values. The criteria is normally a test to see if the item keyfield has a particular value, but if you were changing a company name in your database, the test would be if the field containing the company name was equal to the old name. You might want to update the inventory of an item ordered by a customer. In this case, you would retrieve the id for the product, and the current inventory. You would subtract the number purchased from currentInventory and execute the following SQL:
UPDATE Inventory SET NumberInStock = "+ currentInventory + "WHERE id = "+ productId
Get the complete code for this database servlet by clicking here .