KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > jdbc > ConnectionManager


1 // $Id: ConnectionManager.java,v 1.8 2005/07/06 21:25:52 oneovthafew Exp $
2
package org.hibernate.jdbc;
3
4 import org.hibernate.ConnectionReleaseMode;
5 import org.hibernate.HibernateException;
6 import org.hibernate.engine.SessionFactoryImplementor;
7 import org.hibernate.exception.JDBCExceptionHelper;
8 import org.hibernate.util.JDBCExceptionReporter;
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11
12 import java.sql.Connection JavaDoc;
13 import java.sql.SQLException JavaDoc;
14 import java.io.Serializable JavaDoc;
15 import java.io.ObjectInputStream JavaDoc;
16 import java.io.IOException JavaDoc;
17 import java.io.ObjectOutputStream JavaDoc;
18
19 /**
20  * Encapsulates JDBC Connection management logic needed by Hibernate.
21  * <p/>
22  * The lifecycle is intended to span a logical series of interactions with the
23  * database. Internally, this means the the lifecycle of the Session.
24  *
25  * @author Steve Ebersole
26  */

27 public class ConnectionManager implements Serializable JavaDoc {
28
29     // TODO : would love to remove dependence on SF here, and make this a general purpose thing.
30
// The SF is only really needed to "pass along" to batcher.
31
// Everything we obtain here from factory can (and was) passed in directly.
32

33     private static final Log log = LogFactory.getLog( ConnectionManager.class );
34
35     public static interface Callback {
36         public void connectionOpened();
37         public void connectionCleanedUp();
38     }
39
40     private transient SessionFactoryImplementor factory;
41     private final Callback callback;
42
43     private final ConnectionReleaseMode releaseMode;
44     private transient Connection JavaDoc connection;
45
46     private final boolean wasConnectionSupplied;
47     private transient boolean shouldObtainConnection;
48     private transient Batcher batcher;
49  
50     /**
51      * Constructs a ConnectionManager.
52      * <p/>
53      * This is the form used internally.
54      *
55      * @param factory The SessionFactory.
56      * @param callback An observer for internal state change.
57      * @param releaseMode The mode by which to release JDBC connections.
58      * @param connection An externally supplied connection.
59      */

60     public ConnectionManager(
61             SessionFactoryImplementor factory,
62             Callback callback,
63             ConnectionReleaseMode releaseMode,
64             Connection JavaDoc connection) {
65         this.factory = factory;
66         this.callback = callback;
67
68         this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this );
69
70         this.releaseMode = releaseMode;
71
72         this.connection = connection;
73         wasConnectionSupplied = ( connection != null );
74         shouldObtainConnection = !wasConnectionSupplied;
75     }
76
77     /**
78      * The session factory.
79      *
80      * @return the session factory.
81      */

82     public SessionFactoryImplementor getFactory() {
83         return factory;
84     }
85
86     /**
87      * The batcher managed by this ConnectionManager.
88      *
89      * @return The batcher.
90      */

91     public Batcher getBatcher() {
92         return batcher;
93     }
94
95     /**
96      * Retreives the connection currently managed by this ConnectionManager.
97      * <p/>
98      * Note, that we may need to obtain a connection to return here if a
99      * connection has either not yet been obtained (non-UserSuppliedConnectionProvider)
100      * or has previously been aggressively released (if supported in this environment).
101      *
102      * @return The current Connection.
103      *
104      * @throws HibernateException Indicates a connection is currently not
105      * available (we are currently manually disconnected).
106      */

107     public Connection JavaDoc getConnection() throws HibernateException {
108         if ( connection == null ) {
109             if ( shouldObtainConnection ) {
110                 openConnection();
111             }
112             else {
113                 throw new HibernateException( "Not able to obtain connection" );
114             }
115         }
116         return connection;
117     }
118
119     /**
120      * Is the connection considered "auto-commit"?
121      *
122      * @return True if we either do not have a connection, or the connection
123      * really is in auto-commit mode.
124      *
125      * @throws SQLException Can be thrown by the Connection.isAutoCommit() check.
126      */

127     public boolean isAutoCommit() throws SQLException JavaDoc {
128         return connection == null || connection.getAutoCommit();
129     }
130
131     /**
132      * Is this ConnectionManager instance "logically" connected. Meaning
133      * do we either have a cached connection available or do we have the
134      * ability to obtain a connection on demand.
135      *
136      * @return True if logically connected; false otherwise.
137      */

138     public boolean isLogicallyConnected() {
139         return connection != null || shouldObtainConnection;
140     }
141
142     /**
143      * Is theis ConnectionManager instance "physically" connection. Meaning
144      * do have currently actually have a cached connection.
145      *
146      * @return True if physically connected; false otherwise.
147      */

148     public boolean isPhysicallyConnected() {
149         return connection != null;
150     }
151
152     /**
153      * To be called after execution of each JDBC statement. Used to
154      * conditionally release the JDBC connection aggressively if
155      * the configured release mode indicates.
156      */

157     public void afterStatement() {
158         if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
159             if ( batcher.hasOpenResources() ) {
160                 log.info( "Skipping aggresive-release due to open resources on batcher" );
161             }
162             else {
163                 aggressiveRelease();
164             }
165         }
166     }
167
168     /**
169      * To be called after local transaction completion. Used to conditionally
170      * release the JDBC connection aggressivley if the configured release mode
171      * indicates.
172      */

173     public void afterTransaction() {
174         if ( releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION ) {
175             // TODO : also consider the case where after_statement did not release due to open resources;
176
// do we want to close eveything here?
177
aggressiveRelease();
178         }
179     }
180
181     /**
182      * To be called after Session completion. Used to release the JDBC
183      * connection.
184      *
185      * @return The connection mantained here at time of close. Null if
186      * there was no connection cached internally.
187      */

188     public Connection JavaDoc close() {
189         if ( connection==null ) {
190             shouldObtainConnection = false;
191             return null;
192         }
193         else {
194             return cleanup();
195         }
196     }
197
198     /**
199      * Manually disconnect the underlying JDBC Connection. The assumption here
200      * is that the manager will be reconnected at a later point in time.
201      *
202      * @return The connection mantained here at time of disconnect. Null if
203      * there was no connection cached internally.
204      */

205     public Connection JavaDoc manualDisconnect() {
206         return cleanup();
207     }
208
209     /**
210      * Manually reconnect the underlying JDBC Connection. Should be called at
211      * some point after manualDisconnect().
212      * <p/>
213      * This form is used for ConnectionProvider-supplied connections.
214      */

215     public void manualReconnect() {
216         if ( isLogicallyConnected() ) {
217             throw new HibernateException( "Already connected" );
218         }
219
220         shouldObtainConnection = true;
221     }
222
223     /**
224      * Manually reconnect the underlying JDBC Connection. Should be called at
225      * some point after manualDisconnect().
226      * <p/>
227      * This form is used for user-supplied connections.
228      */

229     public void manualReconnect(Connection JavaDoc suppliedConnection) {
230         if ( isLogicallyConnected() ) {
231             throw new HibernateException( "Already connected" );
232         }
233
234         this.connection = suppliedConnection;
235     }
236
237     /**
238      * Releases the Connection and cleans up any resources associated with
239      * that Connection. This leaves the internal state such that a Connection
240      * cannot be re-obtained. This is intended for use:
241      * 1) at the end of the session
242      * 2) on a manual disconnect of the session
243      *
244      * @return The released connection.
245      * @throws HibernateException
246      */

247     private Connection JavaDoc cleanup() throws HibernateException {
248         try {
249             if ( shouldObtainConnection ) {
250                 // the connection was previously released; nothing to do...
251
shouldObtainConnection = false;
252                 return null;
253             }
254             else {
255                 if (connection==null) {
256                     throw new HibernateException( "Already disconnected" );
257                 }
258
259                 batcher.closeStatements();
260                 Connection JavaDoc c = null;
261                 if ( !wasConnectionSupplied ) {
262                     closeConnection();
263                 }
264                 else {
265                     c = connection;
266                 }
267                 connection = null;
268                 return c;
269             }
270         }
271         finally {
272             callback.connectionCleanedUp();
273         }
274     }
275
276     /**
277      * Performs actions required to perform an aggressive release of the
278      * JDBC Connection.
279      */

280     private void aggressiveRelease() {
281         if ( !wasConnectionSupplied ) {
282             log.debug( "aggressively releasing JDBC connection" );
283             if ( connection != null ) {
284                 closeConnection();
285             }
286             shouldObtainConnection = true;
287         }
288     }
289
290     /**
291      * Pysically opens a JDBC Connection.
292      *
293      * @throws HibernateException
294      */

295     private void openConnection() throws HibernateException {
296         log.debug("opening JDBC connection");
297         try {
298             connection = factory.getConnectionProvider().getConnection();
299         }
300         catch (SQLException JavaDoc sqle) {
301             throw JDBCExceptionHelper.convert(
302                     factory.getSQLExceptionConverter(),
303                     sqle,
304                     "Cannot open connection"
305                 );
306         }
307
308         shouldObtainConnection = false;
309         callback.connectionOpened(); // register synch; stats.connect()
310
}
311
312     /**
313      * Physically closes the JDBC Connection.
314      */

315     private void closeConnection() {
316         if ( log.isDebugEnabled() ) {
317             log.debug(
318                     "closing JDBC connection [" +
319                     batcher.openResourceStatsAsString() + "]"
320                 );
321         }
322
323         try {
324             if ( !connection.isClosed() ) {
325                 JDBCExceptionReporter.logAndClearWarnings( connection );
326             }
327             factory.getConnectionProvider().closeConnection( connection );
328             connection = null;
329         }
330         catch (SQLException JavaDoc sqle) {
331             throw JDBCExceptionHelper.convert(
332                     factory.getSQLExceptionConverter(),
333                     sqle,
334                     "Cannot close connection"
335                 );
336         }
337     }
338
339     /**
340      * Used during serialization.
341      *
342      * @param oos The stream to which we are being written.
343      * @throws IOException Indicates an I/O error writing to the stream
344      */

345     private void writeObject(ObjectOutputStream JavaDoc oos) throws IOException JavaDoc {
346         if ( isPhysicallyConnected() ) {
347             throw new IllegalStateException JavaDoc( "Cannot serialize a ConnectionManager while connected" );
348         }
349
350         oos.writeObject( factory );
351         oos.defaultWriteObject();
352     }
353
354     /**
355      * Used during deserialization.
356      *
357      * @param ois The stream from which we are being read.
358      * @throws IOException Indicates an I/O error reading the stream
359      * @throws ClassNotFoundException Indicates resource class resolution.
360      */

361     private void readObject(ObjectInputStream JavaDoc ois) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
362         factory = ( SessionFactoryImplementor ) ois.readObject();
363         ois.defaultReadObject();
364
365         this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this );
366     }
367
368     /**
369      * Just in case user forgot to commit()/cancel() or close()
370      */

371     protected void finalize() throws Throwable JavaDoc {
372
373         log.debug( "running Session.finalize()" );
374
375         if ( connection != null ) {
376             if ( connection.isClosed() ) {
377                 log.warn( "finalizing with closed connection" );
378             }
379             else {
380                 log.warn("unclosed connection, forgot to call close() on your session?");
381                 // TODO : Should we also call batcher.closeStatements() from here?
382
if ( !wasConnectionSupplied ) {
383                     connection.close();
384                 }
385             }
386         }
387     }
388 }
389
Popular Tags