550 likes | 794 Views
Jboss Seam & Seam Hotel Booking. Sommaire. Jboss Seam Présentation générale Les composants Seam Un modèle de programmation contextuelle Bijection dataModel Intégration JSF/EJB3 Validation des formulaires Navigation Le cycle de vie Seam Hotel Booking Présentation de l’application
E N D
Jboss Seam & Seam Hotel Booking
Sommaire • Jboss Seam • Présentation générale • Les composants Seam • Un modèle de programmation contextuelle • Bijection • dataModel • Intégration JSF/EJB3 • Validation des formulaires • Navigation • Le cycle de vie • Seam Hotel Booking • Présentation de l’application • Authentification • Recherche • Conversation • Sécurité • Navigation
Présentation générale • Framework Open Source développé par Jboss et initié par Gavin King, le père d’Hibernate • Conteneur léger J2EE • Seam est l’un des premiers Framework à supporter les architectures avec état grâce à la notion de conversation • Il utilise un modèle de programmation POJO
Présentation générale • Seam intègre de nombreuses technologies : • Le standard JSF ainsi que les composants graphiques RichFaces, IceFaces (composants graphiques riches), et Ajax4JSF (extension Ajax des composants JSF de base) • Seam Remoting : API JavaScript permettant d'accéder depuis le navigateur à un composant Seam coté serveur • Le standard EJB stateless et stateful d’EJB3 (pour les composants conversationnels) • Le standard JPA et les entités d'EJB 3 pour la persistance • JBoss Drools : sécurité déclarative par règles
Présentation générale • JBoss jBPM : processus fonctionnels et workflows • Support de GWT (Google Web Toolkit) • Les web services (possibilité d’utiliser un composant Seam en tant que web service) • Hibernate Search • Itext : génération de PDF • TestNG : framework pour faciliter la création de tests unitaires • …
Les composants Seam • Les composants prennent en charge les fonctionnalités et la logique métier de l’application web • Ils permettent également de gérer des événements de la couche « vue » ou d’intéragir avec la base de données • Ce sont des objets POJOs • Les composants sont définis par 3 éléments : Le nom, la portée et le rôle (gérés par des annotations)
Les composants Seam • Les composants Seam sont associés à des variables du contexte grâce à l’annotation @Name. Cette déclaration correspond à la balise <managed-bean-name> issue du fichier « faces-config.xml ». Le nom du composant doit être unique dans le système • Un composant Seam est obligatoirement déclaré dans un contexte : • Soit de manière implicite (en fonction du type de composant) • Soit de manière explicite en précisant le scope à l’aide de l’annotation @Scope
Les composants Seam • Un composant peut être rattaché à plusieurs variables de contexte. Le couple d’annotations @Name/@Scope permet de définir un « rôle » par défaut. Il est possible de l’enrichir en ajoutant de nouveaux « rôles » grâce à @Role(name=, scope=) ou @Roles({@Role(), @Role()}) • Seam admet 5 types de composants : • Stateless session bean (scope par défaut : STATELESS) • Stateful session bean (scope par défaut : CONVERSATION) • Entity bean (scope par défaut : CONVERSATION) • JavaBean (scope par défaut : EVENT) • Message-driven beans
Les composants Seam • Le cycle de vie d’un composant peut être géré par les annotations @Create et @Destroy (redéfinition des annotations @PostConstruct et @Predestroy): • @Create : Une méthode annotée de @create sera appelée immédiatement après la création du composant • @Destroy : Une méthode annotée de @destroy sera appelée avant la destruction du contexte
Les composants Seam @Scope(ScopeType.SESSION) @Name("MyDatabaseUtils") Public class MyDatabaseUtils{ @Create public void initDatabase(){ } @Destroy public void closeDbResources(){ } }
Un modèle de programmation contextuelle Ordre de priorité de recherche • Seam introduit de nouveaux contextes : • Event : C’est le plus « petit » contexte de Seam. Un composant associé à un contexte « Event » est détruit à la fin de la requête • Page : Un composant associé à un contexte « Page » est lié à une page spécifique. L’état est stocké sur le client • Conversation • Session : Le composant est géré comme un objet en session HTTP • Process • Application : Le composant associé au contexte « Application » est disponible du début à la fin de l’application. Ce contexte peut s’avérer utile pour le maintien d’informations statiques utiles à plusieurs composants
Un modèle de programmation contextuelle : La conversation • La Conversation : • Une conversation représente généralement une multitude de requêtes qui proviennent du même client • Permet de simplifier la gestion de la Session HTTP. Le contexte de conversation permet de résoudre les problèmes suivants : • Le client ne termine pas la transaction • Le navigateur du client n'accepte pas les cookies • Le client ouvre un autre navigateur et lance une autre action (achat par exemple) • Le client clique sur le bouton Back de son navigateur • Permet de contrôler la durée de vie d’un composant
Un modèle de programmation contextuelle : La conversation • Une conversation est attachée à UN onglet ou à UNE fenêtre d’un navigateur Un utilisateur peut « gérer » plusieurs conversations (donc plusieurs espaces de travail) en même temps sans que celles-ci ne rentrent en « collision » • Il existe 2 types de conversations : • La conversation implicite (ou conversation temporaire) : • Créée par défaut pour chaque requête • Elle démarre à la soumission de la requête et se termine au rendu de la réponse • La conversation explicite (ou conversation longue) : • Créée de manière déclarative (utilisation des annotations @Begin et @End) au niveau de l’application • Permet d’étendre une conversion temporaire en une conversion longue • Non détruite une fois la page de réponse rendue
Un modèle de programmation contextuelle : La conversation Implicite Explicite Par défaut Java pages.xml @Begin / @End JSF Page f:param conversationPropagationou @propagation in seam:link-Element
Un modèle de programmation contextuelle : Process • Le contexte « Process » : • Permet l'intégration de la gestion des processus métiers dans une application. • Il est géré par un moteur BPM (Business Process Management, ici, JBPM). • Il est multi-utilisateur • Il peut subir de nombreuses interactions
Un modèle de programmation contextuelle Application Process Session Request Request Request Conversation Conversation Session Request Request Request Conversation Conversation
Bijection @In et @Out • Seam propose un mécanisme d'injection des dépendances dynamique et bijective cela permet une interaction entre composant • Mécanisme à base d’annotation (pas de fichier XML) • Un composant peut être injecté automatiquement dans un autre composant (@In) • Un composant peut également créer et projeter un autre composant dans le contexte (@Out)
Bijection @In et @Out • Il est possible de forcer la création d’un composant au moment de l’injection (@In(create=true)) • Il est possible de projeter un composant en redéfinissant son scope (@Out(scope=ScopeType.SESSION)) • Seam crée le composant si il n’existe pas et gère son cycle de vie "Inversion of Control" (IoC)
Bijection@In et @Out @Stateful @Name("hotelBooking") public class HotelBookingAction implements HotelBooking { @In private User user; @In @Out private Hotel hotel; … }
Bijection@Factory • @Factory (factory data provider): permet d’initialiser une variable d’un composant Seam à la 1ère demande. En effet, @Factory vérifie que celle-ci est bien initialisée lorsque la couche de présentation demande l’objet mais ne la réinitialise pas si une donnée a été changée • L’annotation @Factory peut être utilisée de 2 manières : • Sur une méthode renvoyant void • Sur une méthode ne renvoyant pas void. Dans ce cas, il faut être attentif au scope : • Si le scope n’a pas été défini de manière explicite, la variable obtient le scope du composant • Si le scope du composant est défini à « Staless » et que le scope n’a pas été défini de manière explicite, le scope par défaut est « Event » • Dans les 2 cas : • La variable sera initialisée si celle-ci ne contient pas de valeur • Elle sera exposée dans le contexte avec le nom défini en paramètre de l’annotation @Factory
DataModel • @DataModel et @DataModelSelection sont directement liés à l’utilisation du composant JSF « dataTable » • @DataModel : Projette un attribut du type List, Map, Set ou Object[] et le convertit en une instance de DataModel (permet de récupérer l’élément actif à partir de la méthode getRowData()) • @DataModelSelection : Permet de conserver l’objet de la ligne courante d’une « dataTable ». Il est également possible de passer celui-ci directement dans une expression EL Objet courant <s:link value="View Hotel" action="#{hotelBooking.selectHotel(hot)}" />
DataModel • Utilisation des annotations @DataModel, @DataModelSelection et @Factory @DataModelprivate List <Person> fans; @DataModelSelection private Person selectedFan; @Factory("fans") public void findFans () { fans = em.createQuery("from Person").getResultList(); } public void delete () { em.remove(selectedFan); } Variable exposée dans le contexte La variable « fans » sera initialisée lors du 1er appel de la page Utilisation de l’objet courant
Intégration JSF/EJB3 • Jboss Seam implémente la JSR-299 (Web Beans) : Principe qui consiste à standardiser l’unification de 2 modèles de composants (JSF et EJB3). Permet de simplifier le modèle de programmation des applications Web • Disparition du Managed Bean et du fichier de configuration « faces-config.xml » • Composant métier invoqué directement depuis la page JSF via L'Expression Language
Intégration JSF/EJB3 <h:commandButton value="Register" action="#{register.register}"/> @Stateful @Name("register") public class RegisterAction implements Register{ @In private User user; @PersistenceContext private EntityManager em; public void register(){ em.persist(user); }}
Intégration JSF/EJB3 EJB3 Session Bean EJB3 Session Bean Page JSF Managed Bean Page JSF EJB3 Entity Bean EJB3 Entity Bean JSF et EJB3 : Approche « classique » JSF et EJB3 avec Jboss Seam
Intégration JSF/EJB3 JSF Page POJO Facade Session EJB Entity EJB POJO Facade JSF Page Managed Bean Session EJB JSF Page JSF Page Entity EJB
Validation des formulaires • Seam utilise le Framework « Hibernate Validator » pour effectuer la validation des contraintes à un seul endroit : le modèle de données • Evite la duplication des règles de validation sur les différentes couches (présentation, persistance, base de données, …) • Une vingtaine de contraintes prédéfinies avec la possibilité d’en créer d’autres (par implémentation de la classe Validator)
Validation des formulaires @Entity @Name("hotel") public class Hotel { private String name; @Length(max=50)@NotNull public String getName() { return name; } } <f:form> <s:validateAll> <h:inputText value="#{hotel.name}" /> </s:validateAll> </f:form> Permet de valider les champs du formulaire en utilisant Hibernate Validator
Validation des formulairesLes messages d’erreurs • 2 façons de gérer les messages d’erreurs : • En utilisant les standards JSF (la balise <h:message>. Cette balise ne rend qu’UN message. Possibilité d’afficher l’ensemble des messages en utilisant <h:messages>) • En utilisant l’approche Seam et sa balise <s:decorate> (plus complexe mais permet une meilleur gestion de l’affichage) • Il est possible de gérer un message d’erreur pour chaque champ. Pour cela, il est nécessaire de préciser l’identifiant du champ dans la balise « for » <h:inputText id="name" value="#{person.name}"/> <h:message for="name" />
Validation des formulairesLes messages d’erreurs • Dans le cas de l’utilisation des annotations du Framework Hibernate Validator : • En cas de violations de contrainte, l'exception « InvalidStateException » est levée création d’un tableau « d’InvalidValues » décrivant chaque échec • (Hypothèse) Le tableau retourné ne contient que les erreurs du champ concerné (via la méthode getInvalidValues()), les messages sont ajoutés au contexte (via la méthode addMessage()) et ceux-ci sont affichés au niveau de la couche présentation par la balise <h:message>
NavigationModèles de navigation • Il existe 2 modèles de navigation : • Le modèle de navigation sans état (stateless navigation model) • Règles standards de navigation de JSF • Règles de navigation de Seam • Le modèle de navigation avec état (stateful navigation model) : • jPDL
Navigation stateless navigation model • Renvoi d’une chaine de caractères (correspondant au nom de la page) avec possibilité de passage de paramètres • Utilisation des règles standards de navigation de JSF (fichier de configuration « faces-config.xml »). Cette méthode présente quelques limitations : • Impossible de définir des paramètres lors d’une redirection • Impossible de démarrer ou de finir une conversation • Impossible d’évaluer une EL expression (seulement la valeur de retour d’une méthode) public String search() { return "/results.jsp?pattern=#{searchAction.searchPattern}";}
Navigation stateless navigation model • Utilisation des règles de navigation de Seam (définies dans le fichier « pages.xml »). Ce mécanisme permet : • D’utiliser des EL expression • De commencer ou terminer une conversation • De passer des paramètres Document source <page view-id="/editDocument.xhtml"> <navigation from-action="#{documentEditor.update}"> <rule if="…"> <end-conversation/> <redirect view-id="/viewDocument.xhtml"> <param name="id" value="#{documentEditor.documentId}"/> </redirect> </rule> </navigation> </page> Eventuelle condition Fin de la conversation Document destination Passage de paramètres
Navigation stateful navigation model • Utilisation du langage de définition jPDL qui permet de décrire les différentes étapes par lesquelles doit passer un utilisateur pour réaliser une tâche • Le pageflow est composé d’états et de transitions • Il existe 2 sortes d’état : • Les décisions : permettent d'aiguiller la navigation en fonction de l'état du système. Chaque décision est associée à l'évaluation d'une expression (booléenne ou non) • Les pages : permettent d'aiguiller la navigation en fonction des actions de l'utilisateur • Les transitions permettent de passer d'un état à l'autre
Navigation stateful navigation model <pageflow-definition> <decision name="has book?" expression="#{}"> <transition name="true" to="rejected" /> <transition name=« false" to=« enter ISBN" /></decision><page name="rejected" view-id=".xhtml"> <end-conversation /> </page>…<page name="confirm checkout" view-id=".xhtml"> <transition name="confirm" to="confirmed"> <action expression="#{beanManager.method()}"/> </transition> <transition name="cancel" to="enter ISBN"/> </page>…</pageflow-definition>0 Méthode retournant un booléen Termine une conversation Méthode associée à une transition <input type="submit" jsfc="h:commandButton" value="Confirm" action="confirm" /> <input type="submit" jsfc="h:commandButton" value="Cancel" action="cancel" />
Présentation de l’application • Seam Hotel Booking est une application web de démonstration qui utilise les principaux concepts du Framework Jboss Seam (contexte conversationnel, intégration JSF/EJB3, …) • Elle permet : • De s’identifier • D’enregistrer un nouvel utilisateur • De rechercher un hôtel et d’en visualiser les détails • De réserver un hôtel (en précisant la date d’arrivée et de départ) • D’annuler une réservation • De visualiser les derniers hôtels sélectionnés (contexte de conversation)
Authentification • Basé sur JAAS (Java Authentication and Authorization Service) : Framework de sécurité robuste et hautement configurable pour la gestion d’authentification utilisateur. Assuré par la classe org.jboss.seam.security.Identity • Le champ login est « bindé » avec « identity.username », password avec « identity.password » (2 attributs de la classe Identity) tandis que le bouton appelle la méthode « identity.login » (vérifie si l’utilisateur est déjà connecté) Attribut username Attribut password Méthode login()
Authentification • L’application Seam Hotel Booking utilise sa propre méthode d’authentification. Elle permet simplement de vérifier que le couple login/password est bien présent dans la base de données • Cette méthode d’authentification doit être déclarée dans le fichier de configuration « components.xml » sous la forme suivante : • La méthode authenticate() renvoie un booléen : • True si le couple login/password (requête SQL) • False sinon <security:identity authenticate-method="#{authenticator.authenticate}"/>
Recherche • La recherche d’un hôtel est paramétrée par un motif (attributs de l’entity bean « Hotel » : nom, adresse, ville ou code postal de l’hôtel) et par le nombre de réponses retournées (5, 10, ou 20) • Utilisation de la balise <a:support> pour le support AJAX Permet de rafraîchir une zone (le résultat de la recherche) de la page à chaque événement JavaScript « onkeyup » (relâchement de la touche) Attribut searchString Méthode find() Attribut pageSize
Recherche • Utilisation de la balise @DataModel afin d’exposer, au contexte, une liste d’hôtels (résultat d’une requête SQL) @PersistenceContext private EntityManager em; @DataModel private List<Hotel> hotels; public void find(){ hotels = em.createQuery("select h from Hotel h where … ").getResultList(); }
Recherche • Utilisation du composant JSF « dataTable » pour afficher la variable « hotels » (liste des hôtels trouvés suite à la recherche) Panel rafraîchi lors de l’action « onkeyup » Liste à afficher Valeur de l’objet courant <a:outputPanel id="searchResults"> <h:dataTable id="hotels" value="#{hotels}" var="hot" rendered="#{hotels.rowCount>0}"><h:column><f:facet name="header">Name</f:facet> #{hot.name} </h:column> </h:dataTable></a:outputPanel> Condition de rendu Valeur de la colonne Entête de colonne
Conversation • Dans le cadre d’une réservation d’hôtel, l’achat DOIT se dérouler en 5 phases : • Phase 1 : l'utilisateur doit effectuer une recherche afin de sélectionner un hôtel. La conversation commence seulement au moment où l’utilisateur clique sur l’action « View Hotel », action rattachée à une méthode annotée par @Begin Résultat d’une recherche
ConversationPhase 1 <s:link value="View Hotel" action="#{hotelBooking.selectHotel(hot)}" /> Déclaration du composant « hotelBooking » @Stateful@Name("hotelBooking") public class HotelBookingAction implements HotelBooking{ @PersistenceContext(type=EXTENDED) private EntityManager em; @In(required=false) @Out private Hotel hotel; @Begin public void selectHotel(Hotel selectedHotel){ hotel = em.merge(selectedHotel); }} Permet d’injecter un EntityManager Gestion du mécanisme de bijection Permet de débuter une conversation longue
ConversationPhase 2 • Phase 2 : L’utilisateur accède au détail de l’hôtel qu’ il vient de sélectionner. Cette page lui permet de continuer sa réservation ou de revenir à sa recherche initiale • Phase 3 : Demande d’informations pour la prise en compte de la réservation (date de début et de fin, nombre de lits, …) • Phase 4 : Récapitulatif de la réservation
ConversationPhase 5 • Phase 5 : Confirmation de la réservation. Cette action fait appel à une méthode annotée par @End : Le contexte passe du statut de conversation longue à conversation temporaire. De plus, il y a enregistrement de la réservation dans la base de données