В первой части этой статьи (см. «Мир ПК», № 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(?
?); 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(? |