View Javadoc

1   /*
2    * Créé le 24 janv. 2005
3    */
4   
5   package atg.metier.dao.jdo;
6   
7   /**
8    * <p>Titre : Pool de connexions aux bases de données en JDO</p>
9    * <p>Description : Gestion de pool de connexion</p>
10   * <p>Copyright : FERRARI Olivier</p>
11   * @author  YSMAL Vincent
12   * @version 1.0
13   * Ce logiciel est régi par la licence CeCILL soumise au droit français et
14   * respectant les principes de diffusion des logiciels libres. Vous pouvez
15   * utiliser, modifier et/ou redistribuer ce programme sous les conditions
16   * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
17   *  sur le site http://www.cecill.info.
18   * 
19   * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
20   * pris connaissance de la licence CeCILL, et que vous en avez accepté les
21   * termes.
22   */
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.IOException;
26  import java.util.Properties;
27  import javax.jdo.JDOHelper;
28  import javax.jdo.PersistenceManager;
29  import javax.jdo.PersistenceManagerFactory;
30  import atg.metier.dao.jdbc.exception.ATGDaoBaseIndisponibleException;
31  import atg.service.constante.AtgConstantes;
32  import atg.service.log.AtgLogManager;
33  import atg.util.service.ATGBasicClass;
34  
35  /**
36   * @author Administrateur
37   */
38  public class ATGDaoJdoPoolDataSource extends ATGBasicClass
39  {
40    //***************************************************************************
41    //Attributs
42    //***************************************************************************
43  
44    /**
45     * Référence du pool par défaut
46     */
47    public static final String                CSTE_DEFAULT_REFERENCE = "Reference par defaut";
48  
49    /**
50     * Instances uniques (pour une référence donnée) du pool de connections
51     */
52  
53    private static java.util.Hashtable        instances              = new java.util.Hashtable();
54  
55    
56    /**
57     * nombre de connections maximum crées sur la base de données
58     */
59    private static final int                  MAX_CONNECTION         = 10;
60  
61    /**
62     * nombre de connections automatiquement crées au démarrage du pool
63     */
64    private static final int                  START_CONNECTION       = 1;
65  
66    /**
67     * Delai d'attente pour l'obtention d'une connection, exemple 3000 = 3
68     * secondes
69     */
70    private static final int                  MAX_WAIT               = 3000;
71  
72    /**
73     * delai au dela duquel une connexion est consideree comme perdue, exemple
74     * 60000 = 60 secondes
75     */
76    private static final int                  MAX_CTRL_TIME          = 60000;
77  
78    /**
79     * Attribut de la connexion standard
80     */
81    /*private String                            DRIVER_NAME            = null;
82    private String                            USER_NAME              = null;
83    private String                            USER_PWD               = null;
84    private String                            DB_NAME                = null;
85  */
86    /**
87     * liste des connections créées mais non utilisées
88     */
89    private java.util.Vector                  freeConnections        = null;
90  
91    /**
92     * nombre de connections en cours (demandées mais pas encore libérées)
93     */
94    private int                               checkedOut             = 0;
95  
96    /**
97     * driver permettant d'obtenir une connection
98     */
99    //protected java.sql.Driver                 driver                 = null;
100 
101   /**
102    * référence du pool de données
103    */
104   protected String                          reference              = "";
105 
106   /**
107    * ensemble des connections creees sur la base de donnees
108    */
109   protected java.util.Hashtable             ctrlConnexion          = null;
110 
111   protected static java.util.logging.Logger logger_                = null;
112 
113   private PersistenceManagerFactory         pmf                    = null;
114 
115   /**
116    * Ecriture des logs
117    */
118   protected java.util.logging.Logger getLogger()
119   {
120     if (logger_ == null)
121         logger_ = AtgLogManager
122             .getLog(AtgConstantes.ATG_LOG_CATEGORY_METIER_DAO_JDO);
123 
124     return logger_;
125   }
126 
127   private ATGDaoJdoPoolDataSource(String reference)
128   {
129     String properties = atg.service.constante.AtgConstantesWF
130         .getValue("PATH_JDO_FILE");
131 
132     Properties props = new Properties();
133     String nomFichier = properties + reference + "_JDO.properties";
134     try
135     {
136       props.load(new FileInputStream(nomFichier));
137       //props.setProperty("host", "128.96.197.228");
138       //logFinest("Connection à la machine : " + "128.96.197.228");
139       //props.setProperty("server", "versant");      
140       pmf = JDOHelper.getPersistenceManagerFactory(props);
141     }
142     catch (FileNotFoundException e)
143     {
144       this
145           .logSevere("Immpossible de trouver le fichier de properties pour le serveur JDO : "
146               + nomFichier);
147     }
148     catch (IOException e)
149     {
150       this.logSevere("Erreur d'entrée/sortie dans le fichier de properties : "
151           + nomFichier);
152     }
153   }
154 
155   /**
156    * Renvoie une connection au serveur de persistance.
157    * 
158    * @return PersistenceManager Reférence vers le gestionnaire de persistance
159    *         Jdo
160    */
161   public PersistenceManager getPersistenceManager()
162       throws ATGDaoBaseIndisponibleException
163   {
164     try
165     {
166       PersistenceManager conn = getPersistenceManager(MAX_WAIT);
167       return conn;
168     }
169     catch (ATGDaoBaseIndisponibleException exception)
170     {
171       // ecriture d'une trace + renvoie de la valeur 'null'
172       logSevere("Impossible de fournir une connection correcte : "
173           + exception.getCause() + " / " + exception.getMessage());
174       throw new ATGDaoBaseIndisponibleException();
175     }
176   }
177 
178   /**
179    * Renvoie une connection à la base de données. Attend au maximum 'timeout'
180    * millisecondes l'obtention de la connection.
181    * 
182    * @param timeout
183    *          Temps d'attente maximum de la connection
184    * @throws java.sql.SQLException
185    *           Exception SQL
186    * @return java.sql.Connection Connection à la base de données
187    */
188   protected synchronized PersistenceManager getPersistenceManager(long timeout)
189       throws ATGDaoBaseIndisponibleException
190   {
191     long startTime = System.currentTimeMillis(); // heure d'appel à la fonction
192     long remaining = timeout; // temps restant
193     PersistenceManager conn = null; // connection à la base
194 
195     while ((conn = getPooledPersistenceManager()) == null)
196     {
197       // la connection n'a pas ete obtenue...
198       try
199       {
200         // attente du delai souhaité
201         // le processus est reveillé des qu'une connection se libère
202         logFinest("Attente d'une connection (" + timeout + ").");
203         wait(timeout);
204       }
205       catch (InterruptedException exception)
206       {
207         // le processus d'attente est interrompu
208       }
209 
210       // calcul du temps restant - temps que l'on peut encore passer
211       // à attendre
212       remaining = timeout - (System.currentTimeMillis() - startTime);
213 
214       if (remaining <= 0)
215       {
216         // le delai d'attente est expiré
217         throw new ATGDaoBaseIndisponibleException(
218             "getPersistenceManager() time-out");
219       }
220     }
221     // vérifie si la connexion demeure valide
222     if (!isConnectionOk(conn))
223     {
224       // problème de connexion => refaire une tentative avec le delai restant
225       return getPersistenceManager(remaining);
226     }
227 
228     // mise a jour des informations de suivi
229     checkedOut++;
230 
231     return conn;
232   }
233 
234   /**
235    * Renvoie une connection.
236    * 
237    * @throws ATGDaoBaseIndisponibleException
238    * @return PersistenceManager Connection au gestionnaire de persistance
239    */
240   protected PersistenceManager getPooledPersistenceManager()
241       throws ATGDaoBaseIndisponibleException
242   {
243     PersistenceManager conn = null;
244 
245     // gestion des traces
246     logFinest("Une connection est demandee au pool.");
247 
248     // existe t'il une connection de libre ?
249     if (freeConnections.size() > 0)
250     {
251       // la première est récupèrée
252       conn = (PersistenceManager) freeConnections.firstElement();
253       freeConnections.removeElementAt(0);
254     }
255     else
256     {
257       // le nombre maximum des connexions creees a ete atteint
258       // => essai de liberation
259       if (checkedOut == MAX_CONNECTION)
260       {
261         logWarning("Attention, le nombre maximum de connexions creees sur le serveur de persitance est atteint : "
262             + MAX_CONNECTION);
263         ckeckCtrlConnexion();
264       }
265 
266       // rmq : pas de 'else' car ckeckCtrlConnexion() peut avoir libere des
267       // connexions
268       // et mis a jour 'checkedOut'
269 
270       // il n'existe pas de connection de libre => on en crée une nouvelle
271       // (dans la limite)
272       if (checkedOut < MAX_CONNECTION) conn = newConnection();
273     }
274 
275     return conn;
276   }
277 
278   /**
279    * Vérifie si toutes les connexions ont bien été rendues au pool.
280    */
281   public void ckeckCtrlConnexion()
282   {
283     // liste des connexions
284     java.util.Enumeration conns = ctrlConnexion.keys();
285 
286     // parcours des connexions et verification de leur validité
287     do
288     {
289       Object conn = conns.nextElement();
290       long delta = System.currentTimeMillis()
291           - ((Long) (ctrlConnexion.get(conn))).longValue();
292       if ((!freeConnections.contains(conn)) && (delta > MAX_CTRL_TIME))
293       {
294         // la connexion est consideree comme perdue
295         // => on la libere
296         ctrlConnexion.remove(conn);
297         logSevere("La connexion " + conn
298             + " est consideree comme perdue : elle n'a pas ete rendue au pool.");
299         // le plus important : mise à jour du compteur de connexions
300         checkedOut--;
301 
302         // fermeture de la connexion
303         ((PersistenceManager) conn).close();
304       }
305     }
306     while (conns.hasMoreElements());
307   }
308 
309   /**
310    * Création d'une nouvelle connection.
311    * 
312    * @throws ATGDaoBaseIndisponibleException
313    * @return PersistenceManager Nouvelle connection à la base de données
314    */
315   protected PersistenceManager newConnection()
316       throws ATGDaoBaseIndisponibleException
317   {
318     this.logFinest("Demande d'une reférence sur le Serveur de persistance");
319     PersistenceManager pm = (PersistenceManager) this.pmf
320         .getPersistenceManager();
321     if (pm != null)
322     {
323       logFinest("  getPersistenceManager()");
324       //pm.currentTransaction().begin();
325     }
326     else
327     {
328       logSevere("Impossible de récupérer une instance de persistance manager");
329     }
330     return pm;
331   }
332 
333   /**
334    * Contrôle si la connection specifiée est valide.
335    * 
336    * @param conn
337    *          Connection à tester
338    * @return boolean Connection valide ou non
339    */
340   protected boolean isConnectionOk(PersistenceManager conn)
341   {
342 
343     // deux tests sonr réalisé sur la connection
344     // 1 - connection fermée ?
345     if (!conn.isClosed())
346     {
347       conn.refreshAll();
348       return true;
349     }
350     else
351       return false;
352 
353   }
354 
355   /**
356    * Renvoie l'instance unique du pool de connections pour la référence donnée.
357    * 
358    * @param reference
359    *          Référence du pool de connection
360    * @return StdPoolDataSource Instance d'un pool d'accès aux données
361    */
362   static public ATGDaoJdoPoolDataSource getInstance(String reference)
363   {
364     if ((instances.get(reference) != null))
365     {
366       // l'instance pour cette référence existe déjà
367       return (ATGDaoJdoPoolDataSource) (instances.get(reference));
368     }
369     else
370     {
371       // création de l'instance 'reference'
372       ATGDaoJdoPoolDataSource instance = new ATGDaoJdoPoolDataSource(reference);
373       // initialisation de cette instance
374       instance.initPool();
375       // ajout de l'instance à la liste des instances
376       instances.put(reference, instance);
377 
378       return instance;
379     }
380   }
381 
382   /**
383    * Initialisation du pool. Appelé lors de l'instanciation.
384    */
385   protected void initPool()
386   {
387     freeConnections = new java.util.Vector();
388 
389     // un certain nombre de connections sont créees automatiquement
390     // lors de l'instanciation du pool
391     for (int i = 0; i < START_CONNECTION; i++)
392     {
393       try
394       {
395         freeConnections.addElement(newConnection());
396       }
397       catch (ATGDaoBaseIndisponibleException exception)
398       {
399         logSevere("Impossible d'initialiser le pool de Connection Jdo!"
400             + exception.getCause() + " / " + exception.getMessage());
401       }
402     }
403   }
404   /**
405    * Libére la connection spécifiée.
406    * @param conn Connection à libérer
407    */
408   public synchronized void release(PersistenceManager conn) {
409     // gestion des traces
410     logFinest("Un persistance manager est liberee : " +
411               (freeConnections.size() + 1) + " persistance manager sont libres.");
412 
413     // la connection est recyclee - ajout à la liste des connections libres
414     freeConnections.addElement(conn);
415     checkedOut--;
416 
417     // on signale aux processus qui 'dorment' (attente d'une connection)
418     // qu'une connection vient de se liberer
419     notifyAll();
420   }  
421 }