Seam framework in_action
-
Upload
michal-orman -
Category
Technology
-
view
2.126 -
download
0
description
Transcript of Seam framework in_action
Seam Framework w akcji
Szew
● Miejsce zszycia kawałków tkaniny, futra itp. wraz z nićmi, którymi te kawałki zszyto.
● Operacyjne zszycie tkanek miękkich.● Naturalne połączenie dwu przylegających do
siebie kości spojonych tkanką łączną.● Połączenie brzegów przedmiotów metalowych
za pomocą nitkowania lub spawania.
JSF
EJB
JSF
Wicket
RichFacesICEFaces
AJAX
GWT
EJB
SpringJPA
Hibernate
Guice
Seam
Facelets
jBMP
DroolsWebBeans
Groovy
one framework to rule them all...
Czym jest Seam?
Stos aplikacji
● Integruje różne frameworki z różnych warstw JEE● Widoku (JSF, RichFaces, ICEFaces, Wicket)● Dostępu do danych (JPA, Hibernate)● i więcej ...
Kontener komponentów
● Integruje komponenty JPA, EJB, POJO oraz JSF w taki sposób, że zaciera granice pomiędzy poszczególnymi warstwami i daje wrażenie korzystania z jednego kontenera
Komponenty i kontenery
Kontener Tworzenie komponentu
EJB @Stateful, @Stateless, @MessageDriven, ejb-jar.xml
JSF faces-config.xml
Spring applicationContext.xml
Kontener servletów web.xml
Komponenty i kontenery
Kontener Tworzenie komponentu
EJB @Stateful, @Stateless, @MessageDriven, ejb-jar.xml
JSF faces-config.xml
Spring applicationContext.xml
Kontener servletów web.xml
Seam @Name, components.xml
Facelets / RichFaces / JSP
JSF
Seam
EJB / Spring / JPA / Hibernate
Seam context variable
Definiowanie komponentu Seam
1. @Name("foo")2. @Scope(ScopeType.CONVERSATION)3. public class Foo { }
Wstrzykiwanie zależności
1. @Name("bar")2. public class Bar {3. 4. @In5. private Foo foo;6.7. }
Wstrzykiwanie zależności cd...
1. @Name("bar")2. public class Bar {3. 4. @In(create = true)5. private Foo foo;6.7. }
Wstrzykiwanie zależności cd...
1. @Name("bar")2. public class Bar {3. 4. @In("#{foo}")5. private Foo foo;6.7. }
Inversion of Control
● Wzorzec projektowy „rozluźniający” powiązania pomiędzy komponentami
● Pozwala komponentom na skupienie się na wykorzystywaniu innych komponentów (serwisów) niż ich wyszukiwaniu
● Wszyscy go dobrze znają...
Inversion of Control
● Zwykle ludzie mówiąc o IoC mają na myśli dependency injection (DI), jeden z przypadków użycia IoC
● Jednakże IoC > DI
DI jest zbyt statyczne
Problem z klasycznym podejściem do DI jest taki, że wstrzykiwanie zależności odbywa się tylko raz, tuż po stworzeniu instancji komponentu. Komponent jest przywiązany do referencji obiektów wstrzykniętych mu w czasie tworzenia.
Komponenty powinny być świadome swojego istnienia w kontekście i powinny brać aktywny udział w zarządzaniu komponentami.
IoC w Seam
● W Seam mamy do czynienia z dependency bijection
● W Seam zależności są wstrzykiwane dynamicznie w czasie życia komponentu, a nie tylko podczas jego tworzenia
● Dependency bijection pozwala na wstrzykiwanie zależności do komponentu, jak i umieszczanie komponentów w kontekście
● Bijection = injection + outjection
Bijection
● Bijection jest to kombinacja injection i outjection● Bijection zachodzi w momencie wywołania
metody (a nie tworzenia komponentu)● Injection w momencie wywołania metody● Outjection po powrocie metody
Disinjection
● Wszystkie pola komponentów do których zostały wstrzyknięte zależności otrzymują wartość null.
● Zapobiega wyciekom pamięci● Rozwiązuje problem serializacji obiektów
Bijection interceptor
Powrót z metody
Komponent wywołującymetodę
Wstrzykiwanie zależnościw pola oznaczone adnotacją @In
Wywołanie metody
Outjection na polach oznaczonychadnotacją @Out
Disinjection na polachoznaczonych adnotacją @In
Interceptor
Bijection w akcji1. @Name("authenticator")2. public class Authenticator {3. @In4. private Credentials credentials;5.6. @Out(scope = ScopeType.SESSION, required = false)7. private Long currentUserId;8.9. @In(create = true)10. private UserQuery userQuery;11.12. public boolean authenticate() {13. userQuery.setUsername(credentials.getUsername());14. User user = userQuery.getSingleResult();15. if (isPasswordValid(credentials.getPassword(), user)) {16. currentUserId = user.getId();17. }18. }19.20. // pozostałe metody21. }
Inne formy wstrzykiwania zależności
● @RequestParameter
● @PersistenceContext
● @DataModelSelection
● @DataModelSelectionIndex
Inne formy dependency outjection
● @DataModel
● @Factory
● @Unwrap
Omijanie bijection
● Wewnętrzne wywołania metod● @BypassInterceptors
Generowanie zdarzeń
Zdarzenia
● Seam posiada wbudowane wsparcie dla wzorca observer, który w znacznym stopniu pozwala redukować zależności między komponentami (budować luźne powiązania)
● Seam pozwala na generowanie zdarzeń z metod jak i ich obsługę z metod komponentów
● Zdarzenia mogą być generowane na wiele sposobów, jak i można generować wiele typów zdarzeń (synchroniczne, asynchroniczne, czasowe, transakcyjne)
Generowanie zdarzeń
1. public String register() {2. // registration action3. Events.instance().raiseEvent("registered");4. return "success";5. }
Generowanie zdarzeń
1. @In private Events events;2.3. public String register() {4. // registration action5. entityManager.persist(newUser);6. events.raiseTransactionSuccessEvent(7. "registered", newUser);8. return "success";9. }
Generowanie zdarzeń
1. @RaiseEvent("registered")2. public String register() {3. // registration action4. return "success";5. }
Generowanie zdarzeń
1. <page view-id="/register.xhtml">2. <navigation 3. from-action="#{userManager.register}">5. <rule if-outcome="success">6. <raise-event type="registered" />7. <redirect 8. view-id="/registered.xhtml" />9. </rule>10. </navigation>11. </page>
Obserwowanie zdarzeń
1. @Name("registrationObserver")2. public class RegistrationObserver {3. @Logger private Log logger;4. 5. @Observer(value="registered", create=true)6. public void onRegisteredEvent(User newUser) {7. logger.info("Registered new user: "8. + newUser.getUsername());9. }10.11. }
Obserwowanie zdarzeń
1. <event type="registered">2. <action 3. execute=4. "#{registrationObserver.onRegisterEvent(newUser)}"5. />6. </event>
Wbudowane zdarzenia
● Inicjalizacja kontenera seamowego● Przypisanie zmiennej kontekstowej (added,
removed)● Zdarzenia cyklu życia komponentów (created,
destroyed)● Zdarzenia związane z autentykacją● Zdarzenia związane z transakcją● i wiele innych...
Metody fabrykujące
Metody fabrukujące w Seam
● Seam posiada wbudowane wsparcie dla wzorca factory method
● Fabryka ma na celu dostarczenie konkretnych danych zamiast instancji komponentu
● Metody fabrykujące oznacza się adnotacją @Factory lub w deskryptorze komponentów
● Metody fabrykujące z założenia mają wypierać użycie „getterów”
Metoda fabrykująca jest wykonywana tylko raz, kolejne wywołania zwracają utworzoną i
umieszczoną w kontekście wcześniej wartość.
Rezultat wywołania fabryki
● Rezultat metody fabrykującej może być przekazany na trzy sposoby:● Przez zwrócenie wartości z metody● Przez outjection (jako, że metoda fabrykująca
znajduje się w komponencie to działa na nim bijection)
● Przez umieszczenie wartości bezpośrednio w kontekście
Przykład metody fabrykujacej
1. @Name("currentUserFactory")2. public void CurrentUserHome {3. @In private EntityManager em;4. 5. @In private Long currentUserId;6.7. @Factory("currentUser")8. public User getCurrentUser() {9. return em.find(User.class, currentUserId);10. }11. }
Przykład metody fabrykujacej
1. @Name("currentUserFactory")2. public void CurrentUserHome {3. @In private EntityManager em;4. 5. @In private Long currentUserId;6.7. @Out private User currentUser;8.9. @Factory("currentUser")10. public User getCurrentUser() {11. currentUser = 12. em.find(User.class, currentUserId);13. }14. }
Unwrap
Unwrap
● Seam pozwala dostarczać dane do kontekstu za pomocą metody oznaczonej adnotacją @Unwrap
● W odróżnieniu od metody fabrykującej taka metoda jest wywoływana za każdym razem, kiedy komponent pod konkretną nazwą jest żądany
@Unwrap a @Factory
● Jest prawdziwym komponentem● Można konfigurować w components.xml
● Można używać adnotacji @Create i @Destroy
● Metoda oznaczona @Unwrap jest wywoływana zawsze
● Komponent może przechowywać stan i obserwować zdarzenia
Metody oznaczone @Unwrap można używać jako swoisty alias.
1. @Name("org.jboss.seam.faces.facesContext")2. @Scope(ScopeType.APPLICATION)3. public class FacesContext {4. @Unwrap public FacesContext getContext() {5. return FacesContext.getCurrentInstance();6. }7. }
@Unwrap w akcji
Method interceptors
Interceptory w Seam
● Seam rozszerza funkcjonalność interceptorów z EJB● Przenosi ją ze świata Java EE to świata POJO
● Pozwala w deklaratywny sposób dodawać logikę AOP
Definiowanie interceptora
1. @AroundInvoke2. public Object methodName(InvocationContext ctx)3. throws Exception { 4. // interceptor logic...5. return ctx.proceed();6. }
Definiowanie interceptora
1. @Interceptor(stateless = true, 2. type = InterceptorType.CLIENT3. public class StatelessInterceptor {4. 5. @AroundInvoke6. public Object methodName(InvocationContext ctx)7. throws Exception { 8. // interceptor logic...9. return ctx.proceed();10. }11.12. }
Cechy interceptora w Seam
● Interceptor może być stateful-owy lub stateless-owy
● Interceptory mogą być przypisywane do komponentów zarówno po stronie serwera jak i klienta
● Interceptory mogą być przypisywane do klas deklaratywnie (stereotypes)
Przypisywanie interceptorów
● @Interceptors
Przypisywanie interceptorów
● @Interceptors
● W Seamie, w odróżnieniu od EJB, adnotacja @Interceptors nie jest nakładana na klasy komponentów, które chcemy interceptować
● Adnotacja ta to meta adnotacja którą nakłada się na inne adnotacje
Przypisywanie interceptorów
1. @Target(TYPE)2. @Retention(RUNTIME)3. @Interceptors(CurrentUserAccessInterceptor.class)4. public @interface VerifyCurrentUserAccess {}5.6. @Name("offerAddAction")7. @VerifyCurrentUserAccess8. public class OfferAddAction { }
Model kontekstowy w Seam
Konteksty w Java Servlet API
● Request● Session● Application
Konteksty w Seam
● Event (request)● Page● Conversation● Session● Application● Business process
Konteksty w Seam
Przeglądarka
Strona 1 Strona 1 Strona 2 Strona 3
event event event event
page page page
conversation conversation
session
Konwersacja
Konwersacja w Seam
● Pozwala przechowywać stan komponentów pomiędzy kolejnymi żądaniami (nawet w przypadku występowania przekierowań)
● Jest bardziej ziarnista niż kontekst sesji● W deklaratywny sposób można kontrolować
zakresy konwersacji (oraz jej propagacje)● Na czas konwersacji Seam rozpina transakcje
(utrzymywana jest sesja połączenia z bazą danych)
Konwersacja w Seam została pomyślana jako kontekst, który przechowuje stan
komponentów w ramach konkretnego przypadku użycia.
A czemu nie w sesji?
● Sesja jest zbyt długa i przechowywanie danych w tym kontekście może prowadzić do wycieków pamięci
● Sesja jest dzielona pomiędzy karty przeglądarki● Sesja nie posiada żadnych mechanizmów
obronnych przed współbieżnym dostępem do danych
● Błędy występujące przez nieprawidłowe wykorzystywanie kontekstu sesji mogą być trudne do reprodukcji w środowisku testowym
Mimo wszystko rozwiązanie leży w... sesji. Trzeba ją tylko odpowiednio podzielić i zarządzać.
Konwersacja to wydzielony segment sesji
cid = 1 cid = 2
cid = 3 cid = 4
Sesja HTTP
Seam zarządza konwersacją
● Każda konwersacja posiada określony czas trwania (timeout)
● Każda konwersacja toczy się w izolacji (nie trzeba się przejmować synchronizacją)
Wymagania konwersacji
● Komponenty muszą implementować interfejs java.io.Serializable
● Czas trwania sesji musi być dłuższy niż czas trwania konwersacji
Stany konwersacji
● Tymczasowa (temporary conversation)● Długa (long-running conversation)● Zagnieżdżona (nested conversation)
Stany konwersacji
● Tymczasowa (temporary conversation)● Długa (long-running conversation)● Zagnieżdżona (nested conversation)
Przejścia stanów konwersacji
Temporary Long-running
begin
end
brak konwersacji kontynuacja konwersacji
Zniszczona Zniszczona
koniec żądania sesja lub konwersacja wygasła
Stany długiej konwersacji
Zniszczona
Foreground Background
Long-running
begin
end
resumed
resumed
concurrent conversation resumed
session timeout conversation or session timeout
Definiowanie zakresów długiej konwersacji
Za pomocą adnotacji1. @Name("bookingAction")2. @Restrict("#{s:hasRole('Customer')}")3. public class BookingAction {4. 5. @Begin(join = true)6. public void fetchRooms() {7. // fetching rooms...8. }9. 10. @End(beforeRedirect = true)11. public void confirm() {12. // confirm booking...13. }14.15. @End(beforeRedirect = true)16. public void cancel() {17. // cancel booking...18. }19. 20. }
Za pomocą adnotacji
1. @Name("roomsFactory")2. public void RoomsFactory {3. @Begin(join = true)4. public void fetchRooms() {5. // fetching rooms...6. }7. }8.9. @Name("bookingAction")10. public void BookingAction {11. @End(beforeRedirect = true)12. public void confirm() {13. // confirmation action...14. }15. }
W deskryptorze strony
1. <page view-id="/booking.xhtml">2. <begin-conversation join="true" />3. </page>
W definicjach nawigacji
1. <page view-id="/booking.xhtml">2. <navigation 3. from-action="#{roomsFactory.fetchRooms}"> 4. <begin-conversation join="true" />5. </navigation>6. 7. <navigation from-action="#{bookingAction.confirm}">8. <end-conversation before-redirect="true" />9. </navigation>10. </page>
Za pomocą komponentów JSF
1. <s:link view-id="/booking.xhtml" propagation="begin" />2.3. <s:button action="#{bookingAction.confirm}" 4. propagation="end" />5.6. <h:commandButton action="#{bookingAction.confirm}">7. <s:conversationPropagation type="end" />8. </h:commandButton>
Zaawansowane zarządzanie stronami w Seam
Cykl JSF
JSF Servlet
Restore View
Apply Request Values
Process Validations
Update ModelValues
InvokeApplication
RenderResponse
Cykl JSF (initial request)
JSF Servlet
Restore View
Apply Request Values
Process Validations
Update ModelValues
InvokeApplication
RenderResponse
Przeglądarka
Cykl JSF (postback)
JSF Servlet
Restore View
Apply Request Values
Process Validations
Update ModelValues
InvokeApplication
RenderResponse
Przeglądarka
Nawigacja w JSF
1. <navigation-rule>2. <from-view-id>/login.xhtml</from-view-id>3. <navigation-case>4. <from-action>5. #{authenticator.login}6. </from-action>7. <from-outcome>loggedIn</from-outcome>8. <to-view-id>/home.xhtml</to-view-id>9. <redirect/>10. </navigation-case>11. </navigation-rule>
Problemy z JSF
● Skrócony cykl JSF● Służy jako dostawca strony● Zakłada, że nie potrzeba wykonywania żadnej logiki
przed wyrenderowaniem strony● Zakłada, że użytkownik posiada uprawnienia do
żądanego zasobu
● Zorientowanie na żądania typu POST● Mało rozwinięte reguły nawigacji
● Brak możliwości wykorzystania EL w regułach
Seam page descriptor
Do czego służy deskryptor strony
● Definiowania reguł nawigacji● Generowania komunikatów (FacesMessages)● Mapowania parametrów URL● Dodawania parametrów URL (dla
przekierowań)● Uruchamiania akcji zanim widok zostanie
wyrenderowany
Do czego służy deskryptor strony cd...
● Sprawdzania reguł bezpieczeństwa● Kontrolowania zakresów konwersacji● Kontrolowania zakresów page flow● Kontrolowania zakresów procesów
biznesowych i zadań● Wywoływanie eventów● Obsługa wyjątków
Deskryptor jest świadomy kontekstu aplikacji
Inteligentna nawigacja w Seam
● Pozwala użyć dowolnej wartości (dostępnej za pomocą EL) do definiowania przekierowań
● Reguły nawigacji są warunkowe● Definiuje jak powinna być propagowana
konwersacja, czy proces biznesowy● Pozwala dodawać komunikaty JSF przed
wyrenderowaniem strony bądź przekierowaniem● Pozwala dodawać parametry URL● Pozwala wywoływać eventy
Definiowanie nawigacji
1. <page view-id="/login.xhtml" scheme="https">2. <navigation>3. <rule if="#{identity.loggedIn}">4. <redirect view-id="/home.xhtml" />5. </rule>6. </navigation>7. </page>
Definiowanie nawigacji
1. <page view-id="/offer/add.xhtml" login-required="true">2. <navigation from-action="#{offerManager.addOffer}">3.4. <rule if-outcome="offerAdded" 5. if="#{offerManager.addNext}">6. <redirect view-id="/offer/add.xhtml" />7. </rule>8.9. <rule if-outcome="offerAdded">10. <redirect view-id="/offer/list.xhtml">11. <param name="userId" 12. value="#{currentUserId}" />13. </redirect>14. </rule>15.16. </navigation>17. </page>
1
2
Definiowanie nawigacji
1. <page view-id="*">2. <navigation from-action="#{identity.logout}">3. <end-conversation before-redirect="true" />4. <redirect view-id="/home.xhtml" />5. </navigation>6.7. <navigation 8. from-action="#{quickSearchAction.performSearch}">9. <redirect view-id="/offers/index.xhtml" />10. </navigation>11.12. <navigation>13. <rule if-outcome="accessDenied">14. <redirect view-id="/denied.xhtml" />15. </rule>16. </navigation>17. </page>
1
2
3
Definiowanie nawigacji
1. <pages login-view-id="/login.xhtml" 2. no-conversation-view-id="/home.xhtml">3.4. <exception5. class="org.jboss.seam.ConcurrentRequestTimeoutException">6. <http-error error-code="503" />7. </exception>8.9. <exception class="org.jboss.seam.security.AuthorizationException">10. <redirect view-id="/denied.xhtml" />11. </exception>12.13. <exception class="javax.persistence.OptimisticLockException">14. <end-conversation />15. <redirect view-id="/error.xhtml" />16. </exception>17.18. </pages>
1
2
3
Definiowanie nawigacji
1. public String goToHome() {2. return "/home.xhtml";3. }
Mapowanie parametrów strony
● Seam pozwala na ustawianie wartości pól komponentu w skróconym cyklu JSF (JSF pozwala tylko w postbacku)
● Parametrem strony może być wartość z formularza (POST) jak i parametr URL (GET)
● Parametry mogą być przypisane do dowolnych pól każdego komponentu Seam (osiągalnego za pomocą EL)
Mapowanie parametrów URL
http://localhost:8080/profile.seam?userId=1
1. <page view-id="/profile.xhtml" login-required="true">2. <param name="userId" value="#{userManager.userId}" />3. </page>
Właściwości parametrów
● Do każdego parametru można zdefiniować JSF-owe walidatory i konwertery takie jak definiuje się dla normalnego postbackowego cyklu
● Można zdefiniować, że parametr jest wymagany bądź opcjonalny
● Parametrów nie trzeba przypisywać do konkretnych pól komponentu, takie parametry mogą po prostu przechowywać dane między kolejnymi żądaniami
URL rewrite
● Seam pozwala na przepisywanie adresów URL zgodnie z szablonem zdefiniowanym w deskryptorze strony
● Seam przepisuje URL-e zarówno żądań przychodzących jak i odpowiedzi aplikacji
URL rewrite
http://localhost:8080/users/1/profile
1. <page view-id="/profile.xhtml" login-required="true">2. <rewrite pattern="/users/{userId}/profile" />3. <param name="userId" value="#{userManager.userId}" />4. </page>
Komponenty przepisujące URL-e
● <s:link> (<h:commandLink>)
● <s:button> (<h:commandButton>)
Komponenty przepisujące URL-e
● <s:link> (<h:commandLink>)
● <s:button> (<h:commandButton>)
● <s:form>
Akcje strony
● Seam pozwala na wykonywanie akcji dla danej strony (view-id) tuż przed jej wyrenderowaniem (nawet w skróconym przebiegu JSF)
● Są to standardowe akcje dla których można definiować reguły nawigacji (wywoływane jednak przed szóstą fazą JSF a nie w piątej!)
● Akcje te mogą służyć do zainicjalizowania widoku – załadowania danych potrzebnych do wyrenderowania strony● Pozwala to na implementacje RESTful-owych URL-i
Akcje strony
http://localhost:8080/users/1/profile
1. <page view-id="/profile.xhtml" login-required="true">2. <rewrite pattern="/users/{userId}/profile" />3. <param name="userId" value="#{userManager.userId}" />4. <action execute="#{userManager.fetchUser}" />5. </page>
Czego Seam nie poprawia
● Komunikatów JSF● Walidacja i konwersja dotyczą tylko
pojedynczych wartości, nie jest łatwo zaimplementować tych operacji na zależnych polach (i trzeba przenosić ten proces do piątej fazy)
● Wstrzykiwanie zależności do walidatorów i konwerterów (można je pobierać za pomocą metod statycznych)
Czego nie omówiono w prezentacji?
● Deskryptor komponentów components.xml
● Seam-gen● WebBeans (JSR-299)● Workspace i pageflow w kontekście konwersacji● Wsparcia Seam dla warstwy widoku
● Komponenty seamowe● Wsparcie dla AJAX
● Testowanie integracyjne● Symulacja cyklu JSF
● Kontekstu business process
Czy Seam daje radę?
Do poduszki
Q&A
Dziękuję za uwagę