Интернет програмиране с Java
Светлин Наков
Java платформата предоставя няколко стандартни средства за създаване на динамични Web-страници. Основната технология, на базата на която се изгражда всичко останало, са Java сървлетите.
Java сървлетите представляват програми на Java, които приемат като вход някакви данни от потребителя, обработват ги и връщат като резултат динамично генериран HTML или друг документ. Например, един сървлет може да приема като входни данни име на потребител и парола, да проверява валидността им по някакъв начин и да пренасочва браузъра към друга страница от Web-приложението, ако са валидни или да връща съобщение за грешка в противен случай.
Да дадем пример за прост HTTP сървлет, който при извикване връща текущата дата и час, форматирани като прост HTML документ. Противно на всички книги за компютри нашият първи пример не е сървлетът „Hello, world!”. Ето как изглежда нашият код:
FirstServlet.java |
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class FirstServlet extends HttpServlet { public void doGet(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException { PrintWriter out = aResponse.getWriter(); out.println("<HTML>"); out.println("The time is: " + new java.util.Date()); out.println("</HTML>"); } } |
Всичко, което прави примерният сървлет FirstServlet, е да наследи класа HttpServlet и в метода doGet() да получи изходния поток на отговора на заявката и да отпечата в него съвсем прост HTML документ, който се състои от 3 реда и съдържа текущата дата и час.
За създаването на Java сървлет за обслужване на Web-заявки е необходимо да се наследи класа javax.servlet.http.HttpServlet и да се припокрие един от методите doGet() или doPost() съответно в зависимост дали сървлетът ще обработва съответно GET или POST HTTP заявки.
И двата метода doGet() и doPost() приемат като
параметри два обекта HttpServletRequest
и HttpServletResponse
.
HttpServletRequest
служи за извличане на входните параметри на сървлета
– данните от HTML форми, изпратени от потребителския Web-браузър, хедърите на
HTTP заявката, информация за клиента, неговия IP адрес и браузър.
HttpServletResponse
служи за изпращане на данни в отговор на получената
HTTP заявка и позволява задаване на HTTP кода на резултата и полетата в хедъра
на HTTP отговора, както и самия текст на отговора.
При простите сървлети по-голямата част от сървлета представлява код, който отпечатва динамично създадения HTML документ в изходния поток на заявката. При по-сложните сървлети се налага работа с HTTP хедърите, извличане на параметри, управление на потребителската сесия, работа с cookies и други специфични действия, които ще разгледаме по-късно.
Не всички сървлетите са HTTP
сървлети. Има сървлети, които обслужват други протоколи и за реализацията им се
наследява класа GenericServlet
, а не HttpServlet
. Ние ще разглеждане само HTTP сървлети и под думата
сървлет ща разбираме винаги HTTP сървлет.
За да изпълним нашият примерен сървлет са необходими няколко стъпки:
1) компилираме сървлета и получаваме съответен .class файл;
2) инсталираме и конфигурираме някакъв Servlet-контейнер, в средата на който ще се изпълнява сървлета;
3) deploy-ваме сървлета в сървлет-контейнера (инсталираме го и го подготвяме за изпълнение):
- създаваме Web-приложение в сървлет-контейнера;
- копираме сървлета в директория WEB-INF/classes на това Web-приложение;
- регистрираме сървлета в конфигурационния файл на Web-приложението web.xml;
4) стартираме Servlet-контейнера и извикваме сървлета чрез заявка от стандартен Web-браузър.
Как се използва Servlet -контейнера Tomcat ще разгледаме след малко, но преди това нека обърнем внимание на една особеност, с която ще се сблъскаме при компилирането – липсата на някои класове, които използваме.
Класовете от пакетите javax.servlet и javax.servlet.http не са стандартна част от JDK. За да ги използваме, е нужно да включим към проекта си библиотеката Servlet API, в която са дефинирани тези класове. Обикновено тази библиотека се разпространява заедно със Servlet-контейнера. Например при Tomcat 3.x в директория lib/common има файл с име servlet.jar, който съдържа тези класове. При Tomcat 4.x файлът servlet.jar се намира в директория common/lib, а при Tomcat 5.x този файл се казва servlet-api.jar и се намира в директорията common/lib на Tomcat. При други Web-контейнери и Web application сървъри Servlet API библиотеката може да се намира на други места.
Ако искаме да компилираме сървлет и компилаторът не намира класовете от пакета javax.servlet, трябва да си включим към проекта или към клас-пътя на компилатора Servlet API библиотеката. Последната версия на тази библиотека може да се изтегли от адрес http://java.sun.com/products/servlet/downloads/.
Сървърът Tomcat представлява безплатен Web-контейнер, който може да изпълнява Java сървлети, JSP страници и Web-приложения. Tomcat е Web-application сървър, написан на Java, който при клиентска HTTP заявка освен статични файлове, може да връща и динамични документи, създадени в резултат от изпълнението на сървлет или JSP.
Сървърът Tomcat е достъпен за свободно изтегляне от адрес http://jakarta.apache.org/tomcat/. След като издърпаме последната версия, която представлява един .zip файл (примерно jakarta-tomcat-5.0.19.zip), трябва да разархивираме този файл в някоя директория. Препоръчва се в името на директорията да не се съдържат интервали, защото интервалите служат за разделители в Java и могат да създадат досадни проблеми.
Ако нямаме инсталиран JDK на нашия компютър, трябва да си инсталираме първо него. Можем да си го издърпаме от http://java.sun.com/j2se/.
За
стартиране на сървъра Tomcat е необходимо първо в променливите на средата да
добавим променливата JAVA_HOME
със стойност директорията където е инсталиран JDK.
Това може да стане например от конзолата с командата “set JAVA_HOME=C:\j2sdk1.4.2_01
” или от настройките за променливите на средата
(environment variables) на операционната система.
За да стартираме самия сървър Tomcat, трябва да изпълним скрипта за стартиране, който се намира в поддиректорията bin на основната директория на Tomcat сървъра. Например ако сме инсталирали Tomcat в директория C:\jakarta-tomcat-5.0.19, нашата bin директория ще бъде C:\jakarta-tomcat-5.0.19\bin.
В
тази поддиректория bin
има файл за стартиране startup.bat
(startup.sh
за Unix/Linux), с който можем да стартираме сървъра.
Стартираме този файл. Появява се цяла поредица от съобщения, възникнали при
началната инициализация на сървъра. Едно от последните съобщения казва, че
сървърът слуша на порт 8080 за идващи HTTP
заявки. Ако няма съобщения за грешки, то сървърът е стартирал успешно.
За да проверим дали всички работи нормално, можем да стартираме нашия Web-браузър и да въведем адреса http://localhost:8080/ (по подразбиране Tomcat работи на порт 8080, а не на стандартния за протокола HTTP порт 80). Ако всичко е наред, ще се появи заглавната страница на Tomcat. Можем да тестваме и стандартните примерни сървлети и JSP страници, като следваме връзките от главната страница.
Да се върнем сега на проблема с изпълнението на примерния сървлет, който написахме преди малко. До момента знаем как се инсталира Tomcat и как се стартира. Остава да се научим как да deploy-ваме сървлети и как да ги извикваме от нашия Web-браузър. За целта трябва да направим ново Web-приложение в сървъра Tomcat, да копираме сървлета в него и да създадем специален конфигурационен файл, в който да опишем сървлета.
Освен
поддиректорията bin
, в директорията на Tomcat има още една важна
поддиректория – webapps
. В тази поддиректория стоят всички инсталирани
Web-приложения. Например директорията webapps\ROOT
е
главната виртуална директория на сървъра и тя също представлява Web-приложение.
За
нашите тестови цели трябва да създадем ново Web-приложение на сървъра (нова
поддиректория в webapps
).
Една
добра стратегия при разучаване на някакъв непознат сървър е когато задавате
имена на нови файлове, директории, имена на проекти, имена на услуги и т.н. да
избирате нестандартно име, за да можете да различите след това стандартните
неща от нещата, които вие сте създали. По този принцип не трябва да кръщавате
новата директория за вашите тестове с имена като test
,
samples
, examples
, tests
, web
, root
и т.н. защото рискувате първо избраното от вас име да
съвпадне с някое служебно име със специално предназначение и второ ще ви е
трудно да различите стандартните имена, идващи по подразбиране със сървъра,
който разучавате, от вашите, които вие сте създали. Аз лично в такива случаи
обикновено задавам за име nakov_directory
, nakov_service
, nakov_cluster
или нещо подобно, което ми подсказва че това име е
избрано от мен и какво точно се крие зад това име – директория, услуга, клъстер
или нещо друго.
Нека наречем нашето ново Web-приложение nakov-webapp. Създаваме директорията nakov-webapp в webapps поддиректорията на сървъра. Ако сега стартираме сървъра, всичко, което се намира в тази директория nakov-webapp, ще е достъпно от адрес http://localhost:8080/nakov-webapp/.
При стартиране Tomcat автоматично прочита всички физически поддиректории от поддиректорията си webapps и ги прави достъпни като виртуални директории в своя Web-сървър. Виртуална директория за даден Web-сървър наричаме ресурс, който се вижда през Web като директория в рамките на URL адреса, съответстващ този на Web-сървър. Например адресът http://localhost:8080/ съответства на главната виртуална директория за Web-сървъра работещ на хоста localhost на порт 8080, а http://localhost:8080/nakov-webapp/ е адреса, който съответства на виртуалната директория /nakov-webapp на същия Web-сървър.
За
да изпълним нашия сървлет е необходимо да го вкараме в новосъздаденото
Web-приложение, т.е. някъде в неговата директория nakov-webapp. Съгласно стандартите от J2EE за Web-приложения
трябва да създадем в nakov-webapp поддиректория с име WEB-INF
и в нея още една поддиректория с име classes
и да копираме компилирания сървлет в нея.
В нашия случай трябва да създадем директорията C:\jakarta-tomcat-5.0.19\webapps\nakov-webapp\WEB-INF\classes и в нея да копираме компилирания сървлет FirstServlet.class.
Нашият компилиран сървлет се намира в директорията, в която стоят всички .class файлове, които са част от приложението. Понеже сървлетът също е .class файл, той си е на мястото. Остава да го регистрираме в Web-приложението, за да стане достъпен от виртуалната директория на приложението.
Всяко Java-базирано Web-приложение има конфигурационен файл с име web.xml, който се намира в поддиректория WEB-INF на директорията на приложението. В този файл се описват различни настройки на приложението, като име и описание на приложението, описание на използваните сървлети, описание на използваните потребителски тагове (custom tags), описание на параметрите към приложението, описание на сървлет-филтри, настройки за сигурността и др.
За да направим нашия сървлет видим през URL адреса на нашето Web-приложение, трябва да го опишем във файла web.xml. Ето един пример как може да стане това:
web.xml |
<web-app> <servlet> <servlet-name>dateServlet</servlet-name> <servlet-class>FirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>dateServlet</servlet-name> <url-pattern>/date</url-pattern> </servlet-mapping> </web-app> |
В тага <servlet> описваме съответствието между име на сървлет и име на клас, който стои за това име. Посоченият клас трябва да се намира в поддиректорията WEB-INF\classes на приложението. В тага <servlet-mapping> описваме съответствието между име на сървлет, което е дефинирано преди това и име на ресурс, под което ще е достъпен сървлета спрямо началния URL адрес на Web-приложението.
В нашия случай след като копираме този файл под име web.xml в поддиректорията WEB-INF на Web-приложението и рестартираме Tomcat сървъра, нашият сървлет става достъпен от адрес http://localhost:8080/nakov-webapp/date.
Можем да извикаме сървлета от стандартен Web-браузър като напишем URL адреса, към който той е закачен. Ето как изглежда резултатът от изпълнението на нашия сървлет в браузъра Internet Explorer:
Ето какъв е HTML кода, върнат от сървлета:
http://localhost:8080/nakov-webapp/date |
<HTML> The time is: Fri Mar 26 00:46:14 EET 2004 </HTML> |
За да си изясним в по-голяма дълбочина какво се случва от момента, в който напишем URL адреса http://localhost:8080/nakov-webapp/date в Web-браузъра до момента, в който браузърът покаже резултата, ще проследим HTTP заявката и съответния HTTP отговор, които браузъра Internet Explorer 6.0 сървъра Tomcat 5.0 си обменят.
Когато потребителят въведе URL адреса на търсения ресурс, Web-браузърът намира по името на хоста (в случая localhost) IP адреса на Web-сървъра, от който трябва да поиска ресурса (в случая това в IP адрес 127.0.0.1).
След като знае IP адреса на Web-сървъра, браузърът взима от URL адреса номера на порта, а ако не е указан порт използва порт 80. В нашия случай се взима номер на порт 8080. Браузърът отваря връзка по протокол TCP към намерения преди това IP адрес на търсения хост на извлечения от URL адреса порт.
След успешно свързване към Web-сървъра браузърът изпраща HTTP заявка за извличане на поискания ресурс. В нашия случай Internet Explorer 6.0 изпраща на Tomcat сървъра следното:
Internet Explorer à Tomcat |
GET /nakov-webapp/date HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */* Accept-Language: bg Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461) Host: nakov:8080 Connection: Keep-Alive |
В заявката си Internet Explorer иска от Web-сървъра ресурс с име /nakov-webapp/date по протокол HTTP/1.1. Браузърът изпраща допълнителна информация относно какви ресурси може да приема и обработва, задава предпочитания език, с който иска да работи, задава каква компресия на данните разбира, задава идентификация, с която обяснява какъв браузър и коя версия е той самия, задава името на хоста, в рамките на Web-сървъра, за който се отнася заявката и накрая обяснява, че предпочита TCP връзката да остане отворена още известно време след като ресурсът бъде върнат. Заявката завършва с празен ред.
От гледна точка на сокет програмирането Tomcat е обикновен многопотребителски TCP сървър, който слуша на порт 8080 и си говори с клиентските приложения по протокол HTTP.
Когато Tomcat получи HTTP заявката от Internet Explorer, той я анализира за да разбере какво иска клиента. От частта /nakov-webapp на искания ресурс Tomcat разбира, че заявката се отнася за Web-приложението с име nakov-webapp. След това Tomcat установява, че в рамките на това Web-приложение е поискан ресурса /date. Този ресурс би могъл да бъде или някакъв сървлет или просто статичен документ, намиращ се в директорията на Web-приложението.
След справка в таблицата с регистрираните сървлети, която Tomcat е създал след като е прочел и анализирал конфигурационния файл на Web-приложението web.xml, сървърът установява, че трябва да извика сървлета FirstServlet. Този сървлет съответства на клас файла FirstServlet.class, който се намира в директорията C:\jakarta-tomcat-5.0.19\webapps\nakov-webapp\WEB-INF\classes на машината, на която работи сървъра. Ако класът все още не е зареден в паметта на Java виртуалната машина от class loader-а на Tomcat, той се зарежда и се подготвя за изпълнение.
Междувременно Tomcat е установил, че заявката идва по метода GET и съответно извиква метода doGet(…) на класа FirstServlet. Методът doGet(…) се изпълнява успешно и връща някакъв поток от символи, който сървърът приема от сървлета и въз основа не него формира HTTP отговора на клиентската заявката. В крайна сметка Tomcat изпраща на Internet Explorer следния HTTP отговор:
Tomcat à Internet Explorer |
HTTP/1.1 200 OK Content-Length: 60 Date: Fri, 26 Mar 2004 10:06:28 GMT Server: Apache-Coyote/1.1 <HTML> The time is: Fri Mar 26 12:06:28 EET 2004 </HTML> |
HTTP отговорът започва с индикатора на протокол HTTP/1.1, код за успех 200 и текстово описание OK. Следва хедър поле, което указва дължината на върнатия ресурс, а след него стоят полетата за дата и за идентификация на сървъра. Забелязва се, че Tomcat се представя за сървър Apache-Coyote/1.1. Всъщност това не е измама. Наистина Tomcat 5.0 използва като подсистема, която обслужва HTTP протокола (HTTP listener) сървъра Apache Coyote, който е Java-базиран HTTP сървър с отворен код. Хедърите завършват с празен ред, а след него следва самия ресурс.
Най-накрая Web-браузърът получава отговора на HTTP заявката, която е изпратил и го интерпретира като HTML документ (понеже не е указано друго в хедърите на HTTP отговора) и го визуализира по подходящ начин.
Това е всичко, което се случва за едно най-обикновено извличане на ресурс през Web.
По
подразбиране в Tomcat всички файлове от главната директория на едно приложение
се публикуват във виртуална директория с име името на приложението. При някои версии
на Tomcat (например 3.x и 4.x) всички .class
файлове от WEB-INF\classes
директорията автоматично се публикуват като сървлети
във виртуална директория с име <име_на_приложението>/servlet
. На това, обаче не може да се разчита и
най-правилният начин да се публикува сървлет е да се опише в конфигурационния
файл web.xml.
Специалната
поддиректория WEB-INF
, заедно с всички нейни поддиректории остава
недостъпна през Web, въпреки че е поддиректория на Web-приложението.
Трябва да имаме предвид, че ако променяме файловете от директорията на нашето Web-приложение, промените ще са видими веднага, но ако променим и прекомпилираме някой файл от classes директорията (например нашият тестов сървлет), е необходимо да рестартираме Tomcat, за да влязат в сила промените. Това се дължи на с кеширането на заредените във виртуалната машина класове, което Tomcat прави за да постигне по-голяма производителност.