001: /*
002: Copyright (C) 2004-2007 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: */
022: package com.mysql.jdbc;
023:
024: import java.sql.Array;
025: import java.sql.Blob;
026: import java.sql.CallableStatement;
027: import java.sql.Clob;
028: import java.sql.DatabaseMetaData;
029: import java.sql.PreparedStatement;
030: import java.sql.SQLException;
031: import java.sql.SQLWarning;
032: import java.sql.Savepoint;
033: import java.sql.Statement;
034: import java.sql.Struct;
035: import java.util.Map;
036: import java.util.Properties;
037:
038: /**
039: * Connection that opens two connections, one two a replication master, and
040: * another to one or more slaves, and decides to use master when the connection
041: * is not read-only, and use slave(s) when the connection is read-only.
042: *
043: * @version $Id: ReplicationConnection.java,v 1.1.2.1 2005/05/13 18:58:38
044: * mmatthews Exp $
045: */
046: public class ReplicationConnection implements java.sql.Connection,
047: PingTarget {
048: protected Connection currentConnection;
049:
050: protected Connection masterConnection;
051:
052: protected Connection slavesConnection;
053:
054: public ReplicationConnection(Properties masterProperties,
055: Properties slaveProperties) throws SQLException {
056: Driver driver = new Driver();
057:
058: StringBuffer masterUrl = new StringBuffer("jdbc:mysql://");
059: StringBuffer slaveUrl = new StringBuffer("jdbc:mysql://");
060:
061: String masterHost = masterProperties
062: .getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
063:
064: if (masterHost != null) {
065: masterUrl.append(masterHost);
066: }
067:
068: String slaveHost = slaveProperties
069: .getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
070:
071: if (slaveHost != null) {
072: slaveUrl.append(slaveHost);
073: }
074:
075: String masterDb = masterProperties
076: .getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
077:
078: masterUrl.append("/");
079:
080: if (masterDb != null) {
081: masterUrl.append(masterDb);
082: }
083:
084: String slaveDb = slaveProperties
085: .getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
086:
087: slaveUrl.append("/");
088:
089: if (slaveDb != null) {
090: slaveUrl.append(slaveDb);
091: }
092:
093: this .masterConnection = (com.mysql.jdbc.Connection) driver
094: .connect(masterUrl.toString(), masterProperties);
095: this .slavesConnection = (com.mysql.jdbc.Connection) driver
096: .connect(slaveUrl.toString(), slaveProperties);
097:
098: this .currentConnection = this .masterConnection;
099: }
100:
101: /*
102: * (non-Javadoc)
103: *
104: * @see java.sql.Connection#clearWarnings()
105: */
106: public synchronized void clearWarnings() throws SQLException {
107: this .currentConnection.clearWarnings();
108: }
109:
110: /*
111: * (non-Javadoc)
112: *
113: * @see java.sql.Connection#close()
114: */
115: public synchronized void close() throws SQLException {
116: this .masterConnection.close();
117: this .slavesConnection.close();
118: }
119:
120: /*
121: * (non-Javadoc)
122: *
123: * @see java.sql.Connection#commit()
124: */
125: public synchronized void commit() throws SQLException {
126: this .currentConnection.commit();
127: }
128:
129: /*
130: * (non-Javadoc)
131: *
132: * @see java.sql.Connection#createStatement()
133: */
134: public Statement createStatement() throws SQLException {
135: Statement stmt = this .currentConnection.createStatement();
136: ((com.mysql.jdbc.Statement) stmt).setPingTarget(this );
137:
138: return stmt;
139: }
140:
141: /*
142: * (non-Javadoc)
143: *
144: * @see java.sql.Connection#createStatement(int, int)
145: */
146: public synchronized Statement createStatement(int resultSetType,
147: int resultSetConcurrency) throws SQLException {
148: Statement stmt = this .currentConnection.createStatement(
149: resultSetType, resultSetConcurrency);
150:
151: ((com.mysql.jdbc.Statement) stmt).setPingTarget(this );
152:
153: return stmt;
154: }
155:
156: /*
157: * (non-Javadoc)
158: *
159: * @see java.sql.Connection#createStatement(int, int, int)
160: */
161: public synchronized Statement createStatement(int resultSetType,
162: int resultSetConcurrency, int resultSetHoldability)
163: throws SQLException {
164: Statement stmt = this .currentConnection.createStatement(
165: resultSetType, resultSetConcurrency,
166: resultSetHoldability);
167:
168: ((com.mysql.jdbc.Statement) stmt).setPingTarget(this );
169:
170: return stmt;
171: }
172:
173: /*
174: * (non-Javadoc)
175: *
176: * @see java.sql.Connection#getAutoCommit()
177: */
178: public synchronized boolean getAutoCommit() throws SQLException {
179: return this .currentConnection.getAutoCommit();
180: }
181:
182: /*
183: * (non-Javadoc)
184: *
185: * @see java.sql.Connection#getCatalog()
186: */
187: public synchronized String getCatalog() throws SQLException {
188: return this .currentConnection.getCatalog();
189: }
190:
191: public synchronized Connection getCurrentConnection() {
192: return this .currentConnection;
193: }
194:
195: /*
196: * (non-Javadoc)
197: *
198: * @see java.sql.Connection#getHoldability()
199: */
200: public synchronized int getHoldability() throws SQLException {
201: return this .currentConnection.getHoldability();
202: }
203:
204: public synchronized Connection getMasterConnection() {
205: return this .masterConnection;
206: }
207:
208: /*
209: * (non-Javadoc)
210: *
211: * @see java.sql.Connection#getMetaData()
212: */
213: public synchronized DatabaseMetaData getMetaData()
214: throws SQLException {
215: return this .currentConnection.getMetaData();
216: }
217:
218: public synchronized Connection getSlavesConnection() {
219: return this .slavesConnection;
220: }
221:
222: /*
223: * (non-Javadoc)
224: *
225: * @see java.sql.Connection#getTransactionIsolation()
226: */
227: public synchronized int getTransactionIsolation()
228: throws SQLException {
229: return this .currentConnection.getTransactionIsolation();
230: }
231:
232: /*
233: * (non-Javadoc)
234: *
235: * @see java.sql.Connection#getTypeMap()
236: */
237: public synchronized Map getTypeMap() throws SQLException {
238: return this .currentConnection.getTypeMap();
239: }
240:
241: /*
242: * (non-Javadoc)
243: *
244: * @see java.sql.Connection#getWarnings()
245: */
246: public synchronized SQLWarning getWarnings() throws SQLException {
247: return this .currentConnection.getWarnings();
248: }
249:
250: /*
251: * (non-Javadoc)
252: *
253: * @see java.sql.Connection#isClosed()
254: */
255: public synchronized boolean isClosed() throws SQLException {
256: return this .currentConnection.isClosed();
257: }
258:
259: /*
260: * (non-Javadoc)
261: *
262: * @see java.sql.Connection#isReadOnly()
263: */
264: public synchronized boolean isReadOnly() throws SQLException {
265: return this .currentConnection == this .slavesConnection;
266: }
267:
268: /*
269: * (non-Javadoc)
270: *
271: * @see java.sql.Connection#nativeSQL(java.lang.String)
272: */
273: public synchronized String nativeSQL(String sql)
274: throws SQLException {
275: return this .currentConnection.nativeSQL(sql);
276: }
277:
278: /*
279: * (non-Javadoc)
280: *
281: * @see java.sql.Connection#prepareCall(java.lang.String)
282: */
283: public CallableStatement prepareCall(String sql)
284: throws SQLException {
285: return this .currentConnection.prepareCall(sql);
286: }
287:
288: /*
289: * (non-Javadoc)
290: *
291: * @see java.sql.Connection#prepareCall(java.lang.String, int, int)
292: */
293: public synchronized CallableStatement prepareCall(String sql,
294: int resultSetType, int resultSetConcurrency)
295: throws SQLException {
296: return this .currentConnection.prepareCall(sql, resultSetType,
297: resultSetConcurrency);
298: }
299:
300: /*
301: * (non-Javadoc)
302: *
303: * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int)
304: */
305: public synchronized CallableStatement prepareCall(String sql,
306: int resultSetType, int resultSetConcurrency,
307: int resultSetHoldability) throws SQLException {
308: return this .currentConnection.prepareCall(sql, resultSetType,
309: resultSetConcurrency, resultSetHoldability);
310: }
311:
312: /*
313: * (non-Javadoc)
314: *
315: * @see java.sql.Connection#prepareStatement(java.lang.String)
316: */
317: public PreparedStatement prepareStatement(String sql)
318: throws SQLException {
319: PreparedStatement pstmt = this .currentConnection
320: .prepareStatement(sql);
321:
322: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
323:
324: return pstmt;
325: }
326:
327: /*
328: * (non-Javadoc)
329: *
330: * @see java.sql.Connection#prepareStatement(java.lang.String, int)
331: */
332: public synchronized PreparedStatement prepareStatement(String sql,
333: int autoGeneratedKeys) throws SQLException {
334: PreparedStatement pstmt = this .currentConnection
335: .prepareStatement(sql, autoGeneratedKeys);
336:
337: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
338:
339: return pstmt;
340: }
341:
342: /*
343: * (non-Javadoc)
344: *
345: * @see java.sql.Connection#prepareStatement(java.lang.String, int, int)
346: */
347: public synchronized PreparedStatement prepareStatement(String sql,
348: int resultSetType, int resultSetConcurrency)
349: throws SQLException {
350: PreparedStatement pstmt = this .currentConnection
351: .prepareStatement(sql, resultSetType,
352: resultSetConcurrency);
353:
354: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
355:
356: return pstmt;
357: }
358:
359: /*
360: * (non-Javadoc)
361: *
362: * @see java.sql.Connection#prepareStatement(java.lang.String, int, int,
363: * int)
364: */
365: public synchronized PreparedStatement prepareStatement(String sql,
366: int resultSetType, int resultSetConcurrency,
367: int resultSetHoldability) throws SQLException {
368: PreparedStatement pstmt = this .currentConnection
369: .prepareStatement(sql, resultSetType,
370: resultSetConcurrency, resultSetHoldability);
371:
372: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
373:
374: return pstmt;
375: }
376:
377: /*
378: * (non-Javadoc)
379: *
380: * @see java.sql.Connection#prepareStatement(java.lang.String, int[])
381: */
382: public synchronized PreparedStatement prepareStatement(String sql,
383: int[] columnIndexes) throws SQLException {
384: PreparedStatement pstmt = this .currentConnection
385: .prepareStatement(sql, columnIndexes);
386:
387: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
388:
389: return pstmt;
390: }
391:
392: /*
393: * (non-Javadoc)
394: *
395: * @see java.sql.Connection#prepareStatement(java.lang.String,
396: * java.lang.String[])
397: */
398: public synchronized PreparedStatement prepareStatement(String sql,
399: String[] columnNames) throws SQLException {
400: PreparedStatement pstmt = this .currentConnection
401: .prepareStatement(sql, columnNames);
402:
403: ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this );
404:
405: return pstmt;
406: }
407:
408: /*
409: * (non-Javadoc)
410: *
411: * @see java.sql.Connection#releaseSavepoint(java.sql.Savepoint)
412: */
413: public synchronized void releaseSavepoint(Savepoint savepoint)
414: throws SQLException {
415: this .currentConnection.releaseSavepoint(savepoint);
416: }
417:
418: /*
419: * (non-Javadoc)
420: *
421: * @see java.sql.Connection#rollback()
422: */
423: public synchronized void rollback() throws SQLException {
424: this .currentConnection.rollback();
425: }
426:
427: /*
428: * (non-Javadoc)
429: *
430: * @see java.sql.Connection#rollback(java.sql.Savepoint)
431: */
432: public synchronized void rollback(Savepoint savepoint)
433: throws SQLException {
434: this .currentConnection.rollback(savepoint);
435: }
436:
437: /*
438: * (non-Javadoc)
439: *
440: * @see java.sql.Connection#setAutoCommit(boolean)
441: */
442: public synchronized void setAutoCommit(boolean autoCommit)
443: throws SQLException {
444: this .currentConnection.setAutoCommit(autoCommit);
445: }
446:
447: /*
448: * (non-Javadoc)
449: *
450: * @see java.sql.Connection#setCatalog(java.lang.String)
451: */
452: public synchronized void setCatalog(String catalog)
453: throws SQLException {
454: this .currentConnection.setCatalog(catalog);
455: }
456:
457: /*
458: * (non-Javadoc)
459: *
460: * @see java.sql.Connection#setHoldability(int)
461: */
462: public synchronized void setHoldability(int holdability)
463: throws SQLException {
464: this .currentConnection.setHoldability(holdability);
465: }
466:
467: /*
468: * (non-Javadoc)
469: *
470: * @see java.sql.Connection#setReadOnly(boolean)
471: */
472: public synchronized void setReadOnly(boolean readOnly)
473: throws SQLException {
474: if (readOnly) {
475: if (currentConnection != slavesConnection) {
476: switchToSlavesConnection();
477: }
478: } else {
479: if (currentConnection != masterConnection) {
480: switchToMasterConnection();
481: }
482: }
483: }
484:
485: /*
486: * (non-Javadoc)
487: *
488: * @see java.sql.Connection#setSavepoint()
489: */
490: public synchronized Savepoint setSavepoint() throws SQLException {
491: return this .currentConnection.setSavepoint();
492: }
493:
494: /*
495: * (non-Javadoc)
496: *
497: * @see java.sql.Connection#setSavepoint(java.lang.String)
498: */
499: public synchronized Savepoint setSavepoint(String name)
500: throws SQLException {
501: return this .currentConnection.setSavepoint(name);
502: }
503:
504: /*
505: * (non-Javadoc)
506: *
507: * @see java.sql.Connection#setTransactionIsolation(int)
508: */
509: public synchronized void setTransactionIsolation(int level)
510: throws SQLException {
511: this .currentConnection.setTransactionIsolation(level);
512: }
513:
514: // For testing
515:
516: /*
517: * (non-Javadoc)
518: *
519: * @see java.sql.Connection#setTypeMap(java.util.Map)
520: */
521: public synchronized void setTypeMap(Map arg0) throws SQLException {
522: this .currentConnection.setTypeMap(arg0);
523: }
524:
525: private synchronized void switchToMasterConnection()
526: throws SQLException {
527: swapConnections(this .masterConnection, this .slavesConnection);
528: }
529:
530: private synchronized void switchToSlavesConnection()
531: throws SQLException {
532: swapConnections(this .slavesConnection, this .masterConnection);
533: }
534:
535: /**
536: * Swaps current context (catalog, autocommit and txn_isolation) from
537: * sourceConnection to targetConnection, and makes targetConnection
538: * the "current" connection that will be used for queries.
539: *
540: * @param switchToConnection the connection to swap from
541: * @param switchFromConnection the connection to swap to
542: *
543: * @throws SQLException if an error occurs
544: */
545: private synchronized void swapConnections(
546: Connection switchToConnection,
547: Connection switchFromConnection) throws SQLException {
548: String switchFromCatalog = switchFromConnection.getCatalog();
549: String switchToCatalog = switchToConnection.getCatalog();
550:
551: if (switchToCatalog != null
552: && !switchToCatalog.equals(switchFromCatalog)) {
553: switchToConnection.setCatalog(switchFromCatalog);
554: } else if (switchFromCatalog != null) {
555: switchToConnection.setCatalog(switchFromCatalog);
556: }
557:
558: boolean switchToAutoCommit = switchToConnection.getAutoCommit();
559: boolean switchFromConnectionAutoCommit = switchFromConnection
560: .getAutoCommit();
561:
562: if (switchFromConnectionAutoCommit != switchToAutoCommit) {
563: switchToConnection
564: .setAutoCommit(switchFromConnectionAutoCommit);
565: }
566:
567: int switchToIsolation = switchToConnection
568: .getTransactionIsolation();
569:
570: int switchFromIsolation = switchFromConnection
571: .getTransactionIsolation();
572:
573: if (switchFromIsolation != switchToIsolation) {
574: switchToConnection
575: .setTransactionIsolation(switchFromIsolation);
576: }
577:
578: this .currentConnection = switchToConnection;
579: }
580:
581: public synchronized void doPing() throws SQLException {
582: if (this.masterConnection != null) {
583: this.masterConnection.ping();
584: }
585:
586: if (this.slavesConnection != null) {
587: this.slavesConnection.ping();
588: }
589: }
590: }
|