PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/testdata/mysql/com/mysql/jdbc/ReplicationConnectionProxy.java

https://gitlab.com/samebug/artifact-metaextractor
Java | 694 lines | 512 code | 85 blank | 97 comment | 169 complexity | 4fff71b5605b91c8bff55bb94eee1cdd MD5 | raw file
  1. /*
  2. Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
  3. The MySQL Connector/J is licensed under the terms of the GPLv2
  4. <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  5. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  6. this software, see the FOSS License Exception
  7. <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  8. This program is free software; you can redistribute it and/or modify it under the terms
  9. of the GNU General Public License as published by the Free Software Foundation; version 2
  10. of the License.
  11. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  12. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. See the GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License along with this
  15. program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  16. Floor, Boston, MA 02110-1301 USA
  17. */
  18. package com.mysql.jdbc;
  19. import java.lang.reflect.Constructor;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.lang.reflect.Method;
  22. import java.sql.SQLException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.Properties;
  26. import java.util.concurrent.Executor;
  27. /**
  28. * Connection that opens two connections, one two a replication master, and another to one or more slaves, and decides to use master when the connection is not
  29. * read-only, and use slave(s) when the connection is read-only.
  30. */
  31. public class ReplicationConnectionProxy extends MultiHostConnectionProxy implements PingTarget {
  32. private ReplicationConnection thisAsReplicationConnection;
  33. private NonRegisteringDriver driver;
  34. protected boolean enableJMX = false;
  35. protected boolean allowMasterDownConnections = false;
  36. protected boolean allowSlaveDownConnections = false;
  37. protected boolean readFromMasterWhenNoSlaves = false;
  38. protected boolean readFromMasterWhenNoSlavesOriginal = false;
  39. protected boolean readOnly = false;
  40. ReplicationConnectionGroup connectionGroup;
  41. private long connectionGroupID = -1;
  42. private List<String> masterHosts;
  43. private Properties masterProperties;
  44. protected LoadBalancedConnection masterConnection;
  45. private List<String> slaveHosts;
  46. private Properties slaveProperties;
  47. protected LoadBalancedConnection slavesConnection;
  48. private static Constructor<?> JDBC_4_REPL_CONNECTION_CTOR;
  49. private static Class<?>[] INTERFACES_TO_PROXY;
  50. static {
  51. if (Util.isJdbc4()) {
  52. try {
  53. JDBC_4_REPL_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4ReplicationMySQLConnection")
  54. .getConstructor(new Class[] { ReplicationConnectionProxy.class });
  55. INTERFACES_TO_PROXY = new Class<?>[] { ReplicationConnection.class, Class.forName("com.mysql.jdbc.JDBC4MySQLConnection") };
  56. } catch (SecurityException e) {
  57. throw new RuntimeException(e);
  58. } catch (NoSuchMethodException e) {
  59. throw new RuntimeException(e);
  60. } catch (ClassNotFoundException e) {
  61. throw new RuntimeException(e);
  62. }
  63. } else {
  64. INTERFACES_TO_PROXY = new Class<?>[] { ReplicationConnection.class };
  65. }
  66. }
  67. public static ReplicationConnection createProxyInstance(List<String> masterHostList, Properties masterProperties, List<String> slaveHostList,
  68. Properties slaveProperties) throws SQLException {
  69. ReplicationConnectionProxy connProxy = new ReplicationConnectionProxy(masterHostList, masterProperties, slaveHostList, slaveProperties);
  70. return (ReplicationConnection) java.lang.reflect.Proxy.newProxyInstance(ReplicationConnection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy);
  71. }
  72. /**
  73. * Creates a proxy for java.sql.Connection that routes requests to a load-balanced connection of master servers or a load-balanced connection of slave
  74. * servers. Each sub-connection is created with its own set of independent properties.
  75. *
  76. * @param masterHostList
  77. * The list of hosts to use in the masters connection.
  78. * @param masterProperties
  79. * The properties for the masters connection.
  80. * @param slaveHostList
  81. * The list of hosts to use in the slaves connection.
  82. * @param slaveProperties
  83. * The properties for the slaves connection.
  84. * @throws SQLException
  85. */
  86. private ReplicationConnectionProxy(List<String> masterHostList, Properties masterProperties, List<String> slaveHostList, Properties slaveProperties)
  87. throws SQLException {
  88. super();
  89. this.thisAsReplicationConnection = (ReplicationConnection) this.thisAsConnection;
  90. String enableJMXAsString = masterProperties.getProperty("replicationEnableJMX", "false");
  91. try {
  92. this.enableJMX = Boolean.parseBoolean(enableJMXAsString);
  93. } catch (Exception e) {
  94. throw SQLError.createSQLException(
  95. Messages.getString("ReplicationConnectionProxy.badValueForReplicationEnableJMX", new Object[] { enableJMXAsString }),
  96. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
  97. }
  98. String allowMasterDownConnectionsAsString = masterProperties.getProperty("allowMasterDownConnections", "false");
  99. try {
  100. this.allowMasterDownConnections = Boolean.parseBoolean(allowMasterDownConnectionsAsString);
  101. } catch (Exception e) {
  102. throw SQLError.createSQLException(
  103. Messages.getString("ReplicationConnectionProxy.badValueForAllowMasterDownConnections", new Object[] { allowMasterDownConnectionsAsString }),
  104. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
  105. }
  106. String allowSlaveDownConnectionsAsString = masterProperties.getProperty("allowSlaveDownConnections", "false");
  107. try {
  108. this.allowSlaveDownConnections = Boolean.parseBoolean(allowSlaveDownConnectionsAsString);
  109. } catch (Exception e) {
  110. throw SQLError.createSQLException(
  111. Messages.getString("ReplicationConnectionProxy.badValueForAllowSlaveDownConnections", new Object[] { allowSlaveDownConnectionsAsString }),
  112. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
  113. }
  114. String readFromMasterWhenNoSlavesAsString = masterProperties.getProperty("readFromMasterWhenNoSlaves");
  115. try {
  116. this.readFromMasterWhenNoSlavesOriginal = Boolean.parseBoolean(readFromMasterWhenNoSlavesAsString);
  117. } catch (Exception e) {
  118. throw SQLError.createSQLException(
  119. Messages.getString("ReplicationConnectionProxy.badValueForReadFromMasterWhenNoSlaves", new Object[] { readFromMasterWhenNoSlavesAsString }),
  120. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
  121. }
  122. String group = masterProperties.getProperty("replicationConnectionGroup", null);
  123. if (group != null) {
  124. this.connectionGroup = ReplicationConnectionGroupManager.getConnectionGroupInstance(group);
  125. if (this.enableJMX) {
  126. ReplicationConnectionGroupManager.registerJmx();
  127. }
  128. this.connectionGroupID = this.connectionGroup.registerReplicationConnection(this.thisAsReplicationConnection, masterHostList, slaveHostList);
  129. this.slaveHosts = new ArrayList<String>(this.connectionGroup.getSlaveHosts());
  130. this.masterHosts = new ArrayList<String>(this.connectionGroup.getMasterHosts());
  131. } else {
  132. this.slaveHosts = new ArrayList<String>(slaveHostList);
  133. this.masterHosts = new ArrayList<String>(masterHostList);
  134. }
  135. this.driver = new NonRegisteringDriver();
  136. this.slaveProperties = slaveProperties;
  137. this.masterProperties = masterProperties;
  138. resetReadFromMasterWhenNoSlaves();
  139. // Initialize slaves connection first so that it is ready to be used in case the masters connection fails and 'allowMasterDownConnections=true'.
  140. try {
  141. initializeSlavesConnection();
  142. } catch (SQLException e) {
  143. if (!this.allowSlaveDownConnections) {
  144. if (this.connectionGroup != null) {
  145. this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection);
  146. }
  147. throw e;
  148. } // Else swallow this exception.
  149. }
  150. SQLException exCaught = null;
  151. try {
  152. this.currentConnection = initializeMasterConnection();
  153. } catch (SQLException e) {
  154. exCaught = e;
  155. }
  156. if (this.currentConnection == null) {
  157. if (this.allowMasterDownConnections && this.slavesConnection != null) {
  158. // Set read-only and fail over to the slaves connection.
  159. this.readOnly = true;
  160. this.currentConnection = this.slavesConnection;
  161. } else {
  162. if (this.connectionGroup != null) {
  163. this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection);
  164. }
  165. if (exCaught != null) {
  166. throw exCaught;
  167. }
  168. throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.initializationWithEmptyHostsLists"),
  169. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
  170. }
  171. }
  172. }
  173. /**
  174. * Wraps this object with a new replication Connection instance.
  175. *
  176. * @return
  177. * The connection object instance that wraps 'this'.
  178. */
  179. @Override
  180. MySQLConnection getNewWrapperForThisAsConnection() throws SQLException {
  181. if (Util.isJdbc4() || JDBC_4_REPL_CONNECTION_CTOR != null) {
  182. return (MySQLConnection) Util.handleNewInstance(JDBC_4_REPL_CONNECTION_CTOR, new Object[] { this }, null);
  183. }
  184. return new ReplicationMySQLConnection(this);
  185. }
  186. /**
  187. * Propagates the connection proxy down through all live connections.
  188. *
  189. * @param proxyConn
  190. * The top level connection in the multi-host connections chain.
  191. */
  192. @Override
  193. protected void propagateProxyDown(MySQLConnection proxyConn) {
  194. if (this.masterConnection != null) {
  195. this.masterConnection.setProxy(proxyConn);
  196. }
  197. if (this.slavesConnection != null) {
  198. this.slavesConnection.setProxy(proxyConn);
  199. }
  200. }
  201. /**
  202. * Has no use in replication connections. Always return <code>false</code>.
  203. *
  204. * @param ex
  205. * The Exception instance to check.
  206. */
  207. @Override
  208. boolean shouldExceptionTriggerConnectionSwitch(Throwable t) {
  209. return false;
  210. }
  211. /**
  212. * Checks if current connection is the masters l/b connection.
  213. */
  214. @Override
  215. public boolean isMasterConnection() {
  216. return this.currentConnection != null && this.currentConnection == this.masterConnection;
  217. }
  218. /**
  219. * Checks if current connection is the slaves l/b connection.
  220. */
  221. public boolean isSlavesConnection() {
  222. return this.currentConnection != null && this.currentConnection == this.slavesConnection;
  223. }
  224. @Override
  225. void pickNewConnection() throws SQLException {
  226. // no-op
  227. }
  228. @Override
  229. void doClose() throws SQLException {
  230. if (this.masterConnection != null) {
  231. this.masterConnection.close();
  232. }
  233. if (this.slavesConnection != null) {
  234. this.slavesConnection.close();
  235. }
  236. if (this.connectionGroup != null) {
  237. this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection);
  238. }
  239. }
  240. @Override
  241. void doAbortInternal() throws SQLException {
  242. this.masterConnection.abortInternal();
  243. this.slavesConnection.abortInternal();
  244. if (this.connectionGroup != null) {
  245. this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection);
  246. }
  247. }
  248. @Override
  249. void doAbort(Executor executor) throws SQLException {
  250. this.masterConnection.abort(executor);
  251. this.slavesConnection.abort(executor);
  252. if (this.connectionGroup != null) {
  253. this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection);
  254. }
  255. }
  256. /**
  257. * Proxies method invocation on the java.sql.Connection interface.
  258. * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]).
  259. */
  260. @Override
  261. Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable {
  262. checkConnectionCapabilityForMethod(method);
  263. boolean invokeAgain = false;
  264. while (true) {
  265. try {
  266. Object result = method.invoke(this.thisAsConnection, args);
  267. if (result != null && result instanceof Statement) {
  268. ((Statement) result).setPingTarget(this);
  269. }
  270. return result;
  271. } catch (InvocationTargetException e) {
  272. if (invokeAgain) {
  273. invokeAgain = false;
  274. } else if (e.getCause() != null && e.getCause() instanceof SQLException
  275. && ((SQLException) e.getCause()).getSQLState() == SQLError.SQL_STATE_INVALID_TRANSACTION_STATE
  276. && ((SQLException) e.getCause()).getErrorCode() == MysqlErrorNumbers.ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION) {
  277. try {
  278. // Try to re-establish the connection with the last known read-only state.
  279. setReadOnly(this.readOnly);
  280. invokeAgain = true;
  281. } catch (SQLException sqlEx) {
  282. // Still not good. Swallow this exception.
  283. }
  284. }
  285. if (!invokeAgain) {
  286. throw e;
  287. }
  288. }
  289. }
  290. }
  291. /**
  292. * Checks if this connection is in a state capable to invoke the provided method. If the connection is in an inconsistent state, i.e. it has no hosts for
  293. * both sub-connections, then throw an invalid transaction state exception. Nevertheless, the methods defined in the ReplicationConnection interface will be
  294. * allowed as they are the only way to leave from an empty hosts lists situation.
  295. */
  296. private void checkConnectionCapabilityForMethod(Method method) throws Throwable {
  297. if (this.masterHosts.isEmpty() && this.slaveHosts.isEmpty() && !ReplicationConnection.class.isAssignableFrom(method.getDeclaringClass())) {
  298. throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.noHostsInconsistentState"),
  299. SQLError.SQL_STATE_INVALID_TRANSACTION_STATE, MysqlErrorNumbers.ERROR_CODE_REPLICATION_CONNECTION_WITH_NO_HOSTS, true, null);
  300. }
  301. }
  302. /**
  303. * Pings both l/b connections. Switch to another connection in case of failure.
  304. */
  305. public void doPing() throws SQLException {
  306. boolean isMasterConn = isMasterConnection();
  307. SQLException mastersPingException = null;
  308. SQLException slavesPingException = null;
  309. if (this.masterConnection != null) {
  310. try {
  311. this.masterConnection.ping();
  312. } catch (SQLException e) {
  313. mastersPingException = e;
  314. }
  315. } else {
  316. initializeMasterConnection();
  317. }
  318. if (this.slavesConnection != null) {
  319. try {
  320. this.slavesConnection.ping();
  321. } catch (SQLException e) {
  322. slavesPingException = e;
  323. }
  324. } else {
  325. try {
  326. initializeSlavesConnection();
  327. if (switchToSlavesConnectionIfNecessary()) {
  328. isMasterConn = false;
  329. }
  330. } catch (SQLException e) {
  331. if (this.masterConnection == null || !this.readFromMasterWhenNoSlaves) {
  332. throw e;
  333. } // Else swallow this exception.
  334. }
  335. }
  336. if (isMasterConn && mastersPingException != null) {
  337. // Switch to slaves connection.
  338. if (this.slavesConnection != null && slavesPingException == null) {
  339. this.masterConnection = null;
  340. this.currentConnection = this.slavesConnection;
  341. this.readOnly = true;
  342. }
  343. throw mastersPingException;
  344. } else if (!isMasterConn && (slavesPingException != null || this.slavesConnection == null)) {
  345. // Switch to masters connection, setting read-only state, if 'readFromMasterWhenNoSlaves=true'.
  346. if (this.masterConnection != null && this.readFromMasterWhenNoSlaves && mastersPingException == null) {
  347. this.slavesConnection = null;
  348. this.currentConnection = this.masterConnection;
  349. this.readOnly = true;
  350. this.currentConnection.setReadOnly(true);
  351. }
  352. if (slavesPingException != null) {
  353. throw slavesPingException;
  354. }
  355. }
  356. }
  357. private MySQLConnection initializeMasterConnection() throws SQLException {
  358. this.masterConnection = null;
  359. if (this.masterHosts.size() == 0) {
  360. return null;
  361. }
  362. LoadBalancedConnection newMasterConn = (LoadBalancedConnection) this.driver.connect(buildURL(this.masterHosts, this.masterProperties),
  363. this.masterProperties);
  364. newMasterConn.setProxy(getProxy());
  365. this.masterConnection = newMasterConn;
  366. return this.masterConnection;
  367. }
  368. private MySQLConnection initializeSlavesConnection() throws SQLException {
  369. this.slavesConnection = null;
  370. if (this.slaveHosts.size() == 0) {
  371. return null;
  372. }
  373. LoadBalancedConnection newSlavesConn = (LoadBalancedConnection) this.driver.connect(buildURL(this.slaveHosts, this.slaveProperties),
  374. this.slaveProperties);
  375. newSlavesConn.setProxy(getProxy());
  376. newSlavesConn.setReadOnly(true);
  377. this.slavesConnection = newSlavesConn;
  378. return this.slavesConnection;
  379. }
  380. private String buildURL(List<String> hosts, Properties props) {
  381. StringBuilder url = new StringBuilder(NonRegisteringDriver.LOADBALANCE_URL_PREFIX);
  382. boolean firstHost = true;
  383. for (String host : hosts) {
  384. if (!firstHost) {
  385. url.append(',');
  386. }
  387. url.append(host);
  388. firstHost = false;
  389. }
  390. url.append("/");
  391. String masterDb = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
  392. if (masterDb != null) {
  393. url.append(masterDb);
  394. }
  395. return url.toString();
  396. }
  397. private synchronized boolean switchToMasterConnection() throws SQLException {
  398. if (this.masterConnection == null || this.masterConnection.isClosed()) {
  399. try {
  400. if (initializeMasterConnection() == null) {
  401. return false;
  402. }
  403. } catch (SQLException e) {
  404. this.currentConnection = null;
  405. throw e;
  406. }
  407. }
  408. if (!isMasterConnection() && this.masterConnection != null) {
  409. syncSessionState(this.currentConnection, this.masterConnection, false);
  410. this.currentConnection = this.masterConnection;
  411. }
  412. return true;
  413. }
  414. private synchronized boolean switchToSlavesConnection() throws SQLException {
  415. if (this.slavesConnection == null || this.slavesConnection.isClosed()) {
  416. try {
  417. if (initializeSlavesConnection() == null) {
  418. return false;
  419. }
  420. } catch (SQLException e) {
  421. this.currentConnection = null;
  422. throw e;
  423. }
  424. }
  425. if (!isSlavesConnection() && this.slavesConnection != null) {
  426. syncSessionState(this.currentConnection, this.slavesConnection, true);
  427. this.currentConnection = this.slavesConnection;
  428. }
  429. return true;
  430. }
  431. private boolean switchToSlavesConnectionIfNecessary() throws SQLException {
  432. // Switch to slaves connection:
  433. // - If the current connection is null. Or,
  434. // - If we're currently on the master and in read-only mode - we didn't have any slaves to use until now. Or,
  435. // - If we're currently on a closed master connection and there are no masters to connect to. Or,
  436. // - If we're currently not on a master connection that is closed - means that we were on a closed slaves connection before it was re-initialized.
  437. if (this.currentConnection == null || isMasterConnection() && (this.readOnly || this.masterHosts.isEmpty() && this.currentConnection.isClosed())
  438. || !isMasterConnection() && this.currentConnection.isClosed()) {
  439. return switchToSlavesConnection();
  440. }
  441. return false;
  442. }
  443. public synchronized Connection getCurrentConnection() {
  444. return this.currentConnection == null ? LoadBalancedConnectionProxy.getNullLoadBalancedConnectionInstance() : this.currentConnection;
  445. }
  446. public long getConnectionGroupId() {
  447. return this.connectionGroupID;
  448. }
  449. public synchronized Connection getMasterConnection() {
  450. return this.masterConnection;
  451. }
  452. public synchronized void promoteSlaveToMaster(String hostPortPair) throws SQLException {
  453. this.masterHosts.add(hostPortPair);
  454. removeSlave(hostPortPair);
  455. if (this.masterConnection != null) {
  456. this.masterConnection.addHost(hostPortPair);
  457. }
  458. }
  459. public synchronized void removeMasterHost(String hostPortPair) throws SQLException {
  460. this.removeMasterHost(hostPortPair, true);
  461. }
  462. public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse) throws SQLException {
  463. this.removeMasterHost(hostPortPair, waitUntilNotInUse, false);
  464. }
  465. public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse, boolean isNowSlave) throws SQLException {
  466. if (isNowSlave) {
  467. this.slaveHosts.add(hostPortPair);
  468. resetReadFromMasterWhenNoSlaves();
  469. }
  470. this.masterHosts.remove(hostPortPair);
  471. // The master connection may have been implicitly closed by a previous op., don't let it stop us.
  472. if (this.masterConnection == null || this.masterConnection.isClosed()) {
  473. this.masterConnection = null;
  474. return;
  475. }
  476. if (waitUntilNotInUse) {
  477. this.masterConnection.removeHostWhenNotInUse(hostPortPair);
  478. } else {
  479. this.masterConnection.removeHost(hostPortPair);
  480. }
  481. // Close the connection if that was the last master.
  482. if (this.masterHosts.isEmpty()) {
  483. this.masterConnection.close();
  484. this.masterConnection = null;
  485. // Default behavior, no need to check this.readFromMasterWhenNoSlaves.
  486. switchToSlavesConnectionIfNecessary();
  487. }
  488. }
  489. public boolean isHostMaster(String hostPortPair) {
  490. if (hostPortPair == null) {
  491. return false;
  492. }
  493. for (String masterHost : this.masterHosts) {
  494. if (masterHost.equalsIgnoreCase(hostPortPair)) {
  495. return true;
  496. }
  497. }
  498. return false;
  499. }
  500. public synchronized Connection getSlavesConnection() {
  501. return this.slavesConnection;
  502. }
  503. public synchronized void addSlaveHost(String hostPortPair) throws SQLException {
  504. if (this.isHostSlave(hostPortPair)) {
  505. return;
  506. }
  507. this.slaveHosts.add(hostPortPair);
  508. resetReadFromMasterWhenNoSlaves();
  509. if (this.slavesConnection == null) {
  510. initializeSlavesConnection();
  511. switchToSlavesConnectionIfNecessary();
  512. } else {
  513. this.slavesConnection.addHost(hostPortPair);
  514. }
  515. }
  516. public synchronized void removeSlave(String hostPortPair) throws SQLException {
  517. removeSlave(hostPortPair, true);
  518. }
  519. public synchronized void removeSlave(String hostPortPair, boolean closeGently) throws SQLException {
  520. this.slaveHosts.remove(hostPortPair);
  521. resetReadFromMasterWhenNoSlaves();
  522. if (this.slavesConnection == null || this.slavesConnection.isClosed()) {
  523. this.slavesConnection = null;
  524. return;
  525. }
  526. if (closeGently) {
  527. this.slavesConnection.removeHostWhenNotInUse(hostPortPair);
  528. } else {
  529. this.slavesConnection.removeHost(hostPortPair);
  530. }
  531. // Close the connection if that was the last slave.
  532. if (this.slaveHosts.isEmpty()) {
  533. this.slavesConnection.close();
  534. this.slavesConnection = null;
  535. // Default behavior, no need to check this.readFromMasterWhenNoSlaves.
  536. switchToMasterConnection();
  537. if (isMasterConnection()) {
  538. this.currentConnection.setReadOnly(this.readOnly); // Maintain.
  539. }
  540. }
  541. }
  542. public boolean isHostSlave(String hostPortPair) {
  543. if (hostPortPair == null) {
  544. return false;
  545. }
  546. for (String test : this.slaveHosts) {
  547. if (test.equalsIgnoreCase(hostPortPair)) {
  548. return true;
  549. }
  550. }
  551. return false;
  552. }
  553. public synchronized void setReadOnly(boolean readOnly) throws SQLException {
  554. if (readOnly) {
  555. if (!isSlavesConnection() || this.currentConnection.isClosed()) {
  556. boolean switched = true;
  557. SQLException exceptionCaught = null;
  558. try {
  559. switched = switchToSlavesConnection();
  560. } catch (SQLException e) {
  561. switched = false;
  562. exceptionCaught = e;
  563. }
  564. if (!switched && this.readFromMasterWhenNoSlaves && switchToMasterConnection()) {
  565. exceptionCaught = null; // The connection is OK. Cancel the exception, if any.
  566. }
  567. if (exceptionCaught != null) {
  568. throw exceptionCaught;
  569. }
  570. }
  571. } else {
  572. if (!isMasterConnection() || this.currentConnection.isClosed()) {
  573. boolean switched = true;
  574. SQLException exceptionCaught = null;
  575. try {
  576. switched = switchToMasterConnection();
  577. } catch (SQLException e) {
  578. switched = false;
  579. exceptionCaught = e;
  580. }
  581. if (!switched && switchToSlavesConnectionIfNecessary()) {
  582. exceptionCaught = null; // The connection is OK. Cancel the exception, if any.
  583. }
  584. if (exceptionCaught != null) {
  585. throw exceptionCaught;
  586. }
  587. }
  588. }
  589. this.readOnly = readOnly;
  590. /*
  591. * Reset masters connection read-only state if 'readFromMasterWhenNoSlaves=true'. If there are no slaves then the masters connection will be used with
  592. * read-only state in its place. Even if not, it must be reset from a possible previous read-only state.
  593. */
  594. if (this.readFromMasterWhenNoSlaves && isMasterConnection()) {
  595. this.currentConnection.setReadOnly(this.readOnly);
  596. }
  597. }
  598. public boolean isReadOnly() throws SQLException {
  599. return !isMasterConnection() || this.readOnly;
  600. }
  601. private void resetReadFromMasterWhenNoSlaves() {
  602. this.readFromMasterWhenNoSlaves = this.slaveHosts.isEmpty() || this.readFromMasterWhenNoSlavesOriginal;
  603. }
  604. }