Java na webovém serveru: první web

Minule jsme řešili spíše administrativní záležitosti. Dnes se podíváme na hlavní komponenty, ze kterých se webová aplikace psaná v Javě skládá, Seznámíme se se základy JSP a JSTL. Probereme podrobněji deployment. Naučíme se ošetřovat HTML výstup a nastavit si vlastní chybové stránky.

Seriál: Java na webovém serveru (16 dílů)

  1. Java na serveru: úvod 8.1.2010
  2. Java na webovém serveru: první web 15.1.2010
  3. Java na webovém serveru: práce s databází 29.1.2010
  4. Java na webovém serveru: práce s databází II 12.2.2010
  5. Java na webovém serveru: lokalizace a formátování 19.2.2010
  6. Java na webovém serveru: autorizace a autentizace 26.2.2010
  7. Java na webovém serveru: autorizace a autentizace II 5.3.2010
  8. Java na webovém serveru: porovnání Javy a PHP 10.3.2010
  9. Java na webovém serveru: Vlastní JSP značky a servlety 17.3.2010
  10. Java na webovém serveru: posílání e-mailů a CAPTCHA 24.3.2010
  11. Java na webovém serveru: píšeme REST API 7.4.2010
  12. Java na webovém serveru: SOAP webové služby 14.4.2010
  13. Java na webovém serveru: hlasování a grafy v SVG 28.4.2010
  14. Java na webovém serveru: Komentáře a integrace s Texy 9.6.2010
  15. Java na webovém serveru: AJAX formuláře 23.6.2010
  16. Java na webovém serveru: implementujeme Jabber 30.6.2010

Nejdříve si synchronizujeme zdrojové kódy našeho programu a aktualizujeme na štítek odpovídající tomuto dílu seriálu.

$ hg pull
$ hg up "2. díl"

Případně si je můžete stáhnout jako bzip2 archiv přes web.

Struktura adresářů

S Netbeans jste asi už pracovali, takže jen pro zopakování – adresář projektu obsahuje tyto podadresáře: build (zkompilované třídy), dist (archiv připravený k distribuci – v našem případě .war), nbproject (metadata projektu), src (zdrojové kódy v Javě), test (zdrojové kódy testů), web (JSP a WEB-INF a konfigurace webu). V IDE vypadá pohled na složky trochu jinak (klasický pohled na adresáře najdete v kartě „Files“).

Java II

V adresářích build a dist asi zatím nic nemáte, protože přeložené třídy a balíčky do Mercurialu nedávám. Aplikaci si zkompilujeme v IDE a ve složce dist se nám objeví distribuční balíček nekurak.net-web.war. Což je vlastně normální ZIP archiv. Obsahuje jak JSP stránky, tak důležitou složku WEB-INF, zkompilované třídy, případné knihovny a všechny konfigurační soubory naší aplikace. V ideálním případě je .war archiv všechno, co potřebujete ke zprovoznění aplikace na aplikačním serveru.

Základní stavební kameny

Svět enterprise Javy je velice rozmanitý a bohatý na technologie. Pro začátek nám ale bude stačit, když se seznámíme s těmito pojmy.

  • Servlet – javovská třída, potomek javax.servlet.http.HttpServlet. Stará se o vyřizování HTTP požadavků (metody doGet(), doPost() atd.). Servlet je namapovaný na určitou cestu v URL, např. /upload . Můžete ho použít v roli controlleru nebo třeba pro nízkoúrovňové věci typu upload/download souborů (pracujete přímo s vstupním a výstupním proudem, máte absolutní kontrolu nad vyřizovaným HTTP požadavkem).
  • JSP – Java Server Pages – dokument založený na XML. Zatím si ho můžete představit jako šablonovací systém. Při nasazení na server se JSP překládají do jazyka Java a následně se kompilují – vznikne z nich normální servlet. JSP nemusíme používat jen pro vytváření HTML či XHTML stránek, můžeme z nich generovat třeba SVG nebo RSS/Atom, jakýkoli XML formát a konec konců i prostý text.
  • Filtr – mezi zdrojem obsahu (JSP, servlet) a klientem může být řetězec filtrů – něco jako unixové roury, skrz které prochází proud dat. Filtry mají různé využití (transformace dat, autorizace, komprese…), jednou jsem psal např. filtr, který ze stránek odstraňoval háčky a čárky, aby se stránky daly prohlížet i v „hloupých“ zařízeních. Filtry v naší aplikaci zatím nebudeme potřebovat.
  • JavaBean – celkem běžná třída (viz dále), která se používá jako prostředník mezi JSP (v jazyce XML) a logikou aplikace (v jazyce Java).
  • EJB – Enterprise JavaBean – komponenta běžící na serveru, která poskytuje určitou službu (implementuje rozhraní), vhodná pro budování modulární architektury. EJB zapouzdřují byznys logiku aplikace a je možné k nim přistupovat i přes síť – např. pomocí protokolu CORBA/IIOP. V naší aplikaci EJB použijeme, ale spíše pro ukázku – webové aplikace v Javě lze úspěšně vytvářet i bez EJB.

Je sice možné psát celý web jen v jazyce Java (použijeme jen servlety) nebo jen v JSP („obohatíme“ je skriptlety), ale není to moc šťastné ani obvyklé řešení. Jistě mi dáte za pravdu, že vytvářet dokument, (X)HTML stránku, je daleko příjemnější v nějakém „dokumentovém“ jazyce (tedy JSP – XML), zatímco programovat se nám bude lépe v jazyce Java. „Programovat“ dokument v programovacím jazyce nebo naopak psát program v jazyce značkovacím není zrovna elegantní.

Využívejme proto možností Javy a těchto dvou jazyků. Java nás přímo vybízí, abychom své aplikaci dali vrstvenou architekturu. Prezentační logika patří do JSP a aplikační logika patří do javových tříd.

Nasazení aplikace na server

Pokud jste byli z PHP zvyklí upravovat skripty přímo na serveru, raději na to zapomeňte. Tento způsob práce je sice možný i v Javě (jedná se o tzv. „hot deploy“ kdy si aplikační server hlídá změny souborů v určitém adresáři), ale bude lepší se chovat trochu „spořádaněji“. Nasazení (nahrání) aplikace na aplikační server se nazývá „deploy“. Ten se dělá buď v nakopírováním .war souboru do příslušného adresáře, nebo použitím HTTP nebo CLI API – záleží na zvoleném aplikačním serveru.

Předejdete tak trapným situacím, kdy po upgradu aplikace záhadně „zmizí“ některé její funkce nebo opravy chyb a nakonec se ukáže, že nějaký váš kolega upravoval aplikaci přímo na serveru a „zapomněl“ dát upravené zdrojáky do verzovacího systému. Pokud chcete mít jistotu, nekompilujte aplikaci na vývojářském desktopu, ale na jiném počítači, na kterém neprobíhá vývoj – pouze se tam stahují zdrojové kódy z verzovacího systému –  takto vytvořené .war archivy nejdříve nasadíte na testovací server a potom na ostrý.

Nasazení aplikace na server můžeme v případě Glassfishe provést z příkazové řádky. Program asadmin se nachází v adresáři bin v instalaci vašeho Glassfishe.

$ asadmin deploy nekurak.net-web.war
Enter admin password for user "admin">
Application deployed successfully with name nekurak.net-web.
Command deploy executed successfully.

Výchozí heslo je prázdné, takže stačí odentrovat. Soubor nekurak.net-web.war obsahující zkompilovanou aplikaci naleznete v adresáři dist v netbeansovém projektu.

Daleko pohodlnější je provádět deploy přímo z IDE. Pokud jste si stáhli Netbeans ve verzi All nebo Java, měli byste mít už vše nastavené.

Java II

Nasadit aplikaci na server pak můžeme jednoduše spuštěním projektu. Možná bude jen potřeba vybrat aplikační server ve vlastnostech projektu – na stejném místě se nachází volba „Deploy on save“ – při uložení JSP nebo jiných zdrojáků se aplikace okamžitě nasadí – velmi užitečné pro ladění.

Tímto způsobem lze pracovat i se vzdálenými servery (používá se HTTP API). Další možností jak dostat naši aplikaci na server je webové administrační rozhraní (port 4848).

Základy JSP

Naši aplikaci začneme stavět trochu „od střechy“ – místo datového modelování a psaní javových tříd se podíváme na JSP (prezentační vrstvu). Možná vám to přijde nezvyklé, ale tenhle postup má něco do sebe a můžete se s ním setkat i v agilních metodikách jako je Getting Real (Designujte rozhraní předtím, než se začne programovat).

Když se podíváte na index.jsp, vidíte XML dokument. Až na hlavičky je to v podstatě obyčejná HTML stránka, do které jsou „sem tam“ vložené JSP značky, které obstarávají aktivní obsah. Toto XML využívá jmenné prostory, zatímco HTML značky jsou v něm „jen tak“, JSP značky začínají jsp:. Abyste tyto značky mohli používat, musíte si jmenný prostor přidat do elementu <jsp:root/>  – příklad:

<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
      xmlns:c="http://java.sun.com/jsp/jstl/core"
      version="2.0">

Tím jsme si do dokumentu přidali dva jmenné prostory. Jak si je pojmenujete, je na vás, nicméně je zvykem používat jsp (základ JSP stránek), c (značky pro větvení, cykly atd.), fn (funkce), fmt (formátování). Kromě těchto standardních knihoven značek si můžete vytvářet i své vlastní a vlastní jmenné prostory (o tom v některém z příštích dílů).

Vkládání stránek

Začneme něčím jednoduchým – dejme tomu, že chceme mít v zápatí každé stránky informaci o copyrightu a licenci. Obsah jiného souboru vložíme pomocí direktivy  include:

<jsp:include page="WEB-INF/casti/paticka.jsp"/>

Vkládat můžeme stránky z libovolného adresáře naší aplikace, nicméně pokud se jedná o (pod)stránky, které budou pouze vkládány a uživatel by k nim přistupovat přímo neměl, je dobré je umístit do nějakého podadresáře ve WEB-INF  – jeho obsah není přes HTTP přímo dostupný.

Při vkládání můžeme předávat parametry a pak se na ně ve vkládané stránce odkazovat – viz index.jsp a paticka.jsp. V tomhle případě je to poněkud samoúčelné, ale je dobré o této možnosti vědět.

Skriptlety

Jak jsme si řekli výše, z JSP stránky nakonec stejně vznikne servlet. Tak vás možná napadne, že by se do tohoto procesu někdy hodilo vstoupit a trochu si ten Java kód vygenerovaný z JSP poupravit. Možné to je a slouží k tomu tzv. skriptlety. Skriptlet je kousek Java kódu uzavřený do příslušných JSP značek – příklad:

<jsp:scriptlet>
    out.println("No nazdar! &lt;br/&gt;");
    out.println("Právě je: " + new java.util.Date());
</jsp:scriptlet>

V proměnné out máme zpřístupněný výstupní proud, který vede ke klientovi. K dispozici jsou i další proměnné ( request, session atd.), po stisknutí Ctrl+mezerník vám Netbeans budou napovídat i metody použitelné na těchto objektech.

Kdo ví, proč jsme museli <br/> zapsat jako entity, má bod.

Po tom, co jsme se skriptlety naučili používat, je zase rychle zapomeneme – skriptlety totiž vedou k nepřehlednému a chybovému kódu. Pokud budete cítit potřebu skriptlet použít, je dobré se zastavit a zamyslet – s velkou pravděpodobností totiž děláte něco špatně. Dost možná se vám začala prolínat prezentační a aplikační logika a programujete v prezentační vrstvě. Programování a logika aplikace patří do javovských tříd, zatímco v JSP se už staráte jen o prezentaci – přebalíte objekty do (X)HTML značek, naformátujete a pošlete uživateli. Přesto jsou v některých případech skriptlety přípustné – např. když se rozhodnete napsat controller pomocí JSP místo servletu… Ale nikdy by neměly tvořit běžnou součást vašeho programu.

Podmínky – if, else

I v prezentační vrstvě je potřeba nějaké to větvení. Slouží k němu JSTL značky ze jmenného prostoru http://java.sun.com/jsp/jstl/core, který se typicky zkracuje c. Jednoduchý podmíněný blok vypadá takhle:

<c:if test="${param.akce == 'informace'}">
    <p>Vypíšeme nějaké informace.</p>
</c:if>

Odstavec se vypíše, pokud GET/POST parametr akce je „informace“.

Do ${…} zapisujeme tzv. expression language a param zde představuje pole, které obsahuje parametry stránky – buď GET/POST z HTTP požadavku nebo parametry předané jinou JSP stránkou – viz „vkládání stránek“ výše.

Pro složitější větvení if, else if… else, použijeme značku <c:choose/> viz soubor  index.jsp.

Použití JavaBean v JSP

Abychom měli v JSP co prezentovat, potřebujeme nějaká data. Ta vezmeme např. v databázi, v souborech nebo z nějakého jiného zdroje (webová služba, EJB…) a do JSP je dostaneme pomocí tzv. JavaBean, což jsou celkem obyčejné třídy s gettery  a settery. Jejich specifikace má sice přes sto stránek, ale v podstatě stačí vědět, že:

  • beana by měla být serializovatelná,
  • mít bezparametrický konstruktor
  • zpřístupňovat svoje proměnné pomocí getterůsetterů

V rámci JSP stránky si potom stačí vytvořit její instanci:

<jsp:useBean
 id="podnikyWeb"
 class="cz.frantovo.nekurak.web.PodnikyWeb"
 scope="request"
/>

Hodnota atributu id je název proměnné pod kterou instance bude přístupná v JSP, class je název instanciované třídy (JavaBeany) a scope je rozsah platnosti. Vytvořený objekt (beana) může být platný v rámci daného HTTP požadavku ( request) nebo uživatelské relace ( session), další možnosti jsou page a application. Pokud direktivu useBean použijete pro stejné id a rozsah víckrát, nic se neděje – použije se již existující instance. Přesný postup naleznete v online nápovědě:

Java II

Použití JavaBeany je snadné. Třída PodnikyWeb má metodu getPodniky(), která vrací seznam podniků. Zatím nemáme žádnou databázi, takže jsou jejich názvy zapsané přímo ve zdrojovém kódu, ale to pro tuto chvíli nevadí. Podniky si vypíšeme např. jako nečíslovaný seznam:

<ul>
    <c:forEach var="p" items="${podnikyWeb.podniky}">
        <li>${p.nazev}</li>
    </c:forEach>
</ul>

Všimněte si, že se v JSP neodkazujeme na metodu getPodniky(), ale na vlastnost podniky (přestože se nakonec metoda getPodniky() zavolá).

Ošetření výstupu, escapování

Při psaní webovým aplikací je velice důležité ošetření vstupů – nikdy nevíme, co se nám zlomyslný uživatel pokusí podstrčit. Někdy mám pocit, že se tohle téma přeceňuje a místo aby lidi hodnotili webovou aplikaci jako takovou, zkoušejí jako první věc, jestli jsou „schopnější“ než její autor a jestli nezapomněl něco ošetřit. Na druhou stranu případy různých SQL injekcí a jiných chyb tu jsou pomalu každý den, takže na tom asi něco bude.

Ale nejde jen o vstupy z formulářů. Na co si dát pozor:

  • Vstup od uživatele – převážně z formulářů. Tyto vstupy většinou přímo nevypisujeme, ale pokud ano, musíme je escapovat, aby nám nerozbořily naši (X)HTML stránku. Viz příklad níže.
  • SQL dotazy – základní pravidlo zní: nelepit SQL z kousků textu, ale používat parametrizované dotazy. Více v příští kapitole, kde se SQL databázím budeme věnovat.
  • Výstup dat z databáze nebo odjinud – možná to zní paranoidně, ale věřit bychom neměli ani svým datům. Pokud na výstupu z databáze nečekáme HTML, měli bychom ho před vypsáním stejně prohnat escapovací funkcí (i když se domníváme, že např. v daném sloupečku žádné nebezpečné znaky nejsou). Odpustit si to můžeme tak leda při vypisování číselných hodnot – např. do datového typu int opravdu nejde nacpat nic, co by nám (X)HTML narušilo. Pokud je HTML přípustné (např. formátovaný text článku), musíme mít buď jistotu, že se do databáze (nebo třeba do souborů) žádné závadné značky nedostanou, nebo text vždy před výstupem prohnat XML validátorem a zkontrolovat, že obsahuje jen povolené značky a atributy (což ale může aplikaci trochu brzdit).

Pro ošetření výstupu do HTML (nebo obecně XML) použijeme následující značku:

<p><c:out value="${param.parametr1}"/></p>

Někdy ale potřebujeme text umístit do atributu (např. title), tam ale výše uvedenou značku nedostaneme. V takovém případě použijeme funkci:

<abbr title="${fn:escapeXml(param.parametr1)}">„escapovaný“</abbr>

Teď můžeme klidně do formuláře zadávat závorky a uvozovky jaké chceme a (X)HTML výstup zůstane nenarušený. Tyto příklady naleznete v souboru  escapovani.jsp.

Osobně doporučuji ošetřovat text co nejpozději – těsně před tím, než by nám mohl uškodit – např. před vypsáním do (X)HTML. Naopak postup, kdy se všechny vstupy ošetří hned jak přijdou z formuláře, je dost nešťastný. Jednak může dojít k několikanásobnému escapování (jistě jste už někdy viděli HTML entity na výstupu, kde neměly co dělat). Jednak nevíme, proti čemu text ošetřujeme – jestli proti narušení HTML/XML (ostré závorky, uvozovky…) nebo proti SQL injekci (apostrofy), takže se pak stává, že escapujeme něco, co nemáme. A jednak nevíme, co se s daty stane potom – např. je ošetříme příliš brzo, uložíme do databáze, jenže tam je někdo ručně upravuje a přidá do nich nebezpečné znaky.

Kódování ve formulářích

Doposud jsme s kódováním znaků neměli problém, vše jsme měli v UTF-8, jak bezstarostný život… Až na jeden detail: při odeslání formuláře si na serveru načteme GET (nebo POST) parametr, chceme ho zobrazit a vidíme něco jako:

ÄžšÅáýÞéýáÃ

Jako výchozí se v tomto případě totiž použije kódování ISO-8859-1. Tato situace se často řeší následujícím skriptletem:

<jsp:scriptlet>
    request.setCharacterEncoding("UTF-8");
</jsp:scriptlet>

Ale jak jsme si již dříve řekli, skriptlety jsou fuj. Výchozí kódování si nastavíme v souboru  sun-web.xml:

<parameter-encoding default-charset="UTF-8" />

Pokud to nechceme nebo nemůžeme udělat (např. máme jiný aplikační server), ani tak se nemusíme uchylovat ke skriptletům. Použijeme značku ze jmenného prostoru  http://java.sun.com/jsp/jstl/fmt:

<fmt:requestEncoding value="UTF-8" />

Více si můžete přečíst v odkazech na konci článku (tento problém lze řešit i filtrem).

Soubor web.xml

Tento soubor obsahuje konfiguraci naší webové aplikace. Pomocí značky <welcome-file-list/> se v něm nastavuje výchozí stránka adresáře – typicky index.jsp, index.html  – tedy obdoba direktivy DirectoryIndex v Apachi. Dále se tu např. definují servlety a filtry a jejich mapování na URL nebo třeba chybové stránky. Tento konfigurační soubor je XML, takže vám ho IDE validuje už během editace a hned se dozvíte, pokud tam máte nějakou syntaktickou chybu. Také vám nabízí konfigurační volby i s dokumentací, takže většinou není potřeba hledat nikde v manuálech. V Netbeans můžete většinu voleb web.xml zadat i přes GUI, přesto je dobré se alespoň na závěr podívat na „surový“ konfigurační soubor.

Chybové stránky

Když se uživateli zobrazí standardní chybová stránka aplikačního serveru, vypadá to jednak neprofesionálně a jednak to může znamenat jisté bezpečnostní riziko (z výpisu chyby se útočník dozví informace, které by vědět nemusel). Proto je dobré si definovat vlastní chybové stránky – to se dělá pomocí značek <error-page/>. Ve web.xml si nastavíme:

<error-page>
    <error-code>404</error-code>
    <location>/WEB-INF/chyby/404.jsp</location>
</error-page>

Nejčastějšími chybami jsou 404 (stránka nenalezena) a 500 (interní chyba serveru). Chybové stránky můžou být definované nejen na základě HTTP kódů, ale i na základě výjimek jazyka Java (viz soubor web.xml). Např. při SQL výjimce můžeme uživateli říct, že došlo k chybě při práci s databází (pokud mu takovou informaci chceme sdělovat).

Chybová stránka může být i JSP, ovšem pokud v ní uděláme chybu, zobrazí se místo ní ta standardní „pětistovková“. Takže raději opatrně – často si vystačíme s chybovými stránkami v prostém (X)HTML.

Závěr

Dnes jsme se seznámili se základními částmi, ze kterých se webová aplikace skládá, a vytvořili svoje první JSP. Měli bychom být (částečně) odolní vůči záškodníkům a neděsit normální uživatele standardními (ošklivými) chybovými hláškami. Příště se budeme věnovat lokalizaci aplikace a práci s relační databází.

Odkazy

Franta Kučera působí jako Java vývojář na volné noze. Programování je jeho koníčkem už od dětství. Kromě toho má rád Linux, relační SŘBD a XML.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 35

Přehled komentářů

Honza Marek Re: Java na webovém serveru: první web
Miki Re: Java na webovém serveru: první web
v6ak Re: Java na webovém serveru: první web
avatar Re: Java na webovém serveru: první web
b*d JavaEE vs. Plone/Zope/Python
pr.rybar Re: JavaEE vs. Plone/Zope/Python
b*d Re: JavaEE vs. Plone/Zope/Python
b*d Re: JavaEE vs. Plone/Zope/Python
pr.rybar Re: JavaEE vs. Plone/Zope/Python
ahl Re: JavaEE vs. Plone/Zope/Python
pr.rybar Re: JavaEE vs. Plone/Zope/Python
alef0 Re: JavaEE vs. Plone/Zope/Python
jondy Re: JavaEE vs. Plone/Zope/Python
pr.rybar Re: JavaEE vs. Plone/Zope/Python
Anonym Re: JavaEE vs. Plone/Zope/Python
pr.rybar Re: JavaEE vs. Plone/Zope/Python
xtr Re: JavaEE vs. Plone/Zope/Python
b*d Re: JavaEE vs. Plone/Zope/Python
dm Webové programování v Javě trochu jinak ...
v6ak Re: Webové programování v Javě trochu jinak ...
xtr Re: Webové programování v Javě trochu jinak ...
v6ak Re: Webové programování v Javě trochu jinak ...
Michal Augustýn velmi pěkný článek
jondy Re: velmi pěkný článek
smilelover Re: velmi pěkný článek
b*d Re: velmi pěkný článek
Michal Augustýn Re: velmi pěkný článek
Jakub D. Re: velmi pěkný článek
tom Re: velmi pěkný článek
jondy Re: velmi pěkný článek
mich Re: velmi pěkný článek
fastcoretux parráda :]
v6ak Chyby: Uživatelům radši nic podrobně neříkat
YF Re: Chyby: Uživatelům radši nic podrobně neříkat
v6ak Re: Chyby: Uživatelům radši nic podrobně neříkat
Zdroj: https://www.zdrojak.cz/?p=3153