В первой части этой статьи (см. «Мир ПК», № 3/99, с. 140) мы познакомились с возможностями сервлетов. Теперь рассмотрим, как настроить и запустить сервлет, после чего разберем конкретный пример сервлета.

Запуск и настройка сервлетов

Настало время остановиться на деталях настройки и запуска сервлетов. Для этого следует поговорить о файле свойств. Имя этого файла — servlet.properties. В нем в виде пар «ключ—значение» хранятся свойства, используемые для конфигурации, создания и инициализации сервлетов. Изначально для любого из сервлетов предопределено два свойства. Первое, servlet.<имя сервлета>.code, определяет имя сервлета и ставит его в соответствие двоичному class-файлу сервлета. Например, если вы скомпилировали класс сервлета с именем MyServletClassName, то получите в результате компиляции файл с именем MyServletClassName. class и можете присвоить ему краткое имя, скажем, myservlet, следующим образом:

servlet.myservlet.code=MyServletClassName

Теперь, когда вы обратитесь к сервлету с именем myservlet, сервер найдет эту строку в файле свойств и, опираясь на найденное значение, загрузит класс MyServletClassName, инициализирует его и передаст ему ваш запрос. Следует помнить о том, что имя class-файла должно задаваться полностью, включая имя пакета, в котором определен класс. Второе свойство, servlet.<имя сервлета>.initargs, определяет передаваемые сервлету параметры инициализации. Значения такого рода параметров могут быть получены сервлетом методом getInitParameter. Если параметров несколько, они отделяются друг от друга запятыми. Пример задания параметров:

servlet.myservlet.initargs= 
someParameterName1=someValue, someParameterName2=otherValue

Файл servlet.properties помещают в определенный каталог на сервере, где хранятся class-файлы. Заметим, однако, что разные серверы допускают альтернативное местоположение файла свойств (по настройке администратора).

Теперь поговорим об утилите servletrunner, которая по сути является простейшим Web-сервером, специально предназначенным для работы с сервлетами. После запуска он «слушает» порт 8080, и если произошел запрос, то, обратившись к сервлету, servletrunner получает от него ответ и пересылает последний программе-клиенту. Командная строка servletrunner имеет различные опции, но полезными могут оказаться лишь следующие:

  • p port — «прослушиваемый» в ожидании запроса порт;
  • t timeout — время тайм-аута в мс;
  • d dir — каталог, где лежат сервлеты;
  • r root — корневой каталог, в котором хранятся документы;
  • s filename — альтернативное имя файла свойств servlet property file name
  • v — отображать выводимые в стандартные потоки данные.

Никаких дополнительных настроек вам не потребуется. Просто запустите утилиту servletrunner и обращайтесь к ней с помощью браузера или другой клиентской программы. Главное, чтобы servletrunner могла найти ваши сервлеты.

Теперь вкратце о том, как обратиться к сервлету из Web-браузера. По умолчанию адрес URL для отправки запроса состоит из нескольких частей: имени компьютера, номера порта, каталога servlet, имени сервлета и списка параметров. К примеру, обратиться к сервлету myservlet, находящемуся на локальном компьютере, можно так:

http://localhost:8080/servlet/myservlet?param=somevalue

Напомним, что, тоже по умолчанию, утилита servletrunner (и некоторые серверы Web) «слушает» запросы к порту с номером 8080. Приведенный выше запрос обращается к сервлету myservlet, передавая ему параметр param со значением somevalue. Это весьма полезно, когда нужно задавать параметры запроса «на лету» (в отличие от параметров инициализации).

Пример сервлета

Чтобы все рассказанное о сервлетах не осталось для вас пустыми словами, создадим собственный сервлет, который будет выводить в окне браузера список файлов в определенном каталоге компьютера-сервера, а заодно и показывать количество файлов и их местоположение. Готовый исходный текст показан в листинге. Мы не станем детально описывать теги HTML, которые сервлет вставляет в генерируемую страницу, — обратитесь самостоятельно к документации по этому языку. Весь вывод данных будет производиться методом println класса PrintWriter, что является наиболее удобным вариантом посылки текстовых данных.

Начнем с того, что опишем класс сервлета, наследующий HttpServlet, и импортируем необходимые классы. После чего займемся методом doGet — главной частью сервлета:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class SampleServlet extends HttpServlet
{
  public void doGet( HttpServletRequest req,  HttpServletResponse  res)throws 
ServletException, IOException
  {

Методом getWriter сервлет получает доступ к потоку вывода, через который серверу посылается результат:

PrintWriter w = res.getWriter(); res.setContentType(?text/html?); generateHeader(?Directory Viewer Servlet?, w);

После чего методом setContentType серверу сообщается, что возвращаемые данные — страница HTML (MIME-тип «text/html»). Если этого не сделать, то сервер решит, что вы посылаете обычный текст. Метод generateHeader, который идет следом, убирает детали генерации стандартного заголовка Web-страницы, чтобы не загромождать исходный текст. Первый параметр этого метода — заголовок генерируемой страницы, а второй — ссылка на поток вывода, куда этот метод должен пересылать данные.

Следующий блок занимается разбором параметров, переданных сервлету:

if((param = req.getParameter(?dirToShow?)) == null)
      if((param = getInitParameter(?dirToShow?)) == null)
      {
        w.println(?

? + ?The dirToShow parameter required!

?); return; }
Рис.1

Сначала сервлет пытается считать значение параметра dirToShow из адреса URL (метод getParameter). Если такого параметра в запросе нет, то предпринимается попытка получить параметр с этим же именем из файла свойств (метод getInitParameter). Если и там параметр не обнаружен, запрос прерывается, а в ответ клиенту посылается сообщение (рис. 1).

Если же параметр найден, то его значение запоминается для дальнейшего использования. В следующем блоке исходного текста проверяется, является ли заданный параметр именем каталога и существует ли каталог с таким именем. Если нет, то следует генерация сообщения об ошибке:

File root = new File(param);
    if(!root.isDirectory())
    {
      w.println(?

? + ?The parameter is not a directory or not exist!

?); return; }

Когда все формальности соблюдены, у каталога запрашивается список всех имеющихся файлов и по нему вычисляется общее количество их в каталоге. Заодно генерируется строка, показывающая полное имя каталога:

    File[] fileList = root.listFiles();
    w.println(?

? + ?Total number of files in the choosen directory - ? + fileList.length + ?

?); w.println(?

? + ?Directory path - ? + param + ?


?);

Имена файлов выводятся методом printName, который приведен чуть ниже. Для форматирования списка применяется таблица, поэтому нужны две дополнительные строки, формирующие с помощью пары тегов

и
таблицу с невидимой рамкой:

w.println(??);
    for(int i = 0; i < fileList.length; i++)
        printName(fileList[i], w);
    w.println(?

?); generateFooter(w);

Завершается вывод генерацией стандартного окончания HTML-страницы с помощью метода generateFooter.

Теперь о методе printName. Он чрезвычайно прост и состоит из строчки, которая проверяет, является ли выводимое имя каталогом или простым файлом. После этого в специально отведенную переменную записывается комментарий о типе. Далее в два столбца в таблицу выводятся тип файла (простой файл или каталог) и его имя:

  private void printName(File name, PrintWriter output)
  {
    String type = name.isDirectory()
        ? ? (Directory)? : ? (File)?;
    output.println(??);
  }
? + type + ?? + name.getName() + ?

Остается лишь рассмотреть вспомогательные методы generateHeader, generateFooter getServletInfo. Они настолько просты, что вы без труда разберетесь с ними самостоятельно:

  private void generateHeader(String title, PrintWriter output)

  {
    output.println(?
?); } private void generateFooter(PrintWriter output) { output.println(? ?); output.flush(); output.close(); } public String getServletInfo() { return ?This servlet shows a content of a directory? + ?mentioned in dirToShow parameter or property.?; } }

Отлаживать подобного рода сервлеты не просто, а очень просто. Нужно только открыть в браузере режим просмотра исходного текста страницы — и все ваши ошибки сразу же становятся видны. Ниже приведен исходный текст файла свойств servlet.properties

:
# dirlist servlet
servlet.dirviewer.code=SampleServlet
servlet.dirviewer.initArgs=
                  dirToShow=D:SUNSDKexamples
Рис.2

Обратите внимание, что символ «обратная косая» в пути к каталогу задается по правилам языка Java, т. е. двойными «обратными косыми». Можно, однако, использовать и одинарную «прямую косую», как это принято в ОС UNIX.

На рис. 2 показано, как примерно будет выглядеть результат работы сервлета. В качестве запроса была введена строка:

http://mitrich/servlet/dirviewer?dirToShow=D:/Sun/SDK/examples

Запускается сервлет следующей строкой:

servletrunner -p 80 -d E:PROJECTSJavaServlet -r E:PROJECTSJavaServlet

Не забудьте, что все каталоги актуальны лишь для компьютера, на котором работает автор. Вы же подставите собственные пути.

Изготовление сервлетов в Borland JBuilder 2

Довольно хорошими возможностями по созданию сервлетов обладает пакет Borland JBuilder 2. Если заглянуть в список имеющихся у него мастеров, то можно обнаружить соответствующую пиктограмму, щелкнув на которой вы запускаете программу генерации исходного текста сервлета (рис. 3).

Рис.3

Сначала потребуется ввести исходные данные для создания нового проекта: название проекта, имя автора и т.д. (рис. 4).

Рис.4

Сам мастер генерации сервлета запускается чуть позже, когда создан проект. Диалоговая панель мастера (рис. 5) предложит создать те элементы сервлета, с которыми вы уже познакомились в предыдущей части статьи. Опция Implement SingleThreadModel добавляет в список наследования класса сервлета интерфейс SingleThreadModel, не разрешающий множественное обращение к сервлету. Generate HTML Page создает Web-страницу для сервлета. И наконец, вы можете включать генерацию нескольких основных методов-обработчиков для сервлета: service, doGet, doPost, doPut, doDelete.

Рис.5

Рис.6

На следующем этапе задаются параметры инициализации. Кроме имени параметра и его типа также задаются: описание параметра; переменная внутри сервлета, которой будет передано значение параметра после его считывания; значение параметра по умолчанию; имя метода сервлета, в котором должно происходить считывание параметра (рис. 6).

Если ввести параметры так, как это показано на предыдущих рисунках, то вы получите проект с двумя Java-классами. Первый представляет собой простейший сервер. Он настроен на порт 8080 и умеет распознавать две опции командной строки -p и -d, которыми можно задать порт сервера и каталог с сервлетами соответственно. Вот исходный текст такого сервера, сгенерированный JBuilder:

package Samples;
import sun.servlet.http.*;
public class ServletServer {
  public static void main(String[] args) {
    boolean portSet = false;
    boolean servletDirSet = false;
    for (int i = 0; i < args.length; i++) {
      if (args[i].equals(?-p?)) {
        portSet = true;
      }
      if (args[i].equals(?-d?)) {
        servletDirSet = true;
      }
    }
    int i = 0;
    String[] arguments = new String[args.length + (servletDirSet ? 0 : 2) + (portSet ? 0 : 2)];
    for (; i < args.length; i++) {
      arguments[i] = args[i];
    }
    if (!portSet) {
      arguments[i++] = ?-p?;
      arguments[i++] = ?8080?;
    }
    if (!servletDirSet) {
      arguments[i++] = ?-d?;
      String servletDir = System.getProperty(?java.class.path?);
      servletDir = servletDir.substring(0,servletDir.indexOf(java.io.File.pathSeparator));
      arguments[i++] = servletDir;
    }
    HttpServer.main(arguments);
  }
}

Сам класс сервлета не делает ничего выдающегося, а просто обрабатывает значение параметра инициализации и элементарным образом реагирует на запросы, выводя некое сообщение:

package Samples;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class TestServlet extends HttpServlet {
  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    //Каталог, содержимое которого нужно показать
    try { pathToDir = getInitParameter(?dirToShow?); } catch (Exception e) { e.printStackTrace(); }
  }
  String pathToDir = ?E:/Projectes/?;
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType(?text/html?);
    PrintWriter out = new PrintWriter (response.getOutputStream());
    out.println(?
?); out.println(??); out.println(??); out.println(??); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(?text/html?); PrintWriter out = new PrintWriter (response.getOutputStream()); out.println(??); out.println(??); out.println(??); out.println(??); out.close(); } public String getServletInfo() { return ?Samples.TestServlet Information?; } }

* * *

Итак, вы получили представление о том, как пишутся сервлеты. К сожалению, без Java Web Server компании Sun трудно отладить сколь-нибудь сложные сервлетные классы, а утилита servletrunner обладает лишь простейшими возможностями по их запуску. Однако компания JavaSoft обещает исправить этот недостаток, включив в следующую версию Java Servlet Development Kit усовершенствованный маленький Web-сервер, с которым процесс отладки и проверки станет намного проще.


Листинг: Пример сервлета

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

public class SampleServlet extends HttpServlet
{ 
  public void doGet( HttpServletRequest req, HttpServletResponse res)
                                     throws ServletException, IOException
  {
    String param;
    PrintWriter w = res.getWriter();
    res.setContentType(?text/html?);
    generateHeader(?Directory Viewer Servlet?, w);
    if((param = req.getParameter(?dirToShow?)) == null)
      if((param = getInitParameter(?dirToShow?)) == null)
      {
        w.println(?

? + ?The dirToShow parameter required!

?); return; } File root = new File(param); if(!root.isDirectory()) { w.println(?

? + ?The parameter is not a directory or not exist!

?); return; } File[] fileList = root.listFiles(); w.println(?

? + ?Total number of files in the choosen directory - ? + fileList.length + ?

?); w.println(?

? + ?Directory path - ? + param + ?


?); w.println(?
?); for(int i = 0; i < fileList.length; i++) printName(fileList[i], w); w.println(?

?); generateFooter(w); } private void printName(File name, PrintWriter output) { String type = name.isDirectory() ? ? (Directory)? : ? (File)?; output.println(?
? + type + ?? + name.getName() + ?
?); } private void generateHeader(String title, PrintWriter output) { output.println(?
?); } private void generateFooter(PrintWriter output) { output.println(? ?); output.flush(); output.close(); } public String getServletInfo() { return ?This servlet shows a content of a directory? + ?mentioned in dirToShow parameter or property.?; } }