POLITECHNIKA GDAŃSKA WYDZIAŁ ELEKTRONIKI TELEKOMUNIKACJI I INFORMATYKI Katedra Architektury Systemów Komputerowych Jarosław Kuchta Instrukcja do laboratorium z przedmiotu Projektowanie Aplikacji Internetowych "Biznesowe" wzorce projektowe Gdańsk 2013/14 1
Wprowadzenie W ćwiczeniu studenci mają za zadanie napisanie aplikacji internetowej w technologii Web Service lub WCF wykorzystującej jednocześnie trzy wzorce projektowe: Transfer Object Assembler Composite Entity Business Delegate 1. Transfer Object Assembler Konieczność pobierania danych z wielu obiektów biznesowych przez warstwę aplikacji skutkuje ścisłym związkiem między warstwą aplikacji a rozproszonym modelem obiektowym. Zmiana modelu obiektowego powoduje konieczność zmiany warstwy aplikacji. Liczne operacje pobrania danych z komponentów rozproszonych powodują zmniejszenie wydajności sieci (wywołania zdalne z narzutem sieciowym). Zarządzanie komponentami biznesowymi przez warstwę aplikacji wymusza włączenie logiki biznesowej do warstwy aplikacji (zastosowanie grubego klienta) Jeśli warstwa biznesowa ma klientów różnych typów, to przeniesienie zmian modelu obiektowego do tych wielu różnych klientów jest dodatkowo utrudnione, jako że ten sam kod biznesowy może występować po stronie klienta w wielu różnych miejscach. Rozwiązaniem tych problemów może być przekazywanie danych wielu różnych komponentów biznesowych w jednym, zbiorczym obiekcie transferowym. Obiekt ten jest tworzony przez połączenie wielu obiektów transferowych z różnych komponentów biznesowych w jeden. Serwer tworzy taki zbiorczy obiekt transferowy w locie, tworząc jednocześnie biznesowy model obiektowy. Jeśli ten model jest złożony, to zbiorczy obiekt transferowy nie powinien być modyfikowany. W tym wzorcu: 1: To asembler obiektów transferowych (Transfer Object Assembly) jest po stronie serwera tym komponentem, który obsługuje żądania klienta co do pobrania danych. 2: W reakcji na to żądanie asembler tworzy obiekt transferowy (Transfer Object), który będzie zawierał dane. 3: Następnie asembler synchronicznie lub asynchronicznie pobiera dane z wielu różnych obiektów: 3a: z komponentów encyjnych (Business Entity), 3b: z komponentów sesyjnych (Business Session). 3c: lub bezpośrednio z obiektów DAO. 4: pobrane dane asembler umieszcza w obiekcie transferowym, 5: który odsyła do klienta 2
1.1. Przykład Aplikacja zarządzania projektem. Klient żąda zbiorczej informacji o projekcie, która zawiera: Dane identyfikacyjne projektu Dane osobowe kierownika projektu Listę zadań w tym projekcie Zasoby dla każdego zadania Klasa złożonego obiektu transferowego public class ProjectDetailsData public ProjectTO projectdata; public ProjectManagerTO projectmanagerdata; public Collection listoftasks; Klasa obiektu transferowego dla zasobów zadania public class TaskResourceTO public String projectid; public String taskid; public String name; public String description; public Date startdate; public Date enddate; public ResourceTO assignedresource; 3
public TaskResourceTO(String projectid, String taskid, String name, String description, Date startdate, Date enddate, ResourceTO assignedresource) this.projectid = projectid; this.taskid = taskid; this.assignedresource = assignedresource; Klasa dla obiektu transferowego zadania public class TaskTO public String projectid; public String taskid; public String name; public String description; public Date startdate; public Date enddate; public assignedresourceid; public TaskTO (String projectid, String taskid, String name, String description, Date startdate, Date enddate, String assignedresourceid) this.projectid = projectid; this.taskid = taskid; this.assignedresource = assignedresource; Klasa dla obiektu transferowego zasobu public class ResourceTO public String resourceid; public String resourcename; public String resourceemail; public ResourceTO (String resourceid, String resourcename, String resourceemail, ) this.resourceid = resourceid; this.resourcename = resourcename; this.resourceemail = resourceemail; 4
Implementacja asemblera obiektów transferowych public class ProjectDetailsAssembler implements javax.ejb.sessionbean public ProjectDetailsData getdata (String projectid) // Tworzenie złożonego obiektu danych ProjectDetailsData pdata = new ProjectDetailsData(); // Pobieranie danych projektu ProjectHome projecthome = ServiceLocator.getInstance().getHome ("Project", ProjectEntityHome.class); ProjectEntity project = projecthome.findbyprimarykey(projectid); ProjectTO projto = project.getdata(); // Dodawanie danych projektu do złożonego obiektu danych pdata.projectdata = projto; // Pobieranie danych menedżera projektu ProjectManagerHome projectmanagerhome = ServiceLocator.getInstance().getHome ("ProjectManager", ProjectEntityHome.class); ProjectManagerEntity projectmanager = projectmanagerhome.findbyprimarykey (projto.managerid); ProjectManagerTO projmgrto = projectmanager.getdata(); // Dodawanie danych menedżera do złożonego obiektu danych pdata.projectmanagerdata = projmgrto; // Pobieranie listy projektów Collection projtasklist = project.gettaskslist(); // Tworzenie listy zasobów dla zadania ArrayList listoftasks = new ArrayList(); Iterator taskiter = projtasklist.iterator(); while (taskiter.hasnext()) TaskTO task = (TaskTO) taskiter.next(); // Pobieranie danych zasobu ResourceHome resourcehome = ServiceLocator.getInstance().getHome ("Resource", ResourceEntityHome.class); ResourceEntity resource = resourcehome.findbyprimarykey (task.assignedresourceid); ResourceTO resto = resource.getresourcedata(); // Tworzenie obiektu transferowego dla zasobu zadania // korzystając z obiektów danych Task i Resource TaskResourceTO trto = new TaskResourceTO (task.projectid, task.taskid, task.name, task.description,task.startdate, task.enddate, resto); // Dodanie obiektu transferowego do listy zadań 5
listoftasks.add(trto); // Dodanie listy zadań do obiektu złożonego pdata.listoftasks = listoftasks; // Pobieranie innych danych obiektu transferowego // Zwracany złożony obiekt danych return pdata; 2. Composite Entity W aplikacjach rozproszonych komponenty klienckie uzyskują dostęp do komponentów encyjnych poprzez ich interfejsy zdalne. Dlatego każde wywołanie potencjalnie przechodzi przez liczne warstwy sieci, nawet wówczas, gdy klient odwołuje się do serwera lokalnego (na tym samym komputerze). Jeśli komponenty encyjne są "drobnoziarniste" oferują wiele szczegółowych metod to liczne wywołania tych metod przez klienta generują duży ruch sieciowy dając jednocześnie duży narzut sieciowy na wykonanie operacji. Komponenty encyjne reprezentują trwałe, rozproszone obiekty biznesowe. Podczas ich projektowania trzeba uwzględniać "ziarnistość" modelowych klas biznesowych. Stosowanie komponentów encyjnych jest opłacalne wówczas, gdy klasy biznesowe oprócz szeregu prostych metod pobierania i ustawiania właściwości ("get" i "set") oferują jeszcze metody bardziej złożone. Takie, "gruboziarniste" obiekty mają zazwyczaj obiekty zależne, które nie mają żadnego znaczenia w świecie rzeczywistym o ile nie są przypisane do jakiegoś nadrzędnego obiektu "gruboziarnistego". Powracającym problemem jest bezpośrednie odwzorowanie modelu klas z analizy w model komponentów encyjnych. Tworzą się wówczas relacje pomiędzy klasami komponentów encyjnych a klasami obiektów zależnych nie uwzględniające ziarnistości klas. Brak odpowiedniego zarządzania ziarnistością komponentów encyjnych powoduje szereg problemów: Konieczność walidacji referencji zdalnych do komponentów encyjnych Bezpośrednie odwzorowanie modelu klas w model EJB nie bierze pod uwagę skutków/wpływu relacji między obiektami. Relacje międzyobiektowe są bezpośrednio tłumaczone na relacje między komponentami encyjnymi. W rezultacie komponent encyjny może zawierać lub przechowywać zdalną 6
referencję do innego komponentu encyjnego. Jednak zarządzanie zdalnymi referencjami między obiektami rozproszonymi wymaga innych technik niż zarządzanie referencjami do obiektów lokalnych. Oprócz zwiększenia złożoności kodu, zmniejsza to elastyczność, ponieważ komponenty encyjne muszą się zmieniać jak tylko następują jakiekolwiek zmiany w relacjach między nimi. Ponadto nie ma gwarancji co do poprawności referencji do komponentów encyjnych w całym czasie działania aplikacji. Takie referencje są ustanawiane dynamicznie za pośrednictwem obiektu domowego komponentu encji i klucza głównego instancji komponentu encyjnego. To pociąga za sobą duży narzut na sprawdzanie poprawności referencji w czasie wykonania. Problemy z zarządzaniem i pielęgnowaniem systemu Implementacja drobnoziarnistych obiektów jako komponentów encyjnych powoduje, że liczba komponentów encyjnych w systemie jest bardzo duża. Każdy komponent encyjny zawiera szereg klas. Dla każdego komponentu encyjnego projektant musi napisać klasy dla interfejsu domowego, interfejsu zdalnego, implementację funkcjonalności i klucza głównego. Ponadto kontener może wymagać szeregu klasy pomocniczych. Tak więc liczba klas do implementacji i utrzymania jest bardzo duża. Problemy z wydajnością sieci Pomiędzy drobnoziarnistymi komponentami encyjnymi zazwyczaj występuje wiele relacji. Komponenty te są implementowane jako obiekty rozproszone. Jeśli jeden komponent wywołuje metody innego komponentu, to wywołanie jest traktowane jako wywołanie zdalne, nawet jeśli oba komponenty rezydują na tej samej maszynie. Jak liczba relacji między komponentami encyjnymi wzrasta, to wydajność całego systemu spada ze względu na zwiększający się narzut sieciowy. Zależność aplikacji od schematu bazy danych Gdy komponenty encyjne są drobnoziarniste, to każda instancja komponentu zazwyczaj reprezentuje pojedynczy rekord w bazie danych. Taki sposób projektowania zachęca do operowania bezpośredniego na bazie danych i jej tabelach, a to z kolei uzależnia aplikację od konkretnej struktury bazy danych. Jeśli struktura bazy danych się zmienia, to zmianie muszą również ulec komponenty encji. Ponieważ klienci operują na tym samym poziomie ziarnistości, to i ich implementacja jest zależna od schematu bazy danych. Konieczność zarządzania relacjami przez klienta Ziarnistość komponentów encyjnych wpływa na przesyłanie danych pomiędzy komponentem a jego klientem. W większości aplikacji klienci zazwyczaj potrzebują większej ilości danych niż tylko jednego lub dwóch rekordów tabeli. W takim przypadku oznacza to konieczność zarządzania relacjami przez klienta. W zależności od wymagań na dane klient może być zmuszony do przeglądania wielu tabel dla uzyskania wymaganych informacji. Wzorzec Composite Entity łączy gruboziarnisty obiekt trwały i jego obiekty zależne w złożony komponent encyjny. Obiekt trwały jest obiektem przechowywanym w pamięci trwałej (np. w bazie danych). Obiekt trwały jest instancją klasy trwałej. W przypadku implementacji w 7
relacyjnej bazie danych klasa trwała jest implementowana jako tabela w bazie danych, a jej instancja (obiekt trwały) jest reprezentowana przez pojedynczy rekord w tabeli. Wyróżnia się gruboziarniste obiekty trwałe i obiekty zależne. Obiekt gruboziarnisty jest implementacją klasy modelowej reprezentującej pewien byt rzeczywiście istniejący w dziedzinie problemu (np. byt "Klient"). Poprzez relacje z tabelą implementującą obiekt gruboziarnisty mogą być powiązane tabele implementujące obiekty zależne. Każdy obiekt zależny reprezentuje dane, które nie mają znaczenia bez powiązania z obiektem gruboziarnistym (przykład: adres zamieszkania i adres korespondencji). We wzorcu Composite Entity obiekty zależne nie są widoczne dla klienta inaczej, jak tylko poprzez obiekt gruboziarnisty. To ten obiekt pobiera z pamięci trwałej obiekty zależne i dostarcza je razem ze swoimi danymi do klienta w postaci jednego, złożonego komponentu encyjnego. Współdziałanie komponentów w tym wzorcu rozpoczyna się od: 1: Żądania pobrania/utrwalenia danych skierowanego od klienta do komponentu encyjnego. Komponent ten zawiera referencję do obiektu gruboziarnistego zaimplementowanego jako klasa trwała. Poprzez tę referencję: 2: komponent encyjny przekazuje żądanie klienta do obiektu gruboziarnistego. Ten z kolei ma zapisane referencje do obiektów zależnych, od których 3a: albo pobiera dane bezpośrednio 3b: albo pośrednio poprzez inne obiekty zależne 8
2.1. Przykład Do poprzedniego przykładu dodajemy dobieranie specjalistów do zadań. W tym celu zaimplementowano obiekt biznesowy Resource, który reprezentuje "zasób ludzki" odpowiedni do wykonania zadania. Obiekt ten ma dwa obiekty zależne: BlockOutTime czas niedostępności "zasobu" z powodu szkolenia, urlopu, zwolnienia i in. Każdy "zasób" może być blokowany wielokrotnie, stąd między klasami Resource i BlockOutTime występuje relacja jeden-wiele. SkillSet zbiór umiejętności jakie posiada "zasób". Wybierany będzie ten "zasób", który posiada umiejętności niezbędne do realizacji zadania. Ponieważ jeden specjalista może mieć wiele umiejętności, to między klasami Resource i SkillSet występuje relacja jeden-wiele. Implementacja obiektu gruboziarnistego package corepatterns.apps.psa.ejb; import corepatterns.apps.psa.core.*; import corepatterns.apps.psa.dao.*; import java.sql.*; import javax.sql.*; import java.util.*; import javax.ejb.*; import javax.naming.*; public class ResourceEntity implements EntityBean public String employeeid; public String lastname; public String firstname; public String departmentid; public String practicegroup; public String title; public String grade; public String email; public String phone; public String cell; public String pager; public String managerid; // Kolekcja zależnych obiektów typu BlockOutTime public Collection blockouttimes; // Kolekcja zależnych obiektów typu SkillSet public Collection skillsets; private EntityContext context; // Implementacja metody Entity Bean public String ejbcreate(resourceto resource) throws CreateException 9
this.employeeid = resource.employeeid; setresourcedata(resource); getresourcedao().create(resource); catch(exception ex) throw new EJBException("Reason:" + ); return this.employeeid; public String ejbfindbyprimarykey(string primarykey) throws FinderException boolean result; ResourceDAO resourcedao = getresourcedao(); result = resourcedao.selectbyprimarykey(primarykey); catch(exception ex) throw new EJBException("Reason:" + ); if (result) return primarykey; else throw new ObjectNotFoundException(); public void ejbremove() // Usuwanie obiektów zależnych if (this.skillsets!= null) // Do tego celu wykorzystujemy obiekt DAO SkillSetDAO skillsetdao = getskillsetdao(); skillsetdao.setresourceid(employeeid); skillsetdao.deleteall(); skillsets = null; if (this.blockouttime!= null) // Tak samo dla drugiej kolekcji obiektów zależnych // Usuwamy dane główne encji korzystając z DAO ResourceDAO resourcedao = new ResourceDAO(employeeId); resourcedao.delete(); catch (ResourceException ex) throw new EJBException("Reason:"+); catch (BlockOutTimeException ex) throw new EJBException("Reason:"+); catch (Exception exception) 10
public void setentitycontext(entitycontext context) this.context = context; public void unsetentitycontext() context = null; public void ejbactivate() employeeid = (String)context.getPrimaryKey(); public void ejbpassivate() employeeid = null; public void ejbload() // Ładujemy dane główne encji z DAO ResourceDAO resourcedao = getresourcedao(); setresourcedata((resourceto) resourcedao.load(employeeid)); catch(exception ex) throw new EJBException("Reason:" + ); public void ejbstore() // Zapisywanie informacji z encji przez DAO getresourcedao().update(getresourcedata()); catch(skillsetexception ex) throw new EJBException("Reason:" + ); catch(blockouttimeexception ex) throw new EJBException("Reason:" + ); public void ejbpostcreate(resourceto resource) // Pobieranie obiektu transferowego zasobu public ResourceTO getresourceto() // Tworzenie nowego obiektu transferowego ResourceTO resourceto = new ResourceTO (employeeid); // Kopiowanie danych z encji do obiektu transferowego resourceto.lastname = lastname; resourceto.firstname = firstname; resourceto.departmentid = departmentid; return resourceto; 11
public void setresourcedata(resourceto resourceto) // Kopiowanie danych z obiektu transferowego do encji employeeid = resourceto.employeeid; lastname = resourceto.lastname; // Metoda pobierania zależnych obiektów transferowych public Collection getskillsetsdata() // Jeśli kolekcja skillsets nie jest załadowana, to załaduj ją najpierw // Patrz ładowanie leniwe return skillsets; // Inne metody get i set // Metody biznesowe public void addblockouttimes (Collection morebots) throws BlockOutTimeException // parametr morebots jest kolekcją obiektów BlockOutTimeTO Iterator moreiter = morebots.iterator(); while (moreiter.hasnext()) BlockOutTimeTO botto = (BlockOutTimeTO)moreIter.next(); if (!(blockouttimeexists(botto))) // dodaj obiekt BlockOutTimeTO botto.setnew(); blockouttime.add(botto); else // Obiekt już istnieje w kolekcji dodanie niemożliwe throw new BlockOutTimeException(); catch(exception exception) throw new EJBException(); public void addskillset(collection moreskills) throws SkillSetException // podobna operacja do addblockouttime() 12
public void updateblockouttime(collection updbots) throws BlockOutTimeException Iterator botiter = blockouttimes.iterator(); Iterator upditer = updbots.iterator(); while (upditer.hasnext()) BlockOutTimeTO botto = (BlockOutTimeTO)updIter.next(); while (botiter.hasnext()) BlockOutTimeTO existingbot = (BlockOutTimeTO) botiter.next(); // porównanie wartości klucza aby znaleść obiekt BlockOutTimeTO if (existingbot.equals(botto)) // Obiekt BlockOutTime znaleziony w kolekcji // zastępujemy nowym botto.setdirty(); // stary obiekt jest zaznaczony jako zmodyfikowany botto.resetnew(); // a nie nowy existingbot = botto; catch (Exception exc) throw new EJBException(); public void updateskillset(collection updskills) throws CommitmentException // implementacja podobna do updateblockouttime Implementacja ładowania leniwego Gdy obiekt złożony ResourceEntity jest ładowany metodą ejbload(), to jedynie dane zasobu są ładowane czyli jedynie abuty zdefiniowane w klasie ResourceEntity, a nie kolekcje obiektów zależnych. Te z kolei są ładowane dopiero, gdy klient wywołuje metodę biznesową, która potrzebuje tych obiektów. Dlatego metoda ejbload() śledzi obiekty zależne ładowane w ten sposób i dołącza je przy przeładowaniu. public Collection getskillsetsdata() throws SkillSetException checkskillsetload(); 13
return skillsets; private void checkskillsetload() throws SkillSetException // Ładowanie na żądanie if (skillsets == null) skillsets = getskillsetdao(resourceid).loadall(); catch(exception exception) throw new SkillSetException(); public void ejbload() // Ładowanie danych głównych zasobu z DAO ResourceDAO resourcedao = new ResourceDAO(employeeId); setresourcedata((resourceto)resourcedao.load()); // Jeśli obiekty zależne są już załadowane, to muszą być załadowane ponownie. // Jeśli nie są załadowane, to nie są ładowane teraz, lecz później if (skillsets!= null) reloadskillsets(); if (blockouttimes!= null) reloadblockouttimes(); throw new EJBException("Reason:"+); Implementacja markera DirtyMarker dla zbioru umiejętności Przy zmianie zbioru umiejętności używanego jako obiektu transferowego są ustawiane znaczniki "dirty", "isnew" i "deleted". public class SkillSetTO implements DirtyMarker, java.io.serializable private String skillname; private String expertiselevel; private String info; private boolean dirty = false; private boolean isnew = true; private boolean deleted = false; public SkillSetTO () 14
// inicjalizacja // Jest to nowy obiekt transferowy setnew(); // Wszyskie metody zmiany wartości muszą ustalać znacznik modyfikacji public setskillname(string newskillname) if (newskillname!= skillname) skillname = newskillname; setdirty(); // znacznik "dirty" stosowany do modyfikowanych obiektów transferowych public void setdirty() dirty = true; public void resetdirty() dirty = false; public boolean isdirty() return dirty; // znacznik "isnew" stosowany do nowych obiektów transferowych public void setnew() isnew = true; public void resetnew() isnew = false; public boolean isnew() return isnew; // znacznik "deleted" stosowany do usuniętych obiektów transferowych public void setdeleted() deleted = true; public boolean isdeleted() return deleted; public void resetdeleted() deleted = false; Implementacja optymalizacji zapisu Gdy obiekt ResourceEntity jest zapisywany metodą ejbstore(), to obowiązkowo są zapisywane tylko dane główne encji. Obiekty zależne są zapisywane tylko wtedy, gdy są zmodyfikowane i ustawiona jest dla nich któraś z flag modyfikacji. public void ejbstore() // Zapisywanie danych głównych encji getresourcedao().update(getresourcedata()); // Jeśli kolekcja obiektów zależnych SkillSets jest załadowana, // to sprawdzamy konieczność aktualizacji bazy danych if (skillsets!= null) // obiekt DAO umożliwi zapisywanie obiektów zależnych SkillSetDAO skillsetdao = getskillsetdao(); 15
// Iterator po kolekcji obiektów zależnych Iterator skilliter = skillset.iterator(); while (skilliter.hasnext()) // Pobieramy obiekt transferowy z iteratora SkillSetTO skill = (SkillSetTO) skilliter.next(); // Sprawdzamy, czy jest to nowo utworzony obiekt zależny if (skill.isnew()) // Jeśli tak, to obiekt transferowy jest dodawany przez DAO do bazy danych skillsetdao.insert(skill); // Flagi modyfikacji są kasowane skill.resetnew(); skill.resetdirty(); // Sprawdzamy, czy obiekt zależny został zaznaczony jako usunięty else if (skill.isdeleted()) // Jeśli tak, to obiekt transferowy jest usuwany przez DAO z bazy danych skillsetdao.delete(skill); // i usuwany z kolekcji obiektów zależnych skillsets.remove(skill); // Wreszcie sprawdzamy, czy obiekt zależny został zmieniony else if (skill.isdirty()) // Jeśli tak, to obiekt transferowy jest zmieniany przez DAO w bazie danych skillsetdao.update(skill); // Flagi modyfikacji są kasowane skill.resetdirty(); skill.resetnew(); // Podobnie implementuje się modyfikację kolekcji BlockOutTime catch(skillsetexception ex) throw new EJBException("Reason:"+); catch(blockouttimeexception ex) throw new EJBException("Reason:"+); catch(commitmentexception ex) throw new EJBException("Reason:"+); 3. Business Delegate Komponenty warstwy prezentacji współdziałają bezpośrednio z komponentami usługowymi. Taka bezpośrednia współpraca powoduje, że komponenty warstwy prezentacji mogą być wrażliwe na szczegóły implementacyjne warstwy usługowej. W 16
rezultacie każda zmiana warstwy usługowej może pociągać za sobą konieczność zmianę implementacji warstwy prezentacji. Ponadto jeśli po stronie klienta brak mechanizmów buforowania i agregacji, to komponenty warstwy prezentacji wysyłają wiele wywołań warstwy usług przez sieć. Wreszcie bezpośrednie korzystanie z API warstwy usługowej zmusza zespół projektowy pracujący nad warstwą prezentacji do zajmowania się rozproszoną naturą technologii EJB. Rozwiązaniem tych problemów jest komponent o nazwie delegat biznesowy (Business Delegate). Działa on po stronie klienta wywołując usługi komponentów biznesowych. W ten sposób ukrywa przed klientem szczegóły implementacji usług biznesowych oraz uniezależnia implementację komponentów prezentacyjnych od zmian zachodzących w warstwie biznesowej. Współdziałanie komponentów: 1. Klient tworzy delegata biznesowego 1.1 W imieniu klienta delegat biznesowy zwraca się do lokalizatora usług o wyszukanie komponentu usługowego 1.1.1. Lokalizator usług wyszukuje odpowiedni komponent usługowy i zwraca go do delegata biznesowego. 2. Klient wywołuje metodę delegata biznesowego, który: 2.1 przekazuje wywołanie od komponentu usługowego 17
3. Klient może zwrócić się do delegata biznesowego o podanie identyfikatora komponentu usługowego. Identyfikator ten będzie używany w późniejszym czasie 3.1 Delegat biznesowy przekazuje żądanie do lokalizatora usług, który: 3.1.1 pobiera uchwyt komponentu usługowego i 3.1.2 przekształca go w łańcuch zwracany do klienta Przy kolejnej okazji: 4. Klient tworzy delegata biznesowego przekazując mu identyfikator określonego komponentu usługowego. 4.1 Delegat biznesowy zwraca się do lokalizatora usług o podanie komponentu o określonym identyfikatorze 4.1.1 Lokalizator usług przekształca identyfikator łańcuchowy w uchwyt komponentu i 4.1.2 Wykorzystuje ten uchwyt do 4.1.2.1 połączenia z odpowiednim komponentem usługowym 5. Od tego momentu klient może korzystać z tego komponentu usługowego 5.1 za pośrednictwem delegata biznesowego 18
3.1. Przykład Do poprzedniego przykładu dodajemy delegata biznesowego po stronie klienta. Implementacja delegata zasobów // imports public class ResourceDelegate // Wykorzystujemy zdalny interfejs fasady sesji private ResourceSession session; // Klasa obiektu domowego fasady sesji private static final Class homeclazz = corepatterns.apps.psa.ejb.resourcesessionhome.class; // Domyślny konstruktor. Wyszukuje obiekt domowy i łączy się z sesją public ResourceDelegate() throws ResourceException ResourceSessionHome home = (ResourceSessionHome) ServiceLocator.getInstance().getHome( "Resource", homeclazz); session = home.create(); catch(servicelocatorexception ex) throw new ResourceException(); catch(createexception ex) throw new ResourceException(); catch(remoteexception ex) throw new ResourceException(); // Konstruktor wykorzystujący identyfikator sesji użytkownika do ponownego połączenia public BusinessDelegate(String id) throws ResourceException super(); reconnect(id); // Zwraca identyfikator sesji użytkownika do ponownego wykorzystania public String getid() return ServiceLocator.getId(session); catch (Exception e) throw new ResourceException(); 19
// metoda ponownego połączenia z wykorzystaniem identyfikatora sesji użytkownika public void reconnect(string id) throws ResourceException session = (ResourceSession) ServiceLocator.getService(id); catch (RemoteException ex) throw new ResourceException(); // Metody biznesowe "proxy" wywołujące metody fasady sesji public ResourceTO setcurrentresource (String resourceid) throws ResourceException return session.setcurrentresource (resourceid); catch (RemoteException ex) throw new ResourceException(); public ResourceTO getresourcedetails() throws ResourceException return session.getresourcedetails(); catch(remoteexception ex) throw new ResourceException(); public void setresourcedetails (ResourceTO vo) throws ResourceException session.setresourcedetails(vo); catch(remoteexception ex) throw new ResourceException(); public void addnewresource(resourceto vo) throws ResourceException session.addresource(vo); catch(remoteexception ex) throw new ResourceException(); 20
4. Zadanie dla studentów Zaprogramować w środowisku Visual Studio albo J2EE aplikację rozproszoną składającą się z części serwerowej i części klienckiej. W części serwerowej zrealizować wzorzec Composite Entity. W części klienckiej wzorzec Business Delegate. Do transferu danych zastosować wzorzec Transfer Object Assembly. We wzorcu Composite Entity zaimplementować leniwe ładowanie lub modyfikację z wykorzystaniem znaczników modyfikacji. Bibliografia Rysunki i przykłady pochodzą z następujących źródeł: http://java.sun.com/blueprints/corej2eepatterns/patterns/transferobjectassembl er.html http://java.sun.com/blueprints/corej2eepatterns/patterns/compositeentity.html http://java.sun.com/blueprints/corej2eepatterns/patterns/businessdelegate.html Literatura uzupełniająca: Gamma et al: Wzorce projektowe, WNT Warszawa 2005 http://student.agh.edu.pl/~zegarow/filez/wzorce/wzorce_projektowe.pdf Buschman et al: Pattern-Oriented Software Architecture Volume 1: A System of Patterns, Wyd. Wiley, Schmidt et al: Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects, Wyd. Wiley, 21