/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.jdbc.support;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.lang.Nullable;

Bean that checks if a database has already started up. To be referenced via "depends-on" from beans that depend on database startup, like a Hibernate SessionFactory or custom data access objects that access a DataSource directly.

Useful to defer application initialization until a database has started up. Particularly appropriate for waiting on a slowly starting Oracle database.

Author:Juergen Hoeller
Since:18.12.2003
/** * Bean that checks if a database has already started up. To be referenced * via "depends-on" from beans that depend on database startup, like a Hibernate * SessionFactory or custom data access objects that access a DataSource directly. * * <p>Useful to defer application initialization until a database has started up. * Particularly appropriate for waiting on a slowly starting Oracle database. * * @author Juergen Hoeller * @since 18.12.2003 */
public class DatabaseStartupValidator implements InitializingBean {
The default interval.
/** * The default interval. */
public static final int DEFAULT_INTERVAL = 1;
The default timeout.
/** * The default timeout. */
public static final int DEFAULT_TIMEOUT = 60; protected final Log logger = LogFactory.getLog(getClass()); @Nullable private DataSource dataSource; @Nullable private String validationQuery; private int interval = DEFAULT_INTERVAL; private int timeout = DEFAULT_TIMEOUT;
Set the DataSource to validate.
/** * Set the DataSource to validate. */
public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }
Set the SQL query string to use for validation.
/** * Set the SQL query string to use for validation. */
public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; }
Set the interval between validation runs (in seconds). Default is 1.
/** * Set the interval between validation runs (in seconds). * Default is {@value #DEFAULT_INTERVAL}. */
public void setInterval(int interval) { this.interval = interval; }
Set the timeout (in seconds) after which a fatal exception will be thrown. Default is 60.
/** * Set the timeout (in seconds) after which a fatal exception * will be thrown. Default is {@value #DEFAULT_TIMEOUT}. */
public void setTimeout(int timeout) { this.timeout = timeout; }
Check whether the validation query can be executed on a Connection from the specified DataSource, with the specified interval between checks, until the specified timeout.
/** * Check whether the validation query can be executed on a Connection * from the specified DataSource, with the specified interval between * checks, until the specified timeout. */
@Override public void afterPropertiesSet() { if (this.dataSource == null) { throw new IllegalArgumentException("Property 'dataSource' is required"); } if (this.validationQuery == null) { throw new IllegalArgumentException("Property 'validationQuery' is required"); } try { boolean validated = false; long beginTime = System.currentTimeMillis(); long deadLine = beginTime + TimeUnit.SECONDS.toMillis(this.timeout); SQLException latestEx = null; while (!validated && System.currentTimeMillis() < deadLine) { Connection con = null; Statement stmt = null; try { con = this.dataSource.getConnection(); if (con == null) { throw new CannotGetJdbcConnectionException("Failed to execute validation query: " + "DataSource returned null from getConnection(): " + this.dataSource); } stmt = con.createStatement(); stmt.execute(this.validationQuery); validated = true; } catch (SQLException ex) { latestEx = ex; if (logger.isDebugEnabled()) { logger.debug("Validation query [" + this.validationQuery + "] threw exception", ex); } if (logger.isInfoEnabled()) { float rest = ((float) (deadLine - System.currentTimeMillis())) / 1000; if (rest > this.interval) { logger.info("Database has not started up yet - retrying in " + this.interval + " seconds (timeout in " + rest + " seconds)"); } } } finally { JdbcUtils.closeStatement(stmt); JdbcUtils.closeConnection(con); } if (!validated) { TimeUnit.SECONDS.sleep(this.interval); } } if (!validated) { throw new CannotGetJdbcConnectionException( "Database has not started up within " + this.timeout + " seconds", latestEx); } if (logger.isInfoEnabled()) { float duration = ((float) (System.currentTimeMillis() - beginTime)) / 1000; logger.info("Database startup detected after " + duration + " seconds"); } } catch (InterruptedException ex) { // Re-interrupt current thread, to allow other threads to react. Thread.currentThread().interrupt(); } } }