Seam framework in_action

Post on 06-Jun-2015

2.126 views 0 download

description

Presentation about Seam Framework

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ę