001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jdbc.support;
018:
019: import java.sql.Connection;
020: import java.sql.SQLException;
021: import java.sql.Statement;
022:
023: import javax.sql.DataSource;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import org.springframework.beans.factory.InitializingBean;
029: import org.springframework.jdbc.CannotGetJdbcConnectionException;
030:
031: /**
032: * Bean that checks if a database has already started up. To be referenced
033: * via "depends-on" from beans that depend on database startup, like a Hibernate
034: * SessionFactory or custom data access objects that access a DataSource directly.
035: *
036: * <p>Useful to defer application initialization until a database has started up.
037: * Particularly appropriate for waiting on a slowly starting Oracle database.
038: *
039: * @author Juergen Hoeller
040: * @since 18.12.2003
041: */
042: public class DatabaseStartupValidator implements InitializingBean {
043:
044: public static final int DEFAULT_INTERVAL = 1;
045:
046: public static final int DEFAULT_TIMEOUT = 60;
047:
048: protected final Log logger = LogFactory.getLog(getClass());
049:
050: private DataSource dataSource;
051:
052: private String validationQuery;
053:
054: private int interval = DEFAULT_INTERVAL;
055:
056: private int timeout = DEFAULT_TIMEOUT;
057:
058: /**
059: * Set the DataSource to validate.
060: */
061: public void setDataSource(DataSource dataSource) {
062: this .dataSource = dataSource;
063: }
064:
065: /**
066: * Set the SQL query string to use for validation.
067: */
068: public void setValidationQuery(String validationQuery) {
069: this .validationQuery = validationQuery;
070: }
071:
072: /**
073: * Set the interval between validation runs (in seconds).
074: * Default is 1.
075: */
076: public void setInterval(int interval) {
077: this .interval = interval;
078: }
079:
080: /**
081: * Set the timeout (in seconds) after which a fatal exception
082: * will be thrown. Default is 60.
083: */
084: public void setTimeout(int timeout) {
085: this .timeout = timeout;
086: }
087:
088: /**
089: * Check whether the validation query can be executed on a Connection
090: * from the specified DataSource, with the specified interval between
091: * checks, until the specified timeout.
092: */
093: public void afterPropertiesSet() {
094: if (this .dataSource == null) {
095: throw new IllegalArgumentException("dataSource is required");
096: }
097: if (this .validationQuery == null) {
098: throw new IllegalArgumentException(
099: "validationQuery is required");
100: }
101:
102: boolean validated = false;
103: long beginTime = System.currentTimeMillis();
104: long deadLine = beginTime + this .timeout * 1000;
105: SQLException latestEx = null;
106:
107: while (!validated && System.currentTimeMillis() < deadLine) {
108: Connection con = null;
109: Statement stmt = null;
110: try {
111: con = this .dataSource.getConnection();
112: stmt = con.createStatement();
113: stmt.execute(this .validationQuery);
114: validated = true;
115: } catch (SQLException ex) {
116: latestEx = ex;
117: logger.debug("Validation query ["
118: + this .validationQuery + "] threw exception",
119: ex);
120: float rest = ((float) (deadLine - System
121: .currentTimeMillis())) / 1000;
122: if (rest > this .interval) {
123: logger
124: .warn("Database has not started up yet - retrying in "
125: + this .interval
126: + " seconds (timeout in "
127: + rest
128: + " seconds)");
129: }
130: } finally {
131: JdbcUtils.closeStatement(stmt);
132: JdbcUtils.closeConnection(con);
133: }
134:
135: try {
136: Thread.sleep(this .interval * 1000);
137: } catch (InterruptedException ex) {
138: // Re-interrupt current thread, to allow other threads to react.
139: Thread.currentThread().interrupt();
140: }
141: }
142:
143: if (validated) {
144: float duration = (System.currentTimeMillis() - beginTime) / 1000;
145: if (logger.isInfoEnabled()) {
146: logger.info("Database startup detected after "
147: + duration + " seconds");
148: }
149: } else {
150: throw new CannotGetJdbcConnectionException(
151: "Database has not started up within "
152: + this .timeout + " seconds", latestEx);
153: }
154: }
155:
156: }
|