Úvod do Servletů

Tato část představuje úvod do servletů. Dozvíte se co to servlety jsou, k čemu slouží a jak fungují. Také si zkusíme dva jednoduché vytvořit.

Co je webový server

V první části tutoriálu jsme si nainstalovali webový server jménem Tomcat. Na tomto serveru budeme spouštět naše webové aplikace. Krátce bych tu chtěl jen popsat, k čemu webový server vlastně slouží. Většina z vás to již určitě ví. Webový server bere požadavky od klientů a něco jim vrací (HTML stránku, obrázek, atd.). Server zjišťuje, jaký obsah má klientovi poslat podle URL (např. https://www.udemy.com/, https://jirkasa.github.io/threejs-navod/...). Pokud pro request nic nenajde, tak vyhodí chybu 404 (nenalezeno). Webový server se skládá z hardwaru a softwaru. Hardware je nějaké fyzické zařízení (počítač) a software představuje nějakou aplikaci (např. Tomcat).

Webový server

Co jsou Servlety

Servlety jsou Java programy, které běží na webovém serveru (např. Tomcatu). Používají se ke zpracování požadavku, který webový server obdržel, a k vytvoření odpovědi, která je odeslána zpět klientovi.

Použitím servletů můžeme vytvářet dynamické webové stránky. To znamená, že obsah HTML stránek, které klient ze serveru obdrží není statický (není v kódu vložený napevno). HTML stránka se generuje v servletu, když na server přijde od klienta požadavek. Díky tomu můžeme na stránce například zobrazovat nějaká data z databáze.

Jak servlety zjednodušeně fungují, můžete vidět v následujícím obrázku. Když na server dorazí požadavek, tak se vybere servlet, který jej zpracuje a vytvoří odpověď (HTML stránku). HTML stránka se poté pošle klientovi.

Jak fungují servlety

Komponenty URL adresy

Když klient vytváří požadavek, který chce poslat na server, používá k tomu komponenty URL adresy. Příklad URL adresy můžete vidět v následující ukázce:

https://string-striker.com/songs?page=2&sort=artist

Komponenty URL jsou zkombinovány a odděleny následujícím způsobem:

schéma://host:port/cesta?query

Co jednotlivé komponenty znamenají, popisuje následující seznam:

  • schéma - určuje protokol, který se má použít pro poslání požadavku (např. http, https...)
  • host - hostitel (např. www.string-striker.com) - může to být doménové jméno nebo IP adresa
  • port - host může být následován portem (pokud není specifikován, tak se použije defaultní - např. pro HTTP 80 a pro HTTPS 443)
  • cesta - specifikuje cestu ke zdroji na hostiteli (většinou je to hierarchická struktura oddělená lomítky)
  • query - na konci URL může být query řetězec (jedná se o parametry, které jsou odděleny znakem "&" a jsou ve tvaru "název=hodnota")

HTTP protokol

V případě webových stránek klient se serverem komunikuje pomocí HTTP protokolu. Pokud klient chce navštívit webovou stránku, tak na server pošle HTTP request. Server mu nazpět pošle HTTP response s HTML stránkou. Pro programování servletů je dobré vědět, z čeho se HTTP request a HTTP response skládá.

Z čeho se skládá HTTP request

HTTP request se skládá z následujících prvků:

  • request line
  • headery
  • body (nepovinné)

Následující ukázka ukazuje příklad request line:

GET /index.html HTTP/1.1

Request line se skládá ze tří položek:

  • metoda
  • cesta (komponenta URL, která je vysvětlena výše)
  • verze HTTP

HTTP metoda slouží k tomu, aby serveru dala najevo, co má udělat s identifikovaným zdrojem. Metoda GET například indikuje, že klient chce, aby mu server poslal nějaká data. Metoda DELETE zase značí, aby server něco smazal. Zde jsem popsal, k čemu jednotlivé metody slouží:

  • GET - slouží k získání informací o zdroji (nějakých dat) - tato metoda se automaticky použije, když do prohlížeče zadáme URL nebo někde klikneme na odkaz
  • HEAD - podobné jako GET, ale server by měl poslat jen headery bez body (jen informace o zdroji bez samotného obsahu)
  • POST - slouží pro odeslání dat na server s cílem vytvořit nový zdroj (třeba novou položku v databázi)
  • PUT - slouží k aktualizaci zdroje (zdroj na serveru se celý vymění za ten, co posílá klient)
  • PATCH - stejně jako metoda PUT slouží k aktualizaci zdroje (narozdíl od metody PUT ale slouží jen k částečné aktualizaci zdroje - aplikuje změny již na existující zdroj)
  • DELETE - slouží ke smazání zdroje

Kromě předchozích metod ještě existují další 3, ale ty pravděpodobně nikdy nepoužijete:

  • OPTIONS - slouží k získání informací o možnostech komunikace se zdrojem
  • TRACE - slouží k diagnostice (klient může získat informace o cestě, kterou požadavek prošel na serveru)
  • CONNECT - slouží k vytvoření tunelu k serveru za pomocí proxy (často se používá při vytváření zabezpečeného spojení - HTTPS)

Je nutno podotknout, že HTTP metody jsou spíš jen taková konvence. Nic nám samozřejmě nebrání na serveru vytvořit něco v databázi i pro GET requesty, které slouží hlavně pro získání dat ze serveru. Často se také v některých projektech třeba používají jen metody GET a POST, obzvlášť pokud se jedná o webové stránky, kde se moc nepracuje s JavaScriptem. Klasické HTML formuláře totiž podporují jen tyto dvě metody. Je to proto, že formuláře by měly mít nějaké inputy - něco co do nich uživatel zadá. Pokud by jen stisknul tlačítko, aby se na serveru například něco odstranilo, tak už by to nebyl tak úplně formulář. HTML formulář může být odeslán jen metodou GET (třeba nějaký vyhledávač) nebo metodou POST (třeba nějaký registrační formulář). Formuláře tak byly prostě navrženy a nic s tím nenaděláme. Pokud chceme používat i jiné HTTP metody, musíme sáhnout po JavaScriptu.

Headery (česky hlavičky) slouží u HTTP requestu k předání informací o requestu ve formě názvu a hodnoty. Mohou obsahovat různé informace, jako je typ obsahu, který má server vrátit, jazyk, cookies a další. Zde je pár příkladů:

Host: www.string-striker.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
Content-Type: text/html

Header "Host" například značí, na jaký server se má request poslat. Pomocí headeru "User-Agent" zase můžeme na serveru zjistit informace o tom, jaký má klient prohlížeč a operačním systém. Přehled standardních headerů si můžete prohlédnout například zde.

Body představuje samotný obsah HTTP requestu. Je nepovinné. HTTP request poslaný metodou GET body například nemá a pokud se potřebují nějaká data poslat, tak se pošlou jako query řetězec (popsáno výše v části Komponenty URL adresy). HTTP request poslaný metodou POST naopak body má, protože slouží k poslání dat na server. Pokud tedy odešleme HTML formulář metodou POST, tak se data formuláře na server pošlou v body.

Z čeho se skládá HTTP response

HTTP response se skládá z následujících prvků:

  • status line
  • headery
  • body (nepovinné ale většinou je potřeba)

Následující ukázka ukazuje příklad response line:

HTTP/1.1 200 OK

Response line se skládá ze tří položek:

  • verze HTTP
  • status kód
  • status text

Status kód určuje, zda byl HTTP request, pro který se HTTP response posílá, úspěšně dokončen. Status kód 200 například značí úspěch, 404 značí že se zdroj nenašel, a tak podobně. Status text je textový popis stavového kódu (např. "OK" pro 200 nebo "Not Found" pro 404). Status kód se skládá ze tří číslic a spadá do jedné z těchto 5 kategorií podle počáteční číslice:

  • 1xx (Informační) - informační odpovědi (např. 100 Continue - server indikuje, že byla obdržena část requestu a klient může pokračovat s dalšími částmi) - těmito kódy se nemusíme vůbec zaobírat
  • 2xx (Úspěšné) - kódy v této kategorii značí úspěšné provedení požadavku (např. 200 OK - standardní úspěšná odpověď, nebo 201 Created - zdroj byl vytvořen)
  • 3xx (Přesměrování) - kódy této kategorie značí, že klient musí podniknout další kroky k získání požadovaného zdroje (např. 301 Moved Permanently - zdroj byl přesunut na jinou adresu)
  • 4xx (Chyba klienta) - kódy z této kategorie značí, že request klienta je neplatný (např. 400 Bad Request - uživatel poslal data ve špatném formátu)
  • 5xx (Chyba serveru) - kódy z této kategorie značí, že na serveru došlo k chybě (např. 500 Internal Server Error - obecná chyba serveru)

Pokud si chcete prohlédnout všechny HTTP status kódy, tak se můžete podívat třeba na tuto stránku. Určitě se je ale neučte nazpaměť. Pokud někdy nebudete vědět jaký status kód použít, tak si jej můžete vyhledat. U klasických serverem renderovaných webových stránek se to stejně podle mě podobně jako u HTTP metod taky moc neřeší. Ale to si jen myslím. Víc se to podle mě řeší u REST API, které se programuje pomocí JAX-RS, což je jiná technologie z Javy EE, stejně jako servlety. O tom se v tomto tutoriálu učit nebudeme. Tento tutoriál je o servletech.

Co jsou headery a body jsem popsal již u HTTP requestu, takže to tu nebudu znovu vysvětlovat. V body se posílá obsah HTTP response (např. HTML stránka, obrázek...).

Balíčky javax.servlet a javax.servlet.http

Pro servlety existují dva balíčky: javax.servlet a javax.servlet.http. V balíčku javax.servlet můžeme najít rozhraní Servlet a třídu GenericServlet, která jej implementuje (a nejen to). V balíčku javax.servlet.http se nachází třída HttpServlet. Rozhraní Servlet poskytuje obecný framework pro vytvoření servletu. Servlet může toto rozhraní implementovat, nebo jej implementovat nepřímo rozšířením třídy GenericServlet nebo HttpServlet. Třída GenericServlet se používá k vytváření servletů, kteří mohou pracovat s jakýmkoliv protokolem. Třída HttpServlet se používá k vytváření HTTP servletů. Tyto servlety mají výstup ve formě HTML stránek.

Následující diagram ukazuje, že třída HttpServlet dědí od třídy GenericServlet, která implementuje rozhraní Servlet. Dále také implementuje rozhraní ServletConfig a Serializable.

Hierarchie Java tříd pro servlety

Rozhraní Servlet

Každý servlet musí implementovat rozhraní Servlet. Toto rozhraní definuje metody, které musí třída, která jej implementuje, implementovat. Následující tabulka je popisuje:

MetodaNávratový typPopis
init(ServletConfig config)voidVolána servlet containerem (Tomcatem) při vytvoření servletu. Slouží k inicializaci (můžeme třeba načíst nějakou konfiguraci, připojit se k databázi, atp.)
service(ServletRequest req, ServletResponse res)voidVolána servlet containerem (Tomcatem), když na server přijde request aby na něj mohl servlet reagovat.
destroy()voidVolána servlet containerem (Tomcatem), když se servlet chystá vyřadit z provozu. Slouží k provedení různých čistících úkonů a tak podobně.
getServletConfig()ServletConfigVrací ServletConfig objekt, který obsahuje inicializační a spouštěcí parametry pro servlet.
getServletInfo()StringVrací informace o servletu (např. autor, verze, copyright...).

Životní cyklus servletu

V předchozí tabulce, která popisuje metody Servlet rozhraní, můžete vidět, že metody init, service a destroy jsou volány v různých chvílích servlet containerem (Tomcatem). Na začátku servlet container vytvoří instanci servlet třídy a vytvořený servlet inicializuje zavoláním init metody. Poté je servlet připraven zpracovávat klientské requesty pomocí metody service. Až se potom servlet container rozhodne servlet vyřadit z provozu, tak se zavolá metoda destroy. Následující diagram tento životní cyklus servletu ukazuje:

Životní cyklus servletu

Servlet request a servlet response

Do metody service, kterou definuje rozhraní Servlet, se jako parametr předávají objekty typu ServletRequest a ServletResponse. Objekt typu ServletRequest můžeme použít k získání informací o requestu, který nám klient poslal. Objekt typu ServletResponse můžeme použít k poslání odpovědi klientovi. Oba tyto objekty vytváří servlet container a volá s nimi service metodu servletu. Co všechno rozhraní ServletRequest a ServletResponse definují za metody si můžete prohlédnout v dokumentaci.

Vytvoření servletu implementací Servlet rozhraní

Vytvořit servlet můžeme třemi cestami:

  • implementací Servlet rozhraní
  • zděděním třídy GenericServlet
  • zděděním třídy HttpServlet

Jako první si zkusíme vytvořit servlet implementací Servlet rozhraní. Vytvoříme si nový Maven projekt a vytvoříme třeba třídu Servlet1, které nastavíme, že toto rozhraní implementuje. Projekt můžeme nazvat třeba jako "uvod-do-servletu". Návod na vytvoření projektu v Eclipse je v první části tutoriálu, tak jej podle něj můžete vytvořit. Soubor pom.xml bude v podstatě stejný jako v návodu. V následující ukázce si kromě třídy Servlet1 můžete po otevření bočního panelu prohlédnout soubory a složky nově vytvořeného projektu.

  • src
    • main
      • java
      • resources
      • webapp
    • test
      • java
      • resources
  • target
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>io.github.jirkasa</groupId>
    <artifactId>uvod-do-servletu</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>uvod-do-servletu</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
import javax.servlet.Servlet;

public class Servlet1 implements Servlet {

}

Jelikož implementujeme rozhraní, tak musíme implementovat metody, které definuje.

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet1 implements Servlet {
    
    @Override
    public void init(ServletConfig config) throws ServletException {
    }
    
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    }
    
    @Override
    public void destroy() {
    }

    @Override
    public ServletConfig getServletConfig() {
    }

    @Override
    public String getServletInfo() {
    }
}

Do metody init nám servlet container předává ServletConfig objekt. Tento objekt obsahuje různé inicializační parametry pro servlet. Od metody getServletConfig se očekává, že jej vrátí. Proto si jej v metodě init musíme uložit a v metodě getServletConfig jej vracet, jak ukazuje následující ukázka.

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet1 implements Servlet {
    private ServletConfig config = null;

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    }
    
    @Override
    public void destroy() {
    }

    @Override
    public ServletConfig getServletConfig() {
        return this.config;
    }

    @Override
    public String getServletInfo() {
    }
}

Metoda getServletInfo slouží k získání informací o servletu (např. autor, verze, atp.). Můžeme v ní vrátit jakýkoliv text.

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet1 implements Servlet {
    private ServletConfig config = null;

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    }
    
    @Override
    public void destroy() {
    }

    @Override
    public ServletConfig getServletConfig() {
        return this.config;
    }

    @Override
    public String getServletInfo() {
        return "Servlet, vytvořený implementací Servlet rozhraní.";
    }
}

Metoda service slouží ke zpracování requestu a poslání odpovědi klientovi. Stejně jako v první části tutoriálu, tak i teď jen pošleme jednoduchou HTML stránku s nějakým textem. Jak jsem již psal, tak objekt třídy ServletRequest, který se do metody service předává, slouží k získání informací o requestu. V našem případě jej nepotřebujeme, protože jen generujeme HTML stránku. Potřebujeme ale objekt třídy ServletResponse, který se do service metody předává jako druhý parametr. Pomocí něj HTML stránku klientovi pošleme.

Následující ukázka ukazuje, jak můžeme HTML stránku poslat. Nejdříve nastavíme header "Content-Type", který může klient použít ke zjištění, co server posílá za obsah. To se dělá pomocí metody setContentType, kde předáme MIME type. Tabulku s různými MIME typy pro soubory webových stránek si můžete prohlédnout třeba zde. Poté v bloku try-with-resources získáme ze ServletResponse objektu PrintWriter, který použijeme k vytvoření HTML stránky. Jak blok try-with-resources funguje tu popisovat nebudu, to se týká Javy obecně. Jednoduše potřebujeme na print writeru po jeho použití zavolat metodu close. Blok try-with-resources je pro tuto operaci takový syntaktický cukřík.

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet1 implements Servlet {
    private ServletConfig config = null;

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        // nastavení headeru Content-Type, který slouží k informování klienta, co za obsah posíláme
        // (posíláme HTML stránku)
        res.setContentType("text/html; charset=utf-8");
        
        // poslání HTML stránky
        try (PrintWriter out = res.getWriter()) {
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta charset=\"UTF-8\">");
            out.println("<title>Úvod do Servletů</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet1</h1>");
            out.println("<p>Servlet, vytvořený implementací Servlet rozhraní.</p>");
            out.println("</body>");
            out.println("</html>");
        }
    }
    
    @Override
    public void destroy() {
    }

    @Override
    public ServletConfig getServletConfig() {
        return this.config;
    }

    @Override
    public String getServletInfo() {
        return "Servlet, vytvořený implementací Servlet rozhraní.";
    }
}

To je vše, co jsme k vytvoření jednoduchého servletu implementací Servlet rozhraní potřebovali udělat. Teď již jen musíme Tomcatu dát vědět, kdy má náš servlet zavolat.

Deployment descriptor (web.xml)

Kdy má náš servlet Tomcat zavolat, můžeme nastavit pomocí souboru web.xml umístěného ve složce WEB-INF nacházející se ve složce webapp. Jedná se o deployment descriptor. Je to XML soubor, který slouží ke konfiguraci webových aplikací. Můžeme s jeho pomocí nastavit, jaké URL má zpracovávat jaký servlet a tak podobně. Přidáme si tedy soubor web.xml do příslušné složky a vložíme pro něj základní kód. Ukazuje jej následující ukázka, nebo jej můžete zkopírovat třeba odsud. Popravdě netuším k čemu jednotlivé atributy kořenového elementu web-app slouží. Prostě to jen zkopíruju, když potřebuji soubor web.xml vytvořit.

  • src/main/webapp/WEB-INF
  • src/main/webapp/WEB-INF
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">
</web-app>

Než můžeme v deployment descriptoru náš servlet namapovat na nějaké URL, tak si jej musíme nadefinovat pomocí elementu servlet. Následující ukázka ukazuje, jak to můžeme udělat. Element servlet má podelementy servlet-name a servlet-class. Elementem servlet-name nastavujeme název servletu a elementem servlet-class nastavujeme třídu pro servlet.

  • src/main/webapp/WEB-INF
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">
    <servlet>
        <servlet-name>PrvniServlet</servlet-name>
        <servlet-class>Servlet1</servlet-class>
    </servlet>
</web-app>

Po nadefinování servletu jej můžeme namapovat na nějaké URL pomocí elementu servlet-mapping. V jeho podelementu servlet-name specifikujeme název servletu, který chceme namapovat, a v podelementu url-pattern můžeme nastavit URL, pro které se má servlet volat. Můžeme si jej namapovat třeba na "/servlet-implementaci-servlet-rozhrani", jak ukazuje následující ukázka.

  • src/main/webapp/WEB-INF
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">
    <servlet>
        <servlet-name>PrvniServlet</servlet-name>
        <servlet-class>Servlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PrvniServlet</servlet-name>
        <url-pattern>/servlet-implementaci-servlet-rozhrani</url-pattern>
    </servlet-mapping>
</web-app>

Pokud si teď naši aplikaci spustíte a navštívíte http://localhost:8080/uvod-do-servletu/servlet-implementaci-servlet-rozhrani, tak uvidíte stejnou stránku, kterou ukazuje následující obrázek.

Stránka pro servlet vytvořený implementací Servlet rozhraní

Vytvoření servletu zděděním třídy GenericServlet

Lepší cesta jak vytvořit servlet, je vytvořit podtřídu abstraktní třídy GenericServlet. Ta implementuje rozhraní Servlet a má až na metodu service všechny jeho metody implementované. Tvorba servletu je pro nás tedy tímto způsobem jednodušší než implementací Servlet rozhraní napřímo. Můžeme implementovat jen metodu service.

Pro ukázku si v našem projektu vytvoříme třídu, kterou můžeme pojmenovat jako Servlet2 a nastavíme, že dědí od třídy GenericServlet.

import javax.servlet.GenericServlet;

public class Servlet2 extends GenericServlet {

}

Nyní můžeme implementovat metodu service a poslat HTML stránku, podobně jako při tvorbě servletu implementací Servlet rozhraní.

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet2 extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html; charset=utf-8");
        
        try (PrintWriter out = res.getWriter()) {
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta charset=\"UTF-8\">");
            out.println("<title>Úvod do Servletů</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet2</h1>");
            out.println("<p>Servlet, vytvořený zděděním třídy GenericServlet.</p>");
            out.println("</body>");
            out.println("</html>");
        }
    }
}

V souboru web.xml můžeme servlet nastavit třeba pro URL "/servlet-zdedenim-tridy-generic-servlet", jak ukazuje následující ukázka.

  • src/main/webapp/WEB-INF
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">
    <servlet>
        <servlet-name>PrvniServlet</servlet-name>
        <servlet-class>Servlet1</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>DruhyServlet</servlet-name>
        <servlet-class>Servlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PrvniServlet</servlet-name>
        <url-pattern>/servlet-implementaci-servlet-rozhrani</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>DruhyServlet</servlet-name>
        <url-pattern>/servlet-zdedenim-tridy-generic-servlet</url-pattern>
    </servlet-mapping>
</web-app>

Po restartu Tomcatu a navštívení http://localhost:8080/uvod-do-servletu/servlet-zdedenim-tridy-generic-servlet by jste měli vidět stránku, kterou ukazuje následující obrázek.

Stránka pro servlet vytvořený zděděním třídy GenericServlet

Inicializační parametry servletů

V souboru web.xml si pro naše servlety můžeme definovat inicializační parametry. Jedná se o hodnoty, které Tomcat předá do servletu když jej inicializuje prostřednictvím init metody. Inicializační parametry se vytváří prostřednictvím init-param elementů uvnitř servlet elementu. Pomocí jeho podelementu param-name specifikujeme název parametru a pomocí podelementu param-value hodnotu parametru.

V našem projektu si třeba pro náš druhý servlet vytvoříme inicializační parametr, pomocí kterého budeme moci měnit nadpis stránky. Následující ukázka ukazuje, jak to můžeme v souboru web.xml udělat.

  • src/main/webapp/WEB-INF
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" version="3.0">
    <servlet>
        <servlet-name>PrvniServlet</servlet-name>
        <servlet-class>Servlet1</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>DruhyServlet</servlet-name>
        <servlet-class>Servlet2</servlet-class>
        <init-param>
            <param-name>nadpis_stranky</param-name>
            <param-value>Můj Servlet</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>PrvniServlet</servlet-name>
        <url-pattern>/servlet-implementaci-servlet-rozhrani</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>DruhyServlet</servlet-name>
        <url-pattern>/servlet-zdedenim-tridy-generic-servlet</url-pattern>
    </servlet-mapping>
</web-app>

V servletu můžeme inicializační parametr získat ze ServletConfig objektu, který nám Tomcat předává do metody init. Přepíšeme ji tedy, inicializační parametr si v ní získáme a uložíme a poté jej budeme používat v metodě service. Následující ukázka ukazuje upravenou třídu Servlet2.

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet2 extends GenericServlet {
    private String nadpisStranky;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // získání inicializačního parametru
        this.nadpisStranky = config.getInitParameter("nadpis_stranky");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.setContentType("text/html; charset=utf-8");
        
        try (PrintWriter out = res.getWriter()) {
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<meta charset=\"UTF-8\">");
            out.println("<title>Úvod do Servletů</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>" + this.nadpisStranky + "</h1>");
            out.println("<p>Servlet, vytvořený zděděním třídy GenericServlet.</p>");
            out.println("</body>");
            out.println("</html>");
        }
    }
}

Po restartu Tomcatu a navštívení http://localhost:8080/uvod-do-servletu/servlet-zdedenim-tridy-generic-servlet by jste měli vidět, že se jako nadpis stránky použil text z inicializačního parametru.

Nadpis stránky podle inicializačního parametru

Pro tuto část je to všechno. Dozvěděli jste se co je to servlet a zkusili jsme si jej vytvořit dvěma způsoby. V příští části, která je o HTTP servletech, si ukážeme třetí a nejlepší způsob jak vytvořit servlet.