JavaServlets: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
(Listenery)
(Definice servletu)
Řádka 7: Řádka 7:
 
Servlety jsou '''nástroj pro obsluhu protokolu HTTP na straně serveru'''. Přesně řečeno, jsou serverovým nástrojem pro obsluhu všech protokolů, které fungují způsobem požadavek-odpověď, nicméně už dlouhá léta je jediným podporovaným protokolem HTTP.
 
Servlety jsou '''nástroj pro obsluhu protokolu HTTP na straně serveru'''. Přesně řečeno, jsou serverovým nástrojem pro obsluhu všech protokolů, které fungují způsobem požadavek-odpověď, nicméně už dlouhá léta je jediným podporovaným protokolem HTTP.
  
Servletem je každá Java třída, která implementuje interface {{JavaEEClass|package=javax/servlet|classPrefix=javax.servlet.|class=Servlet}}. Protože ale z praktického hlediska má smysl uvažovat pouze servlety obsluhující protokol HTTP, je důležitější vědět, že '''HTTP servletem''' je každá třída, která je potomkem {{JavaEEClass|package=javax/servlet/http|classPrefix=javax.servlet.http.|class=HttpServlet}}.
+
Servletem je každá Java třída, která implementuje interface {{JavaEEClass|package=javax/servlet|classPrefix=javax.servlet.|class=Servlet}}. Protože ale z praktického hlediska má smysl uvažovat pouze servlety obsluhující protokol HTTP, je důležitější vědět, že '''HTTP servletem''' je každá třída, která je potomkem třídy {{JavaEEClass|package=javax/servlet/http|classPrefix=javax.servlet.http.|class=HttpServlet}}.
  
 
Servlety jsou základním kamenem, na kterém jsou vystavěny další vrstvy webových aplikací na platformě Java. Servlety jsou nízkoúrovňovým nástrojem
 
Servlety jsou základním kamenem, na kterém jsou vystavěny další vrstvy webových aplikací na platformě Java. Servlety jsou nízkoúrovňovým nástrojem

Verze z 9. 10. 2007, 11:48


Definice servletu

Servlety jsou nástroj pro obsluhu protokolu HTTP na straně serveru. Přesně řečeno, jsou serverovým nástrojem pro obsluhu všech protokolů, které fungují způsobem požadavek-odpověď, nicméně už dlouhá léta je jediným podporovaným protokolem HTTP.

Servletem je každá Java třída, která implementuje interface javax.servlet.Servlet. Protože ale z praktického hlediska má smysl uvažovat pouze servlety obsluhující protokol HTTP, je důležitější vědět, že HTTP servletem je každá třída, která je potomkem třídy javax.servlet.http.HttpServlet.

Servlety jsou základním kamenem, na kterém jsou vystavěny další vrstvy webových aplikací na platformě Java. Servlety jsou nízkoúrovňovým nástrojem pro obsluhu HTTP, takže na jednu stranu je možné s jejich pomocí obsloužit opravdu libovolný HTTP požadavek a vygenerovat libovolnou odpověď, na druhou stranu jsou pro rutinní generování HTML stránek příliš nepohodlné, a proto existují jejich nadstavby, zejména Java Server Pages.

Protokol HTTP

Pro pochopení servletů je důležité chápat, jak funguje protokol HTTP. Protokol HTTP (Hyper Text Transfer Protocol) je základním protokolem pro transport souborů a dat na webu. Jde o jednoduchý textový protokol na aplikační vrstvě, fungující nad TCP/IP. Jeho použití má dvě fáze, požadavek klienta na server, bezprostředně následovaný odpovědí serveru v rámci stejného TCP spojení. Požadavek žádá o webový zdroj, jenž je adresován URL (Uniform Resource Identifier), které má pro protokol HTTP obecnou podobu:

http://user:password@machine:port/path?queryString#fragment
http 
označuje protokol HTTP, případně může být tvaru https, pokud je přenos šifrován pomocí SSL
user:password 
jméno a heslo uživatele, nedoporučuje se jej ale uvádět do URL kvůli bezpečnosti
port 
číslo TCP portu, pokud není uvedeno, je to 80 pro http a 443 pro https
path 
cesta nějakou hierarchií, např. souborovým systémem
queryString 
doplňkové informace, typicky obsah polí z HTML formuláře
fragment 
místo na stránce, je interpretován prohlížečem

Požadavek protokolem HTTP vypadá např. takto:

POST /mujservlet?p1=Pepa&p2=Mirek HTTP/1.1
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-language: cs,en-us;q=0.7,en;q=0.3
Connection: keep-alive
Host: www.cesnet.cz
Keep-Alive: 300
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20050920
Content-type: application/x-www-form-url-encoded
Content-length: 18 
p3=Lojza&p4=Franta

První řádek specifikuje tzv. metodu, která může být GET,POST,PUT,HEAD,DELETE,OPTIONS,TRACE, dále části path a queryString z URL, a verzi protokolu HTTP (1.0 nebo 1.1).

Další řádky až po prázdný řádek jsou tzv. hlavičky, obdobné MIME hlavičkám používaným v e-mailech. Prázdný řádek označuje konec hlaviček. U metody POST následuje za ním ještě datový obsah, jehož typ je určen hlavičkou Content-type. Ve výše uvedeném příkladu je to typ pro předávání dat z HTML formulářů, kdy jednotlivé položky formuláře jsou odděleny znakem ampersand, a mají tvar dvojice jméno=hodnota.

Odpověď serveru na HTTP požadavek vypadá obdobně:

HTTP/1.1 200 OK
Date: Wed, 06 Sep 2006 09:17:38 GMT
Server: Apache/1.3.33 
Connection: close
Content-Type: text/html; charset=utf-8 
<html> ...

První řádek odpovědi určuje kód výsledku (200 OK, 301 Moved Permanently, ...), následují hlavičky, z nichž Content-type určuje typ obsahu za prázdným řádkem.

HTTP protokol je nezávislý na implementaci, tedy zda na straně serveru používáme Java Servlety, CGI programy, PHP, Perl, či něco jiného.

Obsluha HTTP pomocí servletů

HTTP servlety implementují metodu service(). HTTP volání jsou reprezentovány vlákny, které volají metodu service(), a předávají jí dvojici parametrů reprezentujících HTTP požadavek a HTTP odpověď. Vlastní obsluha volání tak může být přímo v této metodě. Její standardní implementace však podle HTTP metody volá některou z metod doGet(), doPost(), doPut(), atd., do kterých je možné umístit obsluhu konkrétních HTTP metod.

Generování HTTP odpovědi

Minimalistický servlet, který generuje vždy stejný HTML text, je zde:

 
 import javax.servlet.http.*;
 import javax.servlet.*;
 import java.io.*;
 
 public class Ukazka extends HttpServlet { 
 
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) 
                      throws ServletException, IOException {
 
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("Hello, world !");
        out.println("</body></html>");
    }
 }

Tento servlet nijak nevyužívá informace z přicházejícího požadavku. Nejdříve nastaví typ obsahu na HTML text v kódování UTF-8, pak získá z objektu reprezentujícího odpověď PrintWriter, do kterého je možné text zapisovat, a text zapíše.

Kromě textů může servlet generovat i binární výstup, v tom případě se místo getWriter() používá getOutputStream():

 
 public class BinarniVystup extends HttpServlet {
 
    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) 
                   throws ServletException, IOException {
 
        response.setContentType("application/octet-stream");
        ServletOutputStream out = response.getOutputStream();
        byte[] obsah = ...;
        out.write(obsah);
    }
 }

Objekt HttpServletResponse obsahuje všechny nástroje pro vygenerování libovolné HTTP odpovědi, je možné měnit kód výsledku pomocí metody setStatus(), měnit HTTP hlavičky pomocí addHeader(), přidávat HTTP cookies pomocí addCookie() nebo přesměrovat prohlížeč na jinou stránku pomocí sendRedirect().

Zpracování HTTP požadavku

Objekt HttpServletRequest obsahuje všechny dostupné informace o HTTP požadavku. Například pro URL

https://franta@www.nekde.cz/mojeapp/mujservlet/mojecesta?p1=A&p2=B&p2=C

obsahuje tyto informace:

metoda příklad návratové hodnoty význam
getScheme() https protokol požadavku
getMethod() GET HTTP metoda
getRemoteUser() franta autentizovaný uživatel
getServerName() www.nekde.cz DNS jméno webserveru
getServerPort() 443 port, na který přišel HTTP požadavek
getRequestURI() /mojeapp/mujservlet/mojecesta celá cesta z URL
getContextPath() /mojeapp část cesty určující webovou aplikaci
getServletPath() /mujservlet část cesty určující servlet
getPathInfo() /mojecesta část cesty za označením servletu
getQueryString() p1=A&p2=B&p2=C queryString z URL
getRequestURL() https://www.nekde.cz/mojeapp/mujservlet/mojecesta URL bez některých částí
getLocale() cs uživatelem preferovaný jazyk
getCharacterEncoding() utf-8 kódování textů
getContentLength() 0 délka těla požadavku
getContentType() null typ těla požadavku u metody POST
getProtocol() HTTP/1.1 verze protokolu
getRemoteAddr() 147.251.3.64 IP adresa stroje s prohlížečem
getRemoteHost() acrab.ics.muni.cz DNS jméno stroje s prohlížečem
getAuthType() BASIC_AUTH způsob autentizace uživatele
getPathTranslated() null umístění souboru na disku, má smysl u JSP
getRequestedSessionId() 5469525CA20AD03E97F1AEAD31303B8E identifikátor session

Hlavičky HTTP požadavku lze získat pomocí metody getHeader(). Obsluhu příchozích dat je možné pojmout jedním ze dvou způsobů. Buď je možné získat tělo požadavku pomocí getInputStream() a zpracovat ho vlastním kódem, což je nutné vždy, když typ obsahu je něco jiného než obsah HTML formuláře bez přiloženého souboru.

V obvyklém případě, kdy požadavek vznikl metodou GET nebo POST z HTML formuláře, nabízí HttpServletRequest položky formuláře předzpracované. Lze je získat pomocí metody getParameter() nebo getParameterValues(). Parametry předané v URL v části queryString a parametry předané v těle POST požadavku jsou spojeny do jedné množiny. Jeden parametr může mít více hodnot, pak getParameter() vrací jen první z nich, kdežto getParameterValues() vrací všechny. Ve výše uvedeném příkladu tedy výsledky budou:

volání metody návratová hodnota
getParameter("p1") A
getParameter("p2") B
getParameterValues("p2") String[] { "B", "C" }

České znaky

Rozhraní servletů pamatuje na to, že existují různá kódování ne-ASCII znaků, například různá kódování češtiny.

Aby se české znaky správně zobrazily na HTML stránce, je nutné specifikovat při volání response.setContentType() (resp. response.setCharacterEncoding()) nějaké kódování, ve kterém je lze vyjádřit, nejlépe UTF-8 nebo iso-8859-2. Pokud není kódování nastaveno, použije se iso-8859-1, ve kterém české znaky nemají vyjádření, a proto místo nich budou zobrazeny otazníky.

Prohlížeče vracejí texty z formulářů v tom kódování, ve kterém byla HTML stránka s formulářem. Pokud jsme tedy například nastavili v setContentType() kódování UTF-8, dostaneme od prohlížeče parametry v kódování UTF-8. Bohužel, standardní kódování příchozích parametrů je iso-8859-1, a metoda getParameter() vrátí nesmyslnou hodnotu, která vznikne použitím UTF-8 bajtů jako iso-8859-1 bajtů.

Pro korektní příjem parametrů je nutné před prvním zavoláním getParameter() zavolat request.setCharacterEncoding(), pak budou parametry překódovány správně.

Okolí servletu

Životní cyklus servletu

Servlety jsou Java třídy, mají tedy instance. Každá instance servletu má definovaný životní cyklus. Po jejím vzniku, ale před tím, než je obsloužen první HTTP požadavek, je zavolána metoda init(), ve které je možné si předpřipravit nějaké zdroje, třeba spojení na databázi. Po ukončení obsluhy požadavků je naopak zavolána metoda destroy(), ve které je možné předpřipravené zdroje zase uklidit.

Servlet API

Servlet nemůže existovat sám o sobě, potřebuje jisté okolí, které převádí příchozí HTTP požadavky na volání servletu. Proto servlety existují uvnitř tzv. servletového kontejneru. Existují samostatné implementace servletových kontejnerů, např. TomCat, Jetty, nebo může být servletový kontejner součástí většího Java EE aplikačního serveru, např. Glassfish, JBoss, IBM WebSphere atd.

Rozhraní mezi servletem a servletovým kontejnerem je specifikováno v tzv. Servlet API, které je dnes součástí Java Enterprise Edition. V současnosti používané verze této specifikace jsou 2.3 a 2.4, nově přibyla 2.5.


Servlet doba vzniku platforma implementace novinky
Servlet 2.5 září 2005 JavaEE 5 , JavaSE 5.0 Jetty 6, Glassfish vyžaduje JavaSE 5.0
Servlet 2.4 listopad 2003 J2EE 1.4, J2SE 1.3 TomCat 5.x web.xml používá XML Schema
Servlet 2.3 srpen 2001 J2EE 1.3, J2SE 1.2 TomCat 4.x Filter
Servlet 2.2 srpen 1999 J2EE 1.2, J2SE 1.2 TomCat 3.x součást J2EE, nezávislé webové aplikace a .war
Servlet 2.1 listopad 1998 nespecifikováno první oficiální specifikace, RequestDispatcher, ServletContext
Servlet 2.0 JDK 1.1 Apache JServ 1.0 součást Java Servlet Development Kit 2.0
Servlet 1.0 červen 1997 Apache JServ 0.9 součást Java Servlet Development Kit 1.0

Webová aplikace a kontext servletu

Servlety jsou v rámci servletového kontejneru organizovány do tzv. webových aplikací, jeden kontejner může zároveň obsahovat více nezávislých webových aplikací. Jedna webová aplikace je tvořena jedním souborem s příponou .war, což je ZIP soubor obsahující servlety, další Java třídy, statické soubory (HTML,CSS,GIF,..) a JSP stránky. Tento soubor je při spuštění aplikace obvykle rozbalen do stejnojmeného adresáře. Jeho adresářová struktura je:

/WEB-INF/web.xml             ... soubor popisující webovou aplikaci
/WEB-INF/classes/*/*.class   ... servlety a jiné Java třídy
/WEB-INF/lib/*.jar           ... JAR balíky
/*/*.jsp                     ... JSP stránky
/*/*.*                       ... HTML, CSS, obrázky atd.

Název webové aplikace (název WAR souboru a adresáře z něho vzniklého) je obvykle použit jako začátek cesty v URL odpovídajících této webové aplikaci. Soubor /WEB-INF/web.xml obsahuje definice mapování URL na servlety, plus další konfigurační údaje. Mapování URL na servlety se provádí buď podle začátku cesty v URL, nebo podle přípony. Následující příklad určuje, že třída MujServlet bude zavolána pro všechna URL začínající prefixem /mujservlet/, nebo končící příponou .muj:

<web-app ...
 ...
 <servlet>
     <servlet-name>mujServlet</servlet-name>
     <servlet-class>cz.nekde.mojeapp.MujServlet</servlet-class>
 </servlet>
 <servlet-mapping>
     <servlet-name>mujServlet</servlet-name>
     <url-pattern>/mujservlet/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
     <servlet-name>mujServlet</servlet-name>
     <url-pattern>*.muj</url-pattern>
 </servlet-mapping>

Pokud by tento popisovač web.xml byl uvnitř souboru s webovou aplikací mojeapp.war, servlet MujServlet by byl vyvolán pro URL s cestou začínající /mojeapp/mujservlet/.


Kromě servletů může webová aplikace obsahovat ješte servletové filtry, což jsou Java třídy implementující interface javax.servlet.Filter, umožňující ovlivnit zpracování HTTP volání dřív, než se dostane k servletům. Do filtrů je vhodné například umístit nastavení kódování českých znaků v požadavku.

Jeden servlet (Java třída) může mít více instancí. Jedna instance může být mapována na různá URL.

Webová aplikace se v terminologii Servlet API nazývá kontext servletu, a každý servlet má možnost o ní získat informace pomocí volání getServletContext(), které vrací objekt ServletContext. S jeho pomocí je možné například získat informace o servletovém kontejneru (jeho jméně a verzi, implementované verzi Servlet API atd). Servlety do něj mohou ukládat libovolné objekty a opět je získávat pomocí setAttribute() a getAttribute(). Protože kontext je jen jeden pro celou webovou aplikaci, mohou tímto způsobem servlety sdílet informace mezi sebou.

Celá webová aplikace i jednotlivé instance servletů mohou dostat konfigurační informace ve formě pojmenovaných řetězců, a to pomocí metod getInitParameter(), které jsou přítomny v interfacech ServletContext (celá aplikace) a ServletConfig (jedna instance servletu). Tyto inicializační parametry mohou být nastaveny buď v souboru /WEB-INF/web.xml, nebo jiným mechanismem závislým na konkrétní implementaci servletového kontejneru. V každém případě umožňují nastavovat proměnlivé údaje beze změn programovacího kódu a rekompilace servletů.


HttpSession

HTTP protokol je bezestavový, tj. každý požadavek musí obsahovat všechny potřebné informace. Servlety poskytují nástroj pro identifikaci jednoho prohlížeče pomocí tzv. HttpSession. Požadavky pocházející od stejného prohlížeče jsou identifikovány buď nastavením Cookie, nebo pokud jsou cookies v prohlížeči vypnuta, pomocí URL rewriting (přepisování URL), kdy je do URL vkládán speciální identifikátor.

O sledování prohlížeče pomocí HttpSession se žádá zavoláním metody

HttpSession session = request.getSession(true);

a protože cookies mohou být vypnuta, URL se musí přepisovat pomocí:

String odkaz = response.encodeURL(request.getContextPath() + "/stranka.jsp");

Session se udržuje obvykle 30 minut od posledního požadavku od prohlížeče.

Spolupráce mezi servlety

Příchozí HTTP požadavek může obsloužit jeden servlet sám, nebo je možné použít složitějšího postupu. Zejména při použití sofistikovaných rámců pro tvorbu webových aplikací (např. Apache Struts), je jeden HTTP požadavek zpracováván celou řadou servletů, které si postupně předávají řízení. Například jeden servlet zpracuje příchozí informace, podle výsledku vybere servlet generující HMTL stránku a předá mu řízení. Ten pro generování různých částí stránky (hlavička, navigační menu, patička, atd.) volá různé další servlety.

Předávání řízení mezi servlety se provádí pomocí třídy javax.servlet.RequestDispatcher, která se získá voláním getRequestDispatcher() na ServletContext nebo HttpServletRequest. Třída RequestDispatcher umožňuje buď předat řízení úplně pomocí forward(), nebo jen dočasně pomocí include().

Rozsahy platnosti atributů

Spolupracující servlety si mohou předávat informace nastavováním tzv. atributů (protože metody pro nastavování se jmenují setAttribute()), i když ve speficikacích JSP se používá pojem rozsahová proměnná (scoped variable). Jde o ukládání libovolných objektů pod řetězcovými klíči, tedy do hashovacích tabulek. Tyto atributy je možné nastavovat na více úrovních:

úroveň (scope) nastavení atributu existence
aplikace ServletContext.setAttribute() v rámci celé aplikace, mezi všemi uživateli
session HttpSession.setAttribute() ve všech voláních jednoho uživatele
HTTP požadavek HttpServletRequest.setAttribute() jen po dobu trvání jednoho HTTP požadavku
JSP stránka JspContext.setAttribute() jen v rámci jedné JSP stránky

Atributy nastavené na úrovni aplikace jsou společné všem servletům a všem uživatelům.

Session znamená, že všechny HTTP požadavky přicházející od jednoho webové prohlížeče sdílí instanci javax.servlet.http.HttpSession, která umožňuje identifikovat jednoho uživatele. Různí uživatelé mají různé session. Stejnojmené atributy tedy mohou mít při nastavení do session různé hodnoty pro různé uživatele.

Atributy nastavené na úrovni HTTP požadavku jsou sdíleny řetězcem servletů zpracovávajícím jeden HTTP požadavek.

Platnost v rámci JSP stránky není definována v rámci Servlet API, ale je zde uvedena pro úplnost. V podstatě znamená, ze atributy v tomto rozsahu platnosti jsou vidět jen v rámci jedné JSP stránky, nepřenáší se do jiných stránek zavolaných pomocí <jsp:include> nebo <jsp:forward>. Používají se pro předávání informací mezi JSP tagy.

Listenery

Webová aplikace může ve web.xml definovat, že určité třídy budou dostávat upozornění na určité události.

  • ServletRequestListener začátek a konec requestu
  • ServletRequestAttributeListener změny atributů requestu
  • HttpSessionListener vytvoření nebo zrušení session
  • HttpSessionAttributeListener změny atributů session
  • ServletContextListener vytvoření a zrušení kontextu aplikace
  • ServletContextAttributeListener změny atributů kontextu aplikace
  • HttpSessionActivationListener aktivace a pasivace session při přenosu mezi VM
  • HttpSessionBindingListener objekt se dozví o svém přidání do session

Shrnutí

Tato stránka je součástí souboru hesel o webových aplikacích. Popisuje Java servlety, což jsou nástroje pro obsluhu HTTP protokolu na straně serveru. Pracují na velice nízké úrovni, takže zatímco umožňují plnou flexibilitu při zpracování HTTP, jsou nepohodlné na programování. Proto je pro tvorbu webových aplikací vhodné použít jejich nadstavby, zejména Java Server Pages a rámce pro tvorbu aplikací, např. Apache Struts. Specifikace Servlet API jsou od jisté verze přímo svázány se specifikacemi Java Server Pages, proto pokračujte tímto tématem.