Java na webu VI. - První aplikace

V dnešním díle si vytvoříme naši první aplikaci, na níž budeme demonstrovat spolupráci JSP se servletem, zpracování formuláře a výpis dat.

25.6.2013 00:00 | Petr Horáček | přečteno 13973×

To co jsme nazvali aplikací v minulém díle opravdu nestálo za moc, pro vytvoření oné jednoduché stránky by stačil pouhý HTTP server, šlo především o vyzkoušení funkčnosti NetBeans. V dnešním díle si ale postavíme aplikaci ke které už bude potřeba trochu více aplikační logiky, pomocí jednoduchého servletu a JSP zde vytvoříme zápisník, který by mohl směle konkurovat Google Keep. Mimo to si představíme některé funkce vývojového prostředí, především pak práci s konfiguračním souborem web.xml.

Nový projekt

Začneme vytvořením nového projektu (Java Web → Web Application) stejně jako v minulém díle. Pojmenujeme jej Zapisnik a za aplikační server zvolíme Apache Tomcat. Nyní je před námi nový projekt s vygenerovaným souborem index.jsp, ten ale můžeme prozatím zavřít.

Servlet

Dejme se do vytvoření servletu. V kartě projektů (v levém horním boxu) otevřete vytvořený Zapisnik a rozbalte Source Packages, zde je místo pro balíčky obsahující servlety a ostatní třídy. Je sice možné vytvořit servlet rovnou v kořenovém balíčku Source Packages, je ale vhodné udržovat pořádek v projektu tříděním tříd do balíčků. Klikněte sem tedy pravým tlačítkem a vytvořte nový balíček (New → Java Package), v zobrazeném průvodci zadejte jako Package Name „servlety“. Balíček pro servlety je nyní hotov.

pozn.: V tutoriálech budu pro balíčky, třídy apod. používat české názvy, v praxi je ale vhodnější vše pojmenovávat anglicky.

Tvorba servletu

Klikněte do balíčku pravým tlačítkem a vytvořte nový servlet (New → Servlet), v zobrazeném formuláři zadejte jako Class Name „Zapisnik“, v dalším kroku odškrtněte „Add information to deployment descriptor (web.xml)“ (přidat informaci do Deployment Descriptoru), změňte Url Pattern (adresu servletu) na „/“ a potvrďte.

package servlety;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Zapisnik extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                                                throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet Zapisnik</title>");        
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet Zapisnik at " + request.getContextPath() + "</h1>");
            out.println("</body>");
            out.println("</html>");
        } finally {        
            out.close();
        }
    }
    // HttpServlet methods. Click on the + sign on the left to edit the code.
}

Zobrazí se připravený servlet Zapisnik s metodou processRequest, který, jak vidíte, vypisuje do output streamu jednoduchou stránku. Asi se divíte že tento úkol neobstarává metoda doPost či doGet. Po zozkliknutí boxu na konci servletu ale zjistíte, že se zde nachází metody přijímající HTTP metody GET i POST, ty však předaný request a response přeposílají okamžitě dál ke zpracování do zmíněné metody processRequest, ta tedy zpracuje příchozí request nezávisle na jeho typu. Poslední metoda vrací krátký popis servletu.

Vraťte se tedy k metodě processRequest a její obsah smažte. V horní části servletu se nyní vedle importu PrintWriteru objevila žlutá žárovka, po jejím rozkliknutí se dozvíte, že dotyčný import není využívaný a může být tedy odstraněn. A když už jsme u toho odstraňování, smažeme i vygenerované komentáře.

Přidání importu
package servlety;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Zapisnik extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                                                throws ServletException, IOException {

    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
                                            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
                                            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    public String getServletInfo() {
        return "Servlet k ničemu";
    }// </editor-fold>
}

Jak bude výsledný zápisník vypadat? Na jediné stránce budou pomocí servletu vypsány uložené poznámky a také formulář pro jejich zapisovaní. Po odeslání vyplněného formuláře se nová poznámka předá servletu a ten ji uloží do seznamu (provizorní databáze). Nakonec servlet opět nechá vykreslit JSP s vypsaným seznamem zápisků a formulářem.

Nejdříve si tedy vytvořme onu provizorní databázi. Na začátek třídy Zapisnik vložte tento kód:

List<String> zapisky = new ArrayList();

U nového řádku se objeví opět ona žlutá žárovka upozorňující na chybu a navrhující její opravu. Klikněte v její nabídce na možnost „Add import for java.util.ArrayList“ a poté na „Add import for java.util.List“, do servletu se přidají importy potřebných balíčků.

Přejděme teď k samotnému zpracování requestů. Do metody processRequest přidejte tento kód:

//část sloužící pro zápis nové poznámky

String zapisek = request.getParameter("zapisek");

if(zapisek != null && !zapisek .equals("")){

zapisky.add(zapisek);

}

//část sloužící pro vygenerování výsledné stránky

request.setAttribute("zapisky", zapisky);

RequestDispatcher disp = request.getRequestDispatcher("/WEB-INF/index.jsp");

disp.forward(request, response);

//část sloužící pro zápis nové poznámky
String zapisek = request.getParameter("zapisek"); 
if(zapisek != null && !zapisek .equals("")){
    zapisky.add(zapisek);
}

//část sloužící pro vygenerování výsledné stránky    
request.setAttribute("zapisky", zapisky);
RequestDispatcher disp = request.getRequestDispatcher("/WEB-INF/index.jsp");
disp.forward(request, response); 

V první části kódu vytvoříme novou proměnnou zapisek a pokusíme se do ní zapsat přijatý stejnojmenný parametr. Pokud nebyl žádný parametr přijat, do proměnné se zapíše null. Pokud byl parametr přijat a zároveň nejde pouze o prázdný řetězec, přidáme jej do seznamu zápisků.

V druhé části předáme do requestu seznam zápisků v podobě atributu. Dále vytvoříme nový RequestDispatcher (pro který je nutné importovat balíček), který se stará o předání objektů request a response dalšímu servletu či JSP. Jako adresáta RequestDispatcheru jsme označili dosud neexistující JSP na adrese /WEB-INF/index.jsp. Funkcí forward je pak Dispatcher spuštěn, přičemž mu jsou předány objekty request (s uloženým parametrem zapisky) a response. To je k servletu vše, vraťme se nyní ještě na chvíli k Deployment Descriptoru.

web.xml

O Deployment Descriptoru uloženém v souboru web.xml jsme si už pověděli v třetím díle seriálu. V našem projektu se tento souboru nachází ve složce /WEB-INF. Při generaci servletu jsme do něj nechaly automaticky zapsat jeho URL, pojďme se nyní blíže podívat na tento konfigurační soubor a nástroje které k jeho úpravám NetBeans nabízí.

Otevřete tedy v kartě Configuration Files soubor web.xml. NetBeans umožňuje snadnou úpravu tohoto souboru prostřednictvím formulářů, mezi kterými se lze pohybovat pomocí záložek na horní liště editoru. Pod záložkou Source se nachází holý XML kód:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>zapisnik</servlet-name>
        <servlet-class>servlety.zapisnik</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>zapisnik</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app> 

Po přepnutí na kartu Servlets se však objeví formulář s již vytvořeným servletem. Je zde jeho název (Servlet Name), popis (Description), výběr třídy, inicializační parametry a nastavení přístupu. Nevyplněný vstup StartupOrder slouží k nastavení pořadí ve kterém se mají servlety nahrávat při startu aplikace. Standarně jsou nahrány až při přijetí prvního requestu, pokud zde ale vyplníte číslo, servlet se nahraje sám.

Zápis URL do Deployment Descriptoru ale není jedinou možností. URL lze třídě servletu předat i pomocí anotace @WebServlet(). Začátek servletu by pak mohl vypadat třeba takto:

...
@WebServlet(name = "Zapisovac", urlPatterns = {"/"})
public class Zapisnik extends HttpServlet {
...

JSP

Již se zdárně blížíme k dokončení naší první aplikace, nyní vytvoříme JSP soubor do kterého budeme vypisovat zápisky a ze kterého zároveň budeme vytvářet nové.

Soubor index.jsp vygenerovaný společně s aplikací se nachází v kořenovém adresáři statických souborů (Web Pages), je tedy běžně přístupný na adrese /index.jsp, my ale potřebujeme aby byl získatelný pouze prostřednictvím servletu, jak toho dosáhneme? Složka /WEB-INF ve které se nachází web.xml má jednu významnou vlastnost, není přístupná z vnější a to se nám skvěle hodí. Přesuneme tedy index.jsp do složky /WEB-INF, otevřeme ho a vložíme do něj tento kód:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Zápisky</title>
    </head>
    <body>
        <h1>Zápisky</h1>
        <form method="POST" action="">
            <input type="text" name="zapisek" />
            <input type="submit" name="odeslat" value="Zapsat" />
        </form>
        <ul>
        <c:forEach var="zapisek" items="${zapisky}">
            <li>${zapisek}</li>
        </c:forEach>
        </ul>
    </body>
</html> 

Jak vidíte, na druhém řádku JSP se nachází import knihovny core, aby ale bylo možné ji použít musíme ji naimportovat i do projektu. Naštestí NetBeans tuto knihovnu obsahuje a nemusíme ji nikde shánět a stahovat. Stačí kliknout v levém sloupci pravým tlačítkem na Libraries a přidat knihovnu (Add Library). Ze zobrazeného seznamu vybereme knihovnu JSTL 1.1 a potvrdíme, nyní už je knihovna přidána do projektu a připravena k použití, vraťme se k index.jsp.

Na hlavičce JSP není nic zvláštního. Formulář se skláda z textového vstupu s názvem zapisek (pomocí něhož poté v servletu přistupujeme k předávané hodnotě) a potvrzovacího tlačítka. Pro odeslání formuláře je využita metoda POST odkazující na prázdnou URL, tedy na tentýž servlet který stránku vygeneroval.

Pod formulářem je vyznačen nečíslovaný seznam uvnitř kterého se nachází cyklus kterému je skrz EL předán seznam zápisků. Jednotlivé zápisky se následně vypisují pomocí EL jako prvky seznamu. Uložme projekt, spusťme aplikaci a pokusme se zapsat zlomyslný nic neříkající zápisek vyjadřující naši radost nad funkční aplikací: „Hehehehe“ Stránka by se měla obnovit i s novým zápiskem.

Naše radost bude ale za nedlouho přerušena. Zkuste do poznámky zapsat „<h1 Hehehehe“ a odešlete. Jak vidíte, zápisek se zobrazil velký a tučný, do seznamu se totiž vypsal neuzavřený tag <h1 který otevřel nadpis a všechny další zápisky změnil na nadpisy, bude tedy nutné vkládaný text escapovat aby k podobným chybám dále nedocházelo, k tomu nám výborně poslouží tag :out z knihovny core, který standardně escapuje všechny vypisované řetězce. Vraťme se do JSP a řádek:

<li>${zapisek}</li> 

nahraďme:

<li><c:out value=”${zapisek}" /></li> 

Po obnovení stránky se JSP znovu zkompiluje a vypisuje již escapované zápisky, nyní by mělo vše probíhat bez problémů. Zkusme ale přidat další zápisek, napříkad: „Příliš žluťoučký kůň úpěl ďábelské ódy.“ Nejspíš zjistíte, že naše aplikace neumí české znaky. Pojďme tedy i tento dnes již poslední problém vyřešit.

Čeština

Při odesílání formuláře prohlížeč převede řetězce do kódování iso-8859-1, servlet ale očekává příchozí data ve formátu UTF-8, je tedy nutné request překódovat. Pro řešení tohoto problému stačí na začátek metody processRequest přidat tento kód:

request.setCharacterEncoding("UTF-8"); 

Nyní by v naší aplikaci měla fungovat i česká diakritika.

Závěr

To je pro dnešek vše. V příštím díle si aplikaci převedeme do architektury MVC, přidáme nové JSP a vytvoříme další skvělé funkce našeho zápisníku.

Zdrojové kódy aplikace naleznete na GitHubu: https://github.com/PetrHoracek/JavaNaWeb

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1980