0% found this document useful (0 votes)
1K views

Dependency Injection

The document is a final thesis project from the University of Mediterranean in Podgorica, Montenegro. It examines software component integration using the Dependency Injection pattern. The thesis contains an introduction on Dependency Injection and an outline of the following chapters, which will cover key concepts, Dependency Injection frameworks like Spring and Tapestry, and practical examples of Dependency Injection implementation.

Uploaded by

necr084
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1K views

Dependency Injection

The document is a final thesis project from the University of Mediterranean in Podgorica, Montenegro. It examines software component integration using the Dependency Injection pattern. The thesis contains an introduction on Dependency Injection and an outline of the following chapters, which will cover key concepts, Dependency Injection frameworks like Spring and Tapestry, and practical examples of Dependency Injection implementation.

Uploaded by

necr084
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

UNIVERZITET MEDITERAN PODGORICA Fakultet za informacione tehnologije-Podgorica

Veselin Rakovi Integracija softverskih komponenata pomou Dependency Injection paterna ZAVRNI RAD

Podgorica, 2011.

UNIVERZITET MEDITERAN PODGORICA Fakultet za informacione tehnologije - Podgorica

Integracija softverskih komponenata pomou Dependency Injection paterna ZAVRNI RAD

Predmet: Projektovanje i razvoj softverskih aplikacija Mentor: doc. dr Dragan uri

Student: Veselin Rakovi Smjer: Softverski inenjering Matini broj: 1302984220014

Podgorica, oktobar, 2011.

1. Uvod
1.1. Dependency Injection
Poslovne aplikacije su kreirane sa ciljem da rijee odreene probleme u poslovanju. Vremenom je tehnologija napredovala to je rezultovalo razvoju poslovnih aplikacija koje su rjeavale vee i kompleksnije probleme. Zbog toga su i same aplikacije postale vee i kompleksnije. Poveanje kompleksnosti aplikacije je iznjedrilo nove probleme koje je trebalo rijeiti da bi softverski inenjeri mogli da izau u susret sve veim zahtjevima svijeta biznisa. Brojni su primjeri kada je bilo lake izgraditi sistem od poetka nego nadograditi i odravati postojei. Rjeenja koja su tada bila aktuelna nisu bila dovoljno dobra za razvoj kompleksnih aplikacija koje su lake za nadogradnju i odravanje. Zato su bila potrebna nova i bolja rjeenja. Jedno od tih rjeenja je Dependecy Injection mehanizam. Korijeni Dependency Injectiona su vezani za programski jezik Java. Zapravo, Java je kolijevka Dependency Injectiona. Krajem devedesetih se pojavilo EJB standard koji je omoguavao pisanje sloenih Java Enterprise aplikacija. Prihvaen je zato to je nudio rjeenja za probleme koja dotad nisu postajala jedno od najvanijih je automatska obrada transakcija. Meutim, nova rjeenja su proizvela nove probleme. Da bi se iskoristile mogunosti EJB tehnologije bilo je potrebno naslijediti pojedine klase ili implementirati odreene interfejse. U tim uslovima je bilo teko pisati objektno orijentisan kod to je rezultovalo aplikacijama koje je teko testirati i odravati. Zbog toga su i nazvane teke (heavyweight) aplikacije. Rjeenje za taj problem je Dependency Injection (DI) tj. frejmvorci koji automatski vre Dependency Injection. Ukratko, frejmvork je softver koji radi odreeni dio posla umjesto programera. Ispostavilo se da je Dependency Injection toliko dobro rjeenje da je naknadno i EJB (verzija 3.0) poeo da ga primjenjuje. Jedan od prvih DI frejmvorka je bio Spring frejmvork koji je i dan danas veoma popularan. Zapravo Spring je dugo bio sinonim za Dependency Injection. Kreiran je od strane grupe programera na elu sa Rod Johnoson-om. Dependency Injection je omoguio pisanje enterprise aplikacija na objektno-orijentisan nain (Plain Old Java Object - POJO programiranje). Umjesto glomaznih klasa sistem je mogue podijeliti na manje dijelove to najbolje ilustruju sledee slike:

VS

Slika 1. Poreenje sistema sklopljenih od velikih i malih dijelova Sistem sklopljen od veih dijelova, predstavljen na lijevoj slici, je tei za razumijevanje i odravanje. Dependency Injection je omoguio razvoj aplikacija koje se sastoje od manjih cjelina koje su, logino, razumljivije. Zbog toga su i nazvane lagane (lightweight) aplikacije. Pored toga Dependency Injection je uinio aplikaciju fleksibilnijom njeni dijelovi su se lako mogli zamijeniti sa drugim, slinim, dijelovima. [12] Dependency Injection odnosno DI frejmvork nije rjeenje za cjelokupan poslovni problem. On predstavlja veoma vanu kariku koja povezuje ostale ali ga nije mogue koristiti u izolaciji. Tada bi bio beskoristan.

1.2. Poglavlja rada


Poslije uvodnog dijela i sadraja slijedi poglavlje o osnovnim pojmovima koji se koriste u radu. Bie rijei o programskom jeziku Java, njegovoj istoriji i upotrebi. Bie objanjeni pojmovi kao to su: softverska komponenta, moduli, servisi. Zatim e biti opisani paterni meu koje spada i sam Dependency Injection. U poglavlju e takoe biti rijei o Dependency Injection-u, odnosno nainu na koji on funkcionie u sprezi sa paradigmom Programiranje preko interfejsa, koja e takoe biti pojanjena. Takoe e biti pojanjen pojam Inversion of Control (Inverzija odgovornosti) koji se vezuje uz Dependency Injection. Zatim e detaljnije biti objanjen pojam frejmvorka i bie opisani sledei frejmvorci:

Apache Tapestry komponentno orijentisan frejmvork za razvoj web aplikacija. Interesantno je da Tapestry ima svoj Dependency Injection frejmvork (Tapestry IoC) kojem e biti posveen dobar dio ovog rada. Spring prvi i jedan od najpopularnijih Dependency Injection frejmvorka. Pored Dependency Injectiona Spring moe obavljati i druge funkcije. Neke od njih e biti koriene u etvrtom poglavlju. Hibernate omoguava pamenje podataka korienjem objektno relacionog mapiranja. Hibernate se moe koristiti u sprezi sa Java Persistence API koji je takoe frejmvork koji koristi objektno relaciono mapiranje. Hibernate JPA kombinacija e biti koriena u praktinim primjerima koji se nalaze u treem poglavlju. TestNG frejmvork za automatsko testiranje.

U poglavlju o osnovnim pojmovima je objanjeno takoe ta je automatsko testiranje i koje vrste testova postoje, ta su transakcije i ta je perzistenicija. Zatim slijedi etvrto poglavlje koje obuhvata najobimniji dio rada. U njemu su dati praktini primjeri iz aplikacije za demonstraciju Dependency Injectiona. kao i frejmvorka koji automatizuju Dependency Injection: Tapestry IoC i Spring. Aplikacija je implementirana pomou sledeih tehnologija: Apache Tapestry 5.2.4, Spring 3.0.5, Hibernate 3.6.0 final i TestNG 5.13.1. Primjeri su sledei:

Prvi primjer se odnosi na stranicu koja ispisuje poruku o greki ukoliko korisnik nije dobro uradio odreeni zadatak. Funkcionalnost je implementirana upotrebom Dependency Injection-a i Tapestry IoC-a a razmotren je i sluaj u kojem DI ne bi bio korien. Drugi primjer je znatno obimniji - sastoji se od nekoliko potprimjera. Oni obuhvataju kreiranje i konfigurisanje servisa za korisnike, preko Tapestry IoC-a. Bie pokazano da Dependency Injection omoguava laku zamjenu postojeeg korisnikog servisa sa kompleksnijim servisom (zbog veze sa bazom podataka) konfigurisanim u Spring frejmvorku i Tapestry IoC-u. Bie takoe prikazane mane i nedostaci njihovih alternativa servisa koji ne koriste Dependency Injection. Trei primjer pokazuje kako se mogu koristiti Tapestry komponente u sprezi sa Tapestry IoC-om za zatitu stranica od neovlaenog pristupa. Prikazani su nedostaci rjeenja su u kojem se ne koristi Dependecy Injection za zatitu od neovlaenog pristupa. U etvrtom primjeru su koriene napredne mogunosti Tapestry IoC i Spring frejmvorka za implementaciju Open Session In View filtera. Izvreno je i poreenje sa primjerom u kojem se ne koristi Dependency Injection. U petom primjeru je prikazano kako je mogue uticati na Tapestry konfiguraciju preko contribute mehanizma po kojem se Tapestry IoC razlikuje od ostalih DI frejmvorka. Na kraju etvrtog poglavlja je dat primjer Late Binding-a kojeg omoguava Dependency Injection.

Na kraju rada se nalazi zakljuak, popis slika, kao i spisak literature koja je koriena za pisanje ovog rada.

2. Sadraj
1 Uvod...........................................................................................1 1.1 Dependency Injection..........................................................................................................................................1 1.2 Poglavlja rada.....................................................................................................................................................2 2 Sadraj.........................................................................................................3 3 Osnovni pojmovi...............................................................................5 3.1 Java......................................................................................................................................................................5 3.2 Softverske komponente i servisi...........................................................................................................................6 3.3 Moduli..................................................................................................................................................................6 3.4 Softverski paterni.................................................................................................................................................6 3.4.1 Dizajn i arhitekturalni paterni....................................................................................................................6 3.4.2 MVC Model-View-Controll patern..........................................................................................................6 3.4.3 Dependency Injection.................................................................................................................................7 3.5 Programiranje preko interfejsa...........................................................................................................................8 3.6 Automatsko testiranje softvera............................................................................................................................9 3.6.1 Unit testiranje.............................................................................................................................................9 3.6.2 Integracioni testovi.....................................................................................................................................9 3.6.3 Funkcionalni testovi....................................................................................................................................9 3.6.4 Testovi prihvatljivosti...................................................................................................................................9 3.6.5 Testovi performansi....................................................................................................................................10 3.7 Frejmvorci..........................................................................................................................................................10 3.7.1 Spring frejmvork........................................................................................................................................14 3.6.2 Apache Tapestry frejmvork.........................................................................................................................11 3.7.3 ORM frejmvorci Hibernate i JPA............................................................................................................11 3.7.4 Frejmvorci za automatsko testiranje..........................................................................................................12 3.8 Perzistentni objekti............................................................................................................................................12 3.9 Transakcije.........................................................................................................................................................12

4. Studijski primjeri.......................................................................13 4.1 Prvi primjer.......................................................................................................................................................13 4.2 Drugi primjer.....................................................................................................................................................15 4.3 Trei primjer......................................................................................................................................................28 4.4 etvrti primjer...................................................................................................................................................34 4.5 Peti primjer........................................................................................................................................................38 4.6 esti primjer.......................................................................................................................................................40 5. Zakljuak..................................................................................................41 6. Reference..................................................................................................42 7. Popis slika................................................................................................................................................................43

3. Osnovni pojmovi 3.1 Java


Java je besplatan, objektno-orijentisani programski jezik otvorenog koda koji se pojavio 1995. godine. Razvio ga je James Gosling za potrebe firme Sun Microsystems. Na Javin razvoj su uticali popularni programski jezici C i C++ kao i Smalltalk, jedan od prvih objektno-orijentisanih jezika. Ono to posebno izdvaja Javu (naroito u vrijeme kada se pojavila) je njena nezavisnost od platforme na kojoj se izvrava. To se postie pomou Javine Virtuelne Maine. Naime, Javine klase se kompajliraju u bajtkod a taj bajtkod se izvrava tj. interpretira na Java Virtuelnoj Maini - JVM. Dovoljno je imati JRE (sadri JVM) za pokretanje programa koji pisan u Javi, uopte nije bitno na kom je operativnom sistemu on napisan. Java ima svoj garbage kolektor koji vri automatsko ienje memorije to ju je takoe posebno izdvajalo od ostalih konkurentnih jezika u vrijeme kada se pojavila. U meuvremenu su se pojavili programski jezici koji se izvravaju na JVM - Scala, Clojure i Groovy. Oni se mogu koristiti u kombinaciji sa Javom. Smatra se da je jedan od najlakih i najjednostavnijih objektno-orijentisanih jezika. Java je, zahvaljujui svojoj otvorenosti, posluila kao osnov za brojne popularne frejmvorke i alate. Oni su dobrim dijelom zasluni za to to je Java danas jedan od najpopularnijih programskih jezika. Zbog te popularnosti na Internetu se moe nai ogromna dokumentacija koja olakava uenje Jave. Pored toga njeno korienje i uenje olakava veliki broj raspoloivih biblioteka. Programski jezik Java je dio Java platforme. Njena izdanja su: Java Standard Edition ili Java SE (J2SE) za razvoj desktop aplikacija Java Enterprise Edition ili Java EE (J2EE ) - za razvoj enterprise aplikacija Java Micro Edition ili Java ME (J2ME) za razvoj aplikacija za mobilne telefone, PDA ureaje i ostale koje ne mogu da podre J2SE ili J2EE zbog hardverskih ogranienja.

Njihov meusoban odnos najbolje ilustruje sledea slika:

Slika 2. Izdanja Java platforme i njihov meusoban odnos

3.2 Softverske komponente i servisi


Softverska komponenta je osnovna jedinica grae softverske aplikacije. Aplikaciju moemo posmatrati kao jednu cjelinu koja se sastoji od elemenata koji su meusobno povezani. Ti elementi su softverske komponente. Izvorni kod softverske komponente se ne mijenja ali se ona moe nadograditi tj. unaprijediti na nain na koji su to predvidjeli kreatori softverske komponente. Jedna softverska komponenta se moe sastojati od drugih softverskih komponenata. Softverske komponente mogu biti upotrijebljene vie puta za rjeavanje razliitih problema unutar aplikacije. Servisi su softverske komponente koji se upotrebljavaju pomou neke vrste interfejsa za daljinsko upravljanje poput soketa. [1]

3.3 Moduli
Softverska aplikacija moe biti podijeljena na module od kojih e svaki biti zaduen za jednu funkciju (ili za jednu grupu funkcija). Modul je softverska komponenta ija je namjena obavljanje jedne funkcije. Modul sadri sve to je potrebno za obavljanje te funkcije i ima neku vrstu monopola nad funkcijom koju obavlja - samo je on (modul) za nju zaduen. [2]

3.4 Softverski paterni


Tokom rada programeri su nailazili na brojne probleme koje je trebalo rijeiti. Primjetili su da se neki od tih problema ponavljaju i da se rjeenja koja su koriena za rjeavanje jednog problema mogu ponovo iskoristiti za rjeavanje drugog problema, istog tipa. Rjeenje nee biti identino ali e njegova sutina biti ista pa se ta sutina moe oznaiti kao uzor ili ablon (pattern) po kojem e biti implementirano rjeenje. Paterni su plod kolektivne inteligencije programera. To znai da su brojni programeri godinama tragali za to boljim rjeenjima za pojedine probleme. Paterni kakve danas imamo su rezultat njihovih zajednikih napora da dou do to kvalitetnijeg i univerzalnijeg rjeenja. Paterni se uveliko koriste za projektovanje savremenih softverskih aplikacija pa poznavanje paterna nam pomae u razumijevanju implementacije tih softverskih aplikacija. [3] 3.4.1 Dizajn i arhitekturalni paterni Treba razlikovati arhitekturalne paterne od dizajn paterna. Arhitekturalni patterni se bavi problemom kako cijeli sistem podijeliti i organizovati u logike cjeline na najbolji mogui nain. Poto se radi od rjeavanju problema u kojem se polazi od sistema tj. od samog vrha kae se da arhitekturalni paterni su rjeenja za probleme najvieg nivoa. S druge strane dizajn paterni funkcioniu na niem nivou. Ako npr. sistem podijelimo na tri sloja, neki od dizajn paterna nam mogu rijeiti problem koji se nalazi unutar nekog od ta tri sloja. Dakle, aritekturalni paterni se bave strukturom sistema a dizajn paterni rjeavanjem problema unutar te strukture. [4] 3.4.2 MVC Model-View-Controller patern MVC je arhitekturalni pattern koji dijeli sistem na tri dijela: View predstavlja ono to krajnji korisnik vidi. Model je sloj orijentisan ka podacima (baza podataka npr) zaduen je za uvanje podataka. Controller je spona izmeu Modela i View-a.

Slika 3. Model-View-Controller Kada korisnik zahtijeva izmjenu nekih podataka, Controller obrauje taj zahtjev i na osnovu njega manipulie Modelom. Zahvaljujui Observer paternu View, koji je zaduen za prikaz podataka, biva obavijeten da je dolo do promjene. U nekim novijim frejmvorcima poput Tapestry-ja ne postoji nikakva direktna veza izmeu View-a i Modela. Za komunikaciju izmeu View-a i modela se ne koristi se Observer pattern ve Controller komponenta koja, u tom sluaju, predstavlja jedinu vezu izmeu View-a i Modela. [5] 3.4.3 Depencency Injection Dependency Injection je arhitekturalni patern koji umanjuje sloenost sistema na taj nain to omoguava loose coupling labavo povezivanje. Ali ta je ustvari loose coupling? Uzmimo za primjer jedan obian raunar. On se sastoji od raunarskih komponenata koje se mogu popraviti, nadograditi, zamijeniti. To je primjer loose couplinga, a njegova suprotnost - tight coupling (veza izmeu komponenata je vrsta) je kada ne bismo mogli da zamijenimo, popravimo i nadogradimo komponente. U emu je problem tight coupling-a? Pa zamislimo da se pokvari jedna komponenta raunara bez koje njegovo funkcionisanje nije mogue. Zbog tight coupling-a ne bismo mogli da zamijenimo komponentu, opravka bi bila teka, moda i nemogua, te bi bili prinueni na kupovinu novog raunara. Ili recimo imate dual-core procesor i odluite da ga zamijenite za neki quad-core. Ako je veza izmeu komponenata vrsta (tight coupling) onda to ne bi bilo mogue uraditi zato to bi matina ploa i procesor bili neraskidivo povezani. ta ovo znai u svijetu programiranja? Recimo da imamo klasu koja predstavlja matinu plou: public class Motherboard { private DualCoreProcessor processor; public Motherboard() { processor = new DualCoreProcessor(); } . } Klasa Motherboard je direktno zavisna od klase DualCoreProcessor. Zbog toga je klasu Motherboard nemogue testirati u izolaciji. To moemo rijeiti tako to emo klasu Motherboard izmijeniti na sledei nain: public class Motherboard { private Processor processor; public Motherboard(Processor processor) { this.processor = processor; } }

Ostale klase: public class DualCoreProcessor implements Processor {} public class Pentium4Processor implements Processor {} public class QuadCoreProcessor implements Processor {} Klasa Motherboard nije vie zaduena za kreiranje klase DualCoreProcessor ve njenu instancu prima preko svog konstruktora. Treba primjetiti jo jednu vanu stvar primjenjena je paradigma programiranje preko interfejsa pa klasa Motherboard nije vie direktno zavisna od klase DualCoreProcessor nego od interfejsa Processor. Zahvaljujui tome ona sada moe ostvariti vezu sa bilo kojom klasom koja implementira interfejs Processor (QuadCoreProcessor, DualCoreProcessor, Pentium4Processor) i to je primjer loose couplinga. Testiranje u izolaciji je sada u mogue treba samo proslijediti Mock objekat klase sa kojom je Motherboard povezana. Poto Motherboard prima instancu klase sa kojom je povezana preko konstruktora rije je o Constructor Injection-u i to je jedan od oblika Dependency Injection-a (u prevodu - ubrizgavanje zavisnosti). Pored Construction Injection-a postoje Setter Injection, Field Injection i Interface Injection. Instancirajmo sada klasu DualCoreProcessor i Motherboard: public static void main (String[] args) { Processor processor = new DualCoreProcessor(); Motherboard motherboard = new Motherboard (processor); } Ovo je primjer runog Dependency Injection-a a moderne aplikacije koriste frejmvorke koji automatizuju Dependency Injection. Frejmvorci koji automatizuju Dependency Injection su karakteristika svih laganih (lightweight) frejmvorka. Prvobitni naziv za Dependency Injection je Inversion of Control IoC. Ustanovljeno je da IoC ima ire znaenje pa je zbog toga prihvaen termin Dependency Injection. Ako bi pojam IoC predstavljao jedan skup, DI bi bio njegov podskup. Primjere za IoC moemo nai u svakodnevnom ivotu - za IoC se vezuje holivudski princip: Ne zovite vi nas, mi emo vas. Dependency Injection frejmvorci se jo zovu i IoC kontejneri. Poznati IoC kontejneri su: Spring, Guice, PicoContainer, Avalon, Apache HiveMind. [1]

3.5 Programiranje preko interfejsa


Svrha programiranja preko interfejsa je da omogui programeru da jedan objekat lako zamijeni drugim.[6] To se postie preko interfejsa. Recimo da imamo sledeu metodu: public void go (Yugo45 yugo45) { yugo.start(); } Klasa Yugo45: public class Yugo45 { public void start() {...} } ta ako elim da automobil Yugo zamijenim sa nekim drugim? Naalost metoda go nas trenutno ograniava samo na Yugo45. Problem se rjeava na sledei nain: public interface Car () { void start(); }

public class Yugo45 imlements Car { public void start() {...} } public class Zastava101 imlements Car { public void start() {...} } public class RenaultClio imlements Car { public void start() {...} } Naravno, svaka od ovih klasa moe da implementira metodu start na drugaiji nain. A sada ono glavno metoda go: public void go (Car car) { car.start(); } Metoda go vie nije ograniena na klasu Yugo45 ve moe primiti (kao argument) instancu bilo koje klase koja implementira interfejs Car.

3.6 Automatsko testiranje softvera


Automatsko testiranje je proces u kojem se pomou koda namijenjenog za testiranje testira jedan dio aplikacije. Runo testiranje je testiranje aplikacije od strane ovjeka. Testiranja se vre da bi se greke u softveru na vrijeme otkrile i ispravile. Automatsko testiranje se najvie koristi kod modernih metodologija razvoja softvera koje podrazumijevaju da se aplikacija u razvoju esto testira.[7] Postoje brojni frejmvorci za automatsko testiranje kao npr: TestNG, Junit, Mockito, Selenium... Njihova svrha je da olakaju programeru pisanje koda za testiranje. Postoji nekoliko vrsta ovakvih testova. 3.6.1 Unit testiranje Unit testiranje je testiranje komponenata u izolaciji od drugih komponenata. Preduslov da raunar radi kao cjelina je da su sve njegove pojedinane komponente ispravne. Unit testiranje bi u tom sluaju bilo kada bi se svaka komponenta testirala pojedinano u izolaciji od ostalih. U programiranju se ta izolacija postie tako to klasi nad kojom se vri Unit testiranje proslijede mock (lairani) objekti klasa sa kojima je povezana.[8] 3.6.2 Integracioni testovi Pomou integracionih testova se provjerava da li su klase ili komponente dobro povezane. Npr. da li je procesor dobro povezan sa matinom ploom, monitor sa grafikom karticom itd. Prilikom ovakvih testiranja se ne koriste Mock objekti.[9] 3.6.3 Funkcionalni testovi Piu se testovi koji testiraju sistem na nain na koji bi ga koristili obini korisnici. Ne testira se unutranjost aplikacije nego se aplikacija pokrene i provjerava se da li npr. sve njene opcije i dijelovi zaista rade onako kako bi trebalo. [10] 3.6.4 Testovi prihvatljivosti (acceptance tests) Testovi prihvatljivosti su funkcionalni testovi koji provjeravaju da li su zadovoljeni korisniki zahtjevi. Jedna aplikacija moe sadrati gomilu opcija koje rade ali je pitanje da li rade ono to korisnik eli. Acceptance testovi upravo to provjeravaju.

3.6.5 Testovi performansi Testovi performansi provjeravaju da li se aplikacija (ili njeni dijelovi) dovoljno brzo izvrava i da li pritom troi odgovarajuu koliinu resursa. [7]

3.7 Frejmvorci
Frejmvork (framework) je softver koji olakava programerima da realizuju aplikaciju koja e raditi posao i biti pogodna za nadogradnju i odravanje. Sam frejmvork je obino zasnovan na nekom od programskih jezika. Frejmvork se moe posmatrati kao osnovica sa kojom programer radi a aplikacija nastaje proirivanjem te osnovice. To proirivanje se vrii programiranjem, podeavanjem opcija i konfiguracionih fajlova i to na nain na koji frejmvork dozvoljava. Da nije dananjih frejmvorka programer bi morao da npr iskuca gomilu i gomilu klasa da bi sebi obezbijedio funkcionalnost koju mu frejmvork nudi. Frejmvork je karakteristian i po inverziji odgovornosti (IoC). On je taj koji je zaduen za upravljanje jednim dijelom aplikacije koja se pie a ne aplikacija. Dakle, programer, za dio za koji je zaduen frejmvork, ne pie kod nego na odreeni nain preputa taj posao frejmvorku.[11] 3.7.1 Spring frejmvork Spring je u osnovi DI frejmvork ali ima i druge funkcije jedna od najbitnijih je AOP. Podijeljen je u nekoliko modula i svaki od njih je zaduen za po jednu funkciju. Core je zaduen za Dependency Injection. Ovaj modul ustvari predstavlja IoC kontejner. Zahvaljujui ovom modulu, mogue je povezati razliite komponente i klase a da pritom nije potrebno da one naslijede neku klasu iz frejmvorka ili implementiraju neki njegov interfejs. Podrava Setter Injection i Constructor Injection. Nain na koji e klase biti povezane je definisan unutar XML konfiguracionog fajla. Od verzije 2.5 Spring povezivanje se moe konfigurisati pomou anotacija tako da XML fajlovi vie nisu neophodni. Mogue je kombinovati anotacijsko podeavanje i podeavanje putem XML-a. Sledea slika ilustruje komponente povezane unutar Spring IoC kontejnera:

Slika 4. Spring IoC kontejner AOP modul je zaduen za Aspektno orijentisano programiranje. AOP omoguava da se na odreenom mjestu u metodi ubrizga odgovarajui kod. Npr. kod za transakcije je uvijek isti i ako je potrebno implementirati recimo 10 metoda od kojih se svaka odvija unutar jedne transakcije, to znai da kod za transakcije je potrebno pisati 10 puta. Zahvaljujuu AOP-u i frejmvorcima koji ga implementiraju nije potrebno pisati dosadan, repetitivan kod. Taj posao moe obaviti frejmvork. U navedenom primjeru on e obmotati metode odgovarajuim kodom za transakcije.

10

Data access and integration modul omoguava lake povezivanje sa bazom podataka preko JDBC. Takoe omoguava integraciju Spring-a sa alatom/frejmvorkom za objektno-relaciono mapiranje. Web and remoting modul omoguava pravljenje kompletne Web aplikacije koja se zasniva na MVC paternu. Pored toga modul obezbjeuje servise koji olakavaju rad sa udaljenim aplikacijama. Testing modul je za automatsko testiranje. Sadri brojne Mock objekte koji olakavaju unit testiranja. [13] 3.7.2 Apache Tapestry framework Apache Tapestry je frejmvork za izradu web aplikacija. Kreiran je od strane Howard Lewis Ship-a i zasniva se na programskom jeziku Java. On predstavlja pandan JSF tehnologiji. Tapestry je lightweight frejmvork izgraen je na bazi svog IoC kontejnera. On se sastoji iz nekoliko modula koji ine Tapestry Registry. Jedan od tih modula je i modul za aplikaciju koju pravimo pomou Tapestry frejmvorka. Tapestry aplikacija koristi MVC patern. Tapestry aplikacija se sastoji iz stranica koje se opet sastoje od komponenata. U osnovi svega su komponente zato se i kae da je Tapestry komponentno orijentisan frejmvork. Veza izmeu komponenata je labava zahvaljujui Tapestry IoC kontejneru. Programer moe koristiti gotove Tapestry komponente, modifikovane Tapestry komponente a moe ih i sam kreirati. Jedna komponenta moe sadrati nekoliko drugih komponenata. Stranica je razdvojena na templejt i klasu. Templejt je prezentacijski dio (ono to korisnik vidi) a u klasi se nalazi logiki dio tj. ono to je ispod haube. Tempejt kod je slian html kodu pa ga zbog toga vole dizajneri mogu se baviti dizajnom stranice bez uplitanja u programski dio. Struktura Tapestry aplikacije je na slici:

Slika 5. Struktura Tapestry aplikacije Tapestry posjeduje zanimljive mogunosti poput live class reloading-a i sistema koji detaljno prikazuje tj. prijavljuje greke u aplikaciji. Live class reloading omoguava programeru da vidi odmah rezultate izmjena koje je napravio u kodu, bez potrebe da restartuje aplikaciju. Error reporting je veoma koristan jer e, kada doe do greke, markirati dio koda u kojem je dolo do greke i o njoj ispisati razumljivu poruku. [5] 3.7.3 ORM frejmvorci - Hibernate i JPA Savremene aplikacije karakterie objektno-orijentisani dizajn. Kao rezultat toga, podaci koje treba sauvati se nalaze u objektima a baze podataka u kojima se uvaju podaci su relacione. Dakle na jednoj strani imamo objekte i njihove veze tj. objektni model a na drugoj tabele i relacije relacioni model. Ta dva modela se umnogome razlikuju. Zbog toga je potrebno da programer pie kod koji e premostiti jaz izmeu ta dva modela. Problem je to je taj kod obiman i repetitivan. Frejmvorci poput Hibernatea oslobaaju programera ovog posla i to zahvaljujui objektno-relacionom mapiranju ono omoguava preslikavanje objektnog modela u relacioni. Zahvaljujui tome, programer ne mora mnogo da razmilja o relacionom modelu odnosno o konverziji objektnog modela u relacioni.

11

Hibernate je jedan od najpopularnijih frejmvorka za perzistenciju podataka putem objektno-relacionog mapiranja. Hibernate ima HQL (Hibernate Query Language) objektno orijentisani upitni jezik. Iako Hibernate ima podrku za SQL preporuljivo je u aplikaciji koristiti HQL. Za razliku od SQL-a, HQL moe da radi sa perzistentnim objektima. Drugi problem sa SQL-om je to postoji ne postoji samo u jednoj ve u vie varijanti koje se meusobno razlikuju to moe predstavljati problem prilikom prelaska na bazu koja koristi drugaiji SQL. EJB3 tehnologija ima svoj ORM frejmvork koji se zove Java Persistence API JPA. JPA ima svoj Entity Manager koji je neka vrsta posrednika izmeu baze podataka i aplikacije, dok je kod Hibernate-a za taj posao zaduen Session Factory. Interesantno je da Hibernate, od verzije 2 ima podrku za JPA. Hibernate recimo moe biti podeen tako da koristi svoj HQL i JPA EntityManager! [4] 3.7.4 Frejmvorci za automatsko testiranje Frejmvorci za automatsko testiranje su alati koji omoguavaju i olakavaju posao automatskog testiranja. Jedan od najpopularnijih frejmvorka za automatsko testiranje je TestNG. Moe se koristiti u kombinaciji sa bibliotekama Mockito i EasyMock koje olakavaju Unit Testiranje na taj nain to kreiraju lairane (Mock) objekte. TestNG nam omoguava da razvrstamo testove u razliite grupe, naznaimo ta e se izvravati prije a ta poslije testa, omoguava nam da snadbijemo metodu koja se testira sa potrebnim objektima. TestNG se moe upariti sa Selenijum frejmvorkom koji slui za funkcionalno testiranje. Selenium radi tako to pokree web aplikaciju i vri testiranje preko browsera. [15]

3.8 Perzistentni objekti


Zamislimo aplikaciju napisanu u nekom objektno orijentisanom jezikom koja ima mogunost da pamti podatke o svojim korisnicima u bazi podataka. Zamislimo takoe da se to odvija pomou metode save (user) gdje je user objekat koji predstavlja korisnika. Ukoliko e podaci o korisniku biti dostupni nakon to u potpunosti izgasimo program onda je objekat user perzistentan. Ukoliko bismo umjesto baze podataka koristili Javinu memoriju onda objekat ne bi bio perzistentan.

3.9 Transakcije
Transakcija je vrsta radnje koja se obavlja nad bazom podataka i koja zadovoljava ACID (Atomicity, Consistency, Isolation, Durability) uslove. Atomicity (Atomnost). Ukoliko se ne izvri sve ono to je predvieno transakcijom ona se ponitava. Ovaj princip se jo zove sve ili nita. Consistency (Konzistentnost). Ako klijent ima na raunu 100 eura i skine sa rauna 30 eura to mora biti evidentirano u bazi podataka. Dakle, ako je nakon toga klijentu na raunu ostalo vie ili manje 70 eura, onda je konzistentnost naruena. Isolation (Izolacija). Jedna transakcija se odvija nezavisno od druge transakcije. Durability (Trajnost). Promjene koje su se desile kao rezultat transakcije moraju biti trajno zapamene u bazi podataka.

12

4. Studijski primjeri
4.1 Prvi primjer:
Student eli da prijavi kurs koji e da pohaa. Nakon to pristupi odgovarajuoj stranici odabira eljeni kurs. Ukoliko je dolo do greke prilikom prijavljivanja bie prebaen na stranicu na kojoj e biti ispisana poruka da je dolo do greke. Evo kako izgleda logika jedne takve stranice: public class ViewCourses { @Inject @Property private Courses courses; @Property private Course currentCourse; @Inject @Property private Applications applications; @Property @Persist private Application application; Object onActionFromApply(String courseName) { Course courseToApply = courses.retreiveSingleCourse(courseName); try { applications.apply(getUser(),courseToApply); } catch (RedudantAppliacationException e) { return ErrorDuringApplication.class; } return ViewStudentApplications.class; } Dakle ako doe do greke nakon apply metode, student e biti prebaen na stranicu ErrorDuringApplication. Ukoliko je sve u redu bie prebaen na stranicu ViewStudentApplications. Ali pretpostavimo da je dolo do greke i da je student prebaen na stranicu ErrorDuringApplication. Evo kako izgleda kod njenog logikog dijela: public class ErrorDuringApplication { private String message; public String getMessage() { return "Error, you are already applied to this course!"; } } Kao to se da vidjeti stranica je zaduena za kreiranje poruke koja e biti prikazana korisniku. Ali ta ako korisnik eli npr. da se odjavi sa kursa? Onda mu treba prikazati drugu poruku a za to je potrebno napraviti novu stranicu. A ta ako imamo dvadesetak slinih situacija? Onda bi trebalo napraviti dvadesetak klasa. Daleko bolje je sledee rjeenje:

13

public class Info { @Persist private String message; public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } } Klasa ErrorDuringApplication je preimenovana u Info. Izvren je loose coupling jer stranica vie nije odgovorna za kreiranje poruke ve joj se poruka proslijeuje preko setera Setter Injection. Nije potrebno praviti pomenutih dvadesetak klasa od kojih e svaka biti odgovorna za kreiranje sopstvene poruke dovoljna je samo jedna klasa kojoj e biti mogue proslijediti poruku. Ali to nije sve. Drugi problem je: kako proslijediti stranici poruku? Trenutno samo mogue usmjeriti korisnika na Info stranicu, ne i proslijediti toj stranici poruku. Za to je potrebno izvriti odgovarajue promjene u klasi ViewCourses: public class ViewCourses { ... @InjectPage private Info infoPage; Object onActionFromApply(String courseName) { Course courseToApply = courses.retreiveSingleCourse(courseName); try { applications.apply(getUser(),courseToApply); } catch (RedudantAppliacationException e) { infoPage.setMessage(e.getMessage()); return infoPage; } return ViewStudentApplications.class; } Pomou anotacije @InjectPage je ubrizgana stranica Info i na njoj su izvrene promjene proslijeena (setovana) joj je odgovarajua poruka. Tapestry IoC nam je omoguio da na jednoj stranici ubrizgamo drugu stranicu i da pritom izvrimo eljene promjene na toj drugoj stranici. Info page nije samo koristan samo za ovu stranicu. Evo kako je Info page iskorien na drugoj stranici: public class ViewStudentApplications { ... @InjectPage private Info infoPage; ... Object onActionFromCancel(String id) { applications.cancel(id); infoPage.setMessage("You have canceled your application"); return infoPage; } U ovom primjeru je dva puta korien Dependency Injection: runi Setter Injection (setMessage), i ubrizgavanje stranice pomou frejmvorka.

14

4.2 Drugi primjer:


Potrebno je napraviti servis koji: uva podatke o korisniku brie podatke o korisniku vraa podatke o jednom korisniku vraa podatke o svim korisnicima

Interfejs Users: public interface Users { Collection<User> retreiveUsers(); User save(User user); User retreiveSingleUser(String userName); User delete(String userName); } Implementacija eljenog servisa - klasa UsersBean implementira interfejs Users i omoguava uvanje podataka u Javinoj memoriji: public class UsersBean implements Users { private Map<String, User> users; public UsersBean(){ this(new HashMap<String, User>()); } public UsersBean(Map<String, User> users) { this.users = users; } public Collection<User> retreiveUsers() { return users.values(); } public User save(User user) { return users.put(user.getUserName(), user); } public User retreiveSingleUser(String userName) { return users.get(userName); } public User delete(String userName) { return users.remove(userName); } } Za realizaciju primjera bie koriene mogunosti Tapestry IoC-a. U klasi AppModule.java potrebno je dodati sledeu metodu:

15

public class AppModule { . public static Users buildUsers() { return new UsersBean(); } } AppModule.java je konfiguracioni fajl modula Aplikacije koji je jedan od Tapestry modula. Tapestry IoC, za razliku od drugih frejmvorka, ne koristi XML fajlove ili druge za konfigurisanje ve obinu Java klasu koja se kompajlira i iju ispravnost provjerava kompajler prilikom kompajliranja. Metoda buildUsers je veoma jednostavna i slui za instanciranje singleton objekta klase UsersBean preko interfejsa Users. Singleton objekat omoguava da podaci o korisnicima budu sauvani na jednom mjestu pomou jednog i samo jednog objekta. Da nisu sauvani na jednom mjestu onda bi bilo teko npr. povratiti podatake o jednom korisniku. ta ako se kreira i koristi vie objekata a jedan od njih zavri u Garbage Collectoru? U tom sluaju bi korisni podaci bili izgubljeni. Metoda buildUsers je kreirana sa ciljem da omogui programeru da na svim Tapestry stranicama moe da pristupi jedinstvenom objektu klase Users. Ali kako mu on pristupa tom objektu? Pogledajmo kod logikog dijela stranice za Registraciju korisnika: public class Registration { @Property @Persist("flash") private User user; @Inject private Users users; @Persist("flash") @Property private Role role; @Component private Form form; @SetupRender public void createObject() { user = new UserBean(); user.setRole(Role.STUDENT); } void onValidateForm(){ if (userNameExists()) { form.recordError("user name already exists, chose another one"); } } Object onSuccess() { users.save(user); return Index.class; } private boolean userNameExists() { return users.retreiveSingleUser(user.getUserName()) != null;

16

} } Obratimo panju na: @Inject private Users users; I na ono to je napisano u AppModule.java klasi: public static Users buildUsers() { return new UsersBean(); } Ovo je primjer Field Injection-a (ubrizgavanje zavisnosti preko atributa) - u atribut users je pomou Tapestry IoC-a ubrizgan objekat klase UsersBean. Ako u klasi komponente deklariemo varijablu i ako iznad nje postavimo anotaciju @Inject onda e Tapestry IoC u tu varijablu ubrizgati najprikladniji objekat tj. komponentu. Preduslov je da je ta komponenta ve konfigurisana za Tapestry IoC. U ovom sluaju konfiguracija je obavljena pomou buildUsers() metode u AppModule.java klasi. Jedan od naina za konfigurisanje servisa u Tapestry IoC-u je kreiranje statike metode iji e naziv poinjati sa build, a drugi nain je korienjem statike metode bind: public static void bind(ServiceBinder binder) { binder.bind(Users.class, UsersBean.class); } Obratimo panju na ulazni argument metode bind binder je referenca ka objektu koji e biti ubrizgan putem Tapestry IoC-a! U AppModule.java se nalazi konfiguracija samo jednog malog dijela Tapestry IoC-a. Binder je unaprijed konfigurisan u nekoj drugoj klasi Tapestry frejmvorka i stoji programeru na raspolaganju samo je potrebno zatraiti njegovo ubrizgavanje preko bind metode. Zahvaljujui: @Inject private Users users; mogue je pristupiti servisu za korisnike na bilo kojoj stranici. Evo kako izgleda kod logikog dijela stranice EditUser: public class EditUser { ... @Inject private Users users; public void onActivate(String userName) { userEdit = users.retreiveSingleUser(userName); this.userName = userName; } public String onPassivate() { return userName; } public Object onSuccess() { users.save(userEdit); return ViewUsers.class; } I ovdje je u polje users ubrizgan singleton objekat klase UsersBean. Anotacija @Inject omoguava da se na jednostavan nain pristupi istom objektu kojem je pristupljeno i na stranici za registraciju.

17

A ta ukoliko se ne koristi Dependency Injection? Kako bi kod izgledao? Jedno od rjeenja: public class UsersBeanAlternative { private static UsersBeanAlternative usersBeanAlternative; private Map<String, User> users = new HashMap<String, User>(); private UsersBeanAlternative(){ } public static UsersBeanAlternative getInstance(){ if(usersBeanAlternative == null) { usersBeanAlternative = new UsersBeanAlternative(); } return usersBeanAlternative; } public Collection<User> retreiveUsers() { return users.values(); } public User save(User user) { return users.put(user.getUserName(), user); } public User retreiveSingleUser(String userName) { return users.get(userName); } public User delete(String userName) { return users.remove(userName); } } U klasi UsersBeanAlternative je primijenjen singleton patern. Poto se ne koristi Tapestry IoC u AppModule nije potrebno nita dodavati ni mijenjati ali je zato potrebno mijenjati kod Tapestry stranica. Evo kako e sada izgledati kod logike Tapestry stranice: public class Registration { @Property @Persist("flash") private User user; @Persist("flash") @Property private Role role; @Component private Form form; @SetupRender public void createObject() { user = new UserBean(); user.setRole(Role.STUDENT); }

18

void onValidateForm(){ if (userNameExists()) { form.recordError("user name already exists, chose another one"); } } Object onSuccess() { UsersBeanAlternative.getInstance().save(user); return Index.class; } private boolean userNameExists() { String userName = user.getUserName(); boolean exists = UsersBeanAlternative.getInstance().retreiveSingleUser(userName) != null; return exists; } } U metodama OnSuccess i userNameExists je dolo do promjena. Zbog toga je potrebno promijeniti svaku stranicu koja koristi ovaj servis. Ukoliko je takvih stranica dvadeset onda bi trebalo mijenjati kod svakoj od njih. A ta ako je potrebno koristiti servis koji je povezan sa bazom podataka? I u tom sluaju bi sve trebalo mijenjati. Izmjene su neophodne zato to su stranice (u ovom sluaju stranica za registraciju) vrsto vezane (tight coupling) za klasu UsersBeanAlternative. Ukoliko programer eli da koristi neki drugi servis, morao bi prvo da raskine vezu sa UsersBeanAlternative na svakoj stranici i klasi koja koristi taj servis. Te promjene je nekad potrebno izvriti ne samo u logikom dijelu koda nego i u samom templejtu. Uzmimo npr. logiki dio koda stranice za pregled svih korisnika (ViewUsers): public class ViewUsers { ... @Inject @Property private Users users; @Property private User currentUser; void onActionFromDelete(String userName) { users.delete(userName); } } Sledei dio koda templejta je zaduen za prikaz svih korisnika koji se nalazi u ViewUsers.tml fajlu: <table width="70%" > <tr> <td> Username </td> <td> Full Name </td> <td> User Role </td> <td> Edit user </td> <td> Delete user </td> </tr> <tr t:type="loop" t:source="users.retreiveUsers()" t:value="currentUser"> <td> ${currentUser.userName}

19

</td> <td> ${currentUser.fullName} </td> <td> ${currentUser.Role} </td> <td> <a href="#" t:type="PageLink" t:page="EditUser" t:context="currentUser.userName">Edit</a> </td> <td> <t:actionlink t:id="delete" context="currentUser.userName">Delete</t:actionlink> </td> </tr> </table> Ako bi veza izmeu komponenata bila vrsta odnosno ako bismo u ovom sluaju koristili UsersBeanAlternative klasu i njene metode, svaki put kada bismo htjeli da zamijenimo servise morali bismo mijenjati ne samo logiki dio koda nego i templejt. Npr.: <tr t:type="loop" t:source="users.retreiveUsers()" t:value="currentUser"> ukoliko bi, u sluaju tight coupling-a, dolo do zamjene servisa, bilo bi potrebno mijenjati dio koji slijedi poslije t:source metoda users.retreiveUsers(). Za razliku od tight couplinga, loose coupling omoguava da se na lak nain zamijene servisi dovoljno je samo izvriti odreene promjene u konfiguracionom fajlu, a kod stranica (logiki dio i templejt) ostaje nepromijenjen. S porastom kompleksnosti aplikacije, mane tight couplinga jo vie dolaze do izraaja to e i biti pokazano na sledeem primjeru. U narednom primjeru bie prikazan servis koji je poput UsersBeanAlternative ali sa jednom bitnom razlikom podaci se uvaju u pravoj bazi podataka pomou JPA i Hibernate frejmvorka. Za pristup bazi podataka je potreban JPA Entity Manager. Preko njega e se uvati i izvlaiti podaci iz baze podataka. Za instanciranje Entity Managera bie korena sledea klasa: public class EntityManagerProvider { private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("vesko-R"); private static EntityManager em; private EntityManagerProvider(){ } public static EntityManager getEntityManager(){ if (em == null) { return emf.createEntityManager(); } return em; } public static void closeEntityManagerFactory() { if (emf != null) { emf.close(); emf = null;

20

} } } Kao to vidi, prvo je potrebno kreirati EntityManagerFactory na osnovu konfiguracije iz persistence.xml fajla. EntityManagerFactory slui za instanciranje EntityManagera. U klasi EntityManagerProvider postoje dvije metode. Jedna je zaduena za kreiranje EntityManagera a druga je zaduena za zatvaranje EntityManagerFactory. Metode su statike (globalne) da bi bile na raspolaganju svim servisima. Servis UsersServiseHibernateAlternative koristi, izmeu ostalog, metodu getEntityManager: public class UsersServiceHibernateAlternative { private static UsersServiceHibernateAlternative usersServiceHibernateAlternative; private EntityManager em = EntityManagerProvider.getEntityManager(); private UsersServiceHibernateAlternative() { super(); } public static UsersServiceHibernateAlternative getInstance() { if (usersServiceHibernateAlternative == null) usersServiceHibernateAlternative = new UsersServiceHibernateAlternative(); return usersServiceHibernateAlternative; } public Collection<User> retreiveUsers() { Collection<User> allUsers = null; EntityTransaction tx = null; try { tx = em.getTransaction(); tx.begin(); allUsers = em.createQuery("from UserBean").getResultList(); tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } return allUsers; } public User retreiveSingleUser(String userName) { EntityTransaction tx = null; List<User> user = null; try { tx = em.getTransaction(); tx.begin(); Query query = em.createQuery("from UserBean as u where u.userName = :userName"); query.setParameter("userName", userName); user = query.getResultList(); tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } if(!user.isEmpty()) return user.get(0);

21

return null; } public User save(User user) { EntityTransaction tx = null; User userUpdate = null; try { tx = em.getTransaction(); tx.begin(); userUpdate = em.merge(user); tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } return userUpdate; } public User delete(String userName) { User userToDelete = retreiveSingleUser(userName); EntityTransaction tx = null; try { tx = em.getTransaction(); tx.begin(); em.remove(userToDelete); tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } return userToDelete; } } UsersServiceHibernateAlternative je raen po uzoru na UsersBeanServiceAlternative. U pitanju je klasa koja koristi Singleton patern i ne koristi Dependency Injection ali, za razliku od UsersBeanServiceAlternative, komunicira sa bazom podataka preko EntityManagera. Zbog toga se moe posmatrati kao naprednija odnosno kompleksnija varijanta klase UsersBeanServiceAlternative. Ona takoe ima problem koji je imao UsersBeanServiceAlternative servis - tight coupling na stranicama koje koriste servis. Pored toga, poveanje kompleksnosti je iznjedrilo nove probleme koje treba rijeiti. Jedan od problema je to se klasa ne moe testirati u izolaciji. Sledei kod predstavlja pokuaj da se uradi unit test klase UsersServiceHibernateAlternative: public class UsersServiceHibernateAlternativeTest { private UsersServiceHibernateAlternative usersHibernateUnderTest; private User userMock; @BeforeMethod public void setUp(){ usersHibernateUnderTest = UsersServiceHibernateAlternative.getInstance(); userMock = Mockito.mock(User.class); }

22

@Test public void save(){ User savedUser = usersHibernateUnderTest.save(userMock); Assert.assertEquals(savedUser, userMock); } } Klasu nije mogue testirati u izolaciji zato to se unutar nje instancira Entity manager, odnosno, problem je u tome to je ona vrsto vezana za Entity Manager. Pravo unit testiranje bi bilo kada bilo mogue proslijediti lairani (Mock) objekat za Entity Manager. Bez Dependecy Injection-a nije mogue uraditi unit testiranje a bez unit testiranja razvoj savremenih aplikacija je nezamisliv. Drugi problem sa UsersServiceHibernateAlternative je to je naruen tzv. DRY princip Don't Repeat Yourself. Kod za transakciju se ponavlja u svakoj metodi: EntityTransaction tx = null; try { tx = em.getTransaction(); tx.begin(); //do something here tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } Potrebno je instancirati odgovarajuu klasu za transakciju, zapoeti transakciju, zavriti je ili vratiti se na poetno stanje ukoliko je dolo do greke. Ponavljanje ovakvog koda za transakciju (zamislite da imamo oko 100-ak metoda u svim servisima) je dosadan i otupljuju posao, klase postaju ogromne i tee za razumijevanje. Rjeenje navedenih problema lei u upotrebi Spring frejmvorka. Za primjer uzmimo sledeu klasu: public class UsersServiceHibernate implements Users { @PersistenceContext private EntityManager em; @Transactional public Collection<User> retreiveUsers() { return em.createQuery("from UserBean").getResultList(); } @Transactional public User retreiveSingleUser(String userName) { Query query = em.createQuery("from UserBean as u where u.userName = :userName"); query.setParameter("userName", userName); List<User> user = query.getResultList(); if(!user.isEmpty()) return user.get(0); else return null; } @Transactional public User save(User user) { User savedUser = em.merge(user); return savedUser;

23

} @Transactional public User delete(String userName) { User userToDelete = retreiveSingleUser(userName); em.remove(userToDelete); return userToDelete; } } Ova klasa je duplo manja od UsersServiceHibernateAlternative a radi isti posao. Nema vie koda za transakciju u metodama. Isto tako, klasa EntityManagerProvider vie nije potrebna. Da bi UsersServiceHibernate servis radio potreban je Spring framework podeen na sledei nain: <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="src/main/resources/persistence.xml" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="edu.rakovic.elearning.hibernate.UsersServiceHibernate" class="edu.rakovic.elearning.hibernate.UsersServiceHibernate"/> EntityManagerFactory je sada komponenta za koju je odgovoran Spring: <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> property name="persistenceXmlLocation" value="src/main/resources/persistence.xml" /> </bean> U Spring-ovom modulu za objektno-relaciono mapiranje postoji klasa LocalContainerEntityManagerFactoryBean u paketu org.springframework.orm. Bean komponenta sa identifikatorom entityManagerFactory je nastala tako to je u Spring-ovoj klasi LocalContainerEntityManagerFactoryBean pomou Setter Injectiona ubrizgana putanja persistence.xml fajla koji sadri podatke neophodne za kreiranje Entity Manager Factory. Kod za transakcije nije potrebno vie pisati zahvaljujui sledeoj komponenti: <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> Springov modul za objektno-relaciono mapiranje ima svoj Transaction Manager koji je zaduen za transakcije. Da bi bio iskorien, potrebno je povezati prethodno definisanu komponentu koja predstavlja Entity Manager Factory sa Spring-ovom klasom JpaTransactionManager. Povezivanje se vri na sledei nain: <property name="entityManagerFactory" ref="entityManagerFactory" /> U pitanju je Setter Injection koji se realizuje pomou frejmvorka. Na kraju, da bi bilo mogue koristiti Transaction Manager preko anotacija (@Transactional) potrebno je dodati sledee: <tx:annotation-driven transaction-manager="transactionManager" />

24

Sada umjesto koda koji se ponavljao: EntityTransaction tx = null; try { tx = em.getTransaction(); tx.begin(); //do something here tx.commit(); } catch (RuntimeException e) { if ( tx != null && tx.isActive() ) tx.rollback(); e.printStackTrace(); } je dovoljno napisati @Transactional iznad metode. U klasi UsersServiseHibernate se pomou Springa ubrizgava Entity Manager: @PersistenceContext private EntityManager em; Entity Manager se, prije ubrizgavanja (Field Injection), kreira preko Entity Manager Factory koji je podeen u Springu. Meutim, nije samo to dovoljno. Potrebno je, u Spring konfiguraciji, dodati sledee: <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> EntityagerManFactory obilato koristi resurse te ga je potrebno zatvoriti kada nije potreban. Ukoliko je konfigurisan preko Springa, a EntityManager se koristi na taj nain to se ubrizgava u odgovarajue polje pomou @PersistanceContext anotacije, onda e Spring da se pobrine za pravovremeno zatvaranje EntityManagerFactory-ja. U suprotnom bi programer sam morao o tome da brine. Na kraju je potrebno da se u xml fajlu UsersServiceHibernate konfigurie kao komponenta: <bean id="edu.rakovic.elearning.hibernate.UsersServiceHibernate" class="edu.rakovic.elearning.hibernate.UsersServiceHibernate"/> Drugi nain da se ovo uradi je ubacivanje anotacije @Repository u klasu: @Repository public class UsersServiceHibernate implements Users {.} Dakle, Spring od verzije 2.5 podrava konfigurisanje pomou anotacija. Da bi koristili anotacije potrebno je u xml fajl dodati sledee: <context:annotation-config/> <context:component-scan base-package="edu.rakovic.elearning"/> Time se Springu naznaava paket u ijim klasama su koriene anotacije. Prikazanu konfiguraciju Springa, pored UsersServiceHibernate servisa, mogu koristiti i drugi servisi: public class ContentsServiceHibernate implements Contents { @PersistenceContext private EntityManager em; @Transactional public Content save(Content content) { em.persist(content);

25

return content; } @Transactional public Collection<Content> retreive() { List<Content> all = em.createQuery("from ContentBean").getResultList(); return all; } } Zahvaljujui Springu ne treba vie pisati kod za transakcije i Entity manager. Naravno, da bi ovaj servis bilo mogue upotrijebiti potrebno je koristiti @Repository anotaciju ili ga konfigurisati u xml fajlu: <bean id="edu.rakovic.elearning.hibernate.ContentsServiceHibernate" class="edu.rakovic.elearning.hibernate.ContentsServiceHibernate"/> Ali kako koristiti ove servise? Kako ih instancirati? Konfigurisane komponente se izvlae iz Springovog konteksta koji se takoe mora instancirati. Jedan od naina da se to uradi je tako to se prilikom instanciranja konteksta naznaava putanja do xml fajla u kojem je Spring konfigurisan: ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/resources/applicationContext.xml"); Servis se moe instancirati, odnosno izvui iz Springovog konteksta na sledei nain: UsersServiceHibernate users = context.getBean(UsersServiceHibernate.class.getName()); Poto UsersServiceHibernate implementira interfejs Users, onda se moe uraditi sledee: Users users = context.getBean(UsersServiceHibernate.class.getName()); Ko jo implementira interfejs Users? Ranije predstavljena UsersBean klasa koja pamti podatke u Javinoj memoriji! Ona se moe instancirati na sledei nain: Users users = new UsersBean(); Ovo se moe iskoristiti. Sjetimo se buildUsers(): public static Users buildUsers() { return new UsersBean(); } Ona vraa objekat koji se ubrizgava u stranice pomou @Inject metode. Taj objekat je instanciran preko interfejsa Users. To znai da je mogue uraditi sledee: public static Users buildUsers() { return (Users) context.getBean(UsersServiceHibernate.class.getName()); //return new UsersBean(); } Servise su zamijenjeni izmijenom samo jedne linije koda! Nije potrebno mijenjati kod Tapestry stranice iznad polja: @Inject private Users users; je mogue ubrizgati bilo koji objekat koji je instanciran preko interfejsa Users. U ovom sluaju taj objekat je, prije ubrizgavanja pomou @Inject anotacije, izvaen iz Springovog konteksta a zahvaljujui Springu njemu su ubrizgane druge komponente, potrebne za, izmeu ostalog, rad sa bazom podataka.

26

Dakle, ukoliko bi komponente (u ovom sluaju Tapestry stranice) bile vrsto vezane za neki servis onda bi, prilikom zamjene servisa (za neki slian) bilo potrebno prekidati te veze na svakoj stranici. Meutim, ako su komponente labavo povezane i ako se koristi IoC kontejner, sve to treba je izvriti odreene promjene u konfiguraciji IoC kontejnera. To je laki posao zato to je lake izvriti promjene u jedan ili dva konfiguraciona fajla nego mijenjati logiku i templejt recimo dvadesetak Tapestry stranica. Dependency Injection moe pomoi u Unit testiranju klase UsersServiceHibernate. Da bi Unit testiranje moglo da se odradi potrebno je dodati seter metodu za EntityManager. public class UsersServiceHibernate implements Users { @PersistenceContext private EntityManager em; public void setEm(EntityManager em) { this.em = em; } . } Evo kako sada izgleda Unit test UsersServiceHibernate klase: public class UsersServiceHibernateTest { private UsersServiceHibernate usersHibernateUnderTest; private EntityManager entityManagerMock; private User userMock; private Query queryMock; private List<User> usersReturn; @BeforeMethod public void setUp() { createMocksAndReturnList(); usersHibernateUnderTest = new UsersServiceHibernate(); usersHibernateUnderTest.setEm(entityManagerMock); } private void createMocksAndReturnList() { entityManagerMock = Mockito.mock(EntityManager.class); userMock = Mockito.mock(User.class); queryMock = Mockito.mock(Query.class); usersReturn = new LinkedList<User>(); usersReturn.add(userMock); Mockito.when(queryMock.getResultList()).thenReturn(usersReturn); } @Test public void testSave() { Mockito.when(entityManagerMock.merge(userMock)).thenReturn(userMock); User savedUser = usersHibernateUnderTest.save(userMock); assertEquals(savedUser, userMock); } @Test public void retreiveUsers() { when(entityManagerMock.createQuery("from UserBean")).thenReturn(queryMock); when(entityManagerMock.createQuery("from UserBean") .getResultList()).thenReturn(usersReturn); Collection<User> retreivedUsers = usersHibernateUnderTest.retreiveUsers();

27

assertTrue(retreivedUsers.contains(userMock)); } @Test public void testRetreiveSingleUser() { Mockito.when(entityManagerMock. createQuery("from UserBean as u where u.userName = :userName")).thenReturn(queryMock); assertEquals(usersHibernateUnderTest.retreiveSingleUser("test"), userMock); } @Test public void testDeleteUser() { Mockito.when(entityManagerMock. createQuery("from UserBean as u where u.userName = :userName")).thenReturn(queryMock); assertEquals(usersHibernateUnderTest.delete("test"), userMock); } } Sada je mogue objektu klase UsersServiceHibernate proslijediti lairani (Mock) objekat za Entity Manager: @BeforeMethod public void setUp() { createMocksAndReturnList(); usersHibernateUnderTest = new UsersServiceHibernate(); usersHibernateUnderTest.setEm(entityManagerMock); } Metoda createMocksAndReturnList() je zaduena za kreiranje svih Mock objekata koji su potrebni za test. UsersHibernateUnderTest se instancira pomou new operatora onda mu se, preko setera, proslijeuje Mock EntityManager objekat. Rije je o runom Setter Injecton-u koji ne bi bio mogu da u klasi UsersServiceHibernate nije napisana seter metoda za EntityManager odnosno atribut em.

4.3 Trei primjer:


Jedan od naina za sprijeavanje neovlaenog pristupa Tapestry stranicama je korienje Session State objekta. Dio logike Login stranice: @SessionState @Property private String userNameAuth; @Property private String userName; void onValidateForm() { if (authenticate(userName, password)) { userNameAuth = userName; } else { loginForm.recordError("We couldn't authenticate you. Try again or register."); } } Session State objekat je String userNameAuth. Ukoliko je logovanje uspjeno on e sadrati korisniko ime a u suprotnom njegova vrijednost e biti null. Session state objektu moemo pristupiti na svakoj stranici korienjem @SessionState anotacije. Primjer stranica Index, odnosno njen logiki dio:

28

public class Index { @SessionState private String userName; private boolean userNameExists; Object onActivate() { if (!userNameExists) { return Login.class; } else return null; } } Ovaj kod provjerava da li userName ima vrijednost. Ukoliko nema to znai da se korisnik nije ulogovao i da ga treba preusmjeriti na stranicu za logovanje. Problem je to ovaj kod potrebno iskucati na svakoj stranici kojoj je potrebno pristupiti kao ulogovan korisnik. Na taj nain se kri DRY Don't Repeat Yourself princip. Ukoliko je potrebno provjeriti da li je npr. ulogovani korisnik administrator, onda e repetitivan kod biti jo dui. Primjer je Main stranica: public class Main { @SessionState private String userName; private boolean userNameExists; Object onActivate() { if (!userNameExists) { return Login.class; } else return null; @Inject private Users users; private User user; public User getUser() { return users.retreiveSingleUser(userName); } public boolean isUserAdmin(){ if(authenticator.getUser().getRole() == Role.ADMINISTRATOR) return true; return false; } public boolean isUserTeacher(){ if(authenticator.getUser().getRole() == Role.TEACHER) return true; return false; } public boolean isUserStudent(){ if(authenticator.getUser().getRole() == Role.STUDENT) return true; return false; } }

29

Pored koda za provjeru da li je korisnik ulogovan dodato je jo par metoda. Metoda getUser() je potrebna da bi Main stranica mogla da prikae puno ime ulogovanog korisnika. Metoda getUser() koristi Users servis pa ga je potrebno ubrizgati. Metode isUserAdmin(), isUserTeacher(), isUserStudent() provjeravaju tip (ulogu) ulogovanog korisnika. One se mogu iskoristiti na razne naine. Npr. u metodi koja se prva izvrava na stranici onActivate() - se moe dodati kod koji provjerava da li je korisnik administrator i preusmjerava ga na odgovarajuu stranicu ukoliko nije administrator. Metode se mogu iskoristi i u templejtu. Dio Main.tml koda: <t:if t:test="userTeacher"> <h3>Welcome teacher!</h3> <p> [<t:pagelink t:page="AddCourse">Add course</t:pagelink>]</p> <p> [<t:pagelink t:page="ViewTeachingCourses">View Teaching Courses</t:pagelink>]</p> </t:if> Na stranici Main su linkovi Add Course i View Teaching Courses dostupni samo korisniku koji posjeduje nalog tipa Teacher, to provjerava metoda isUserTeacher(). Pomou Dependecy Injection-a i Tapestry komponenata je mogue eliminisati repetitivan kod. Prvo je potrebno kreirati Tapestry komponentu koja e sluiti za provjeru identiteta korisnika. Tapestry komponenta se nalazi u components paketu, ima svoju logiku i templejt ba kao i Tapestry stranice. Logiki dio komponente koja e provjeravati da li je korisnik ulogovan: public class Authenticator { @Inject private Response response; @SessionState private String userNameAuth; private boolean userNameAuthExists; public boolean isUserLoggedIn(){ return userNameAuthExists; } public String authenticate() throws IOException { if (!isUserLoggedIn()) { response.sendRedirect("login"); } return ""; } } Tempejt komponente: <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"> ${authenticate()} </html> Metoda authenticate(), koja se poziva u templejtu, provjerava da li je korisnik ulogovan i ukoliko nije alje ga na stranicu za logovanje. Usmjeravanje na stranicu za logovanje se vri pomou servisa Response odnosno njene metode sendRedirect. Servis Response se ubrizgava u Tapestry komponentu na sledei nain: @Inject private Response response;

30

U HTTP protokolu server na osnovu zahtjeva (Request) generie odgovor (Response). Pomou Tapestry frejmvorka odgovor se moe poslati korienjem servisa Response. Potrebno ga je ubrizgati na odgovarajuu stranicu i iskoristiti neke od njegovih metoda. Komponenta Authenticator se koristi tako to se templejtu Tapestry stranice doda sledee: <t:authenticator/> Vie nema potrebe na svakoj stranici pisati kod za provjeru da li je korisnik ulogovan. Authenticator, Tapestry komponenta koja se koristi, automatski poziva metodu authenticate() koja provjerava da li je korisnik ulogovan. Pomou Tapestry komponente Authenticator je mogue provjeriti da li je korisnik ulogovan ali ne i tip korisnika. Zbog toga je potrebno dopuniti logiki dio koda komponente: public class Authenticator { @Inject private ComponentResources resources; @Inject private Response response; @Inject private Users users; @SessionState private String userNameAuth; private boolean userNameAuthExists; public User getUser() { return users.retreiveSingleUser(userNameAuth); } public boolean isUserLoggedIn(){ return userNameAuthExists; } public String authenticate() throws IOException { response.disableCompression(); if (!isUserLoggedIn()) { response.sendRedirect("login"); } if (isPageName("ViewUsers") && !isUserAdmin()) { response.sendError(401, "You are not allowed to view that page!"); } if (isPageName("ViewStudentApplications") && !isUserStudent()) { response.sendRedirect("main"); } if (isPageName("AddCourse") && !isUserTeacher()) { response.sendError(401, "You are not allowed to view that page!"); } return ""; } private boolean isPageName(String pageName) { return resources.getPageName().equalsIgnoreCase(pageName); }

31

public boolean isUserAdmin() { return getUser().getRole() == Role.ADMINISTRATOR; } public boolean isUserTeacher() { return getUser().getRole() == Role.TEACHER; } public boolean isUserStudent() { return getUser().getRole() == Role.STUDENT; } } Metode isUserAdmin(), isUserTeacher() i isUserStudent() koriste metodu getUser() koja vraa objekat korisnika koji je ulogovan. Metoda getUser() se oslanja na servis za korisnike kao i korisniko ime koje se uva kao Session State objekat: @Inject private Users users; @SessionState private String userNameAuth; public User getUser() { return users.retreiveSingleUser(userNameAuth); } Sada je na Main stranici, kao i na svim ostalim stranicama, mogue koristiti metode isUserAdmin(), isUserTeacher() i isUserStudent(). Na logikom dijelu stranice Main sada ima manje koda: @InjectComponent @Property private Authenticator authenticator; Komponenta Authenticator je ubrizgana da bi se koristile njene metode. Njih je mogue koristiti u logikom dijelu koda i u templejtu. Da komponenta nije podeena tako da automatski poziva authenticate() metodu onda bi bilo potrebno pozvati je u logikom dijelu koda: void onActivate() { authenticator.authenticate(); } Pored anotacije @InjectComponent je dodata i anotacija @Property (alternativa njoj su seter i geter metode). Bez nje ne bi bilo mogue u templejtu pristupiti metodama komponente Authenticator. Dio koda templejta Main stranice: <t:authenticator/> <p> The currently logged user is: <a href="#" t:type="PageLink" t:context="authenticator.user.userName">${authenticator.user.fullname}</a>.</p> t:page="UserProfile"

<t:if t:test="authenticator.userTeacher"> <h3>Welcome teacher!</h3> <p> [<t:pagelink t:page="AddCourse">Add course</t:pagelink>]</p> <p> [<t:pagelink t:page="ViewTeachingCourses">View Teaching Courses</t:pagelink>]</p> </t:if> Ovaj dio :<t:if t:test="authenticator.userTeacher"> koristi isUserTeacher() metodu preko geter metode authenticator atributa, koja je generisana pomou @Property anotacije.

32

Komponentu Authenticator ne bi bilo mogue ubrizgati u logiki dio putem @InjectComponent anotacije da u templejtu nije dodato sledee: <t:authenticator/> Main stranica sadri link ka profilu korisnika koji je ulogovan. Profil se prikazuje u zavisnosti od konteksta linka a sam kontekst, preko authenticator getera, koristi getUser metodu i getUserName metodu: t:type="PageLink" t:page="UserProfile" t:context="authenticator.user.userName" U klasi Authenticator se takoe nalazi sledee: @Inject private ComponentResources resources; private boolean isPageName(String pageName) { return resources.getPageName().equalsIgnoreCase(pageName); } Tapestry ima komponentu ComponentResources preko koje je, putem Dependecy Injection-a, mogue pristupiti resursima bilo koje komponente koja se koristi. Izmeu ostalog, mogue je doi i do imena stranice koja koristi komponentu (Authenticator) putem metode getPageName(). Metoda isPageName provjerava da li se string koji je unijet preko njenog ulaznog argumenta poklapa sa imenom stranice na kojoj se nalazi komponenta. Metoda authenticate(), koja je dopunjena, koristi nove metode: public String authenticate() throws IOException { if (isPageName("ViewUsers") && !isUserAdmin()) { response.sendError(401, "You are not allowed to view that page!"); } if (isPageName("ViewStudentApplications") && !isUserStudent()) { response.sendRedirect("main"); } if (isPageName("AddCourse") && !isUserTeacher()) { response.sendError(401, "You are not allowed to view that page!"); } return ""; } Prvi uslov provjerava da li je korisnik, koji pokuava pristupiti ViewUsers stranici, administrator. Ukoliko nije bie preusmjeren na stranicu koja e obavijestiti korisnika da je dolo do greke i prikazati poruku "You are not allowed to view that page!". Tu stranicu generie sam frejmvork. U drugom uslovu je koriena metoda sendRedirect() pa e korisnik koji nema ovlaenje za pristup stranici biti preusmjeren na stranicu Main. Ukoliko se koristi Session State objekat, preporuljivo je konfigurisati ga u AppModulu. public void contributeApplicationStateManager (MappedConfiguration<Class, ApplicationStateContribution> configuration) { ApplicationStateCreator<String> creator = new ApplicationStateCreator<String>() { public String create() { return new String(); } }; configuration.add(String.class, new ApplicationStateContribution("session", creator)); }

33

Tapestry frejmvork sadri Mapiranu konfiguraciju na koju je mogue uticati putem Dependency Injectiona. Metoda contributeApplicationStateManager preko argumenta prima Mapiranu konfiguraciju i doprinosi joj preko add metode. U primjeru je Session State (nekadanje ime je Application State) podeen za objekte tipa String. Na ovaj nain se izbjegavaju greke koje se mogu javiti ukoliko je Session State objekat tipa String. Mapirana konfiguracija se ubrizgava metodi contributeApplicationStateManager preko Tapestry IoC kontejnera. Mapirana konfiguracija se moe koristiti na samo u AppModulu nego i u drugim modulima sve to treba je zatraiti njeno ubrizgavanje. Zahvaljujui tome, velike sisteme je mogue razbiti u male dijelove koji su laki za razumijevanje i odravanje.

4.4 etvrti primjer:


est problem kod Web aplikacija je to se Hibernate Sesija ili Entity Manager zatvore prije nego to je potrebno. Zbog toga dolazi do LazyInitializationException. Ova greka se javlja zbog pokuaja da se izvri upit nad bazom podataka nakon to je konekcija je sa bazom zatvorena. Hibernate nije svjestan onoga to se deava na View nivou i esto zatvori Hibernate Sesiju ili Entity Manager prije nego to korisnik zavri posao preko korisnikog interfejsa. Rjeenje je da se vrijeme njihove aktivnosti produi na taj nain to e se HTTP zahtjev, zajedno sa odgovorom na zahtjev, uiniti transakcionim. Onda e se Entity Manager ili Hibernate Sesija zatvoriti tek nakon zavretka transakcije. Ovo rjeenje je poznato i kao Open Session In View patern. Njegova implementacija u Tapestry aplikaciji: public class OpenSessionInViewFilter implements RequestFilter { @Transactional public boolean service(Request request, Response response, RequestHandler requestHandler) throws IOException { return requestHandler.service(request, response); } } Request filter ve postoji u Tapestry frejmvorku i samo ga treba implementirati. Request i Response e se odvijati u okviru transakcije za koju e se brinuti Spring-ov Transaction Manager. On je ve konfigurisan u drugom primjeru pa je u Springov konfiguracioni fajl jo samo potrebno dodati sledee: <bean id="OpenSessionInViewFilter" class="edu.rakovic.elearning.filter.OpenSessionInViewFilter"> </bean> Komponenta OpenSessionInViewFilter je konfigurisana i bie instancirana preko Springa zbog Transaction Managera. Tapestry frejmvork je ustvari jedan veliki filter kroz koji prolazi svaki zahtjev (Request) tako da se dobija filtriran odgovor (Response). Zapravo Request se obrauje pomou brojnih filtera koje Tapestry posjeduje. Tapestry IoC omoguava programeru da ubaci svoje filtere. OpenSessionInViewFilter moe biti jedan od tih filtera. Potrebno je implementirati interfejs RequestFilter i preko contribute metode u AppModulu konfigurisati odnosno ubaciti filter: public void contributeRequestHandler (OrderedConfiguration<RequestFilter> filter) { RequestFilter openSessionInViewFilter = (RequestFilter) context.getBean("OpenSessionInViewFilter"); filter.add("osvf",openSessionInViewFilter,"before:Ajax"); } OrderedConfiguration<RequestFilter> e biti ubrizgan putem Dependecy Injectiona. Nakon toga se OpenSessionInViewFilter izvlai iz Springovog konteksta i preko metode add dodaje u OrderedConfiguration kao jo jedan filter. Filteru je dodat identifikator osvf kao i parametar before:ajax. Naime, filter e biti ubaen u grupu filtera koji se izvravaju odreenim redosledom to najbolje ilustruje sledea slika:

34

Slika 6. Tapestry - procesiranje zahtjeva Na desnoj strani se nalazi Ajax filter (filteri su oznaeni utom bojom). Parametrom before:ajax je naznaeno da se dodati OpenSessionInViewFilter izvrava prije Ajax filtera. Ureena konfiguracija (OrderedConfiguration<RequestFilter>) je koriena ba zato to se filteri izvravaju odreenim redosledom. Na taj redosled je mogue uticati to je i pokazano na primjeru. Konfiguracija OpenSessionInViewFiltera u AppModulu moe biti realizovana i na sledei nain: public static RequestFilter buildOpenSessionInViewFilter() { return (RequestFilter) context.getBean("OpenSessionInViewFilter"); } public void contributeRequestHandler (OrderedConfiguration<RequestFilter> filter, RequestFilter openSessionInView) { filter.add("osvf",openSessionInView,"before:Ajax"); }

35

OpenSessionInViewFilter je konfigurisan kao obian servis a metoda contributeRequestHandler je promijenjena tako da joj se neophodan filter proslijeuje kao ulazni parametar. Tapestry IoC e sam nai OpenSessionInViewFilter (zato je i konfigurisan kao servis) i ubrizgati ga. Servis se naknadno moe zamijeniti drugim to je jedna od prednosti Dependency Injectiona. Ukoliko bi u AppModule bio konfigurisan jo jedan slian filter onda bi pomou anotacije @InjectService bilo potrebno naglasiti koji e filter biti ubrizgan: public void contributeRequestHandler (OrderedConfiguration<RequestFilter> filter) { @InjectService("OpenSessionInViewFilter") RequestFilter openSessionInView) { filter.add("osvf",openSessionInView,"before:Ajax"); } A evo primjera implementacije OpenSessionInViewFiltera iz zvanine Hibernate dokumentacije [16]: public class HibernateSessionConversationFilter implements Filter { private static Log log = LogFactory.getLog(HibernateSessionConversationFilter.class); private SessionFactory sf; public static final String HIBERNATE_SESSION_KEY = "hibernateSession"; public static final String END_OF_CONVERSATION_FLAG = "endOfConversation"; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { org.hibernate.classic.Session currentSession; // Try to get a Hibernate Session from the HttpSession HttpSession httpSession = ((HttpServletRequest) request).getSession(); Session disconnectedSession = (Session) httpSession.getAttribute(HIBERNATE_SESSION_KEY); try { // Start a new conversation or in the middle? if (disconnectedSession == null) { log.debug(">>> New conversation"); currentSession = sf.openSession(); currentSession.setFlushMode(FlushMode.NEVER); } else { log.debug("< Continuing conversation"); currentSession = (org.hibernate.classic.Session) disconnectedSession; } log.debug("Binding the current Session"); ManagedSessionContext.bind(currentSession); log.debug("Starting a database transaction"); currentSession.beginTransaction(); log.debug("Processing the event"); chain.doFilter(request, response); log.debug("Unbinding Session after processing"); currentSession = ManagedSessionContext.unbind(sf);

36

// End or continue the long-running conversation? if (request.getAttribute(END_OF_CONVERSATION_FLAG) != null || request.getParameter(END_OF_CONVERSATION_FLAG) != null) { log.debug("Flushing Session"); currentSession.flush(); log.debug("Committing the database transaction"); currentSession.getTransaction().commit(); log.debug("Closing the Session"); currentSession.close(); log.debug("Cleaning Session from HttpSession"); httpSession.setAttribute(HIBERNATE_SESSION_KEY, null); log.debug("<<< End of conversation"); } else { log.debug("Committing database transaction"); currentSession.getTransaction().commit(); log.debug("Storing Session in the HttpSession"); httpSession.setAttribute(HIBERNATE_SESSION_KEY, currentSession); log.debug("> Returning to user in conversation"); } } catch (StaleObjectStateException staleEx) { log.error("This interceptor does not implement optimistic concurrency control!"); log.error("Your application will not work until you add compensation actions!"); // Rollback, close everything, possibly compensate for any permanent changes // during the conversation, and finally restart business conversation. Maybe // give the user of the application a chance to merge some of his work with // fresh data... what you do here depends on your applications design. throw staleEx; } catch (Throwable ex) { // Rollback only try { if (sf.getCurrentSession().getTransaction().isActive()) { log.debug("Trying to rollback database transaction after exception"); sf.getCurrentSession().getTransaction().rollback(); } } catch (Throwable rbEx) { log.error("Could not rollback transaction after exception!", rbEx); } finally { log.error("Cleanup after exception!"); // Cleanup log.debug("Unbinding Session after exception"); currentSession = ManagedSessionContext.unbind(sf); log.debug("Closing Session after exception"); currentSession.close(); log.debug("Removing Session from HttpSession"); httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);

37

} // Let others handle it... maybe another interceptor for exceptions? throw new ServletException(ex); } } public void init(FilterConfig filterConfig) throws ServletException { log.debug("Initializing filter..."); log.debug("Obtaining SessionFactory from static HibernateUtil singleton"); sf = HibernateUtil.getSessionFactory(); } public void destroy() {} } U primjeru se koristi Hibernate Session koji je ekvivalent JPA Entity Manager-u. U primjeru se ne koriste Spring i Tapestry frejmvorci. Kod : chain.doFilter(request, response); je pandan sledeem: requestHandler.service(request, response); ali je razlika ostatku koda i razumljivosti istog, velika. Klasa HibernateSessionConversationFilter je ogromna. Njen kod je teko razumjeti to se pokualo nadoknaditi komentarima i logovima. Komentari su esto indikator da je kod lo odnosno nerazumljiv, pa se piu da bi pomogli u razumijevanju koda[17]. Veliina koda iz HibernateSessionConversationFilter bi se znaajno smanjila ukoliko bi se koristio Spring-ov Transaction Manager a Springovi izuzeci bi uinili veinu logova i komentara izlinim. U prvom primjeru Spring u sprezi sa Tapestry-jem ini veinu koda upotrijebljenog u drugom primjeru, izlinim! To dobar primjer kako frejmvorci vre inverziju odgovornosti (Inversion of Control) rade odreeni posao umjesto programera i na taj nain mu olakavaju posao. Dependency Injection je je tu samo jedna, ali veoma vana karika.

4.5 Peti primjer:


Contribute metoda u AppModulu moe biti upotrijebljena za konfiguraciju raznih dijelova Tapestry frejmvorka. Npr mogu se ubaciti novi validatori: public static void contributeValidatorMacro (MappedConfiguration<String, String> configuration) { configuration.add("username", "required, minlength=3, maxlength=13"); configuration.add("pass", "required, minlength=4, maxlength=14"); } Drugi nain: @Contribute(ValidatorMacro.class) public static void combineValidators(MappedConfiguration<String, String> configuration) { configuration.add("username", "required, minlength=3, maxlength=13"); configuration.add("pass", "required, minlength=4, maxlength=14"); }

38

U oba sluaja se dodaju novi parametri konfiguraciji servisa ValidationMacro. Potrebno je imati contribute metodu (ili anotaciju) i naznaiti kojem servisu treba dodati nove parametre putem add metode. Validacija se moe koristiti u templejtu: <input t:id="username" t:type="textfield" t:value="user.userName" t:label="Username:" t:validate="username"/> <input t:id="password" t:type="passwordfield" t:value="user.password" t:validate="pass"/> Pored t:validate je potrebno dodati identifikator iz mapirane konfiguracije (pass i username). Drugi nain je da se u logiki dio koda ubace anotacije: @Validate(username) private String userName; @Validate(pass) private String password; U aplikaciji je koriena verzija za templejt. Ukoliko validacija ne bi bila konfigurisana u Tapestry IoC kod u temlejtu bi ovako izgledao: <input t:id="username" t:type="textfield" t:value="user.userName" t:label="Username:" t:validate=" required, minlength=3, maxlength=13"/> <input t:id="password" t:type="passwordfield" t:value="user.password" t:validate="required, minlength=4, maxlength=14"/> Novi nain validacije (konfigurisan putem Tapestry IoC-a) je bolji, jer, pored toga to smanjuje koliinu koda u temlejtu, mogue ga je upotrijebiti vie od jedanput. Tapestry IoC se razlikuje od ostalih IoC kontejnera po tome to je njegovu konfiguraciju mogue mijenjati preko contribute metode. Konfiguracija koja se putem Dependency Injectiona ubrizgava u contribute metodu moe biti: Neureena kolekcija. Primjer: public void contributeHibernateEntityPackageManager(Configuration<String> configuration) { configuration.add("mypackage"); } Neureena kolekcija se koristi kada nije bitan redosled konfiguracije. Ureena lista, odnosno ureena konfiguracija: public void contributeRequestHandler (OrderedConfiguration<RequestFilter> filter) {} Ureena konfiguracija je koriena u primjeru za OpenSessionInView filter kada je bilo potrebno ubaciti filter na odgovarajue mjesto da bi se izvravao prije drugih filtera. Mapirana konfiguracija. Ona se koristi u primjeru za validaciju. Koristi je i metoda koju ima veina Tapestry aplikacija: public static void contributeApplicationDefaults( MappedConfiguration<String, String> configuration) { configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en"); configuration.add(SymbolConstants.PRODUCTION_MODE, "false"); }

39

Razlika izmeu Mapirane konfiguracije i konfiguracije koja koristi ureenu listu je ista kao i razlika izmeu mape i liste za razliku od liste, mapa ima jedinstvene kljueve za sve objekte koje sadri. Metoda contributeAplicationDefaults omoguava podeavanje podrazumijevanih vrijednosti Tapestry aplikacije. Tako npr. aplikacija koja ima metodu iz primjera e koristiti engleski jezik i detaljno prijavljivati greke zato to je Production Mode iskljuen.

4.6 esti primjer:


Servisi koje koristi Tapestry aplikacija se mogu pregledati tako to se u browseru doda putanji /core/servicestatus:

Slika 7. Prikaz statusa Tapestry servisa Stranica prikazuje servise koji se nalaze u Registry-ju kao i njihov status. Servisi mogu imati sledei status: Builtin ugraeni servis. Defined servis koji je definisan ali jo nije pozvan. Takav status ima Users servis prilikom pokretanja aplikacije. U pitanju je primjer Late Binding-a kojeg omoguava Dependency Injection. Servis se uitava samo onda kada je to potrebno. Na taj nain se manje optereuje server i poboljavaju se performanse . Virtual servis je uitan ali nijedna njegova metoda nije potrebna. Kada se samo pristupi Login stranici servis Users e dobiti status Virtual zato to se u atribut login stranice ubrizgava servis Users. Real servis je uitan iskorien za neku radnju. Nakon uspjenog logovanja korisnika servis Users e dobiti status real zato to je prilikom logovanja koriena njegova metoda retreiveSingleUser. Late Binding se moe izbjei dodavanjem anotacije @EagerLoad iznad metode koja je zaduena za instanciranje servisa tada e on dobiti status Real automatski prilikom pokretanja aplikacije. Ovu mogunost treba obazrivo koristiti jer moe doi do pada performansi aplikacije.

40

5. Zakljuak
U ovom radu prikazan je arhitekturalni patern Dependency Injection upotrebom Spring i Tapestry IoC frejmvorka koji ga implementiraju. Pokazano je da: Dependency Injection se moe obaviti runo ili automatski preko frejmvorka koji se zovu IoC kontejneri. Dependency Injection se javlja u nekoliko oblika: Constructor Injection, Setter Injection, Field Injection. Ako se komponente se ubrizgavaju preko konstruktora klase onda je rije o Constructor Injection-u. Field Injection oznaava ubrizgavanje komponente direktno u polje odnosno atribut klase. Kada se komponente ubrizgavaju preko seter metode onda je rije o Setter Injectionu. Takoe sama metoda ne mora biti seter da bi mogla da primi komponentu kao ulazni parametar. Dependency Injection, odnosno labavo povezivanje koje DI omoguava, poveava fleksibilnost aplikacije na taj nacin sto omoguava laku zamjenu servisa. Sve to treba je izvriti odreene promjene u konfiguraciji IoC kontejnera. Zahvaljujui Dependency Injectionu izbjegnuto je pisanje pisanje klasa koje rade slian posao. Na stranici za prijavljivanje greaka je dovoljno ostaviti mogunost da se proslijedi odgovarajua poruka putem Dependency Injectiona. Zahvaljujui DI izbjegnuto je pisanje repetitivnog koda koji ini klasu obimnijom i nerazumljivijom. Springov modul za Aspektno orijentisano programiranje u sprezi sa modulom zaduenim za Dependency Injection omoguava ubrizgavanje koda koji se ponavlja u svakoj metodi. Tapestry komponente se ubrizgati u mogu koristiti vie puta zahvaljujui Dependency Injectionu. Na taj nain se takoe eliminie kod koji se ponavlja. DI ini lakim unit testiranje, bez kojeg je razvoj savremenih aplikacija nezamisliv. Dovoljno je proslijediti Mock (lairani) objekat i testiranje moe da pone. Proslijeeni Mock objekat mijenja pravi a zamjenu je mogue izvriti zahvaljujuu Dependency Injectionu. Dependency Injection je spona koja povezuje komponente od kojih se aplikacija sastoji - Spring ak ima ORM module koji u sprezi sa Dependency Injectionom omoguavaju integraciju sa frejmvorcima za objektno-relaciono mapiranje (Hibernate i Java Persistence API), odnosno povezivanje sa komponentama drugih frejmvorka. Dependency Injection frejmvork odnosno IoC kontejner je poput vedskog stola a Dependecy Injection je poput noa kojim se programer posluuje na taj nain to trai ubrizgavanje odreenih komponenata kada su mu potrebne. Dependency Injection ini sloeni frejmvork pristupanim i lakim za konfiguraciju i nadogradnju. Mogue je, na relativno lak nain, uticati na sloen frejmvork, poput Tapestry-ja i to veoma lako. Za to nije potrebno naslijediti klase ili implementirati odgovarajue interfejse - dovoljno je samo ubrizgati novu konfiguraciju u odgovarajui servis. Konfiguracija Tapestry IoC se vri u samom programskom jeziku dok se Spring frejmvork konfigurie preko xml fajla ili anotacija. Depencency Injection omoguava nadogradnju aplikacije na nain na koji to nije ekplicitno predvieno i na taj nain ini sistem otvorenom a ne zatvorenom cjelinom. Performanse sistema se poveavaju ukoliko se koristi Dependency Injection. To je zbog toga to DI omoguava Late Binding servisi e biti uitani samo ukoliko budu potrebni.

Zakljuci vode ka tome da arhitekturalni patern Dependency Injection omoguava laku nadogradnju, razumijevanje i odravanje sistema na ta softverski inenjeri potroe najvie vremena i enegrije.

41

6. Reference
[1] Martin Fowler, Inversion of Control Containers and the Dependency Injection pattern , http://martinfowler.com/articles/injection.html, August 2011 [2] Jrgen Haas, "Modular programming, http://linux.about.com/cs/linux101/g/modularprogramm.htm, August 2011 [3] Brad Appleton, Patterns and Software: Essential Concepts and Terminology, http://www.cmcrossroads.com/bradapp/docs/patterns-intro.html, August 2011 [4] Buschmann, F. et al, Pattern-Oriented Software Architecture, Volume 1, Wiley, 1996. [5] Igor Drobizako, Tapestry 5 in Action, Early Access Edition, Chapter 1, http://manning.com/drobiazko/, Manning, August 2011. [6] Wikipedia Programming against software interfaces, http://en.wikipedia.org/wiki/Interface_%28computing %29#Programming_against_software_interfaces, August 2011. [7] Lasse Koskela Test Driven Development, Manning, 2007. [8] Wikipedia Unit Testing, http://en.wikipedia.org/wiki/Unit_testing, August 2011. [9] Wikipedia Integration Testing, http://en.wikipedia.org/wiki/Integration_testing, August 2011. [10] Sue Hildreth The Basics of Testing, http://www.informit.com/articles/article.aspx?p=333473&rll=1, August 2011 [11] Wikipedia, Software framework, http://en.wikipedia.org/wiki/Software_framework, August 2011. [12] Chris Richarson, POJOs in Action, Manning, 2006. [13] Craig Walls, Spring in Action, 3rd edition, Manning, 2011. [14] Christian Bauer, Gavin King, Java Persistence with Hibernate, Manning, 2006. [15] Cdric Beust, Hani Suleiman, Next Generation Java Testing , Pearson Education Inc., 2007. [16] Official Hibernate documentation, Open Session In View Filter, http://community.jboss.org/wiki/OpenSessionInView September 2011. [17] Martin Fowler et al, Refactoring: Improving the Design of Existing Code , Addison-Wesley, 2002.

42

7. Popis slika
Slika 1. Poreenje sistema sklopljenih od velikih i malih dijelova................................................................................1 Slika 2. Izdanja Java platforme i njihov meusoban odnos.............................................................................5 Slika 3. Model-View-Controller....................................................................................7 Slika 4. Spring IoC kontejner.....................................................................................10 Slika 5. Struktura Tapestry aplikacije....................................................................................11 Slika 6. Tapestry - procesiranje zahtjeva..............................................................................35 Slika 7. Prikaz statusa Tapestry servisa................................................................................40

43

You might also like