IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java EE Discussion :

Un Ejb Stateless qui se comporte comme unEjb Stateful


Sujet :

Java EE

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut Un Ejb Stateless qui se comporte comme unEjb Stateful
    Bonjour,

    Comment se fait-il qu'un EJB Stateless se comporte comme un EJB Stateful ?
    Normalement entre deux appels de méthodes, l'EJB stateless devrait perdre la mémoire !
    Or le cumul se fait parfaitement.
    Pire, le même EJB utilisé dans une servelet se comporte de la même façon, le cumul se fait là aussi !
    Franchement j'ai du mal à comprendre !
    Si quelqu'un pouvait éclairer ma lanterne, il serait le bienvenu !

    Le contexte technique :
    Jboss 5.1, Eclipse Hellios
    Ci-dessous le code de l'EJB stateless et son utilisation dans un JFrame.

    L'interface de l'EJB :
    package pkgCumul;
    import javax.ejb.Remote;
    @Remote
    public interface CumulerRemote {
    public void addition();
    public void soustraction();
    public void produit();
    public void division();
    public float getCumul();
    public void setNb1(float _nb1);
    public float getNb1();
    public void destroy();
    }

    Le bean :
    package pkgCumul;
    import javax.annotation.PreDestroy;
    import javax.ejb.Stateless;
    @Stateless(mappedName="CumulerRemote")
    public class Cumuler implements CumulerRemote {
    private float _nb1, _cumul;
    private void setCumul(float _cumul) {
    this._cumul = _cumul;
    }
    public float getCumul() {
    return _cumul;
    }
    public void setNb1(float _nb1) {
    this._nb1 = _nb1;
    }
    public float getNb1() {
    return _nb1;
    }
    public Cumuler() {
    setCumul(0);
    }
    public void addition(){

    setCumul(getCumul() + getNb1() );
    }
    public void soustraction(){
    setCumul(getCumul() - getNb1() );
    }
    public void produit(){
    setCumul(getCumul() * getNb1() );
    }
    public void division(){
    setCumul(getCumul() / getNb1() );
    }
    @PreDestroy
    public void destroy(){
    setCumul(0);
    }
    }

    L'instanciation au chargement de la fenêtre :
    public void windowOpened(java.awt.event.WindowEvent e) {
    try{
    Context ctx = new InitialContext();
    unCumul = (CumulerRemote)ctx.lookup("CumulerRemote");
    }
    catch(NamingException ne){
    JOptionPane.showMessageDialog(null, "Erreur sur initialisation de l'EJB");
    }
    }

    Sur le clic du bouton Cumuler :
    public void actionPerformed(java.awt.event.ActionEvent e) {
    Effectuer_Calcul();
    }

    La procédure utilisant l'EJB :
    private void Effectuer_Calcul(){
    float nb1;
    nb1 = Float.parseFloat(txtNb1.getText());
    int operation = cbOperation.getSelectedIndex();
    unCumul.setNb1(nb1);
    switch (operation){
    case 0 : unCumul.addition();
    break;
    case 1 : unCumul.soustraction();
    break;
    case 2 : unCumul.produit();
    break;
    case 3 : unCumul.division();
    break;
    }
    lCumul.setText(String.valueOf(unCumul.getCumul()));
    }

  2. #2
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 70
    Par défaut
    Normalement entre deux appels de méthodes, l'EJB stateless devrait perdre la mémoire !
    Ca ce n'est spécifié nul part. Donc pas de supposition.
    Le contrat entre le fournisseur de conteneur d'EJB et le développeur est que le stateless ne doit contenir aucune information sur l'"état".
    Deux requêtes successibles venant du même client peut être prise en charge par le même stateless ou pas (c’est au conteneur de décider).
    Des requêtes de clients différents peuvent être traitées par le même EJB ou pas.
    Si le conteneur est libre de la réutilisation des stateless, il n'a aucune obligation de leurs récyclages (mise à null des variables d'instances). Charge au développeur de veiller à cela.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut
    Bonjour,
    Tout d'abord merci pour votre réponse.
    Ok, j'ai donc sur-interprété les spécifications. Ce qui explique le résultat (pour moi) surprenant que deux clients différents partagent le même cumul !
    Entre-temps je n'ai pas chômé, j'ai transformé mon stateless en un stateful. pour être sûr de mon coup je l'ai supprimé du serveur Jboss puis je l'ai déployé sur ce même serveur, j'ai lancé deux clients interrogeant ce serveur Jboss et j'ai obtenu le même résultat, à savoir que mes deux clients (web) partagent le même cumul.
    Aurais-je là aussi fait une mauvaise interprétation : l'instance du stateful est liée à la session du client donc deux clients deux instances donc deux cumuls différents, mais ce n'est pas le cas.
    Il me semblait que lorsque ma requête HTTP était finie, l'instance tenue par le contexte de cette requête devait être libérée (donc remise dans le pool) et que la méthode Destroy devait être invoquée puisque dans cette norme EJB3, ce n'est plus au programmeur de gérer le cycle de vie, mais c'est le serveur d'application qui s'en charge.
    Du coup je me demande si là aussi je n'ai pas tiré des plans sur la comète ;-)
    Merci encore pour votre aide.

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 70
    Par défaut
    Aurais-je là aussi fait une mauvaise interprétation : l'instance du stateful est liée à la session du client donc deux clients deux instances donc deux cumuls différents, mais ce n'est pas le cas.
    Je crois malheureusement que oui, tu as sans doute mal interprété une de ces nombreuses spec.

    Il me semblait que lorsque ma requête HTTP était finie, l'instance tenue par le contexte de cette requête devait être libérée (donc remise dans le pool) et que la méthode Destroy devait être invoquée puisque dans cette norme EJB3, ce n'est plus au programmeur de gérer le cycle de vie, mais c'est le serveur d'application qui s'en charge.
    1- qu'appelles tu contexte de la requête ? Je suppose je tu veux dire contexte de la servlet, est ce bien cela ?
    2- une servlet n'est pas ‘systématiquement’ détruite à la fin d'une requête ?
    3- Il n'y a pas une servlet par requête (resp. client) mais une servlet pour plusieurs requêtes (resp. clients)

    ce n'est plus au programmeur de gérer le cycle de vie
    C'est tout à fait vrai pour un stateless mais pas pour un statefull. C'est pour cela qu'il y a la méthode @Remove dans le statefull pour indiquer au conteneur de EJB que le client n'a plus besoin de l'instance de l'EJB.
    Pour un statefull, la méthode @Destroy n'est en général (je dis bien en général) appelée par le conteneur qu'à après l'appelle de la méthode @Remove par le client.
    Donc, en résumé, le programmeur a son mot à dire dans la gestion du cycle de vie d'un statefull (contrairement au stateless).

    J'aimerais bien savoir comment tu as implémenté ta (tes) servlet(s) pour utiliser le EJB statefull.

    Supposons un scénario (qui me semble correspondre au tien).

    Si j'ai une servlet appelée SevletA qui fait une injection d'un EJB appelé StatefullA.
    Si un client X envoie une requête à ServletA. Voila ce qui se passe (D’après ce que j’ai compris) :
    1 - Le conteneur de servlet crée une instance ServletAIns de ServletA (conf. par défaut). Injecte la dépendant vers StatefullA (en réalité un proxy mais voyons le comme une instance de StatefullA)
    2- Le conteneur crée un thread (THREADA)
    3- THREADA appelle la méthode service de ServletAIns qui a son tour appelle StatefullA.
    4- Le conteneur renvoie la réponse au client X
    5- Le conteneur détruit le thread THREADA
    Une fois la réponse envoyée au client, le thread TREADA est détruit mais pas l'instance ServletAIns ni celle de StatefullA.
    Si un autre client Y envoie une requête à ServletA un nouveau thread sera créé pour utiliser la même instance ServletAIns (donc la même instance StatefullA)
    NB: Si X et Y avaient envoyé leurs requête au même instant t cela n'aurait rien changé, deux threads seront créer et utiliseront les mêmes instances ServletAIns et StatefullA.

    Morale de l'histoire, injecté un statefull dans une servlet est un très très très mauvaise pratique (c'est une habitude à ne pas prendre).

    Pour rattaché un statefull à un utilisateur http on le met dans la session.
    public StatefullA getClientService(HttpServletRequest req){
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        StatefullA rep = (StatefullA)req.getSesion().getAttribut("com.myCompany.MyStateFull"); 
          if(rep==null){
            Context ctx = InitContax(....);
            rep = ctx.lookup(ejb/MyStateFull);
            req.getSesion().setAttribut("com.myCompany.MyStateFull",rep);
     
          }
          return rep;
     
       }

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut
    Bonjour,
    > 1- qu'appelles tu contexte de la requête ?
    Lorsque j'envoie l'URL http://MonServeur/MaPage.jsp, il s'agit d'une requête qui sera interprétée dans le cas présent par le serveur d'applications qui montera la servlet qui va piloter cette page, cette servlet va s'exécuter dans un contexte : PAGE, REQUEST, SESSION et donc de fait l'ensemble des composants nécessaires à la résolution de cette requête (la réponse sous forme d'un flux HTML) s'exécute dans ce contexte.

    > I... en réalité un proxy mais voyons le comme une instance de StatefullA)
    Oui à ce propos, il y a fort longtemps j'ai travaillé sous VB6 et ComPlus, à l'époque on construisait de façon explicite un proxy vers le composant réel et l'application cliente référençait ce proxy. Dans J2EE, (pour le moment) je n'ai rien vu de tel, mais je ne suis qu'au début de mes pérégrinations, à chaque jour suffit sa peine ;-)

    > Une fois la réponse envoyée au client, le thread TREADA est détruit mais pas l'instance ServletAIns ni celle de StatefullA.
    Je ne suis pas certain de ça, je pense au contraire que l'on a bien affaire à deux instances, encore faudrait-il s'entendre sur les mots. Quelque soit le système (JRE, CLR ...), on a affaire à du code ré-entrant, en gros les méthodes sont mises en facteur commun et chaque instance dispose de son espace mémoire pour gérer ses données, donc dans ce cadre deux clients ont bien deux instances de la même servlet (multi-tâches, multi-utilisateurs).
    Il semblerait d'ailleurs que contrairement aux EJB, les servlets ne soient pas gérées par un pool.

    > Morale de l'histoire, injecté un statefull dans une servlet est un très très très mauvaise pratique (c'est une habitude à ne pas prendre).
    Peut-être mais d'une part comment faire autrement dans le cadre d'un MVC classique ?
    D'autre part c'était bien mon souci premier (voir autre question posée sur ce même forum) à savoir comment faire pour que ce soit le modèle (Bean) qui référence l'objet métier (EJB) et non pas le contrôleur, la réponse étant EE6 et CDI + beans.xml.
    Enfin l'exemple donné :
    StatefullA rep = (StatefullA)req.getSesion().getAttribut("com.myCompany.MyStateFull");

    se situe bien dans une servlet ? Ou alors je n'ai rien compris :-( ce qui commencerait à faire beaucoup.

    Merci encore pour les réponses. Je vois bien qu'il me reste un long chemin à faire, mais tout cela m'intéresse bougrement ;-)

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 70
    Par défaut
    Bonjour,
    Citation Envoyé par clem_alain Voir le message
    > 1- qu'appelles tu contexte de la requête ?
    Lorsque j'envoie l'URL http://MonServeur/MaPage.jsp, il s'agit d'une requête qui sera interprétée dans le cas présent par le serveur d'applications qui montera la servlet qui va piloter cette page, cette servlet va s'exécuter dans un contexte : PAGE, REQUEST, SESSION et donc de fait l'ensemble des composants nécessaires à la résolution de cette requête (la réponse sous forme d'un flux HTML) s'exécute dans ce contexte.
    Donc on est bien d'accord qu'il s'agit du context de la servlet (Juste un jeu de mots).


    Citation Envoyé par clem_alain Voir le message
    > I... en réalité un proxy mais voyons le comme une instance de StatefullA)
    Oui à ce propos, il y a fort longtemps j'ai travaillé sous VB6 et ComPlus, à l'époque on construisait de façon explicite un proxy vers le composant réel et l'application cliente référençait ce proxy. Dans J2EE, (pour le moment) je n'ai rien vu de tel, mais je ne suis qu'au début de mes pérégrinations, à chaque jour suffit sa peine ;-)
    En J2EE, on ne peut utiliser les EJB session qu'au travers de proxies. Les proxies sont crées exclusivement par le serveur d'application et stockés dans un espace JNDI. Le développeur n'a plus qu'a interrogé ce JNDI pour les récupérer les instances (l'injection est aussi une sorte de lookup JNDI).


    Citation Envoyé par clem_alain Voir le message
    > Une fois la réponse envoyée au client, le thread TREADA est détruit mais pas l'instance ServletAIns ni celle de StatefullA.
    Je ne suis pas certain de ça, je pense au contraire que l'on a bien affaire à deux instances, encore faudrait-il s'entendre sur les mots. Quelque soit le système (JRE, CLR ...), on a affaire à du code ré-entrant, en gros les méthodes sont mises en facteur commun et chaque instance dispose de son espace mémoire pour gérer ses données, donc dans ce cadre deux clients ont bien deux instances de la même servlet (multi-tâches, multi-utilisateurs).
    Il semblerait d'ailleurs que contrairement aux EJB, les servlets ne soient pas gérées par un pool.
    Non, je ne suis pas sur, on a bien une seule instance de la servlet au sens large du terme instance (espace mémoire partagé).
    Voila ce que dit le JSR (la spécification) à propos (qu tu trouveras à l’adresse ):

    For a servlet not hosted in a distributed environment (the default), the servlet
    container must use only one instance per servlet declaration. However, for a servlet
    implementing the SingleThreadModel interface, the servlet container may
    instantiate multiple instances to handle a heavy request load and serialize requests
    to a particular instance.
    Ensuite concernant la gestion de la mémoire:

    SRV.2.3.3.1 Multithreading Issues
    A servlet container may send concurrent requests through the service method of
    the servlet. To handle the requests, the Servlet Developer must make adequate provisions
    for concurrent processing with multiple threads in the service method.
    Although it is not recommended, an alternative for the Developer is to implement
    the SingleThreadModel interface which requires the container to guarantee
    that there is only one request thread at a time in the service method. A servlet
    container may satisfy this requirement by serializing requests on a servlet, or by
    maintaining a pool of servlet instances. If the servlet is part of a Web application
    that has been marked as distributable, the container may maintain a pool of servlet
    instances in each JVM that the application is distributed across.
    For servlets not implementing the SingleThreadModel interface, if the
    service method (or methods such as doGet or doPost which are dispatched to the
    service method of the HttpServlet abstract class) has been defined with the
    synchronized keyword, the servlet container cannot use the instance pool
    approach, but must serialize requests through it. It is strongly recommended that
    Developers not synchronize the service method (or methods dispatched to it) in
    these circumstances because of detrimental effects on performance.
    Il y a une et une seule instance de servlet pour servir plusieurs clients (ou j'ai rien compris).

    > Morale de l'histoire, injecté un statefull dans une servlet est un très très très mauvaise pratique (c'est une habitude à ne pas prendre).
    Peut-être mais d'une part comment faire autrement dans le cadre d'un MVC classique ?
    D'autre part c'était bien mon souci premier (voir autre question posée sur ce même forum) à savoir comment faire pour que ce soit le modèle (Bean) qui référence l'objet métier (EJB) et non pas le contrôleur, la réponse étant EE6 et CDI + beans.xml.
    Il y a aussi des design patterns à ce sujet. Il faut que tu regardes du coté des patterns comme ServiceLocator et bussinessDelegate en environnement EJB.
    Pour le MVC couplé aux EJB il y a une bonne référence d'implémentation, le Java BluePrints Solutions Catalog que tu trouveras sur le site de sun (https://blueprints.dev.java.net/serv...&folderID=3349).
    Tu peux le prendre comme référence pour ton apprentissage.

    Citation Envoyé par clem_alain Voir le message
    Enfin l'exemple donné :
    StatefullA rep = (StatefullA)req.getSesion().getAttribut("com.myCompany.MyStateFull");

    se situe bien dans une servlet ? Ou alors je n'ai rien compris :-( ce qui commencerait à faire beaucoup.
    Oui cet exemple se situe bien dans une servlet mais utilise la session pour stocker l'instance d'EJB, ce qui change tout.

    Quant on injecte un statefull dans une servlet, une seule instance de ce statefull est partagée par tous les clients de la servlet (notons plusieurs clients/ instance de servlet). Ce qui ne peut pas être un comportement désiré car un statefull est fait pour être unique par client.
    J'ai fait ce bout de code pour montrer que si tu veux utiliser une statefull dans ta servlet alors il te faut faire un jndi lookup (pas d'injection) et ensuite stocker l'instance de l'ejb dans la session de l'utilisateur.
    Dans ce cas un même utilisateur trouvera toujours la même instance de l'EJB tant que la session reste valide.

Discussions similaires

  1. Réponses: 4
    Dernier message: 28/09/2014, 16h18
  2. Réponses: 0
    Dernier message: 02/09/2009, 20h27
  3. Applet qui se comporte comme un textarea ?
    Par Rakken dans le forum Applets
    Réponses: 1
    Dernier message: 10/11/2008, 16h26
  4. GUI qui se comporte comme VS
    Par AlligO dans le forum Windows Forms
    Réponses: 2
    Dernier message: 21/05/2008, 13h09
  5. Browser qui se comporte comme mozilla ou comme ie, etc.
    Par Bad_Lemon dans le forum Réseau
    Réponses: 5
    Dernier message: 25/04/2007, 07h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo