TP11_EJB3_GlassFish

TP11_EJB3_GlassFish

Modifié par Pascal GRAFFION le 2015/09/28 17:09

TP 11 - Stateless/Stateful EJB3 dans Java EE 6 (avec GlassFish)

Les Entreprise Java Bean, ou EJB, sont des composants serveurs qui respectent les spécifications d'un modèle édité par Sun. Ces spécifications définissent une architecture, un environnement d'exécution (un serveur d'application) et un ensemble d'API. Ainsi, les EJB possèdent certaines caractéristiques comme la réutilisabilité, la possibilité de s'assembler pour construire une application, etc ...

Le respect de ces spécifications permet d'utiliser les EJB de façon indépendante du serveur d'applications dans lequel ils s'exécutent. Celui-ci fournit un ensemble de fonctionnalités telles que la gestion du cycle de vie du bean, la sécurité, l'accès concurrent et les transactions. En réalité, c'est dans un conteneur que s'exécute un EJB et il lui est impossible de s'exécuter en dehors.

Le but des EJB est de faciliter la création d'applications distribuées pour les entreprises. Ils sont en fait des objets distants utilisant RMI/IIOP (un mélange de la syntaxe RMI et du protocole de communication utilisé par Corba) : cela permet de garantir que les EJB pourront être utilisés via un quelconque code client.

Les composants EJB se classent dans deux catégories :

  • Session Beans (Stateless/Stateful)
  • Message Driven Beans

(Les composants Entity Beans présents en EJB2 ont disparu en EJB3; les EJB entité sont directement liés à la base de données via un mapping objet-relationnel au travers de JPA.)

Par opposition aux Stateless Session Beans (composants sans état), les Stateful Session Beans (avec état) associent les requêtes à un client spécifique, unissant client et bean dans une relation un à un. Ce type de composant fournit ainsi un ensemble de traitements via des méthodes, mais il a aussi la possibilité de conserver des données entre les différents appels de méthodes d'un même client. Une instance particulière est donc dédiée à chaque client qui sollicite ces services et ce tout au long du dialogue entre les deux entités.

Les données conservées par le bean sont stockées dans les variables d'instances. Les données sont donc conservées en mémoire. Généralement, les méthodes proposées par le composant permettent de consulter et de mettre à jour ces données. 

La plateforme J2EE possède deux technologies lui permettant de stocker l'état d'un client : les sessions HTTP et les Stateful Session Bean. Le HTTPSession est plutôt utilisé pour stocker la logique de présentation alors que le bean avec état stocke la logique métier.

Hello PetStore !

L'exemple ci-après utilise un Stateful Session Bean pour stocker des chaînes de caractères.

Tout comme les Stateless Session Bean, les composants avec états sont au moins constitués de deux fichiers :

  • L'interface métier HelloService permet de définir les services que propose le composant EJB. Elle définit les méthodes métiers qu'expose le composant. Dans notre cas les deux méthodes addHello() et sayHello().
import javax.ejb.Remote;

@Remote
public interface HelloService {
    String JNDI_NAME = "java:global/hello/hello-ejb/HelloSB!HelloService";

   void addHello(String hello);
    String sayHello() ;
}
  • La classe d'implémentation du bean (HelloServiceBean), c'est-à-dire le code métier à proprement parler. La différence majeure avec un bean sans état, est que nous pouvons déclarer des attributs, mais surtout, les utiliser en étant sûr de leur contenu. Ainsi, dans notre composant, il y a un attribut de type String (1) qui stocke les chaînes de caractères envoyé par le client. La méthode sayHello (2) renvoie le contenu de cet attribut alors que la méthode addHello (3) concatène de nouvelles chaînes de caractère.
import javax.ejb.Stateful;

@Stateful (name="HelloSB")
public class HelloServiceBean implements HelloService {

   // ======================================
   // =             Attributes             =
   // ======================================
   private String buffer = "";

   // ======================================
   // =            Constructors            =
   // ======================================
   public HelloServiceBean() {
   }

   // ======================================
   // =     Category Business Methods      =
   // ======================================
   public String sayHello() {
       return buffer;
   }

   public void addHello(String hello) {
        buffer += hello + " ";
   }
}

(Noter que l'interface de fabrique nécessaire en EJB2 a disparu. Plus besoin non plus du descripteur de déploiement ejb-jar.xml.)

Il ne reste plus qu'à développer la classe cliente. Celle-ci a tout d'abord besoin d'obtenir le contexte initial de JNDI (1) pour retrouver l'EJB qui porte le nom HelloService.JNDI_NAME (utilisé aussi dans l'annotation @Stateful). Elle obtient ainsi l'interface HelloService. Ce n'est qu'à partir de cette interface remote que la classe cliente pourra appeler les méthodes métiers du composant (3).

import javax.naming.InitialContext;
import javax.naming.Context;
import java.util.Hashtable;

public class Main {

   public static void main(String[] args) {

        InitialContext ic = null;

       try {
            ic = new InitialContext(); // (1)
           HelloService hello = (HelloService) ic.lookup(HelloService.JNDI_NAME);  // (2)
           hello.addHello("Hello"); // (3)
           System.out.println(hello.sayHello()); // (3)
           hello.addHello("Petstore");
            System.out.println(hello.sayHello());
            hello.addHello("!");
            System.out.println(hello.sayHello());
       } catch (Exception e) {
            e.printStackTrace();
       }
   }
}

Note : le code de la classe Main pourrait être simplifié en utilisant une simple annotation @EJB pour déclarer et instancier l'EJB ... à condition qu'il soit exécuté dans un container léger (ACC) :

import javax.ejb.EJB;

public class Main2 {
   @EJB
   private static HelloService hello;
   /*
     * Warning : NullPointerException at execution time
     * because only a container can inject the reference.
     * => should be executed in a ACC (Application Client Container)  :
     *  %GLASSFISH_HOME%/bin/appclient Main2
     */

   
   public static void main(String[] args) {

       try {
            hello.addHello("Hello");
            System.out.println(hello.sayHello());
            hello.addHello("Petstore");
            System.out.println(hello.sayHello());
            hello.addHello("!");
            System.out.println(hello.sayHello());
       } catch (Exception e) {
            e.printStackTrace();
       }
   }
}

Outils

Pour qu'un EJB puisse être utilisé, il doit s'exécuter à l'intérieur d'un serveur d'application (un container). Il existe plusieurs serveurs d'applications respectant les spécifications J2EE et pouvant déployer des EJB. Nous utiliserons GlassFish 4.

Pour installer GlassFish, vous devez télécharger l'archive GlassFish 4 puis la dézipper sur votre ordinateur. Vous devez alors trouver dans %GLASSFISH_HOME% les  principaux répertoires suivants:

RépertoireContenu
bintous les scripts permettant le démarrage, l'arrêt de GlassFish, notamment le script startserv.bat pour windows et startserv.sh pour linux
modulestoutes les librairies utilisées pour le fonctionnement du serveur GlassFish
domains/domain1tout le nécessaire pour la version "standalone" du serveur.

C'est dans le répertoire domains/domain1/autodeploy que l'on installera nos applications.
[...]\GLG203\TP11_EJB3\Hello> ant deploy
   [...]
build:
     [echo] Creates the Hello PetStore EJB Jar
deploy:
     [copy] Copying 1 file to C:\Applications\java\glassfish-4.0\glassfish\domains\domain1\autodeploy

BUILD SUCCESSFUL

Le lancement de GlassFish se fait par la commande %GLASSFISH_HOME%/bin/startserv domain1 .

Le serveur détecte alors la présence de l'EJB hello.ear et le déploie sous le nom java:global/hello/hello-ejb/HelloSB.
> %GLASSFISH_HOME%/bin/startserv domain1
...
Infos:   Selecting file C:\Applications\java\glassfish-4.0\glassfish\domains\domain1\autodeploy\hello.ear for autodeployment
...
Infos:   EJB5181:Portable JNDI names for EJB HelloSB: [java:global/hello/hello-ejb/HelloSB, java:global/hello/hello-ejb/HelloSB!HelloService]
Infos:   EJB5182:Glassfish-specific (Non-portable) JNDI names for EJB HelloSB: [HelloService, HelloService#HelloService]
Infos:   hello a été déployé en 938 ms.
Infos:   [AutoDeploy] Déploiement automatique exécuté : C:\Applications\java\glassfish-4.0\glassfish\domains\domain1\autodeploy\hello.ear.  

On peut constater sur le résultat de l'exécution que le composant Stateful conserve son état entre deux appels; la méthode hello() appelée a bien concaténé les chaînes de caractères qu'on lui a passées en argument.

[...]\GLG203\TP11_EJB3\Hello> runClient.bat
java -cp classes;C:\Applications\java\glassfish-4.0\glassfish\lib\gf-client.jar Main
Hello
Hello Petstore
Hello Petstore !

A chaque fois que l'on relance l'application, un résultat identique est affiché; ce qui montre que le Stateful Session bean est bien attaché à un seul client.

GlassFish fournit aussi une console d'administration à l'adresse http://localhost:4848 dans laquelle on peut retrouver les EJB déployés.

Expression des besoins

Depuis que le catalogue des animaux de compagnie est consultable en ligne par n'importe quel internaute, YAPS doit faire face à de sérieux problèmes de montée en charge. En effet, son serveur fonctionne maintenant 24h/24 et 7j/7 et doit continuer à supporter les traitements effectués par ses employés et franchisés.

Le problème du service 24/7, c'est que le serveur doit toujours être disponible. En cas de panne, ou de tâche administrative (arrêt et démarrage), c'est toute la production qui est arrêtée. Pour palier ces problèmes de disponibilité et de montée en charge, YAPS décide d'acheter de nouveaux serveurs et de les monter en cluster.

Un cluster est un groupe physique de serveurs qui exécutent simultanément des applications tout en donnant l'impression au monde extérieur de ne constituer qu'un seul serveur logique. On peut ajouter ou retirer dynamiquement des serveurs du cluster en fonction de la charge (c'est-à-dire du volume de clientèle). Des équilibreurs de charge sont utilisés pour distribuer les requêtes des clients entre les différents serveurs du cluster. Ils équilibrent la charge en répartissant les requêtes des clients sur plusieurs serveurs.

De plus, YAPS se rend compte que certains internautes saisissent des URL erronées dans leur navigateur et atterrissent sur la page d'erreur par défaut. Elle veut intercepter cette erreur et afficher une page contenant son logo et permettant de rediriger l'internaute sur la bonne adresse.

Après avoir fait une étude de marché, YAPS veut mettre également en place un système de caddy électronique pour ses clients. Ces derniers, une fois authentifiés, pourront consulter le catalogue d'animaux domestiques et avoir la possibilité de les placer dans un caddy. Ils pourront en ajouter autant qu'ils le désirent, en modifier la quantité, les supprimer du caddy puis, « passer à la caisse ». Lorsque le client valide son caddy, une commande est créé automatiquement.

Vue utilisateur

Hormis la gestion du caddy, les besoins utilisateurs restent inchangés puisque cette nouvelle évolution est purement technique et non fonctionnelle. L'application Petstore Server doit être réarchitecturée pour permettre une meilleure disponibilité et évolutivité.

Diagramme de cas d'utilisation

UCCaddy.png

Figure 1 - Use case concernant le caddy du client

Cas d'utilisation « Gérer son caddy »

NomGérer son caddy.
RésuméPermet à un client de gérer son caddy d'animaux domestiques.
ActeursInternaute.
DescriptionLorsqu'un internaute s'authentifie, il peut alors visualiser le catalogue d'animaux domestiques et en ajouter dans son caddy (shopping cart). Un menu Add to cart (ajouter au panier) vient s'ajouter en face de chaque article. Un simple clic permet d'ajouter un article. Si l'internaute veut modifier cette quantité par défaut, il peut le faire. Ainsi que supprimer un article qu'il ne voudrait plus. Lorsque la quantité d'un article est inférieure ou égale à zéro, l'article est automatiquement supprimé du caddy.Durant sa session, le client peut visualiser le contenu de son caddy quand bon lui semble. Lorsque le caddy est vide, un message en avertit le client. Lorsqu'il contient au minimum un article, le système l'affiche avec sa description, sa quantité, son prix unitaire et le sous-total (prix unitaire * quantité). Le montant total du caddy est aussi renseigné.Ce caddy est illimité en taille, un client peut donc acheter autant d'articles qu'il souhaite.
Post-conditionsLes articles sont ajoutés et mis à jour dans le caddy.

Cas d'utilisation « Valider son caddy »

NomValider son caddy.
RésuméPermet à un client de valider son caddy d'animaux domestiques.
ActeursInternaute.
Pré-conditionsCas d'utilisation Gérer son caddy.
DescriptionLorsqu'un internaute achète des animaux domestiques, il les ajoute tout d'abord dans un caddy électronique, puis, le valide. Cette validation permet au système de créer un bon de commande (voir cas d'utilisation Gérer les commandes) avec les informations contenues dans le caddy ainsi que les données du client.Une fois la commande créée, le caddy du client est automatiquement vidé.
Post-conditionsUne commande est créée.

Ecrans

Lorsqu'un internaute saisit une adresse URL erronée, son navigateur lui affiche une page d'erreur par défaut.

DefaultErrorPage.png

Figure 2 - Page d'erreur par défaut

Ce que la société demande c'est d'intercepter cette erreur, et d'afficher une page plus conviviale invitant l'internaute à retourner sur la page d'accueil.

CustomizedErrorPage.png

Figure 3 - Page d'erreur pour les adresses invalides

Lorsque l'internaute s'authentifie, un nouveau menu apparaît en haut de la page : Cart. Ce lien permet d'afficher le contenu du caddy. Si ce dernier est vide, la page affiche un message avertissant le client.

ScrEmptyCaddy.png

Figure 4 - Le caddy est vide

Pour remplir le caddy il suffit de se rendre sur la page des articles et de cliquer sur le lien Add to cart. Cette action ajoute dans le caddy l'article sélectionné avec sa quantité à un.

ScrAddToCart1.png

Figure 5 - Ajouter un article à partir de la page liste

ScrAddToCart2.png

Figure 6 - Ajouter un article à partir de la page détail

ScrShowCart.png

Figure 7 - Page permettant de gérer le contenu du caddy

L'écran ci-dessus s'affiche lorsqu'on ajoute un article dans le panier ou lorsque l'on clique sur le lien Cart. Cette page permet de visualiser et de modifier le contenu du caddy. Sur la colonne de gauche est listé le nom de l'article ainsi que la description du produit auquel il appartient. Sur la colonne de droite le prix unitaire de l'article ainsi que son sous-total (quantité * prix unitaire). Au bas du tableau s'affiche le montant total du caddy.

Au centre de la liste, le client a la possibilité de supprimer un article (en cliquant sur Remove) ou de mettre à jour sa quantité (cliquer sur Update). Les sous-totaux et le total sont automatiquement mis à jour.

Lorsque le contenu de son caddy lui convient, le client le valide en cliquant sur Check Out. Il est alors redirigé vers une page qui lui confirme la création de la commande et lui en donne le numéro (order id).

ScrShowOrder.png

Figure 8 - Page affichant la confirmation de la commande

Analyse et conception

Vue logique

Stateless EJB

Pour réarchitecturer l'application Petstore Server afin qu'elle puisse s'exécuter dans un cluster, il faut transformer les classes de services en composants distribuables. Rappelons-nous que ces classes de services suivent le design pattern façade. Il existe un pattern équivalent pour les architectures à base d'EJB : le SessionFacade.

Le pattern SessionFacade applique l'idée de façade GoF aux EJB. Les EJB s'exécutant dans un environnement propre, l'appel distant à ces objets se fait au travers d'appel réseau RMI. Les objets doivent donc être sérialisés puis transmis, ce qui consomme des ressources mémoires, processeurs et réseaux. C'est pourquoi le design pattern SessionFacade permet de limiter ces appels pour effectuer des opérations sur des objets distants. Dans notre cas, la SessionFacade s'occupe de faire le lien entre les interfaces utilisateurs et les objets du domaine. Une SessionFacade est représentée par un Stateless Session EJB.

Pour transformer les façades en SessionFaçade il faut tout d'abord renommer les classes xxxService en xxxServiceBean et les xxxServiceRemote en xxxService. Chaque service peut avoir une troisième classe, l'interface de fabrique qui se nomme xxxServiceHome.

Les "interfaces de fabrique" (les home interface) définissent une variable statique contenant le nom JNDI (1) du composant. Ainsi, un client de CustomerService pourra retrouver ce composant en utilisant la variable CustomerServiceHome.JNDI_NAME.

/**
 * This interface provides the JNDI name of the CustomerService EJB
 */

public interface CustomerServiceHome {

   // https://glassfish.java.net/javaee5/ejb/EJB_FAQ.html#What_is_the_syntax_for_portable_global_
   static final String JNDI_NAME = "java:global/yapswtp11/CustomerSB!com.yaps.petstore.server.service.customer.CustomerService";
}

Les interfaces métiers restent inchangées hormis le fait qu'elles utilisent maintenant l'annotation @Remote au lieu d'hériter de java.rmi.Remote (noter aussi que les méthodes ne lèvent plus RemoteException).

/**
 * This interface gives a remote view of the CustomerServiceBean. Any distant client that wants to call
 * a method on the CustomerServiceBean has to use this interface.
 */

@Remote
public interface CustomerService {
  CustomerDTO authenticate(String customerId, String password) throws FinderException, CheckException;

  CustomerDTO createCustomer(CustomerDTO customerDTO) throws CreateException, CheckException;

  CustomerDTO findCustomer(String customerId) throws FinderException, CheckException;

 void deleteCustomer(String customerId) throws RemoveException, CheckException;
 void updateCustomer(CustomerDTO customerDTO) throws UpdateException, CheckException;

  Collection findCustomers() throws FinderException;
}

La classe CustomerServiceBean implémente chacune des méthodes de l'interface CustomerService; elle utilise l'annotation Stateless pour préciser le nom ainsi que le nom JNDI de l'EJB : 

/**
 * This class is a session facade for all customer services.
 */

@Stateless (name="CustomerSB")
public class CustomerServiceBean extends AbstractRemoteService implements CustomerService {
 // ...

CustomerService, CatalogService et OrderService sont trois composants appelables par des Delegate se trouvant chez les applications clientes. Ils doivent donc être distant et hériter, d'une manière ou d'une autre, de l'interface Remote. Rappelons que dans l'architecture de Petstore Server il existe un service qui n'est pas distant, c'est-à-dire qu'il n'est pas directement utilisé par les interfaces utilisateurs : CreditCardService. En effet, ce service n'hérite pas de la classe AbstractRemoteService et n'est utilisé que par les autres services. Pour ce genre de service, il est inutile de le transformer en composant distant, donc d'utiliser les mêmes remaniements que le CustomerService. Par contre, on peut le transformer en composant local, c'est-à-dire uniquement appelable par des composants du même serveur d'application et non des classes externes.

Afin d'améliorer les performances, l'architecture EJB contient (à partir de la version 2.0) des beans locaux. Un bean local n'est accessible que si le client est la même JVM, ce qui est le cas lorsque le composant CustomerService accède au CreditCardService. Dans ce type d'appel, les passages de paramètres se font donc par référence, alors que les appels distants se font par valeur.

Pour les composants locaux, la spécification EJB utilise deux interfaces différentes. Au lieu de Home et Remote, on parle alors de LocalHome et Local. L'interface CreditCardServiceLocalHome fait office de factory alors que CreditCardServiceLocal permet aux autres composants d'appeler des méthodes sur le CreditCardServiceBean.

Il faut donc créer les deux nouvelles interfaces CreditCardServiceLocalHome et CreditCardServiceLocal, et remanier le code de CreditCardServiceBean.

Notre nouvelle architecture EJB, bien qu'utilisant le protocole RMI, n'a plus besoin des classes telles que RegisterService (qui servait à enregistrer les classes de services) ou RMIConstant (qui contenait toutes les constantes utilisées par le protocole). Le serveur d'application gère maintenant tous ces aspects.

Stateful EJB

Le caddy est représenté par un Stateful Session Bean. Ce bean possède un attribut de type Map qui stocke uniquement l'identifiant de l'article sélectionné par le client, ainsi que la quantité. Le composant ShoppingCartBean possède plusieurs méthodes permettant d'agir sur le contenu de la Map :

NomRôle
getCart()Retourne l'attribut shoppingCart de type Map.
Collection getItems()Renvoie une liste de ShoppingCartDTO, ce qui permet l'affichage du contenu du caddy.
addItem(String itemId)Rajoute un article dans le caddy.
removeItem(String itemId)Supprime un article du caddy.
updateItemQuantity(String itemId, int newQty)Modifie la quantité d'un article.
getTotal()Retourne le montant total du caddy.
empty()Vide le caddy.

Ci-dessous le diagramme de classe montre toutes les classes impliquées dans le composant ShoppingCart ainsi que la classe ShoppingCartDelegate et ShoppingCartDTO.

CDShoppingCart.png

Figure 10 - Composant Shopping Cart

Lorsque le client décide d'acheter les animaux contenus dans son caddy électronique, le système crée une nouvelle commande. Ainsi, le composant OrderService se voit enrichi d'une nouvelle méthode (createOrder) qui lui permet d'accomplir cette action à partir de l'identifiant du client et de son caddy (représenté par une Map).

@Remote
public interface OrderService {

  String createOrder(final String customerId, Map shoppingCart) throws CreateException, CheckException;
   
  OrderDTO createOrder(OrderDTO orderDTO) throws CreateException, CheckException;

  OrderDTO findOrder(String orderId) throws FinderException, CheckException;

 void deleteOrder(String orderId) throws RemoveException, CheckException;
}

Vue processus

Le client, quel qu'il soit (GUI, Servlet ou autre) a besoin d'un moyen de localiser les objets distants, en l'occurrence notre Session Façade. Dans notre architecture, ce rôle est attribué aux classes Delegate. Celles-ci ont donc maintenant à utiliser le service de nommage JNDI au lieu du service RMI. Le code qui permet d'accéder à JNDI peut être isolé dans une même et seule classe : le Service Locator.

Dans le but de rendre un système flexible et ré-utilisable, il existe le design pattern Service Locator, dont le seul but est de localiser des objets spécifiques dans l'arbre JNDI. Cette classe fournit ainsi plusieurs methodes permettant de retrouver des Home interfaces (getHome), des LocalHome (getLocalHome) voire même des variables de type String ou booléen qui pourraient être stockées dans le service de nommage.

Par exemple, ci-dessous le code pour retrouver une interface de fabrique distante. En lui passant le nom JNDI, la méthode recherche l'objet (1) dans l'arbre et le retourne (2).

/**
 * This class gives POJOs, Servlets or JSPs methods to look for resources (like home, remote interfaces...).
 * It follows the singleton pattern
 */

public class ServiceLocator {
   // ...
   public Object getHome(String jndiHomeName) throws ServiceLocatorException {
        Object home = (Object) cache.get(jndiHomeName);
       if (home == null) {
           try {
                home = ic.lookup(jndiHomeName); // (1)
               cache.put(jndiHomeName, home);
           } catch (Exception e) {
               throw new ServiceLocatorException(e);
           }
       }
       return home;  // (2)
   }

Le diagramme de séquence ci-dessous nous montre comment la classe CustomerDelegate peut appeler une méthode distante de l'EJB CustomerService en utilisant le Service Locator.

SDCustomerService.png

Figure 11 - Diagramme de séquence d'appel à l'EJB CustomerService

Le Service Locator implémente le design pattern Singleton, le premier appel qu'effectue la classe CustomerDelegate est donc un getInstance(). Avec cette instance, elle retrouve l'interface CustomerServiceHome en utilisant la méthode getHome avec, en paramètre, le nom JNDI du composant (c'est-à-dire ejb/stateless/CustomerService). Avec cette interface de fabrique, le Delegate crée une interface Remote puis accède enfin aux méthodes du composant.

Le Service Locator utilise l'objet InitialContext (la racine de l'arbre) pour retrouver (lookup) les objets dans le service de nommage.

Un service fondamental dans n'importe quel système informatique est le service de nommage : le moyen par lequel des noms sont associés aux objets et les objets sont trouvés grâce à leurs noms. Java Naming and Directory Interface (JNDI) fournit les fonctionnalités de nommage et d'annuaire aux applications écrites en Java. Toutes ces fonctionnalités sont effectuées relativement à un contexte. Il n'y a pas de racines absolues. Par conséquent, JNDI définit un contexte initial, InitialContext, qui fournit un point de départ pour les opérations de nommage et d'annuaire. Une fois que vous avez un contexte initial, vous pouvez l'utiliser pour rechercher d'autres contextes et objets.

Le caddy

Le caddy est mis à jour uniquement par l'application Petstore Web. Il n'existe pas d'équivalent dans l'application swing. Pour cela, chaque action d'ajout d'article dans le caddy, de mise à jour ou de suppression se fait uniquement au travers de servlet. Le paquetage com.yaps.petstore.web.servlet.cart comporte les servlets suivantes :

NomRôle
AddItemToCartServletAjoute un article dans le caddy.
CheckoutServletValide le caddy en créant un bon de commande.
RemoveItemFromCartServletSupprime un article du caddy.
UpdateCartServletMet à jours la quantité d'un article.
ViewCartServletRécupère le contenu du caddy pour l'afficher.

Le diagramme de séquence ci-dessous nous montre comment l'application web ajoute un article dans le caddy et en modifie la quantité.

En tout premier lieu, le client clique sur le lien Add to cart de la page item.jsp. Cette action appelle la AddItemToCartServlet en passant l'identifiant de l'article. La servlet invoque la classe delegate qui recherche le Stateful Session Bean ShoppingCart (utilisant le ServiceLocator) et appelle la méthode addItem(). Cette méthode rajoute l'identifiant de l'article avec une quantité à 1 dans la Map. Cette mise à jour terminée, la servlet AddItemToCartServlet délègue l'affichage à la servlet ViewCartServlet .

La ViewCartServlet invoque à deux reprises le ShoppingCartDelegate. Une première fois pour afficher la liste des articles avec leur description et prix (getItems) puis une deuxième fois pour afficher le montant total du caddy (getTotal).

La mise à jour des quantités du caddy suit la même logique. En cliquant sur le bouton Update, la servlet UpdateCartServlet appelle le bean ShoppingCart (via le delegate) qui met à jour la quantité, puis délègue l'affichage à la servlet ViewCartServlet.

SDAddArticle.png

Figure 12 - Ajout d'un article dans le caddy et mise à jour de la quantité

Vue implémentation

Les paquetages de cette nouvelle version de l'application restent identiques hormis la création du paquetage com.yaps.petstore.common.locator où l'on trouve le ServiceLocator. Les paquetages des services contiennent maintenant trois classes au lieu de deux (Home, Remote et Bean). On retrouve ainsi les trois composants distants (CustomerService, CatalogService et OrderService) et le composant local CreditCardService.

Pour pouvoir compiler et exécuter ces nouvelles classes et interfaces, vous devez rajouter la librairie %GLASSFISH_HOME%/lib/gf-client.jar fournie avec GlassFish. De même, pour que le Service Locator puisse parcourir l'arbre JNDI, il lui faut la présence dans le classpath du fichier jndi.properties ou d'autres fichiers de configuration. Ces fichiers de configuration se trouvent dans le répertoire %YAPS_PETSTORE%/src/config/jndi.

Les interfaces et la classe du composant ShopingCart se trouvent dans le paquetage com.yaps.petstore.server.cart. Les servlets, elles, ont été mises dans un paquetage propre.

RepertoiresPackages.png

Figure 13 - Paquetages du composant et des servlets

Architecture

Dans le diagramme de composants ci-dessous, on découvre le ShoppingCart.

TP11ComponentDiag.png

Figure 14 - Diagrammes de composants avec le ServiceLocator et les EJB

Vue déploiement

Les fichiers déployés sont plus nombreux et leur contenu est différent. Les taches Ant du fichier build.xml vous permettent de tous les créer.

La partie serveur pourrait être packagée dans le fichier petstore.ear (Extension Archive). En fait, pour pouvoir développer et déboguer depuis Netbeans on a tout mis dans l'archive yapswtp11.war; ceci permet de rendre l'application petstore accessible depuis l'URL http://localhost:8080/yapswtp11 et d'avoir des noms JNDI du type "java:global/yapswtp11/CustomerSB"
Le fichier yapswtp11.war généré par la cible ant yaps-war contient toute la partie web et les archives common.jar et server.jar comme dans les versions précédentes. La nouveauté est la présence des fichiers services-ejb.jar et cart-ejb.jar :

  • services-ejb.jar contient les quatres Stateless Session Bean de l'application
  • cart-ejb.jar contient le Stateful Session Bean ShoppingCart.

DDDeploiement.png

Figure 15 - Diagramme de déploiement avec le composant ShoppingCart (cart-ejb.jar)

cart-ejb.jar contient les interfaces et classes du composant ShoppingCart.

Repertoirescart-ejb.png

Figure 16 - Fichier cart-ejb.jar

Le fichier services-ejb.jar contient les classes des quatres EJB de l'application : Remote, Home et Bean. Le répertoire META-INF comporte les deux descripteurs XML informant le container de la composition de ces composants. 

Repertoiresservices-ejb.png

Figure 17 - Fichier services-ejb.jar

Enfin, le fichier services-ejb-client.jar est utilisé par les clients de l'application (web et swing). Il contient les interfaces distantes visibles des EJB, c'est-à-dire uniquement les interfaces Home et Remote. Il ne contient pas, bien sûr, les classes du composant local CreditCardService qui, lui, n'est pas accessible par des clients extérieurs. 

Repertoiresservices-ejb-client.png

Figure 18 - Fichier services-ejb-client.jar

Comme nous l'avons vu, les applications clientes doivent aussi rajouter dans leur classpath le fichier ${glassfish.home}/lib/gf-client.jar contenant les classes de l'API des EJB. Ce fichier doit donc être déployé avec l'application Petstore Client que ce soit pour les employés de YAPS ou par les franchisés. Pour ces derniers, le déploiement se faisant par Java Web Start, il faut signer tous les jar nécessaires et les rajouter dans le fichier de déploiement admin.jnlp.

<?xmlversion="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080/adminps" href="admin.jnlp">
   <information>
       <title>Admin YAPS Petstore </title>
       <vendor>PetStore</vendor>
       <description>Admin YAPS Petstore Console</description>
       <icon href="http://localhost:8080/adminps/logo.gif"/>
       <icon kind="splash" href="http://localhost:8080/adminps/logo.gif"/>
   </information>
   <security>
       <all-permissions/>
   </security>
   <resources>
       <j2se version="1.6"/>
       <jar href="signed-client.jar"/>
       <jar href="signed-common.jar"/>
       <jar href="signed-services-ejb-client.jar"/>
       <jar href="gf-client.jar"/>
   </resources>
   <application-desc main-class="com.yaps.petstore.client.ui.Menu"/>
</jnlp>

Pour que le serveur web puisse interpréter une erreur de saisie dans l'adresse URL et rediriger automatiquement vers une page d'erreur, il suffit d'utiliser le fichier web.xml de l'application web. Ce fichier permet de signaler au serveur des actions à effectuer en cas de tel ou telle erreur. Dans notre cas, la page non trouvée est représenté par un code d'erreur HTTP 404. Lorsque le serveur détecte cette erreur (1), il redirige l'internaute automatiquement vers la page notfound.jsp (2).

<web-app>
    (...)
   <error-page>
       <error-code>404</error-code> // 1

       <location>/notfound.jsp</location>  // 2
   </error-page>
</web-app>
Cette nouvelle architecture de l'application Petstore contient des serveurs en cluster, un serveur dédié à la base de données ainsi qu'un serveur web. Pour ne pas vous demander de mettre en place une telle logistique, nous utiliserons une distribution beaucoup plus simple. En effet, pour cet exercice, ainsi que pour ceux à venir, une simple instance de GlassFish fera l'affaire. En effet, GlassFish est livré avec un container EJB mais aussi avec un serveur web. Il suffit donc de rajouter dans le fichier petstore.ear l'application Petstore Web petstore.war et de le déployer.

Implémentation

Vous devez développer les composants CatalogService, ShoppingCart avec leur Delegate ainsi que l'interface locale CreditCardServiceLocal du composant local CreditCardService . La création d'une commande à partir du caddy doit aussi être développée dans le composant OrderServiceBean. Il ne vous restera plus qu'à développer 2 servlets et 2 pages JSP utilisées dans la gestion du caddy.

L'archive des sources fournis contient l'application petstore sous la forme d'un projet NetBeans. Le plus pratique est donc d'ouvrir ce projet sous NetBeans (File -> Open Project ... puis sélectionner le répertoire xxx/TP11.ess/Yaps). Un nouveau projet appelé yapswtp11 apparait alors vous permettant de développer, déployer et déboguer l'application web http://localhost:8080/yapswtp11 directement depuis NetBeans.

Pour autant, la façon de rendre le résultat final de votre TP reste identique aux TP précédents et consiste à utiliser ant avec les cibles check, build, deploy, yaps-test et yaps-mark

(Attention, si vous avez déployé ou exécuté l'application web yapswtp11 depuis NetBeans, il vous faudra la supprimer de GlassFish pour éviter des conflits de noms : sous NetBeans sélectionnez le projet yapswtp11 puis l'item "Clean" du menu contextuel.)

Recette utilisateur

Téléchargez les classes de test représentant la recette utilisateur et exécutez la classe AllTests. Celle-ci appellera, entre autres, les classes testant les Delegate et les EJB.

Les tests Selenium sont enrichis par un nouveau test validateOrder.html qui simule la création d'une commande par l'internaute.

Résumé

Références

Enterprise JavaBeans Technology http://www.oracle.com/technetwork/java/javaee/ejb/index.html

The Java EE 6 Tutorial http://java.sun.com/javaee/6/docs/tutorial/doc

Core J2EE Patterns - Session Facade http://www.oracle.com/technetwork/java/sessionfacade-141285.html

Core J2EE Patterns - Service Locator http://www.oracle.com/technetwork/java/servicelocator-137181.html

What is a stateful session bean? http://www.jguru.com/faq/view.jsp?EID=917

HTTP Session Object vs Stateful EJB http://www.theserverside.com/discussions/thread.tss?thread&#95;id=552

Mastering Enterprise JavaBeans (2nd Edition) Ed Roman & Al. Wiley. 2001.

Java EE 5 http://antoniogoncalves.org/2007/06/01/java-ee-5/

Tags:
Créé par Pascal GRAFFION le 2013/12/19 14:02

This wiki is licensed under a Creative Commons 2.0 license
XWiki Enterprise 7.1.1 - Documentation