Rozproszonych Wzorce projektowe warstwy aplikacji qhta@eti.pg.gda.pl J.Kuchta@eti.pg.gda.pl 1
Wzorce projektowe warstwy aplikacji Front Controller Zapewnia wspólny punkt obsługi Ŝąda dań dla komponentów warstwy prezentacji Dispatcher View Zapewnia przekazywanie Ŝąda dań klienta do wybranych komponentów w widoku (wybór r widoku przez klienta) Service To Worker Zapewnia przekazywanie Ŝąda dań klienta do wybranych komponentów w widoku (wybór r widoku przez serwer) 2/31 2
Front Controller 3/31 3
Front Controller - motywacje Mechanizm obsługi Ŝąda dań warstwy prezentacji moŝe być zdecentralizowany lub scentralizowany. Mechanizm zdecentralizowany powoduje problemy: Redundancja kodu dostępu do komponentów w usługowych ugowych Brak spójno jności w nawigacji między róŝnymi r widokami Trudniejsza pielęgnacja (częste zmiany w róŝnych r miejscach) 4/31 System rozproszony potrzebuje dla warstwy prezentacji centralnego punktu dostępowego, który będzie integrował usługi, zarządzał odtwarzaniem zawartości, prezentacją treści i nawigacją. Jeśli takiego centralnego punktu dostępowego brakuje, to pojawiają się następujące problemy: KaŜdy widok zapewnia sobie sam dostęp do komponentów usługowych, co powoduje redundancję kodu. KaŜdy widok sam organizuje nawigację, co moŝe powodować niespójność nawigacji między róŝnymi widokami Sterowanie rozproszone jest trudniejsze w pielęgnacji i modyfikacji z powodu konieczności zmian w wielu róŝnych miejscach 4
SpostrzeŜenia enia i wymagania Wspólne usługi ugi systemowe rozpoczynają i kończ czą przetwarzanie w tym samym Ŝądaniu. Przy odtwarzaniu i manipulacji danymi konieczne sąs punkty decyzyjne. Potrzebny jest centralny punkt monitorujący. Wiele róŝnych r widoków w odpowiada na podobne Ŝądania biznesowe. Logika zarządzania widokami jest dość złoŝona. ona. 5/31 Ponadto zauwaŝono, Ŝe w systemach wykorzystujących wspólne komponenty usługowe: Komponenty usługowe często realizują operacje w pojedynczych Ŝądaniach. Dla przykładu usługi ochrony potrzebują jednego Ŝądania dla uwierzytelnienia klienta. Potrzebne są punkty decyzyjne, w których klient będzie mógł podejmować decyzje co do odtwarzania danych i ich zapisywania. Potrzebny jest centralny punkt monitorujący działania uŝytkownika i postęp przetwarzania w całej witrynie. Wiele róŝnych widoków odpowiada na podobne Ŝądania biznesowe. Logika zarządzania widokami jest często dość złoŝona. 5
Rozwiązanie zanie Zastosowanie centralnego kontrolera frontowego (Front Controller) do obsługi Ŝąda dań. Kontroler frontowy: zarządza obsług ugą Ŝąda dań wywołuje usługi ugi ochrony (np( np.. uwierzytelnienia) przekazuje Ŝądania przetwarzania do komponentów usługowych ugowych zarządza wyborem odpowiedniego widoku w odpowiedzi na Ŝądanie zarządza wyborem strategii tworzenia zawartości zarządza obsług ugą błędów 6/31 Przez zcentralizowanie sterowania Front Controller zmniejsza wielkość kodu Java w skrypletach zawartych na stronach JSP wspierając wielokrotne wykorzystanie tego samego kodu. Kontroler frontowy często współpracuje z komponentem przekaźnika (Dispatcher View), który jest odpowiedzialny za zarządzanie widokami i nawigację. Przekaźnik wybiera widok i przekazuje sterowanie do tego widoku. Przekaźnik moŝe być zawarty bezpośrednio w kontrolerze lub umieszczony w osobnym komponencie. ChociaŜ wzorzec Front Controller sugeruje centralne przetwarzanie Ŝądań, nie ogranicza on liczby komponentów obsługujących Ŝądania. Aplikacja moŝe wykorzystywać wiele kontrolerów frontowych, po jednym dla osobnego zbioru usług. 6
Struktura komponentów Client requests Front Controller «servlet» Servlet Front «JSP» JSP Front 7/31 Głównym komponentem wzorca jest Front Controller, który moŝe być zrealizowany w technologii serwletów lub technologii JSP. 7
Przekazywanie Ŝąda dań Client Front Controller Dispatcher View Helper 1: Send (Request) 1.1: Delegate (Request) 1.1.1: Forward (Request) 2: Send (Request) 2.1: Forward (Request) 3: Send (Request) 3.1: Forward (Request) 8/31 1. Klient wysyła Ŝądania do kontrolera frontowego. 1.1 Kontroler frontowy rozpoznaje Ŝądania i przekazuje je do przekaźnika 1.1.1 Przekaźnik wybiera określony widok i przekazuje do niego Ŝądanie klienta 2. W drugim przypadku klient wysyła Ŝądanie do kontrolera frontowego, a ten 2.1 bezpośrednio przekazuje Ŝądanie do wybranego przez siebie widoku 3. W trzecim przypadku klient wysyła do kontrolera frontowego Ŝądanie, które nie wymaga obsługi przez widok, 3.1 lecz przez obiekt pomocniczy 8
Strategie implementacyjne Implementacja przez serwlet moŝliwo liwość wykorzystania interpretera komend (wzorzec Command [GoF]) Implementacja przez stronę JSP 9/31 9
Przykład implementacja przez serwlet public class EmployeeController extends HttpServlet {... // Handles the HTTP GET method. protected void doget(httpservletrequest request, HttpServletResponse response) { processrequest(request, response); } // Handles the HTTP POST method. protected void dopost(httpservletrequest request, HttpServletResponse response) { processrequest(request, response); } // Processes requests for both HTTP GET and POST methods protected void processrequest (HttpServletRequest equest, HttpServletResponse response) { String page; // ApplicationResources provides a simple API for retrieving constants // and other preconfigured values ApplicationResources resource = ApplicationResources.getInstance(); // Use a helper object to gather parameter specific information. RequestHelper helper = new RequestHelper(request); Command cmdhelper= helper.getcommand(); // Command helper perform custom operation 10/31 W implementacji przez serwlet: metoda doget obsługuje Ŝądania przekazywane przez metodę HTTP GET metoda dopost obsługuje Ŝądanie przekazywane przez metodę HTTP POST obie te metody korzystają ze wspólnej procedury processrequest która wykorzystuje pomocniczy komponent interpretera komend (cmdhelper) i na podstawie odpowiedzi wybiera odpowiedni widok, do którego przekazuje Ŝądanie klienta (dispatch) //----------------------------------------------------------------------- public class Controller extends HttpServlet { // Handles the HTTP GET method. protected void doget(httpservletrequest request, HttpServletResponse response) { processrequest(request, response); } // Handles the HTTP POST method. protected void dopost(httpservletrequest request,httpservletresponse response) { processrequest(request, response); } // Processes requests for both HTTP GET and POST methods. protected void processrequest(httpservletrequest request, HttpServletResponse response) { String page; // ApplicationResources provides a simple API for retrieving constants // and other preconfigured values ApplicationResources resource = ApplicationResources.getInstance(); // Use a helper object to gather parameter specific information. RequestHelper helper = new RequestHelper(request); Command cmdhelper= helper.getcommand(); // Command helper perform custom operation page = cmdhelper.execute(request, response); // dispatch control to view 10
Przykład implementacja przez stronę JSP <%@page contenttype="text/html"%> <%@ page import="corepatterns.util.*" %> <html> <head><title>jsp Front Controller</title></head> <body> <h3><center> Employee Profile </h3> <% <jsp:usebean id="employee" scope="request" class="corepatterns.util.employeeto"/> <FORM method=post > <table width="60%"> <tr> <td> First Name : </td> <td> <input type="text" name="<%=constants.fld_firstname%>" value="<jsp:getproperty name="employee" property="firstname"/>"> </td> </tr> <tr> <td> Last Name : </td> <td> <input type="text" name="<%=constants.fld_lastname%>" value="<jsp:getproperty name="employee" property="lastname"/>"> </td> </tr> <tr> 11/31 Implementacja przez stronę JSP, chociaŝ semantycznie równowaŝna, jest mniej popularna od implementacji przez serwlet z dwóch powodów: PoniewaŜ kontroler frontowy przetwarza Ŝądania niekoniecznie związane z formatowaniem widoku, więc implementacja tego komponentu przez stronę JSP wydaje się być nieporozumieniem. PoniewaŜ technologia JSP bazuje na znacznikach JSP w kodzie HTML, więc śledzenie wykonywania programu jest mocno utrudnione. //----------------------------------------------------------------------- <%@page contenttype="text/html"%> <%@ page import="corepatterns.util.*" %> <html> <head><title>jsp Front Controller</title></head> <body> <h3><center> Employee Profile </h3> <% <jsp:usebean id="employee" scope="request" class="corepatterns.util.employeeto"/> <FORM method=post > <table width="60%"> <tr> <td> First Name : </td> <td> <input type="text" name="<%=constants.fld_firstname%>" value="<jsp:getproperty name="employee" property="firstname"/>"> </td> </tr> <tr> <td> Last Name : </td> <td> <input type="text" name="<%=constants.fld_lastname%>" value="<jsp:getproperty name="employee" property="lastname"/>"> </td> </tr> <tr> <td> Employee ID : </td> <td> <input type="text" 11
Strategie adresowania kontrolera Adresowanie fizyczne http://some.server.com/resource1.jsp http://some.server.com/servlet/controller Adresowanie logiczne http://some.server.com/process process=resource1.jsp process=servletcontroller 12/31 Przy przekazywaniu Ŝądań do kontrolera frontowego pojawia się problem adresowania tego kontrolera. Najprostszym rozwiązaniem jest jawne wpisanie nazwy fizycznego komponentu implementującego kontroler do URL, jak np.: http://some.server.com/resource1.jsp http://some.server.com/servlet/controller Oczywiście przy zmianie sposobu implementacji adresy URL będą musiały ulec zmianie. Dlatego wskazane jest uŝycie nazwy logicznej zasobu, np.: http://some.server.com/process a następnie w konfiguracji witryny przypisanie nazwy logicznej do zasobu fizycznego: process=resource1.jsp process=servletcontroller 12
Zalety i wady stosowania wzorca Centralizacja sterowania większa łatwość śledzenia Ŝąda dań Uwaga: pojedynczy, wraŝliwy punkt systemu Wspomaganie zarządzania bezpieczeństwem Pojedynczy punkt kontroli (mniej zasobów) Wspomaganie powtórnego wykorzystania kodu Wspólny kod komponentów w przeniesiony do sterownika 13/31 Centralizacja sterowania Kontroler frontowy stanowi centralny punkt obsługi Ŝądań. Ułatwia to śledzenie Ŝądań i ich obsługi. Z drugiej jednak strony stanowi punkt wraŝliwy, potencjalnie naraŝony na błędy implementacji. Wspomaganie zarządzania bezpieczeństwem Kontroler frontowy moŝe realizować funkcje ochronne (np. uwierzytelnienia). PoniewaŜ taki punkt kontrolny jest pojedynczy, to kontrola uwierzytelnienia jest łatwiejsza, a ponadto zuŝywa się na to mniej zasobów. Wspomaganie powtórnego wykorzystania kodu Kontroler frontowy powoduje, Ŝe często wykorzystywany kod komponentów warstwy prezentacji zostaje przeniesiony do wspólnego sterownika. 13
Dispatcher View 14/31 14
Dispatcher View Zapewnia przekazywanie Ŝąda dań klienta do określonego widoku Łączy wzorce Front Controller i View Helper W odróŝnieniu od wzorca Service To Worker rola przekaźnika ograniczona lub umiarkowana 15/31 Wzorzec Dispatcher View jest stosowany w aplikacji złoŝonej z wielu widoków. Zapewnia przekazywanie Ŝądań klienta do odpowiedniego widoku wówczas, gdy to serwer decyduje, do którego widoku musi trafić Ŝądanie. Wzorzec łączy w sobie rozwiązania wzorców Front Controller i View Helper (ten drugi wzorzec zdefiniowany jest w warstwie prezentacji). Wzorzec Dispatcher View jest podobny do wzorca Service To Worker, ale w odróŝnieniu od niego rola komponentu przekazującego Ŝądania jest ograniczona lub umiarkowana. 15
Ograniczona rola przekaźnika Do wyboru widoku nie wykorzystuje się zewnętrznych zasobów. Informacje zawarte w Ŝądaniu sąs wystarczające ce do określenia widoku, do którego trzeba przekazać Ŝądanie. Przykład: http://some.server.com/servlet/controller?next=login.jsp 16/31 Ograniczona rola przekaźnika polega na tym, Ŝe do wyboru widoku aplikacja nie potrzebuje dostępu do Ŝadnych dodatkowych zasobów. Oznacza to, Ŝe dane przekazywane przez klienta w Ŝądaniu są wystarczające do określenia widoku, do którego trzeba przekazać Ŝądanie. Dane są zakodowane w URL poprzez parametry Ŝądania, np.: http://some.server.com/servlet/controller?next=login.jsp 16
Umiarkowana rola przekaźnika Klient przekazuje Ŝądanie bezpośrednio do sterownika, z parametrem kwerendy opisującym akcję do podjęcia: Przykład http://some.server.com/servlet/controller?action=login 17/31 Umiarkowana rola przekaźnika polega na tym, Ŝe wybór widoku następuje na podstawie parametrów przekazywane przez klienta, np.: http://some.server.com/servlet/controller?action=login 17
Struktura komponentów Client requests «servlet» Front Controller delegates Dispatcher dispatches 1 1..* «JSP» View 1 uses * Helper 18/31 Głównym komponentem wzorca jest przekaźnik (Dispatcher). Komponent ten często współdziała z kontrolerem frontowym (Front Controller) implementowanym w formie serwletu. Przekazuje Ŝądania z kontrolera do wybranego widoku (np. strony JSP). Widok moŝe wykorzystywać dodatkowe obiekty pomocnicze. 18
Współdzia działanie anie komponentów Client «servlet» Front Controller Dispatcher «JSP» View Helper Value Object Business Service 1: Request 1.1: Delegate 1.1.1: Dispatch 1.1.1.1: Retrieve 1.1.1.1.1: Get Data 1.1.1.2: Get Property 19/31 Przy współdziałaniu komponentów we wzorcu Dispatcher View: 1. Klient przesyła Ŝądanie do kontrolera frontowego 1.1 Kontroler rozpoznaje Ŝądanie i przesyła je do przekaźnika 1.1.1 Przekaźnik wybiera widok (stronę JSP) na podstawie danych zawartych w Ŝądaniu 1.1.1.1 Widok korzysta z obiektu pomocniczego, który 1.1.1.1.1 pobiera dane z komponentu usługowego zwracającego obiekt transferowy 1.1.1.2 który przenosi dane dla widoku 19
Strategie implementacyjne Dla widoków przez serwlet przez JSP Dla obiektu pomocniczego przez komponenty EJB przez custom tags 20/31 Komponenty widoku mogą być implementowane w technologii serwletów albo jako strony JSP. W pierwszym przypadku obiekty pomocnicze są implementowane jako komponenty EJB, a w drugim przez znaczniki uŝytkownika (custom tags). 20
Client Strategia "przekaźnik w sterowniku" Dispatcher «JSP» View Helper Value Object Business Service 1: Request 1.1: Dispatch 1.1.1: Retrieve 1.1.1.1: Get Data 1.1.2: Get Property 21/31 Wzorzec Dispatcher View i Front Controller mogą być zaimplementowane w jednym komponencie. 21
Client Strategia "przekaźnik w widoku" «JSP» View Dispatcher «JSP» View Helper Value Object Business Service 1: Request 1.1: Delegate 1.1.1: Dispatch 1.1.1.1: Retrieve 1.1.1.1.1: Get Data 1.1.1.2: Get Property 22/31 Wzorzec Dispatcher View moŝe być teŝ zaimplementowany w komponencie widoku w technologii JSP. 22
Przykład implementacja z kontrolerem w serwlecie public class Controller extends HttpServlet { // Handles the HTTP GET method. protected void doget(httpservletrequest request, HttpServletResponse response) { processrequest(request, response); } // Handles the HTTP POST method. protected void dopost(httpservletrequest request,httpservletresponse response) { processrequest(request, response); } // Processes requests for both HTTP GET and POST methods. protected void processrequest(httpservletrequest request, HttpServletResponse response) { String nextview;... nextview = request.getparameter("nextview");... dispatch(request, response, nextview); } // Dispatcher method protected void dispatch(httpservletrequest request, HttpServletResponse response, String page) { RequestDispatcher dispatcher = getservletcontext().getrequestdispatcher(page); dispatcher.forward(request, response); 23/31 W poniŝszym przykładzie wzorzec Dispatch View jest implementowany wraz ze wzorcem Front Controller przez serwlet. Od poprzedniego przykładu implementację odróŝnia metoda processrequest, w której nazwę następnego widoku (nextview) pobiera się z parametru Ŝądania. //----------------------------------------------------------------------- public class Controller extends HttpServlet { // Handles the HTTP GET method. protected void doget(httpservletrequest request, HttpServletResponse response) { processrequest(request, response); } // Handles the HTTP POST method. protected void dopost(httpservletrequest request,httpservletresponse response) { processrequest(request, response); } // Processes requests for both HTTP GET and POST methods. protected void processrequest(httpservletrequest request, HttpServletResponse response) { String nextview;... nextview = request.getparameter("nextview");... dispatch(request, response, nextview); } // Dispatcher method protected void dispatch(httpservletrequest request, HttpServletResponse response, String page) { RequestDispatcher dispatcher = getservletcontext().getrequestdispatcher(page); dispatcher.forward(request, response); } } 23
Przykład implementacja przez stronę JSP <%@ taglib uri="/web-inf/corepatternstaglibrary.tld" prefix="corepatterns" %> <html> <head><title>accountdetails</title></head> <body> <corepatterns:accountquery queryparams="custid,acctkey" scope="request" /> <h2><center> Account Detail for <corepatterns:account attribute="owner" /></h2> <br><br> <tr> <td>account Number :</td> <td><corepatterns:account attribute="number" /></td> </tr> <tr> <td>account Type:</td> <td><corepatterns:account attribute="type" /></td> </tr> <tr> <td>account Balance:</td> <td><corepatterns:account attribute="balance" /></td> </tr> <tr> 24/31 Implementacja przez stronę JSP, chociaŝ semantycznie równowaŝna, jest mniej popularna od implementacji przez serwlet z dwóch powodów: PoniewaŜ kontroler frontowy przetwarza Ŝądania niekoniecznie związane z formatowaniem widoku, więc implementacja tego komponentu przez stronę JSP wydaje się być nieporozumieniem. PoniewaŜ technologia JSP bazuje na znacznikach JSP w kodzie HTML, więc śledzenie wykonywania programu jest mocno utrudnione. //----------------------------------------------------------------------- <%@ taglib uri="/web-inf/corepatternstaglibrary.tld" prefix="corepatterns" %> <html> <head><title>accountdetails</title></head> <body> <corepatterns:accountquery queryparams="custid,acctkey" scope="request" /> <h2><center> Account Detail for <corepatterns:account attribute="owner" /></h2> <br><br> <tr> <td>account Number :</td> <td><corepatterns:account attribute="number" /></td> </tr> <tr> <td>account Type:</td> <td><corepatterns:account attribute="type" /></td> </tr> <tr> <td>account Balance:</td> <td><corepatterns:account attribute="balance" /></td> </tr> <tr> <td>overdraft Limit:</td> <td><corepatterns:account attribute="overdraftlimit" /></td> </tr> <table border=3> 24
Zalety wzorca Centralizuje sterowanie i ułatwia u powtórne uŝycie kodu i utrzymanie kodu Ułatwia podział aplikacji na warstwy Ułatwia podział ról l między projektantów 25/31 Centralizuje sterowanie i ułatwia powtórne uŝycie kodu i utrzymanie kodu Centralizacja sterowania, wykorzystanie wspólnego kodu w wydzielonym komponencie oraz ułatwienie pielęgnacji kodu to te same zalety, co w przypadku wzorca Front Controller. Ułatwia podział aplikacji na warstwy Wykorzystanie obiektów pomocniczych jasno oddziela warstwę prezentacji od przetwarzania w warstwie aplikacji. Stanowi lepsze rozwiązanie od skrypletów, które w większych projektach przestają być wystarczające. Ułatwia podział ról między projektantów Wydzielenie logiki aplikacji z warstwy prezentacji ułatwia podział ról między projektantów na tych, którzy zajmują się wizualną stroną projektu i tych, którzy zarządzają logiką aplikacji. 25
Service To Worker 26/31 26
Service To Worker Podobnie jak wzorzec Dispatcher View łączy wzorce Front Controller i View Helper w komponencie przekaźnika. Przekaźnik moŝe e być bardziej zaawansowany. MoŜe e wywoływa ywać usługi ugi biznesowe dla określenia, który widok jest najbardziej odpowiedni. 27/31 Wzorzec Service To Worker spełnia tę samą rolę co Dispatcher View. RóŜnica polega na tym, Ŝe rola przekaźnika moŝe być duŝo większa niŝ we wzorcu Dispatcher View. Przekaźnik sam określa, do którego widoku wysłać Ŝądanie klienta. W tym celu moŝe wykorzystywać usługi biznesowe. 27
Struktura wzorca Client requests «servlet» Front Controller delegates dispatches Dispatcher 1 1..* «JSP» View 1 1 1 uses uses uses * * * Helper 28/31 RóŜnica w strukturze wzorca Service To Worker w stosunku do Dispatcher View polega na tym, Ŝe nie tylko komponent widoku korzysta z obiektów pomocniczych, ale równieŝ przekaźnik i kontroler frontowy. 28
Współdzia działanie anie komponentów Client «servlet» Front Controller Dispatcher «JSP» View Helper Value Object Business Service 1: Request 1.1: Retrieve 1.1.1: Get Data 1.2: Delegate 1.2.1: Choose View 1.2.1.1: Get Data 1.2.2: Dispatch 1.2.2.1: Get Property 29/31 Przy współdziałaniu komponentów we wzorcu Service To Worker: 1. Klient przesyła Ŝądanie do kontrolera frontowego 1.1 Kontroler odtwarza obiekt pomocniczy (np. poprzez zapamiętany identyfikator) 1.1.1 Obiekt pomocniczy pobiera dane z komponentu usługowego 1.2 Kontroler przekazuje Ŝądanie klienta wraz z danymi z komponentu usługowego do przekaźnika 1.2.1 Ten wykorzystuje obiekt pomocniczy do ustalenia, który widok będzie najbardziej odpowiedni 1.2.1.1 Obiekt pomocniczy moŝe potrzebować więcej danych z komponentu usługowego 1.2.2 Przekaźnik przekazuje Ŝądanie do wybranego widoku, który 1.2.2.1 pobiera dane z obiektu transferowego zwróconego przez komponent usługowy 29
Strategie i zalety jak dla wzorca Dispatcher View 30/31 Strategie implementacyjne wzorca oraz zalety jego stosowania są takie same jak dla wzorca Dispatcher View. 30
Literatura Przykłady i rysunki pochodzą z następuj pujących źródeł: http://java.sun.com/blueprints/corej2eepatterns/patterns/frontcontroller.html ntroller.html http://java.sun.com/blueprints/corej2eepatterns/patterns/dispatcherview.html herview.html http://java.sun.com/blueprints/corej2eepatterns/patterns/servicetoworker.html 31/31 31