Java na webovém serveru: Komentáře a integrace s Texy

Možnost vkládat komentáře, resp. schopnost přijímat od uživatelů formátovaný text, to je vlastnost, bez které se neobejde skoro žádný web. V dnešním díle přidáme do naší aplikace podporu komentářů a ukážeme si, jak je kontrolovat, aby nám do nich potenciální útočník nemohl podstrčit žádná závadná data, která by narušila naši stránku. Kromě XHTML a prostého textu umožníme čtenářům psát komentáře ještě v jednom formátu – Texy.
Seriál: Java na webovém serveru (16 dílů)
- Java na serveru: úvod 8. 1. 2010
- Java na webovém serveru: první web 15. 1. 2010
- Java na webovém serveru: práce s databází 29. 1. 2010
- Java na webovém serveru: práce s databází II 12. 2. 2010
- Java na webovém serveru: lokalizace a formátování 19. 2. 2010
- Java na webovém serveru: autorizace a autentizace 26. 2. 2010
- Java na webovém serveru: autorizace a autentizace II 5. 3. 2010
- Java na webovém serveru: porovnání Javy a PHP 10. 3. 2010
- Java na webovém serveru: Vlastní JSP značky a servlety 17. 3. 2010
- Java na webovém serveru: posílání e-mailů a CAPTCHA 24. 3. 2010
- Java na webovém serveru: píšeme REST API 7. 4. 2010
- Java na webovém serveru: SOAP webové služby 14. 4. 2010
- Java na webovém serveru: hlasování a grafy v SVG 28. 4. 2010
- Java na webovém serveru: Komentáře a integrace s Texy 9. 6. 2010
- Java na webovém serveru: AJAX formuláře 23. 6. 2010
- Java na webovém serveru: implementujeme Jabber 30. 6. 2010
Nálepky:
Naše aplikace obsahuje (nebo spíš bude obsahovat) databázi hospod a jiných podobných podniků, a tak by bylo dobré, kdyby je návštěvníci mohli komentovat a napsat, jak se jim tam líbilo. Web je postavený na XHTML (nebo HTML) a tak se přímo nabízí tenhle formát použít i pro zadávání komentářů. Někteří uživatelé ho ale neovládají, a tak je dobré jim dát i jinou možnost – psaní v prostém textu (což bude většině lidí stačit) nebo Texy. Texy! je dílo Davida Grudla a slouží k snadnému zápisu formátovaného textu (i když mně osobně přijde přirozenější psát ostré závorky)
Přejdeme rovnou k věci a jako obvykle si stáhneme aktuální zdrojové kódy aplikace z Mercurialu:
$ hg pull $ hg up "14. díl"
Případně je můžete stáhnout jako bzip2 archiv přes web.
Proces zpracování komentáře
Při odesílání si uživatel může vybrat, v jakém formátu komentář posílá (v další verzi bychom mohli přidat automatickou detekci). Tím se nám proces rozděluje na tři větve – v prostém textu alespoň přidáme na konce řádků <br/>, aby se řádky neslily v jeden nekonečný nepřehledný tok textu (když už si uživatel dal práci a napsal několik odstavců). V případě Texy předáme vstupní text externí službě. A do XHTML pak doplníme odstavce, pokud je uživatel nezadal sám. Pak se cesty opět spojují a komentář odešleme k validaci. Ta je důležitá, aby nám na stránku někdo nevložil škodlivý kód (javascript injection) nebo třeba příliš velké písmo nebo neuzavřenou HTML značku, která by narušila naši stránku.
Napojení na Texy
Jelikož je knihovna Texy napsaná v PHP a my používáme Javu, musíme nějak vyřešit propojení těchto dvou světů. Existuje sice i port Texy do Javy, ale vzhledem k tomu, že chybí formální specifikace Texy, rozhodl jsem se použít originální PHP implementaci. Vytvořil jsem velice jednoduché „API“ – pomocí HTTP POST odešleme prostý text na URL http://nekurak.net/texy/http/
a jako odpověď dostaneme XHTML. Implementace rozhraní na straně Javy:
public class Texy { /** TODO: parametrizovatelnost */ private static final String URL_SLUZBY = "http://nekurak.net/texy/http/"; private static final Logger log = Logger.getLogger(Texy.class.getSimpleName()); public String preved(String text) throws TexyVyjimka { OutputStreamWriter wr = null; BufferedReader rd = null; try { URL url = new URL(URL_SLUZBY); URLConnection spojeni = url.openConnection(); spojeni.setDoOutput(true); /** Odešleme data */ wr = new OutputStreamWriter(spojeni.getOutputStream()); wr.write(URLEncoder.encode(text, "UTF-8")); wr.flush(); /** Přijmeme odpověď */ rd = new BufferedReader(new InputStreamReader(spojeni.getInputStream())); StringBuffer vysledek = new StringBuffer(); String radka; while ((radka = rd.readLine()) != null) { vysledek.append(radka); } return vysledek.toString(); } catch (Exception e) { throw new TexyVyjimka("Chyba při zpracovávání textu: " + text, e); } finally { try { wr.close(); } catch (IOException e) { log.log(Level.WARNING, "Selhalo zavírání OutputStreamWriteru", e); } try { rd.close(); } catch (IOException e) { log.log(Level.WARNING, "Selhalo zavírání BufferedReaderu", e); } } } }
Na straně serveru (PHP) je implementace tohoto rozhraní velice snadná, ale v Javě kvůli ní musíme psát víc kódu než je nutné (ošetřování chyb, řešení nízkoúrovňových operací). Texy funkcionalita je zapouzdřena ve třídě cz.frantovo.nekurak.ext.Texy
, takže v budoucnu můžeme snadno přepsat rozhraní na SOAP webové služby či XML-RPC – nebo použít nativní implementaci Texy v Javě (port odkazovaný výše). Případně můžete v komentářích navrhnout, jak byste tuto úlohu řešili pomocí REST API, ale nemyslím si, že by to v tomto případě bylo vhodné.
XML schéma – XSD
Ať už uživatel zadal text pomocí Texy nebo jako prostý text, klíčová je nakonec validace výsledného XHTML.
Pro kontrolu povolených značek (a vůbec formátování) použijeme XML Schéma. Díky tomu nebudeme muset moc programovat a pouze deklarujeme, jaké značky případně atributy jsou povolené. Vytvořené schéma je velmi jednoduché – povoluje jen odstavce, tučné a skloněné písmo a ruční zalomení řádku. Další značky a atributy si můžete podle potřeby přidat do schématu v souboru komentář.xsd
. Např. povolit odkazy nebo odrážky.
<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="div"> <xs:complexType> <xs:choice minOccurs="1" maxOccurs="unbounded"> <xs:element name="p"> <xs:complexType mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="em" type="xs:string"/> <xs:element name="strong" type="xs:string"/> <xs:element name="br"/> </xs:choice> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema>
Jelikož každé XML musí mít kořenový element, použili jsme <div/>. Mohli bychom si vymyslet i vlastní kořenovou značku a vůbec celý formát, ale podobnost s XHTML se nám hodí – uživatelé se nemusí učit novou syntaxi a my nemusíme dělat další konverzi při vypisování na stránku.
Podpora XSD v Netbeans
V Netbeans existuje pěkný plugin pro vizuální návrh XML schématu. Ve výchozí instalaci ho pravděpodobně nemáte dostupný, ale dá se to snadno napravit – stačí si přidat vývojářské úložiště zásuvných modulů – jeho URL je:
http://updates.netbeans.org/netbeans/updates/6.8/uc/m1/dev/catalog.xml.gz
a doinstalovat si „XML Schema and WSDL“.
Validace v Javě
Kód, který se stará o kontrolu XML oproti schématu se nachází v metodě zkontroluj()
třídy Komentare
(v modulu nekurak.net-lib).
public static Document zkontroluj(String komentar) throws KomentarovaVyjimka { try { URL soubor = Komentare.class.getClassLoader().getResource("cz/frantovo/nekurak/util/komentář.xsd"); SchemaFactory tovarnaSchemat = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = tovarnaSchemat.newSchema(soubor); DocumentBuilderFactory tovarnaDB = DocumentBuilderFactory.newInstance(); tovarnaDB.setSchema(schema); DocumentBuilder db = tovarnaDB.newDocumentBuilder(); db.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException e) throws SAXException { throw e; } public void error(SAXParseException e) throws SAXException { throw e; } public void fatalError(SAXParseException e) throws SAXException { throw e; } }); Document dokument = db.parse(new ByteArrayInputStream(komentar.getBytes("UTF-8"))); return dokument; } catch (Exception e) { throw new KomentarovaVyjimka("Neplatný komentář: " + komentar, e); } }
Pomocí vlastního ErrorHandleru
můžeme zjistit, v čem přesně dokument nevyhovuje a sdělit to uživali, aby věděl, co má opravit. V případě platného XML vrací metoda zkontroluj()
DOM (který zatím k ničemu nevyužíváme) a v případě chyby vyhazuje výjimku – ta v sobě obsahuje informaci, které elementy jsou chybné, ale v ne příliš čitelné podobě.
Validace v Linuxu
Správnost XML dokumentů si můžeme kontrolovat i bez Javy a nějakých složitých IDE, stačí nám k tomu příkazová řádka a nástroj xmllint
. V linuxových distribucích jako je Debian nebo Ubuntu si ho nainstalujeme pomocí:
$ aptitude install libxml2-utils
A kontrolu provádíme jednoduše příkazem:
xmllint --schema komentář.xsd komentář.xml
Pokud jsme nikde neudělali chybu, program nám odpoví výpisem XML souboru a hláškou komentář.xml validates
. To se nám může hodit pro různé ladění nebo dávkové testování XML dokumentů na disku.
Validace při výstupu v JSP
Tak, jak jsme aplikaci vytvořili, teď závisí její bezpečnost na kvalitě dat v databázi. Pokud zajistíme, že se do ní bude zapisovat jen přes aplikaci (tzn. zkontrolované komentáře) nebo že do ní nikdo nezanese ručně chyby, můžeme aplikaci nechat tak, jak je. Pokud ale chceme vyšší bezpečnost, je lepší kontrolovat validitu komentářů i při jejich vypisování a nespoléhat se na kvalitu dat v databázi.
K tomu můžeme použít vlastní JSTL funkci nkfn:zkontrolujKomentar()
. Pomocí tohoto kódu vypíšeme platné komentáře s formátováním a neplatné bez něj – escapované:
<c:out value="${k.komentar}" escapeXml="${!nkfn:zkontrolujKomentar(k.komentar)}" />
Další možností je, nevypsat chybné komentáře vůbec a zobrazit místo nich hlášku. Tento kód najdete v mercurialu v historii verzí souboru komentareVypis.tag
. Jestli kontrolovat data jen na vstupu nebo i na výstupu, je z velké části kompromis mezi bezpečností a výkonem. S nastavením můžete experimentovat a vyzkoušet si, jestli se zapnutá validace vůbec na výkonu nějak viditelně projeví.
Závěr
Dnes jme do naší aplikace doplnili podporu komentářů a ukázali jsme si ještě jeden způsob, jak lze provázat aplikace – byť ne tak sofistikovaný jako webové služby, REST nebo jiné standardnější postupy. Také jsme nakousli téma XML a jeho validace v Javě. Jen připomínám, že aplikaci si můžete vyzkoušet na adrese nekuřák.net a jméno a heslo je: zdrojak.root.cz/heslo – pokud si nechcete zakládat vlastní účet.
Odkazy
- Texy! – PHP knihovna pro snadné formátování textu.
- JTexy – její port do jazyka Java.
- XML schémata – stránka Jiřího Koska o XML schématech.
- Libxml2 – XML parser a sada nástrojů napsaná v C.
- Relax NG – další způsob definice XML dokumentů.
- Schematron – totéž, ale trochu jinak.
Proč u svatého Stallmana rvete to do texy a zpátky přes DNS resolver, všechny možné síťové vrstvy a webový server? Normální člověk by si udělal php-cli skript (ne. opravdu nemyslím php-cgi), který by pak z javy spouštěl a data do něj rval pípou, nebo by šlo z javy spustit php fastcgi a nějak to tahat přes něj.
Asi za to může celá ta tendence všechno přesouvat na web a dělat jako že umět psát opravdové programy už není potřeba… (ale pak musí stejně přijít opravdoví chlapi s pravděpodobně většími platy a programovat operační systémy, servery, databáze, prohlížeče a programovací jazyky pro všechny ty programátory webů, kteří se toho bojí)
Jinak pokuď už chcete zkoušet různá harakiri, tak mrkněte na tohle:
http://php-java-bridge.sourceforge.net/
http://www.zend.com/en/products/server-ce/
ale opakuji, že bych do podobných sebevražd nešel…
A v cem je problem? Tohle bylo asi nejjednodussi nastavit a hlavne si to muze vyzkouset kazdy. Pokud budes chtit rychlost, proste si naimplementujes navrhnuty interface jinym zpusobem. Autor, ale timto ukazal pouziti nekolika technologii, ktere pravdepodobne bude ctenar v budoucnu pouzivat.
Vzhledem k tomu, že se ta operace provádí jen při psaní nových komentářů (což bude výrazně méně časté než čtení) a vzhledem k tomu, kolik HTTP požadavků musí proběhnout při načtené každé stránky*, mi to přijde zanedbatelné. Výhody spouštění php jako podprocesu mi přijdou menší než nevýhody – jednak to snižuje bezpečnost (php běží pod stejným uživatelem jako javový server) a jednak tam to PHP musím mít. Když to proložíme HTTP protokolem, tak je to sice na první pohled nehospodárné, ale dělat něco jako SOA v malém – mít jednu (nebo několik málo) službu v rámci organizace/firmy, která bude zodpovědná za Texy a ostatní ji budou využívat – na jiných serverech nemusí být PHP vůbec nainstalované, nikdo ho nemusí konfigurovat a udržovat. Než php-cli to by spíš stál za zvážení ten javovský port texy – pořádně ho otestovat a průběžně sledovat jestli je kompatibilní, jestli se s novými verzemi nerozchází od originálu. Případně zkusit jestli by PHP verze nešla rozchodit v Quercusu…
*) a tím nemyslím jen tuhle konkrétní aplikaci, ale weby obecně – navíc tam se nekomunikuje v rámci localhostu, ale s vnější (pomalou) sítí. I když většina těch požadavků dostane odpověď „304 Not Modified“, ty hlavičky tam stejně proletí – vedle toho ta jedna komunikace navíc s Texy opravdu nic není.
Vypadlo mi tam:
„ale dělat něco jako SOA“ → „ale umožňuje nám dělat něco jako SOA“
Ve Quercu se mi podařilo rozchodit jen starou verzi Texy pro PHP4 a to ještě jen špatně s nutností několika úprav.
Výborně naopak funguje PHP/Java bridge pomocí JSR 223. Jen nedoporučuji používat přímo na aplikačním serveru. Lépe vytvořit jednoduchý Texy-miniserver komunikovat nativními zprávami s ním.
java na webovém serveru, to jako vážně?! to dneska ještě někdo dělá?
Provokace, nebo neznalost?
skor borec, co si precital clanok o „pomalosti java 1.1“ z 12.3.1998 a zbuchal uz 3 webshopy v php v celkovom objeme 12.4kB zdrojoveho kodu, co ho nepochybne radi medzi odbornikov :-)
A k tomu má nejspíš dojem, že java na webovém serveru = java na webu = java applet ve stránce. :-)
dojmy a pojmy z mé osoby o které absolutně nic nevíte nechme stranou. zkuste mi napsat, kdy a proč bych měl použít na web javu místo ruby/php/pythonu? v čem spočívájí výrazné výhody?
Zkusime to jinak: Jaka je vyrazna nevyhoda Java EE?
Velmi špatná architektura, viz
http://www.springsource.org/about
Java EE chce být univerzální, ale není, zato je ale složitá. Podobně jsou na tom i další technologie, třeba XML nebo MS LINQ to SQL.
zložitá je len pre toho kto má v hlave nasraté, my ostatní sa neustále vzdelávame. a čo je prosím ťa zložité na LINQ to SQL ?
nemam ziaden vyhraneny nazor ani na php ani na javu … len ak by bol nejaky priklad na nejaku komplikovanejsiu webaplikaciu v jave… lebo zatial co si pamatam resp. nepamatam ziadnu stranku v jsp, ktora by dokazala odpovedat … ako to povedat … svizne… Neviem ci to je teda sposobene javou alebo len zlou konektivitou serverov – i ked tie by sa dali pomocou pingu odmerat a odpocitat.
Nemusi to byt ziadna wikipedia ani facebook, len nieco co je navstevovane a nieco jednoznacne vykonava okrem vracania statickeho textu.
java na webu neznamena pouze jsp, jsp je v podstate mrtva vec
Java bude rychlejsie ako PHP.
je dost, např. LinkedIn.com.
Facebook.com zvažoval Javu jako jednu z alternativ k ukrutně pomalému PHP, nakonec začali překládádat PHP kód do C++ a kompilovat do binárek.
Váš údiv je zcela na místě. Psát v dnešní době novou aplikaci v Javě je sebemrskačství.
Proč? (no flame plz.)
Protože existují lepší jazyky s lepšími knihovnami.
Porovnávám podle následujících kritérií: znovupoužitelnost, přehlednost, typová kontrola, možnost paralelismu.
Jaké konkrétně jazyky?
Třeba Haskell, F#. Pro JVM třeba Clojure, Scala.
hmm, v tom jsem asi žádný produkční web napsaný neviděl. A co třeba GNUstep? Nad ním jsem viděl jednu pěknou webovou aplikaci, působilo to dost svižně a vůbec fajn :-) Neví někdo jak dobře se v tom píše? (Objective-C zatím leží na mém TODO seznamu věcí, ke kterým se snad jednou dostanu, časem)
Ve Scalle jsou psane nektere komponenty (ne frontend) Twitteru, Novell ve Scale (framework Lift) implementoval Pulse, FourSquare take pouziva Scalu i Lift.
To ale samozrejme ani zdaleka neznamena, ze je Java vzdy spatna volba. Ma vyzrale knihovny a ekosystem (trebas Apache odvedl uzasny kus prace), komu nevyhovuje aktualni podoba Java EE „podle Sunu“, tak muze sahnout po Springu, neni problem, pokud mate penize, sehnat slusne programatory, jsou k dispozici knihy, navody, IDEs, servery pro CI…
Prosím o vyšší četnost těchto článku!
Jsou výborné, děkuji.
Myslím samozřejmě o Java na straně serveru ;-)
u výpisů zdrojového kódu není obarvena syntaxe a vůbec je to nějaké „smrsklé“, u tohoto – http://zdrojak.root.cz/clanky/happstack-cast-treti/ je to v pořádku, Opera 10.53 win, takže asi chyba redakce/autora.
Opraveno, díky za upozornění.
Hoj,
rád bych podotknul, že JTexy je ve vývoji… tj. rozhodně nepřeparsuje vše.
Není čas… Pokud by se někdo chtěl přidat, jen uvítám :)
Ondra
Vytvořil jsem Texy Docker image. https://github.com/OndraZizka/texy-docker/
A potom ho používám víceméně stejně.