Сервлеты
В
главе 19
была упомянута технология CGI. Ее суть в том, что сетевой клиент, обычно браузер, посылает Web-серверу информацию вместе с указанием программы, которая будет обрабатывать эту информацию. Web-сервер, получив информацию, запускает программу, передает информацию на ее стандартный ввод и ждет окончания обработки. Результаты обработки программа отправляет на свой стандартный вывод, Web-сервер забирает их оттуда и отправляет клиенту. Написать программу можно на любом языке, лишь бы Web-сервер мог взаимодействовать с ней.
В технологии Java такие программы оформляются как
сервлеты
(servlets). Это название не случайно похоже на название "апплеты". Сервлет на Web-сервере играет такую же роль, что и апплет в браузере, расширяя его возможности.
Чтобы Web-сервер мог выполнять сервлеты, в его состав должна входить JVM и средства связи с сервлетами. Обычно все это поставляется в виде отдельного модуля, встраиваемого в Web-сервер. Существует много таких модулей: Resin, Tomcat, JRun, JServ. Они называются на жаргоне
сервлетными движками
(servlet engine).
Основные интерфейсы и классы, описывающие сервлеты, собраны в пакет javax.servlet. Расширения этих интерфейсов и классов, использующие конкретные особенности протокола HTTP, собраны в пакет javax.servlet.http. Еще два пакета— javax.servlet.jsp и javax.servlet.jsp.tagext — предназначены для связи сервлетов со скриптовым языком JSP (JavaServer Pages). Все эти пакеты входят в состав J2SDK Enterprise Edition. Они могут поставляться и отдельно как набор JSDK (Java Servlet Development Kit). Сервлет-ный движок должен реализовать эти интерфейсы.
Основу пакета javax.servlet составляет интерфейс servlet, частично реализованный в абстрактном классе GenericServiet и его абстрактном подклассе HttpServiet. Основу этого интерфейса составляют три метода:
init (Servietconfig config)
— задает начальные значения сервлету, играет ту же роль, что и метод init () в апплетах;
service(ServletRequest req, ServletResponse resp)
— выполняет обработку поступившей сервлету информации req и формирует ответ resp;
destroy ()
— завершает работу сервлета.
Опытный читатель уже понял, что вся работа сервлета сосредоточена в методе service о. Действительно, это единственный метод, не реализованный в классе GenericServiet. Достаточно расширить свой класс от класса . GenericServiet и реализовать в нем метод service (), чтобы получить собственный сервлет. Сервлетный движок, встроенный в Web-сервер, реализует интерфейсы ServletRequest и ServletResponse. Он Создает объекты req и resp, заносит всю поступившую информацию в объект req и передает этот объект сервлету вместе с пустым объектом resp. Сервлет принимает эти объекты как аргументы req и resp метода service о, обрабатывает информацию, заключенную в req, и оформляет ответ, заполняя объект resp. Движок забирает этот ответ и через Web-сервер отправляет его клиенту.
Основная информация, заключенная в объекте req, может быть получена методами read() и readLine() ИЗ байтового потока класса ServletlnputStream, непосредственно расширяющего класс inputstream, или из символьного потока класса BufferedReader. Эти потоки открываются, соответственно, методом req.getlnputStream() или методом req.getReader (). Дополнительные характеристики запроса можно получить многочисленными методами getxxxo объекта req. Кроме того, класс GenericServlet реализует массу методов getxxxo, позволяющих получить дополнительную информацию о конфигурации клиента.
Интерфейс servietResponse описывает симметричные методы для формирования ответа. Метод getoutputstreamo открывает байтовый поток класса ServletOutputStream, непосредственно расширяющего класс OutputStream. МеТОД getWriter () открывает символьный поток класса PrintWriter.
Итак, реализуя метод service!), надо получить информацию из входного потока объекта req, обработать ее и результат записать в выходной поток объекта resp.
Очень часто в объекте req содержится запрос к базе данных. В таком случае метод service о обращается через JDBC к базе данных и формирует ответ resp из полученного объекта ResultSet.
Протокол HTTP предлагает несколько методов передачи данных: GET, POST, PUT, DELETE. Для их использования класс GenericServlet расширен классом HttpServlet, находящимся В пакете javax.servlet.http. В этом классе есть методы для реализации каждого метода передачи данных:
doGet(HttpServletRequest req, HttpServletResponse resp)
doPost(HttpServletRequest req, HttpServletResponse resp)
doPut(HttpServletRequest req, HttpServletResponse resp)
doDelete(HttpServletRequest req, HttpServletResponse resp)
Для работы с конкретным HTTP-методом передачи данных достаточно расширить свой класс от класса HttpServlet и реализовать один из этих методов. Метод service () переопределять не надо, в классе HttpServlet он только определяет, каким HTTP-методом передан запрос клиента, и обращается к соответствующему методу doXxxo. Аргументы перечисленных методов req и resp — это объекты, реализующие Интерфейсы HttpServletRequest и HttpServletResponse, расширяющие интерфейсы ServletRequest и ServietResponse, соответственно.
Интерфейс HttpServletRequest к тому же описывает множество методов getxxx (), позволяющих получить дополнительные свойства запроса req.
Интерфейс HttpServletResponse описывает методы addxxxO и setxxxo, дополняющие ответ resp, и статические константы с кодами ответа Web-сервера.
В листингах П.2 и П.З те же действия, что выполняет программа листинга П. 1, реализованы с помощью сервлета. Апплет теперь не нужен, в окно браузера выводится HTML-форма, описанная в листинге П.2.
Листинг П.2.
HTML-форма запроса к базе данных
<html><head><title> JDBC Servlet</title></head>
<body>
<form method = "POST" action = "/servlet/JdbcServlet">
<pre>
URL базы: <input type = "text" size = "40" name = "url"
value = "jdbc:oracle:thin:@localhost:1521:ORCL">
Имя: <input type = "text" size = "40" name = "login"
value = "scott">
Пароль: <input type = "password" size = "40" name = "password"
value = "tiger">
Запрос:
<textarea rows = "5" cols "70" name = "query">
SELECT * FROM dept
</textarea>
<input type = "submit" value = "Послать">
</pre>
</form>
</body>
</html>
Сервлет получает из этой формы адрес базы url, имя login и пароль password пользователя, а также запрос query. Он обращается к базе данных Oracle через драйвер JDBC Oracle Thin, формирует полученный ответ в виде HTML-страницы и отправляет браузеру.
Листинг П.3
. Сервлет, выполняющий запрос к базе Oracle i
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import j ava.sql.*;
public class JdbcServlet extends HttpServlet{ static)
try{
DriverManager.registerDriver(
new oracle.j dbc.driver.OracleDriver());
)catch(SQLException e){
System.err.println(e) ;
}
)
private Vector results = new Vector();
private int n;
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException{
ServletExceptionf String url = req.getParameter("url") ,
login = req.getParameter("login") ,
password = req.getParameter("password") ,
query = req.getParameter("query");
// Задаем MIME-тип и кодировку для выходного потока pw
resp.setContentType("text/html;charset=windows-1251");
PrintWriter pw = resp.getWriter();
try{
Connection con =
DriverManager.getConnection(url, login, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
n = rsmd.getColumnCount(); while (rs.next()){
String s = " ";
for (int i = 1; i <= n; i++)
s += " " + rs.getObject(i);
results.addElement(s); }
rs.close();
st.close();
con.close();
}catch(SQLException e){
pw.println("From doPostf): " + e) ;
}
pw.println("<html><head><title>Answers</titlex/head>");
рw.рrintln("<body><h2>Результаты 3anpoca</h2>");
n = results.size();
for (int i = 0; i < n; i++)
pw.println((String)results.elementAt(i) + "<br>");
pw.println("</bodyx/html>") ;
pw.flush() ;
pw.close () ;
}
}
Применение сервлета позволило "облегчить" клиент — браузер не загружает апплет, а только отправляет запрос и получает ответ. Вся обработка запроса ведется в сервлете на сервере.
В системе J2SDKEE (Java 2 SDK Enterprise Edition) HTML-файл и сервлет образуют один Web-компонент. Они упаковываются в один файл с расширением war (web archive) и помещаются в так называемый Web-контейнер, управляемый Web-сервером, расширенным средствами J2SDKEE.