1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.jdbc.support;
  17. import java.sql.Connection;
  18. import java.sql.SQLException;
  19. import java.sql.Statement;
  20. import javax.sql.DataSource;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.springframework.beans.factory.InitializingBean;
  24. import org.springframework.jdbc.CannotGetJdbcConnectionException;
  25. /**
  26. * Bean that checks if a database has already started up. To be referenced
  27. * via "depends-on" from beans that depend on database startup, like a Hibernate
  28. * SessionFactory or custom data access objects that access a DataSource directly.
  29. *
  30. * <p>Useful to defer application initialization until a database has started up.
  31. * Particularly appropriate for waiting on a slow-starting Oracle database.
  32. *
  33. * @author Juergen Hoeller
  34. * @since 18.12.2003
  35. */
  36. public class DatabaseStartupValidator implements InitializingBean {
  37. public static final int DEFAULT_INTERVAL = 1;
  38. public static final int DEFAULT_TIMEOUT = 60;
  39. protected final Log logger = LogFactory.getLog(getClass());
  40. private DataSource dataSource;
  41. private String validationQuery;
  42. private int interval = DEFAULT_INTERVAL;
  43. private int timeout = DEFAULT_TIMEOUT;
  44. /**
  45. * Set the DataSource to validate.
  46. */
  47. public void setDataSource(DataSource dataSource) {
  48. this.dataSource = dataSource;
  49. }
  50. /**
  51. * Set the SQL query string to use for validation.
  52. */
  53. public void setValidationQuery(String validationQuery) {
  54. this.validationQuery = validationQuery;
  55. }
  56. /**
  57. * Set the interval between validation runs (in seconds).
  58. * Default is 1.
  59. */
  60. public void setInterval(int interval) {
  61. this.interval = interval;
  62. }
  63. /**
  64. * Set the timeout after which a fatal exception will be thrown.
  65. * Default is 60.
  66. */
  67. public void setTimeout(int timeout) {
  68. this.timeout = timeout;
  69. }
  70. /**
  71. * Check whether the validation query can be executed on a Connection
  72. * from the specified DataSource, with the specified interval between
  73. * checks, until the specified timeout.
  74. */
  75. public void afterPropertiesSet() {
  76. if (this.dataSource == null) {
  77. throw new IllegalArgumentException("dataSource is required");
  78. }
  79. if (this.validationQuery == null) {
  80. throw new IllegalArgumentException("validationQuery is required");
  81. }
  82. boolean validated = false;
  83. long beginTime = System.currentTimeMillis();
  84. long deadLine = beginTime + this.timeout * 1000;
  85. SQLException latestEx = null;
  86. while (!validated && System.currentTimeMillis() < deadLine) {
  87. Connection con = null;
  88. Statement stmt = null;
  89. try {
  90. con = this.dataSource.getConnection();
  91. stmt = con.createStatement();
  92. stmt.execute(this.validationQuery);
  93. validated = true;
  94. }
  95. catch (SQLException ex) {
  96. latestEx = ex;
  97. logger.debug("Validation query [" + this.validationQuery + "] threw exception", ex);
  98. float rest = ((float) (deadLine - System.currentTimeMillis())) / 1000;
  99. if (rest > this.interval) {
  100. logger.warn("Database has not started up yet - retrying in " + this.interval +
  101. " seconds (timeout in " + rest + " seconds)");
  102. }
  103. }
  104. finally {
  105. JdbcUtils.closeStatement(stmt);
  106. if (con != null) {
  107. try {
  108. con.close();
  109. }
  110. catch (SQLException ex) {
  111. // ignore
  112. }
  113. }
  114. }
  115. try {
  116. Thread.sleep(this.interval * 1000);
  117. }
  118. catch (InterruptedException ex) {
  119. // ignore
  120. }
  121. }
  122. if (validated) {
  123. float duration = (System.currentTimeMillis() - beginTime) / 1000;
  124. logger.info("Database startup detected after " + duration + " seconds");
  125. }
  126. else {
  127. throw new CannotGetJdbcConnectionException("Database has not started up within " +
  128. this.timeout + " seconds", latestEx);
  129. }
  130. }
  131. }