Category Archives: Spring

Skrytý drahokam Springu

Dnes jsem koukal na rozhovor s Rodem Johnsonem, v kterém mluvil o novinkách ve Springu 3.0. Mezi řečí se zmínil o anotaci @Configurable, která je už ve Springu 2.5 a označil ji za skrytý drahokam. Protože jsem o něčem takovém slyšel poprvé, tak jsem se podíval do dokumentace a docela mě to zaujalo. (Teda, teď koukám, že jsem se o něčem podobném zmiňoval už před půl rokem zde, ach ta paměť). V zásadě jde o to, že obvykle funguje dependency injection jen u bean, které spravuje Spring. Takže například nejde použít u JPA entit a podobně. Což je docela škoda, protože pak musíme dělat umělou servisní vrstvu a objektový model tím docela trpí. Máme na jednu stranu třídy, které drží stav (entity) a na druhou stranu servisní třídy, v kterých je jen kód a stavu mají pomálu. Ale ve správném objektovém programování bychom měli mít třídy, které kombinují oboje. Takže například objekt User by měl mít metodu remove(), která by ho smazala z databáze. Jenže to by znamenalo, že by každá instance této třídy musela mít v sobě odkaz na EntityManager nebo ještě lépe DAO, což neumíme injektnout.

Možná si říkáte, že jsem se zbláznil, že to je ošklivé. Musím se přiznat, že mi je tento přístup taky cizí, ale asi je to jen otázka zvyku. Nejspíš jsem jen zvyklý na to procedurální programování, kde mám servisní procedury v singletonu a jim předhazuji hloupé datové objekty, které se o sebe neumějí postarat samy.

Anotace @Configurable je tu právě od toho, aby nám usnadnila injektování i do bean, které nejsou spravovány Springem. Předvedu na příkladu. Mám třídu Bean, které se dá podstrčit nějaký text.

@Configurable
public class Bean {
	private String text;
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
}

Mým cílem je zprovoznit tento test.

	public void testIt()
	{
		Bean bean = new Bean();
		assertEquals("Hallo",bean.getText());
	}

Všimněte si, že vytvářím novou instanci a koukám se, co je v ní nastaveno za text. Podle všeho by tam mělo být null. Ale pokud správně poladím Spring, tak tam může být cokoliv.

Prvním krokem je samozřejmě anotace @Configurable, která označí beanu jako kandidáta na injektování. V druhém kroku musím nastavit, co chci injektnout. Mohu například zvolit klasickou XML konfiguraci.

	<bean class="net.krecan.configurable.Bean" scope="prototype">
		<property name="text" value="Hallo"/>
	</bean>

	<context:spring-configured/>
	
	<context:load-time-weaver/

Zde si všimněme několika věcí. Například toho, že Spring pozná to kam má co injektnout automaticky, podle jména třídy. Nicméně dají se použít i vlastní jména. Pak si všimněme použití rozsahu „protoype“. To dává smysl, já mohu new zavolat kolikrát chci (i když tam to protoype nedáte, tak se to chová stejně, takhle je to jen hezčí). No a pak už nám zbývá jen magie. Pomocí prvního kouzelného slůvka <context:spring-configured/> zařídíme, aby se použil AspectJ aspekt, který to všechno zařídí. Ano, musí se použít AspectJ, zázraky ani Spring nedovede. Zatím.

Druhé kouzelné slůvko <context:load-time-weaver/> nastaví AspectJ, tak aby si podle potřeby při startu aplikace upravil třídy. Aby to totiž všechno fungovalo, tak je potřeba upravit bytecode. A to buď při kompilaci nebo při startu aplikace. Já jsem zvolil druhou variantu, která bohužel také vyžaduje, abyste to celé spouštěli s atributem virtuálního stroje -javaagent:spring-agent-2.5.6.jar. Pokud si kód budete zkoušet sami, tak na to prosím nezapomeňte.

A to je vše, testy projdou bez problému. Vidíme, že Spring umí injektovat i do tříd, nad kterými na první pohled nemá kontrolu. A to aniž bychom nějak zvlášť museli študovat AspectJ. Všechno už máme pěkně připraveno. Samozřejmě, uvedený příklad je umělý, ale pokud bychom našli dost odvahy, tak můžeme bez problémů injektnout EntityManager přímo do entit. A to může být docela užitečné.

Zdrojový kód je ke stažení zde, obzvláště doporučuji extra zvrhlou třídu MagicBean. No a pokud chcete umět Spring tak skvěle jako já, tak se mi ozvěte, můžu vás to naučit :-).

Webové služby à la Spring

V poslední době docela často používal knihovnu Spring WS. Chtěl bych se s vámi podělit o zkušenosti, které jsem s tímto modulem udělal. Nejdřív bych měl ale upozornit, že nejsem expert na webové služby. O tom co je to port nebo binding mám jen mlhavou představu. Na druhou stranu jsem to nikdy vědět nepotřeboval. Nečekejte také žádný návod jak to používat. Původně jsem ho sice chtěl napsat, ale pak mi došlo, že bych jen kopíroval existující dokumentaci.
Než se ale pustíme do Spring-WS, začnu trochu zeširoka. Na webové služby se můžeme dívat ze dvou úhlů. Můžeme je vnímat jako trochu lepší vzdálené volání metod (RPC). Prostě označím metodu vhodně zvolenou anotací a nějaký framework se mi postará o to, aby tato metoda byla vzdáleně volatelná. Nijak zvlášť mě nezajímá to, jak bude komunikace probíhat. Prostě beru webové služby jako takovou trochu modernější Corbu nebo RMI.
Druhý přístup se na webové služby dívá z druhé strany. Bere je jako trochu vylepšený způsob výměny XML dokumentů. Při tomto přístupu je pro mě důležitý formát výměny dat, od něj se pak odvíjí způsob volání.
Samozřejmě oba přístupy nakonec skončí obdobně, liší se jen tím, co je pro mě důležitější. Spousta lidí se shoduje v tom, že druhý přístup dává větší smysl. Obvykle se dočtete, že při návrhu webové služby by se mělo začít u definice XML dokumentů, které budou sloužit pro výměnu dat (contract first). Dává to smysl. Definice dat je to, kde se budeme střetávat s konzumenty naší webové služby, XSD je to, co vidí a proti čemu programují. Nikoho nezajímá, jakou signaturu má naše metoda, konzumenty zajímá co všechno nám musí poslat a co my jim na oplátku pošleme jako odpověď.
Spring WS je postaven právě kolem contract-first přístupu. A musím přiznat, že je postaven docela dobře. Nečekejte super kvalitu, na kterou jste zvyklí od Springu. Spring WS je psán jinou partou lidí a občas člověk narazí na místa, která by šla udělat lépe. Nicméně i tak je to kvalitní knihovna a pokud jste zvyklí na Spring, konfigurace Spring WS vám přijde přímočará.
Než se vrhnu do popisu toho, jako co všechno Spring WS umí, zamysleme se nad tím, co si od takové knihovny představujeme. Já od ní chci několik věcí. Chci, abych se nemusel starat o mydlinky kolem SOAPu, chci se zabývat pouze zpracováním nákladu (payloadu), který mi v těch mýdlových bublinách přišel. Takže od WS knihovny chci, aby mi zpracovala SOAP, vybalila náklad a předala mi ho k zpracování. Protože nerad pracuji s XML, chci, aby mi pomohla s převodem z XML do objektů. U odpovědi chci aby udělala to samé, ale pozpátku. Pak by se také hodilo, aby mi pomohla s vygenerováním WSDL a pokud je to nutné s WS-Security. Když se na tyto požadavky podívám, zjistím, že už to všechno je vyřešeno. Zpracování SOAPu umí SAAJ, na serializaci XML máme taky hromadu frameworků. Stačí to všechno jenom pospojovat. A přesně to dělá Spring WS.
Co se mi na něm líbí je jednoduchá konfigurovatelnost. Nestalo se mi, že bych narazil na něco, co bych neuměl nakonfigurovat. Všechno je možné nastavit, snadno přeprogramovat a podobně.
Samozřejmostí je volba způsobu serializace z a do XML. Máte na výběr JAXB2, Castor, XStream, moje oblíbené XMLBeans a několik dalších. Vytvoření vlastního způsobu serializace je jen otázkou implementace dvou rozhraní.
Pokud potřebujete něco pokročilejšího, můžete použít interceptory. Ty fungují obdobně jako filtry u HTTP requestu, jsou ale spouštěny na úrovni SOAP dotazu a odpovědi. Spring WS obsahuje interceptor pro logování dotazů a odpovědí, který je docela užitečný. Navíc obsahuje interceptor, který umí spustit XSLT transformaci nad příchozím dotazem. To je super, pokud potřebujete upravovat formát dat. Například přidáte element do odpovědi. Ale v mezidobí, než všichni vaši klienti přejdou na novou verzi, musíte některým z nich vracet odpověď bez tohoto elementu. Není nic snazšího než napsat příslušnou XSLT šablonu, která ho z odpovědi vyhodí. To vám ušetří starosti s údržbou několika verzí té samé služby.
Interceptory navíc nabízejí skvělé místo pro úpravy jak dotazu tak odpovědi. Narazil jsem na následující problém, SAAJ negeneroval XML deklaraci na začátku XML dokumentu. Takové to <?xml version="1.0"?>. Bohužel jeden klient se toho dožadoval. Po půlhodině strávené výzkumem toho kde je chyba, jsem přišel na to, že z neznámého důvodu SAAJ implicitně XML deklaraci negeneruje. Všechno vyřešil trojřádkový interceptor, který nastavil správnou property tak, aby ji používal.
Co mě také potěšilo je generování WSDL. Člověk by si řekl, že je to samozřejmost. Nicméně například XFire měl s generováním WSDL z existujících XSD souborů pěkné problémy. Zamotal se v několikanásobných importech stejného XSD souboru a vracel mi nevalidní WSDL. To byl jeden z důvodů proč jsme přešli na Spring WS. (Druhý důvod byl, že už jeho vývoj skončil, teď se jmenuje CXF a konfigurace rozhodně není kompatibilní). Spring WS se stejným schématem neměl nejmenší problém, naopak potěšil takovou maličkostí jako je automatická změna URL webové služby ve WSDL podle toho, kde je nasazen.
Takže abych to shrnul, pokud děláte webové služby stylem contract-first a jste zvyklí na Spring, pak je Spring WS rozhodně dobrá volba. Jeho nevýhodou je, že vám nedá nic zadarmo. Chcete generovat WSDL? Musíte si to nakonfigurovat. Chcete validovat dotazy pomocí XSD? Musíte nastavit správný interceptor. Trochu nešikovné je, že pro integraci s frameworkem musíte implementovat jeho rozhraní nebo rozšířit jeho třídu. Nicméně pokud nemáte stovky různých webových služeb, tak vás pár obalovacích piditříd nezabije. Pokud je i malá obalovací třída pro vás moc, pak můžete použít anotace. Ty jsem ale nezkoušel, takže nemohu sloužit.
Mezi nevýhody Spring WS patří, že nepodporuje anotaci @WebMethod. Autoři tvrdí, že je to záměrně, že prý lidi nabádá ke škodlivému pohledu na webové služby jako na vzdálené volání metod. Nevím, jejich anotace @Endpoint mi nepřipadá o moc lepší. Ale i přes tyto nevýhody mi Spring WS připadá jako dobrá volba. Dává vám svobodu volby a to je podle mě docela užitečná věc.

Specific environment setting with Spring

There is one issue that people usually have with J2EE deployment. I call it specific environment setting. Imagine that you have several environments that you want to deploy your J2EE application to. For example development, test, stress test, staging or production. For all those environments you need specific settings like JDBC URL, JDBC user name and password, path to a directory for data export etc. The question is where to put such setting. If you store it in the WAR (JAR, EAR, SAR or other AR) you have to create specific archive for every environment.
It is not a big problem, provided you have an automated build. Nevertheless I do not like this solution. I like to have the same archive for all environments. This way I am sure that the application on production is exactly the same as the one I have tested on the test environment. Moreover, there is a problem with passwords. You have to make them part of the build process and effectively publish them by doing so.
One possible solution is to use JNDI. It is probably the best solution if you can use it. But what if you don’t use big and heavy J2EE server. What if you use a servlet container like Tomcat or Jetty instead. In this case I have one of my favorite tricks for you. I use Spring configuration similar to the following one:

 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<list>
			<value>
			  classpath:main-config.properties
			</value>
			<value>
			  file:///${user.home}/spring-test.properties
			</value>
		</list>
	</property>
	<property name="ignoreResourceNotFound" value="true"/>
	<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
 </bean>

<bean class="net.krecan.spring.TstBean">
	<property name="property1" value="${property1}"/>
	<property name="property2" value="${property2}"/>
	<property name="property3" value="${property3}"/>
</bean>

Spring tries to load a configuration file from user home directory. If the file is found, properties from the main-config file are overwritten by those found in the spring-test file. And you can even overwrite those by defining a system property with the same name.
I like it. This way every developer can have his own configuration. There is no danger of accidentally committing passwords to a source control system. You can use exactly the same WAR on every environment. It is relatively safe, although some system administrators do not like it. This way you can distribute an application over the internet without forcing users to unpack it, edit a config file and pack it again afterwards.