Category Archives: Spring

EJB bez EJB

Líbí se vám nový zápis EJB pomocí anotací? Potřebujete vaši EJB aplikaci nasadit i mimo aplikační server? Potřebujete nakonfigurovat funkční testy EJB? Potom je tu pro vás projekt Pitchfork.

EJB 3 nám umožňují psát svoje beany jako normální oanotované POJO. Což nám kromě zjednodušení vývoje přináší i jiné výhody. Jednou z největších je to, že můžeme použít náš kód i mimo kontejner. Nic nám nebrání v tom, abychom vzali EJB a nakonfigurovali je například pomocí Springu. Ale moment, proč bychom je měli konfigurovat? Vždyť už v nich máme anotace, které nám popisují závislosti mezi beany. Máme tam dokonce i anotace, které nám popisují hranice a typ transakcí. Proč nenaučit Spring, aby si tyto anotace přečetl a sám se podle nich nakonfiguroval?

Naštěstí nic takového dělat nemusíme, vyřešili to za nás BEA a Interface21 projektem Pitchfork. Nebudu vás už dál napínat a ukážu vám jak to nakonfigurovat (plnou verzi konfiguračního souboru najdete zde):

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
	<!-- JPA EntityManagerFactory -->
	<bean id="entityManagerFactory"
	 	class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
 		<property name="persistenceUnitName" value="MyPU" />
 	</bean>
	<bean id="txManager"
		class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	 </bean>

	<bean
	 	class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<!-- ejb post processor -->
	<bean class="org.springframework.jee.ejb.config.JeeEjbBeanFactoryPostProcessor">
	 	<property name="serviceEnvironment">
	 		<bean class="org.springframework.jee.config.SimpleServiceEnvironment">
	 			<property name="transactionManager" ref="txManager" />
	 		</bean>
	 	</property>
	 </bean>

	<!-- ***************** EJBs *************************** -->
	<bean id="beanA" class="net.krecan.ejb.BeanA" />
	<bean id="beanB" class="net.krecan.ejb.BeanB" />
</beans>

První co jsme nadefinovali je entityManagerFactory, ta nám zprovozní JPA. Dále musíme přidat transaction manager, který se nám bude starat o transakce. Ani to není žádná věda. Dále máme PersistenceAnnotationBeanPostProcessor, který prohledá všechny beany v aplikačním kontextu, projde si jejich anotace a když narazí na @PersistenceUnit nebo @PersistenceContext, injektne tam co je potřeba. Doposud jsme si vystačili se standardními třídami ze Spring 2. Teď přichází na řadu Pitchfork. JeeEjbBeanFactoryPostProcessor je třída, která opět prohledá všechny beany v aplikačním kontextu, projde si jejich anotace a když narazí na EJB anotaci, tak podle ní nakonfiguruje Spring. Tzn. nastaví závislosti mezi beany a podle potřeby vytvoří transakční proxy. Všimněte si tu prosím konfigurace transakcí. Trvalo mi docela dlouho, než jsem přišel na to, že se to musí nakonfigurovat právě takto.

A to je vše, teď už jenom musíme vyjmenovat všechny beany, které chceme použít. To je nezbytný krok. Musíme říci, které třídy chceme konfigurovat. Tím Springu ušetříme spoustu práce s procházením všech tříd v classpath a hledáním těch správných anotací. Sobě tím ušetříme spoustu času při startu kontejneru.

Vidíme tedy, že se dá Springem docela dobře nahradit hodně z funkcionality EJB kontejneru. Otvírá nám to spoustu možností, od snadného testování EJB až po jejich nasazení bez aplikačního serveru. Docela užitečná věc, nemyslíte?

EJB 3.0 vs. Spring

O víkendu jsem měl chvíli čas a jelikož jsem zjistil, že jsem snad poslední, kdo neposlouchá slavný podcast, tak jsem si pustil jeho čtvrté vydání. Neodolal jsem a psal jsem i poznámky, které se teď pokusím sepsat v srozumitelné podobě. Nejprve bych předeslal, že jsem veliký fanoušek a zastánce Springu, takže ode mě nečekejte nestrannost.

J2EE ≠ EJB

První důležitý bod je to, že Spring se nestaví proti J(2)EE, právě naopak. Dokonce i Rod Johnson (autora Springu) píše, že J2EE je hodně dobrá skupina standardů. Ale je mylné se domnívat, že J2EE = EJB. Pokud se podíváte co to je J2EE tak EJB je jenom jedna část z více než dvaceti! A byl právě důvod, proč vznikl Spring – umožnit snadno používat funkcionalit J2EE.

Vraťme se ale k EJB. Ty nám přinášejí následující věci (převzato z [1]):
1.Declarative Transactions Management
2.Remoting
3.Clustering
4.Resource pooling
5.Security
6.Thread management
7.EJB instance pooling
8.Business object management

Zajímavých je pravděpodobně jenom prvních pět bodů, zbytek využijete jenom pokud máte dostatek odvahy a vrhnete se do SFSB nebo entity bean. V případě že používáte jenom SLSB a MDB, vám poslední tři body žádný užitek nepřinesou (kromě pár starostí).

Projděme si tedy podrobně přínosy, které nám EJB přináší a porovnejme si je Springem

Declarative Transactions Management – Spring nám také umožňuje definovat transakce pomocí anotací (klidně i stejných jako používá EJB) a XML. Navíc tyto transakce fungují i pokud nemáme k dispozici JTA.

Remoting – Spring umožňuje remoting pomocí RMI, Hessian, Burlap, HTTP invokeru a JAX-RPC. Největší legrace je to, že když se rozhodnu změnit technologii, stačí mi změnit pár řádek v konfiguraci. Kód aplikace zůstane nezměněn

Clustering – Jedna z největších výhod EJB. Ale pozor, pokud používáte pouze bezstavové objekty jako SLSB a MDB, je clustering velice snadný. Jste si jisti, že k tomu potřebujete něco tak komplikovaného jako EJB?

Resource pooling – Například pool databázových připojení. To ovšem není vymoženost EJB jako spíše aplikačních serverů. Možné nahradit pomocí C3P0 nebo DBCP

Security – EJB nám poskytují rozsáhlé možnosti zabezpečení. Škoda jen, že si to každý dodavatel aplikačního serveru naimplementoval po svém, takže se můžeme rozloučit s přenositelností. Byla by to jedna z nejužitečnějších přínosů EJB, kdyby ovšem byla dostatečně definována.

Všimněte si že, JTA je ve výčtu chybí. Je to samostatný standard, na EJB zcela nezávislý. Takže argument typu „Pokud potřebujete distribuované transakce, musíte používat EJB“ je naprosto zcestný. JTA můžeme dokonce používat i mimo AS, například pokud použijeme JOTM.

Vidíme tedy, že Spring umí to samé jako EJB. Jak se tedy rozhodnout? V době EJB 2.1 bylo rozhodnutí snadné, pokud jste nepotřebovali žádnou ze specialit EJB jako je clusterování, MDB nebo SFSB bylo lepší zvolit Spring. Ušetřili jste si tak neuvěřitelné množství naprosto zbytečné práce s EJB.

Doba pokročila, EJB prošly kosmetickou operací, při které na starý ošklivý obličej našili masku anotací. Ta ošklivost je tam pořád, ale už ji nevidíme. Což mimo jiné znamená, že už můžeme EJB používat, aniž bychom museli psát (nebo generovat) hromady zbytečného kódu.

Za tu dobu ale i Spring hodně pokročil. Objevily se message driven POJOs, Terracota nám zajistila snadné clusterování i jiných než bezstavových objektů a SFSB pořád ještě nikdo nepoužívá.

Jak si tedy vybrat? Jelikož jsem fanoušek Springu, je odpověď jasná, mám pro to následující důvody:

Zjednodušuje práci s projekty třetích stran

Volání kódu někoho jiného je v podstatě jediné co Spring umí. Umožňuje mi snadno navázat můj kód na různé knihovny a další frameworky. Je důležité si uvědomit, že Spring nic nedělá (až na pár vyjímek). Jenom práci deleguje buď na knihovny nebo aplikační server.

Snadnost práce s nízkoúrovňovými Java standardy

Spring nám zjednodušuje práci s Java standardy, které jsou často hodně nízkoúrovňové. Hezkým příkladem je třeba Java Mail API. Kdo ho někdy používal v čisté podobě ví, že je to API těžko použitelné a kód který ho používá je netestovatelný.

Další příklad je i JDBC, dle mého názoru jedna z nejdůležitějších částí Java API. Opět je to nízkoúrovňové rozhraní, které by nemělo být používáno přímo. Spring nám jeho použití výrazně usnadňuje a navíc nám zamezuje udělat chybu. Malá anketní otázka: když používáte JDBC, jste si jisti, že děláte správně všechno to TCFTC (try{}catch{}finally{try{}catch{}})?

Svoboda výběru

Tím se dostáváme k svobodě výběru. EJB mě nutily pro perzistenci používat entity beany. Když se přišlo na to, že jsou nepoužitelné, nutí mě používat JPA. Pokud ale JPA nechci nebo nemohu používat, už mi nezbude nic jiného než JDBC. Spring mi na druhou stranu říká: „Já ti věřím, že svým potřebám rozumíš natolik, že víš, kterou technologii si vybrat. Ať si vybereš cokoliv, jsem ochoten ti pomoci.“ Takže mi zjednodušuje práci s JDBC, iBatis, JPA, JDO atp. Pokud nevím co si mám vybrat, měl bych se přeškolit na .NET, tam mi to řeknou.

Snadnost konfigurace aneb „Nemáte-li Spring, musíte si ho napsat“

Všiml jsem si, že kdo nepoužívá Spring, napíše si obvykle jeho obdobu. V každé aplikaci potřebuji konfiguraci. Ať už je to čtení nastavení z properties souboru, nebo nějaký druh XML konfigurace. Jelikož mi EJB s tímto ani trochu nepomohou, musím si to obvykle napsat sám. Často se tvoří nějaká SLSB, která konfiguraci načítá a ostatní beany ji využívají. Ale to je přesně to co mi zajišťuje Spring!

Dependency injection

Myslím tím opravdovou dependency injection. Tzn. že si mohu injektnout (šlehnout?) cokoliv kamkoliv. Do jakékoliv třídy mohu vložit string, odkaz na soubor, jméno třídy, referenci na cokoliv, mapu, list, preperties soubor, zkrátka cokoliv mě napadne. EJB jsou v tomto směru dost omezené. Zde bych si dovolil varování. Tato vlastnost Springu je neuvěřitelně návyková. Jakmile si na DI zvyknete, už se vám bez ní bude neuvěřitelně špatně pracovat. Takže pokud chcete pracovat s EJB, radši si se Springem nezačínejte.

Spolupráce s MVC frameworky

Spring podporuje všechny možné MVC frameworky. EJB podporují JSP, servlety a JSF. SLSB prostě nikam jinam neinjektujete.

Dostupnost prostředí

Aplikaci napsanou ve Springu mohu použít v jakémkoliv prostředí, které mi poskytuje služby, které potřebuji. Pokud souhlasíme s tvrzením, že 90% procent aplikací je jednoduchých, tak nám stačí Tomcat nebo Jetty. Aplikaci, napsanou v EJB 3, mohu zatím nasadit na jednom jediném AS, který na tom jen tak mimochodem není moc dobře se stabilitou.

A jaké jsou nevýhody Springu? Jednou z největších je politika. Za EJB stojí velké společnosti a neuvěřitelné množství peněz. Velcí klienti jako třeba banky mají (nebo spíše měli) nedůvěru k open source řešením. I když i to se mění, už i BEA začala výrazně podporovat Spring. To by moho nedůveřivcům něco naznačit. A už i velké společnosti pomalu začínají chápat, že použití nebo úprava open source řešení jim zajistí levnější a kvalitnější produkt, než kdyby jen spoléhali na standardy nebo řešení velkých výrobců.

Objevuje se argument, že EJB 3 jsou pro programátora snazší než Spring. Existuje ale projekt Pitchfork, který Springu umožňuje číst EJB anotace a konfigurovat svůj application context podle nich (o Pitchworku plánuji něco napsat v brzké době). EJB 3 navíc nejsou vůbec jednoduché, jenom se tak na povrchu tváří. Kdo si myslí, že jsou jednoduché, ať mi odpoví na následující otázky. První kdo na ně odpoví a nemá iniciály M.R. získá nehynoucí slávu:

1) EJB 3 umožňují získat referenci na SLSB z JNDI následovně


    InitialContext ic = new InitialContext();
    Hello  hello = (Hello)ic.lookup(Hello.class.getName());
    hello.hello();

Otázka je následující:
a) Na co ukazuje reference hello? (Je to home objekt, nějaká proxy, …)
b) Musí mít každý klient svoji referenci, nebo je bezpečné ji sdílet?
c) Je potřeba ji nějak vracet do poolu?
d) Co se stane, když po tom co referenci získám a před tím než ji použiji dojde k přerušení spojení s aplikačním serverem?

2)Je bezpečné injektnout SFSB do servletu?

Reference

[1] Johnson, Rod; J2EE Developement without EJB; Wiley Publishing; 2004

[2] Sriganesh, Rima Patel; Mastering Enterprise Java Beans 3.0; Wiley Publishing; 2006

Jak jsem udělal chybu aneb chyba ve Springu

 

Ano, i já, tvor neobyčejně dokonalý se mohu dopustit chyby. Chtěl bych o jedné z nich napsat, nebyla rozhodně první, ani poslední, byla ale dost zajímavá.

Všechno začalo dost nevině, při práci na svém úžasném projektu m2-proxy jsem jedné třídě potřeboval předat seznam dvojic hodnot. Abych si zjednodušil konfiguraci přes Spring, přetížil jsem setter tak, aby uměl pracovat s mapou, kterou následně přetransformuje na kolekci dvojic. Kód vypadá následovně:

public class TestBean { private static final Log LOG = LogFactory.getLog(TestBean.class); public void setData1(Map map) {

LOG.debug("Setting map data1"); } public void setData1(List list) { LOG.debug("Setting list data1"); } }

Už asi tušíte v čem je problém, když ve Springu napíši následující konfiguraci, nevím, která verze setteru bude použita.

<bean name="testBean1" class="net.krecan.test.TestBean">
<property name="data1">
   <map>
       <entry key="a" value="b">
       <entry key="c" value="d">
   </map>
</property>
</bean>

Na první pohled by to měla být ta s mapou, nicméně je tu také možnost, že mi bude předán seznam jehož jediným prvkem bude mapa. Nyní se můžeme vrhnout do studia dokumentace všeho možného, abychom zjistili, jak Spring zafunguje nebo můžeme zkusit jednodušší možnost „Zeptej se stroje“. Napíši si test, který mi zjistí, jak se to zachová. Odpověď mě uspokojí, můj test mi řekne, že se zavolá metoda s parametrem Map, což je přesně to co potřebuji. Ne celý problém zapomenu, programuji dál a m2-proxy vypublikuji.

Jeden z prvních uživatelů (z těch asi 30, co si m2-proxy stáhli), mi asi druhý den po vypublikování nahlásí chybu, z které plyne, že se u něj při inicializaci volá metoda setData s parametrem List a ne Map. Čím to tak může být, mě to při všech testech fungovalo! Vrhnu se do zkoumání a zjistím zajímavý fakt. Na mém počítači se zavolá ta metoda, která je v zdrojovém kódu uvedena jako první! To znamená, že výsledné chování programu, závisí na takovém detailu, jako je pořadí metod ve zdrojovém kódu. Navíc na jiných JRE se to všechno může chovat úplně jinak. Evidentně jde o chybu ve Springu, pomocí reflekce procházejí seznam metod a když narazí na první s vhodnou signaturou, tak ji použijí. Nelenil jsem a přidal jsem do JIRY Springu hlášení o chybě. Po pár dnech mi odpověděl Juergen Hoeller:

Toto je ve skutečnosti definováno JavaBeans specifikací: Přetěžování není povoleno u setterů a getterů, protože každá vlastnost musí mít jasný typ.

Takže chyba je jako vždy na mé straně, řešení je jednoduché, stačí přejmenovat jeden ze setterů (což jsem mimochodem udělal, hned jak jsem přišel na to v čem je problém)

Jaké si z celé historie odnáším poučení?

  1. Testovat důkladně. Musím se přiznat, že jsem moc nezkoušel, která verze přetížené metody se zavolá. Prostě jsem to spustil a jelikož to fungovalo, na celý problém jsem zapomněl. Kdybych to zkoušel důkladně, asi bych otestoval, co se stane, když dám do Spring konfigurace List. Vyjimka, kterou bych dostal by mě upozornila, že něco není v pořádku
  2. Nedělat prasárny. Člověk může stokrát slyšet, že programovat se má tak, aby bylo na první pohled vidět co program dělá. Nicméně v realitě se to ne vždy podaří.