Střípky z prototypování: Wicket, Spring, REST
Měl jsem to štěstí, že jsem se teď mohl několik týdnů věnovat prototypování. Štěstí, protože je to jeden z mých nejoblíbenějších aspektů softwarového inženýrství.
Všechny prototypy vedly k cílovému, zbrusu novému řešení. V kontextu naší firmy, by to normálně dělalo oddělení R&D, ale z kapacitních a projektových důvodů to skončilo v našem delivery týmu. Rád jsem se toho ujal.
Vzhledem k tomu, že by popis řešení a implementace zabraly mnoho článků, rozhodl jsem se vytáhnout jen několik zajímavějších fragmentů z následujících témat (a i ty rozdělím do dvou-tří zápisů):
- REST contract-first
- Wicket + Spring
- Wicket + REST via Spring
- WebSockets
- Embedded Neo4j
- Embedded Infinispan
REST contract-first
O tomhle tématu už jsem psal v článku REST contract-first: Swagger & Gradle. Zmiňuju ho pro úplnost, ať je to na jednom místě a také protože vychází ze stejného konceptu prototypu, jako všechny následující.
Wicket + Spring
Use case
Na počátku bylo slovo: udělejte novou aplikaci. Technologie nikdo neřešil — po pravdě, existovalo rozlehlé architektonické vakuum. Tak jsem se do toho vložil a na základě tehdejších informací a kontextu jsem vybral dvě technologie: Wicket na webové GUI a Spring na “všechno ostatní”.
“Všechno ostatní” zde znamená:
- REST služby a klienti
- Business logika
- (Potenciálně) persistence
- (Finálně) security
Implementace
Wicket má se Springem dobré vztahy už po léta… i když, vo-cuď-pocuď. Zkrátka, bývávalo, že jste si ze Springu vzali jen to co potřebujete. Kdepak, už je to pěkně nenažraný cvalík, který si pozve spoustu bratříčků. A skloubit ho s něčím specifickým… je docela práce.
Nicméně. Způsob, jak propojit Wicket a Spring je dvojí — jde o to, kdo koho instancuje. První způsob — kdy Wicket instancuje Springovský kontext, ze kterého je potom možné injektovat do Wicketovských komponent přes anotaci @SpringBean — je dobře popsaný ve Wicketovské Reference Guide: Integration Wicket with Spring.
Druhý způsob je flexibilnější, avšak, není nikde moc zdokumentovaný a když už, tak jen pomocí konfigurace web.xml. Funguje to tak, že WicketFilter instancuje SpringWebApplicationFactory, která prohledá Springovský kontext a instancuje Wicketovskou WebApplication. Výhodou je, že se dá injektovat přímo do Wicket aplikace, což u předešlého způsobu nejde.
Pokud chceme oželet web.xml
(ano, prosím!), vypadá konfigurace filtru takto:
Prozatím pomiňme, že filter rozšiřuje JavaxWebSocketFilter (viz sekce WebSockets), obyčejný WicketFilter postačí také.
Prototype repozitory
Pokud vám to neštymuje dohromady, klíčové jsou 3 třídy:
- WicketAppFilter
- SpringContextLoaderListener (klasika)
- WicketApplication (anotace @Component).
Poučení
Tady se žádné velké překvapení nekoná — funguje to stejně dobře, jako když jsem s Wicketem a Springem pracoval před osmi lety poprvé, jen se nám oba frameworky mocně posunuly ve verzích. Snad jen, že na takovém malém prototypu se jednoduše nacvičí přepnutí z jednoho typu instancování na druhý.
Wicket + REST via Spring
Use case
Spring a Wicket nám teď pěkně kohabitují. Ale chceme přidat RESTové služby — v současnosti všudypřítomná a triviální záležitost. Ne tak docela.
Spring nabízí REST buď v rámci Spring MVC, nebo Spring Boot. Přiznám se, tady mne dost zaskočilo, že Spring nenabízí RESTové služby samostatně, podobně jako ty SOAPovské (Spring WS). Nejsem expert na Spring, takže mi nejspíš něco schází, ale zatím to vidím, že dostává na frak buď modularita, nebo snadnost použití. Přitom jediný, co vlastně potřebuji je DispatcherServlet.
K tomu use casu — potřeboval jsem dedikované URL pro REST, které by neobsluhoval Wicket a zpracovávat/produkovat JSON zprávy.
Implementace
Řešení přišlo ve dvou krocích — zaregistrovat DispatcherServlet a podstrčit JSON mapování (z nějakého důvodu to Spring nedělá out-of-the-box). Dobral jsem se k řešení, které je sice funkční, ale rozhodně ne elegantní a možná i špatný design. (Zde bych byl vděčný za jakékoliv navedení na “správnou cestu”.)
Zaregistrova DispatcherServlet jen tak přes @WebServlet annotaci mi nešlo — lítaly z toho tuny výjimek, protože ve Springovském kontextu chybělo spousty věcí ze Spring webové aplikace. Všechno to, co vám tam přidá anotace @EnableWebMvc. Tudy cesta nevedla.
Vyřešil jsem to dynamickým zaregistrováním servletu přes ServletRegistration, když jsem si přes Wicket vytáhnul ServletContext:
Druhý problém — přesvědčit Spring, aby chroustal JSON — mám vyřešený jen zpola: sice “zázračně” zafungovalo angažování komba AnnotationMethodHandlerAdapter a MappingJackson2HttpMessageConverter. Bohužel, je první třída @deprecated a to já nesnáším (pokudn není zbytí).
Spring doporučuje migrovat na RequestMappingHandlerAdapter, jenže k tomu chybí jakákoliv dokumentace a Google mlčí. Moje otázka na StackOverflow je také bez odpovědi.
Řešení, na které nejsem hrdý (účel světí prostředky):
Prototype repozitory
Klíčové třídy:
- WicketApplication (ServletRegistration)
- SpringRestConfiguration (AnnotationMethodHandlerAdapter)
- PersonApiController (klasika)
Poučení
Spring je fajn. Ale přijde mi po těch letech, že je míň flexibilní a narostl do stejného molocha jako Java EE. Pokud potřebujete něco specifického, budete se brodit Springovskými výjimkami a zoufale prohledávat StackOverflow. Mimochodem, další moje StackOverflow otázka o Springu je také nezodpovězena.
Říkám si, jestli to za to stojí, se přehrabovat tak hluboko ve vnitřnostech Springu, aby člověk implementoval relativně jednoduché věci. Trochu mi to zavání vendor lock-in: dobře si rozmyslet, jestli stavět heterogenní aplikaci, nebo použít homogenní Spring.
Příště
V pokračování střípků se podíváme na WebSockety a jak do “standardní” webové aplikace přidat “reactive-like” chování.
Repository všech prototypů
Související články
- Střípky z prototypování II: WebSockets (TBD)
- Střípky z prototypování III: Neo4j, Infinispan (TBD)
- REST contract-first: Swagger & Gradle