View Javadoc

1   /**
2    * This file is part of GoldenGate Project (named also GoldenGate or GG).
3    * 
4    * Copyright 2009, Frederic Bregier, and individual contributors by the @author
5    * tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    * 
8    * All GoldenGate Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   * 
13   * GoldenGate is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Public License along with
18   * GoldenGate . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package goldengate.common.database;
21  
22  import goldengate.common.logging.GgInternalLogger;
23  import goldengate.common.logging.GgInternalLoggerFactory;
24  
25  import java.sql.Connection;
26  import java.sql.SQLException;
27  import java.sql.Savepoint;
28  import java.util.LinkedList;
29  import java.util.List;
30  
31  import goldengate.common.database.exception.GoldenGateDatabaseNoConnectionException;
32  import goldengate.common.database.exception.GoldenGateDatabaseSqlException;
33  import goldengate.common.database.model.DbModelFactory;
34  
35  // Notice, do not import com.mysql.jdbc.*
36  // or you will have problems!
37  
38  /**
39   * Class to handle session with the SGBD
40   * 
41   * @author Frederic Bregier
42   * 
43   */
44  public class DbSession {
45      /**
46       * Internal Logger
47       */
48      private static final GgInternalLogger logger = GgInternalLoggerFactory
49              .getLogger(DbSession.class);
50  
51      /**
52       * DbAdmin referent object
53       */
54      public DbAdmin admin = null;
55  
56      /**
57       * The internal connection
58       */
59      public Connection conn = null;
60  
61      /**
62       * Is this connection Read Only
63       */
64      public boolean isReadOnly = true;
65  
66      /**
67       * Is this session using AutoCommit (true by default)
68       */
69      public boolean autoCommit = true;
70  
71      /**
72       * Internal Id
73       */
74      public long internalId;
75  
76      /**
77       * Number of threads using this connection
78       */
79      public int nbThread = 0;
80  
81      /**
82       * To be used when a local Channel is over
83       */
84      public boolean isDisconnected = true;
85  
86      /**
87       * List all DbPrepareStatement with long term usage to enable the recreation
88       * when the associated connection is reopened
89       */
90      private final List<DbPreparedStatement> listPreparedStatement = new LinkedList<DbPreparedStatement>();
91  
92      static synchronized void setInternalId(DbSession session) {
93          session.internalId = System.currentTimeMillis();
94          try {
95              Thread.sleep(1);
96          } catch (InterruptedException e) {
97              Thread.currentThread().interrupt();
98          }
99      }
100 
101     /**
102      * Create a session and connect the current object to the connect object
103      * given as parameter.
104      * 
105      * The database access use auto commit.
106      * 
107      * If the initialize is not call before, call it with the default value.
108      * 
109      * @param connext
110      * @param isReadOnly
111      * @throws GoldenGateDatabaseNoConnectionException
112      */
113     public DbSession(Connection connext, boolean isReadOnly)
114             throws GoldenGateDatabaseNoConnectionException {
115         if (connext == null) {
116             logger.error("Cannot set a null connection");
117             throw new GoldenGateDatabaseNoConnectionException(
118                     "Cannot set a null Connection");
119         }
120         conn = connext;
121         try {
122             conn.setAutoCommit(true);
123             this.isReadOnly = isReadOnly;
124             conn.setReadOnly(this.isReadOnly);
125             isDisconnected = false;
126             setInternalId(this);
127         } catch (SQLException ex) {
128             // handle any errors
129             logger.error("Cannot set properties on connection!");
130             error(ex);
131             conn = null;
132             isDisconnected = true;
133             throw new GoldenGateDatabaseNoConnectionException(
134                     "Cannot set properties on connection", ex);
135         }
136     }
137 
138     /**
139      * Create a session and connect the current object to the server using the
140      * string with the form for mysql for instance
141      * jdbc:type://[host:port],[failoverhost:port]
142      * .../[database][?propertyName1][
143      * =propertyValue1][&propertyName2][=propertyValue2]...
144      * 
145      * By default (if server = null) :
146      * "jdbc:mysql://localhost/r66 user=r66 password=r66"
147      * 
148      * The database access use auto commit.
149      * 
150      * If the initialize is not call before, call it with the default value.
151      * 
152      * @param server
153      * @param user
154      * @param passwd
155      * @param isReadOnly
156      * @throws GoldenGateDatabaseSqlException
157      */
158     public DbSession(String server, String user, String passwd,
159             boolean isReadOnly) throws GoldenGateDatabaseNoConnectionException {
160         if (!DbModelFactory.classLoaded) {
161             throw new GoldenGateDatabaseNoConnectionException(
162                     "DbAdmin not initialzed");
163         }
164         if (server == null) {
165             conn = null;
166             logger.error("Cannot set a null Server");
167             throw new GoldenGateDatabaseNoConnectionException(
168                     "Cannot set a null Server");
169         }
170         try {
171             conn = DbModelFactory.dbModel.getDbConnection(server, user, passwd);
172             conn.setAutoCommit(true);
173             this.isReadOnly = isReadOnly;
174             conn.setReadOnly(this.isReadOnly);
175             setInternalId(this);
176             DbAdmin.addConnection(internalId, this);
177             isDisconnected = false;
178         } catch (SQLException ex) {
179             // handle any errors
180             isDisconnected = true;
181             logger.error("Cannot create Connection");
182             error(ex);
183             conn = null;
184             throw new GoldenGateDatabaseNoConnectionException(
185                     "Cannot create Connection", ex);
186         }
187     }
188 
189     /**
190      * Create a session and connect the current object to the server using the
191      * DbAdmin object. The database access use auto commit.
192      * 
193      * If the initialize is not call before, call it with the default value.
194      * 
195      * @param admin
196      * @param isReadOnly
197      * @throws GoldenGateDatabaseSqlException
198      */
199     public DbSession(DbAdmin admin, boolean isReadOnly)
200             throws GoldenGateDatabaseNoConnectionException {
201         if (!DbModelFactory.classLoaded) {
202             throw new GoldenGateDatabaseNoConnectionException(
203                     "DbAdmin not initialzed");
204         }
205         try {
206             conn = DbModelFactory.dbModel.getDbConnection(admin.getServer(),
207                     admin.getUser(), admin.getPasswd());
208             conn.setAutoCommit(true);
209             this.isReadOnly = isReadOnly;
210             conn.setReadOnly(this.isReadOnly);
211             setInternalId(this);
212             DbAdmin.addConnection(internalId, this);
213             this.admin = admin;
214             isDisconnected = false;
215         } catch (SQLException ex) {
216             // handle any errors
217             isDisconnected = true;
218             logger.error("Cannot create Connection", ex);
219             error(ex);
220             conn = null;
221             throw new GoldenGateDatabaseNoConnectionException(
222                     "Cannot create Connection", ex);
223         } catch (NullPointerException ex) {
224             // handle any errors
225             logger.error("Cannot create Connection:" + (admin==null), ex);
226             conn = null;
227             throw new GoldenGateDatabaseNoConnectionException(
228                     "Cannot create Connection", ex);
229         }
230     }
231 
232     /**
233      * Create a session and connect the current object to the server using the
234      * string with the form for mysql for instance
235      * jdbc:type://[host:port],[failoverhost:port]
236      * .../[database][?propertyName1][
237      * =propertyValue1][&propertyName2][=propertyValue2]...
238      * 
239      * By default (if server = null) :
240      * "jdbc:mysql://localhost/r66 user=r66 password=r66"
241      * 
242      * 
243      * If the initialize is not call before, call it with the default value.
244      * 
245      * @param server
246      * @param user
247      * @param passwd
248      * @param isReadOnly
249      * @param autoCommit
250      * @throws GoldenGateDatabaseSqlException
251      */
252     public DbSession(String server, String user, String passwd,
253             boolean isReadOnly, boolean autoCommit)
254             throws GoldenGateDatabaseNoConnectionException {
255         if (!DbModelFactory.classLoaded) {
256             throw new GoldenGateDatabaseNoConnectionException(
257                     "DbAdmin not initialzed");
258         }
259         if (server == null) {
260             conn = null;
261             logger.error("Cannot set a null Server");
262             throw new GoldenGateDatabaseNoConnectionException(
263                     "Cannot set a null Server");
264         }
265         try {
266             this.autoCommit = autoCommit;
267             conn = DbModelFactory.dbModel.getDbConnection(server, user, passwd);
268             conn.setAutoCommit(this.autoCommit);
269             this.isReadOnly = isReadOnly;
270             conn.setReadOnly(this.isReadOnly);
271             setInternalId(this);
272             DbAdmin.addConnection(internalId, this);
273             isDisconnected = false;
274         } catch (SQLException ex) {
275             isDisconnected = true;
276             // handle any errors
277             logger.error("Cannot create Connection");
278             error(ex);
279             conn = null;
280             throw new GoldenGateDatabaseNoConnectionException(
281                     "Cannot create Connection", ex);
282         }
283     }
284 
285     /**
286      * Create a session and connect the current object to the server using the
287      * DbAdmin object.
288      * 
289      * If the initialize is not call before, call it with the default value.
290      * 
291      * @param admin
292      * @param isReadOnly
293      * @param autoCommit
294      * @throws GoldenGateDatabaseSqlException
295      */
296     public DbSession(DbAdmin admin, boolean isReadOnly, boolean autoCommit)
297             throws GoldenGateDatabaseNoConnectionException {
298         if (!DbModelFactory.classLoaded) {
299             throw new GoldenGateDatabaseNoConnectionException(
300                     "DbAdmin not initialzed");
301         }
302         try {
303             this.autoCommit = autoCommit;
304             conn = DbModelFactory.dbModel.getDbConnection(admin.getServer(),
305                     admin.getUser(), admin.getPasswd());
306             conn.setAutoCommit(this.autoCommit);
307             this.isReadOnly = isReadOnly;
308             conn.setReadOnly(this.isReadOnly);
309             setInternalId(this);
310             DbAdmin.addConnection(internalId, this);
311             this.admin = admin;
312             isDisconnected = false;
313         } catch (SQLException ex) {
314             // handle any errors
315             isDisconnected = true;
316             logger.error("Cannot create Connection");
317             error(ex);
318             conn = null;
319             throw new GoldenGateDatabaseNoConnectionException(
320                     "Cannot create Connection", ex);
321         }
322     }
323 
324     /**
325      * Change the autocommit feature
326      * 
327      * @param autoCommit
328      * @throws GoldenGateDatabaseNoConnectionException
329      */
330     public void setAutoCommit(boolean autoCommit)
331             throws GoldenGateDatabaseNoConnectionException {
332         if (conn != null) {
333             this.autoCommit = autoCommit;
334             try {
335                 conn.setAutoCommit(autoCommit);
336             } catch (SQLException e) {
337                 // handle any errors
338                 logger.error("Cannot create Connection");
339                 error(e);
340                 conn = null;
341                 isDisconnected = true;
342                 throw new GoldenGateDatabaseNoConnectionException(
343                         "Cannot create Connection", e);
344             }
345         }
346     }
347 
348     /**
349      * @return the admin
350      */
351     public DbAdmin getAdmin() {
352         return admin;
353     }
354 
355     /**
356      * @param admin
357      *            the admin to set
358      */
359     public void setAdmin(DbAdmin admin) {
360         this.admin = admin;
361     }
362 
363     /**
364      * Print the error from SQLException
365      * 
366      * @param ex
367      */
368     public static void error(SQLException ex) {
369         // handle any errors
370         logger.error("SQLException: " + ex.getMessage() + " SQLState: " +
371                 ex.getSQLState() + "VendorError: " + ex.getErrorCode());
372     }
373 
374     /**
375      * To be called when a client will start to use this DbSession (once by
376      * client)
377      */
378     public void useConnection() {
379         nbThread ++;
380     }
381 
382     /**
383      * To be called when a client will stop to use this DbSession (once by
384      * client)
385      */
386     public void endUseConnection() {
387         nbThread --;
388         if (nbThread <= 0) {
389             disconnect();
390         }
391     }
392 
393     /**
394      * Close the connection
395      * 
396      */
397     public void disconnect() {
398         if (conn == null) {
399             logger.warn("Connection already closed");
400             return;
401         }
402         try {
403             Thread.sleep(DbAdmin.WAITFORNETOP);
404         } catch (InterruptedException e1) {
405             Thread.currentThread().interrupt();
406         }
407         if (nbThread > 0) {
408             logger.info("Still some clients could use this Database Session: " +
409                     nbThread);
410         }
411         removeLongTermPreparedStatements();
412         DbAdmin.removeConnection(internalId);
413         isDisconnected = true;
414         try {
415             conn.close();
416         } catch (SQLException e) {
417             logger.warn("Disconnection not OK");
418             error(e);
419         }
420         logger.info("Current cached connection: "+DbModelFactory.dbModel.currentNumberOfPooledConnections());
421     }
422 
423     /**
424      * Check the connection to the Database and try to reopen it if possible
425      * 
426      * @throws GoldenGateDatabaseNoConnectionException
427      */
428     public void checkConnection() throws GoldenGateDatabaseNoConnectionException {
429         try {
430             DbModelFactory.dbModel.validConnection(this);
431             isDisconnected = false;
432             if (admin != null)
433                 admin.isConnected = true;
434         } catch (GoldenGateDatabaseNoConnectionException e) {
435             isDisconnected = true;
436             if (admin != null)
437                 admin.isConnected = false;
438             throw e;
439         }
440     }
441 
442     /**
443      * 
444      * @return True if the connection was successfully reconnected
445      */
446     public boolean checkConnectionNoException() {
447         try {
448             checkConnection();
449             return true;
450         } catch (GoldenGateDatabaseNoConnectionException e) {
451             return false;
452         }
453     }
454 
455     /**
456      * Add a Long Term PreparedStatement
457      * 
458      * @param longterm
459      */
460     public void addLongTermPreparedStatement(DbPreparedStatement longterm) {
461         this.listPreparedStatement.add(longterm);
462     }
463 
464     /**
465      * Due to a reconnection, recreate all associated long term
466      * PreparedStatements
467      * 
468      * @throws GoldenGateDatabaseNoConnectionException
469      * @throws GoldenGateDatabaseSqlException
470      */
471     public void recreateLongTermPreparedStatements()
472             throws GoldenGateDatabaseNoConnectionException,
473             GoldenGateDatabaseSqlException {
474         GoldenGateDatabaseNoConnectionException elast = null;
475         GoldenGateDatabaseSqlException e2last = null;
476         logger.info("RecreateLongTermPreparedStatements: "+listPreparedStatement.size());
477         for (DbPreparedStatement longterm: listPreparedStatement) {
478             try {
479                 longterm.recreatePreparedStatement();
480             } catch (GoldenGateDatabaseNoConnectionException e) {
481                 logger.warn(
482                         "Error while recreation of Long Term PreparedStatement",
483                         e);
484                 elast = e;
485             } catch (GoldenGateDatabaseSqlException e) {
486                 logger.warn(
487                         "Error while recreation of Long Term PreparedStatement",
488                         e);
489                 e2last = e;
490             }
491         }
492         if (elast != null) {
493             throw elast;
494         }
495         if (e2last != null) {
496             throw e2last;
497         }
498     }
499 
500     /**
501      * Remove all Long Term PreparedStatements (closing connection)
502      */
503     public void removeLongTermPreparedStatements() {
504         for (DbPreparedStatement longterm: listPreparedStatement) {
505             longterm.realClose();
506         }
507         listPreparedStatement.clear();
508     }
509     
510     /**
511      * Remove one Long Term PreparedStatement
512      * @param longterm
513      */
514     public void removeLongTermPreparedStatements(DbPreparedStatement longterm) {
515         listPreparedStatement.remove(longterm);
516     }
517 
518     /**
519      * Commit everything
520      * 
521      * @throws GoldenGateDatabaseSqlException
522      * @throws GoldenGateDatabaseNoConnectionException
523      */
524     public void commit() throws GoldenGateDatabaseSqlException,
525             GoldenGateDatabaseNoConnectionException {
526         if (conn == null) {
527             logger.warn("Cannot commit since connection is null");
528             throw new GoldenGateDatabaseNoConnectionException(
529                     "Cannot commit since connection is null");
530         }
531         if (isDisconnected) {
532             checkConnection();
533         }
534         try {
535             conn.commit();
536         } catch (SQLException e) {
537             logger.error("Cannot Commit");
538             error(e);
539             throw new GoldenGateDatabaseSqlException("Cannot commit", e);
540         }
541     }
542 
543     /**
544      * Rollback from the savepoint or the last set if null
545      * 
546      * @param savepoint
547      * @throws GoldenGateDatabaseNoConnectionException
548      * @throws GoldenGateDatabaseSqlException
549      */
550     public void rollback(Savepoint savepoint)
551             throws GoldenGateDatabaseNoConnectionException,
552             GoldenGateDatabaseSqlException {
553         if (conn == null) {
554             logger.warn("Cannot rollback since connection is null");
555             throw new GoldenGateDatabaseNoConnectionException(
556                     "Cannot rollback since connection is null");
557         }
558         if (isDisconnected) {
559             checkConnection();
560         }
561         try {
562             if (savepoint == null) {
563                 conn.rollback();
564             } else {
565                 conn.rollback(savepoint);
566             }
567         } catch (SQLException e) {
568             logger.error("Cannot rollback");
569             error(e);
570             throw new GoldenGateDatabaseSqlException("Cannot rollback", e);
571         }
572     }
573 
574     /**
575      * Make a savepoint
576      * 
577      * @return the new savepoint
578      * @throws GoldenGateDatabaseNoConnectionException
579      * @throws GoldenGateDatabaseSqlException
580      */
581     public Savepoint savepoint() throws GoldenGateDatabaseNoConnectionException,
582             GoldenGateDatabaseSqlException {
583         if (conn == null) {
584             logger.warn("Cannot savepoint since connection is null");
585             throw new GoldenGateDatabaseNoConnectionException(
586                     "Cannot savepoint since connection is null");
587         }
588         if (isDisconnected) {
589             checkConnection();
590         }
591         try {
592             return conn.setSavepoint();
593         } catch (SQLException e) {
594             logger.error("Cannot savepoint");
595             error(e);
596             throw new GoldenGateDatabaseSqlException("Cannot savepoint", e);
597         }
598     }
599 
600     /**
601      * Release the savepoint
602      * 
603      * @param savepoint
604      * @throws GoldenGateDatabaseNoConnectionException
605      * @throws GoldenGateDatabaseSqlException
606      */
607     public void releaseSavepoint(Savepoint savepoint)
608             throws GoldenGateDatabaseNoConnectionException,
609             GoldenGateDatabaseSqlException {
610         if (conn == null) {
611             logger.warn("Cannot release savepoint since connection is null");
612             throw new GoldenGateDatabaseNoConnectionException(
613                     "Cannot release savepoint since connection is null");
614         }
615         if (isDisconnected) {
616             checkConnection();
617         }
618         try {
619             conn.releaseSavepoint(savepoint);
620         } catch (SQLException e) {
621             logger.error("Cannot release savepoint");
622             error(e);
623             throw new GoldenGateDatabaseSqlException("Cannot release savepoint", e);
624         }
625     }
626 }