View Javadoc

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