Problème variable static - application web tomcat

Problème variable static - application web tomcat - Java - Programmation

Marsh Posté le 01-06-2007 à 15:24:00    

Hello,  
 
j'ai un pti souci en développant une appli web tournant sur tomcat.
 
J'utilise un singleton pour tout ce qui est accès à la base de données, il existe donc une variable statique qui stock le singleton une fois instancié. Voilà le problème : je viens du PHP (me tapez pas), et une variable statique n'est pas partagée entre les différents utilisateurs : chaque utilisateur a donc "son" singleton d'accès à la base.
 
Sous tomcat, les variables statiques sont partagées pour tous les utilisateurs. J'imagine qu'il y a un moyen pour rendre ces variables uniques pour chaque session utilisateur, mais je ne sais pas trop comment faire de manière simple. Vous pourriez m'orienter svp ? :)

Reply

Marsh Posté le 01-06-2007 à 15:24:00   

Reply

Marsh Posté le 02-06-2007 à 00:07:11    

Ce n'est pas une variable static qu'il te faut.
 
Si tu cherches à avoir un objet par session d'utilisateur, tu fais :
 

Code :
  1. HttpSession session = request.getSession();
  2. session.setAttribute("anyName", anyObject); // pour sauvegarder l'objet en session
  3. Object o = session.get("anyName" ); // pour récupérer l'objet en session


 
request est ici une variable de type HttpServletRequest, donc selon comment et avec quoi tu développes, ce code est à placer dans une Action, un Servlet, une JSP...
 
Par contre, je questionne le fait de mettre un objet lié à l'accès au données en session. Je ne connais pas le détail, mais pour des questions d'architecture (n-tiers etc.) j'éviterais de faire ce que je crois comprendre que tu fais.

Reply

Marsh Posté le 02-06-2007 à 16:49:16    

Clairement pour les raisons d'architecture que tu évoques ça le fait pas trop de mettre l'objet en session. Je voulais plutôt dire "spécifique à chaque utilisateur" que "spécifique à une session".
 
Ce que je souhaiterais, c'est que le singleton soit propre à chaque utilisateur, donc que la variable statique stocke plusieurs objets d'accès à la base, un par utilisateur, que ça soit un peu un "pool" de connections à la base.
 
Mais comment faire ça de manière propre ? Par exemple je pourrai faire remonter un identifiant d'utilisateur jusqu'à la couche d'accès aux données,  mais c'est pas super propre je trouve.
 
Peut-être faut-il que je me tourne vers les threads ? Une variable statique est-elle partagée entre threads ?
 
(je pensais par défaut qu'avec tomcat chaque utilisateur lançait un thread différent, qui ne partageaient pas d'infos entre eux, visiblement je me trompe quelque part ... je pourrais donc éventuellement faire en sorte que chaque requête utilisateur lance un thread différent, mais ... ça marcherait ? :p)

Message cité 1 fois
Message édité par Djebel1 le 02-06-2007 à 16:50:46
Reply

Marsh Posté le 03-06-2007 à 18:09:19    

Thread Local Pattern

Reply

Marsh Posté le 04-06-2007 à 14:44:17    

En cherchant dans google je trouve pas beaucoup de liens explicatifs. Tu pourrais m'en dire plus ou me filer des liens sympas stp ?

Reply

Marsh Posté le 09-06-2007 à 18:14:22    

Djebel1 a écrit :

Clairement pour les raisons d'architecture que tu évoques ça le fait pas trop de mettre l'objet en session. Je voulais plutôt dire "spécifique à chaque utilisateur" que "spécifique à une session".
 
Ce que je souhaiterais, c'est que le singleton soit propre à chaque utilisateur, donc que la variable statique stocke plusieurs objets d'accès à la base, un par utilisateur, que ça soit un peu un "pool" de connections à la base.
 
Mais comment faire ça de manière propre ? Par exemple je pourrai faire remonter un identifiant d'utilisateur jusqu'à la couche d'accès aux données,  mais c'est pas super propre je trouve.
 
Peut-être faut-il que je me tourne vers les threads ? Une variable statique est-elle partagée entre threads ?
 
(je pensais par défaut qu'avec tomcat chaque utilisateur lançait un thread différent, qui ne partageaient pas d'infos entre eux, visiblement je me trompe quelque part ... je pourrais donc éventuellement faire en sorte que chaque requête utilisateur lance un thread différent, mais ... ça marcherait ? :p)


 
Personnellement, j'éviterais de trafiquer avec les threads. ça donne des solutions élégantes et courtes, mais difficilement comprises par d'autres ; sans doute à cause du côté "contexte magique", transverse... Je laisse l'argumentation là, n'étant pas expert de ce genre d'implémentation, duquel je m'éloignes autant que possible ; en dépit du côté pratique indéniable.
 
Tel que tu présentes le problème, je me demande si une solution de ce type ne pourrait pas être satisfaisante :

Code :
  1. class Contexts {
  2.     private Map contexts = new HashMap();
  3.     private static Contexts instance;
  4.     public static Contexts getInstance() {
  5.         if (instance == null) instance = new Contexts();
  6.         return instance;
  7.     }
  8.     private Contexts() {}
  9.     public Context getContext(User u) { return (Context) contexts.get(u); }
  10.     ...
  11. }


Il s'agit bien d'un Singleton, qui gère des contextes utilisateur. Il reste à définir ce qu'est un User au sens métier, et un Context au sens de ton application. Un contexte pourrait contenir diverses informations, préférences, etc.
J'étudierais dans quelle mesure utiliser un ID de session HTTP est une bonne / mauvaise clé pour identifier un User, ou si un nom d'utilisateur, numéro de qqch (INSEE, SIREN, FINESS...) est plus approprié.
Pour le contexte, je me poserais la question de la montée en charge : combien d'utilisateurs, combien d'objets dans le contexte, quelle lourdeur en moyenne pour chaque objet, etc.
De façon générale, je me poserais la question de ce qui rends les données propres à un utilisateur.
Ce ne sont que des pistes.


Message édité par Cherrytree le 09-06-2007 à 18:14:53
Reply

Marsh Posté le 09-06-2007 à 18:23:32    

Djebel1 a écrit :

En cherchant dans google je trouve pas beaucoup de liens explicatifs. Tu pourrais m'en dire plus ou me filer des liens sympas stp ?


Un cas d'utilisation classique de ThreadLocal avec Hibernate ; présenté à l'origine dans le bouquin Hibernate in Action, qui l'explique en détail. J'ai trouvé ce code à http://www.koders.com/java/fid462B [...] A2D58.aspx.
 

Code :
  1. package org.apache.turbine.util.hibernate;
  2. /*
  3. * Copyright 2001-2004 The Apache Software Foundation.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License" )
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. *     http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. import net.sf.hibernate.HibernateException;
  18. import net.sf.hibernate.JDBCException;
  19. import net.sf.hibernate.Session;
  20. import net.sf.hibernate.SessionFactory;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. /**
  24. * This class is used to get Hibernate Sessions and may
  25. * also contain methods (in the future) to get DBConnections
  26. * or Transactions from JNDI.
  27. */
  28. public class HibernateUtils
  29. {
  30.     //~ Static fields/initializers =============================================
  31.     public final static String SESSION_FACTORY = "hibernate/sessionFactory";
  32.     public static final ThreadLocal session = new ThreadLocal();
  33.     private static SessionFactory sf = null;
  34.     private static HibernateUtils me;
  35.     private static Log log = LogFactory.getLog(HibernateUtils.class);
  36.     static {
  37.         try
  38.         {
  39.             me = new HibernateUtils();
  40.         }
  41.         catch (Exception e)
  42.         {
  43.             log.fatal("Error occurred initializing HibernateUtils" );
  44.             e.printStackTrace();
  45.         }
  46.     }
  47.     //~ Constructors ===========================================================
  48.     private HibernateUtils() throws HibernateException, JDBCException
  49.     {}
  50.     //~ Methods ================================================================
  51.     public static Session currentSession() throws PersistenceException
  52.     {
  53.         Session s = (Session) session.get();
  54.         if (s == null)
  55.         {
  56.             s = PersistenceManager.openSession();
  57.             if (log.isDebugEnabled())
  58.             {
  59.                 log.debug("Opened hibernate session." );
  60.             }
  61.             session.set(s);
  62.         }
  63.         return s;
  64.     }
  65.     public static void closeSession() throws HibernateException, JDBCException
  66.     {
  67.         Session s = (Session) session.get();
  68.         session.set(null);
  69.         if (s != null)
  70.         {
  71.             if (s.isOpen())
  72.             {
  73.                 s.flush();
  74.                 s.close();
  75.                 if (log.isDebugEnabled())
  76.                 {
  77.                     log.debug("Closed hibernate session." );
  78.                 }
  79.             }
  80.         }
  81.         else
  82.         {
  83.             log.warn("Hibernate session was inadvertently already closed." );
  84.         }
  85.     }
  86. }


Le concept est là, bien présent, mais il est dommage que le développeur ait nommé son ThreadLocal "session", que l'on confond volontiers avec le type Session également manipulé par la classe HibernateUtils.

Reply

Marsh Posté le 14-06-2007 à 11:31:59    

merci pour ces réponses :)
 
Les deux solutions que tu proposes pourraient convenir, je vais voir ce qui m'arrange le plus, je reviens t'embêter au cas si j'ai un souci ^^

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed