All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.agimatec.dbmigrate.util.BusyLocker Maven / Gradle / Ivy

The newest version!
package com.agimatec.dbmigrate.util;

import com.agimatec.dbmigrate.HaltedException;
import com.agimatec.jdbc.JdbcDatabase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.sql.Statement;

/**
 * Description: Implements the behavior to prevent dbmigrate to execute more than once at the same time
 * on the same database
* It INSERTs a "busy" version into the database and DELETEs it afterwards. *

* Date: 14.10.14
*

* * @since 2.5.19 */ public class BusyLocker implements DatabaseLocker { protected final String BUSY_VERSION = "busy"; protected static final Logger log = LoggerFactory.getLogger(BusyLocker.class); private final DBVersionMeta lockMeta; /** * number of attempts. -1 = unlimited. */ private int maxAttempts = -1; /** * number of millis to wait between attempts. default = 10000 (10 seconds) */ private int delayBetweenAttempts = 10 * 1000; protected boolean ownLock = false; public int getMaxAttempts() { return maxAttempts; } public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; } public int getDelayBetweenAttempts() { return delayBetweenAttempts; } public void setDelayBetweenAttempts(int delayBetweenAttempts) { this.delayBetweenAttempts = delayBetweenAttempts; } public BusyLocker(DBVersionMeta dbVersionMeta) { this.lockMeta = createLockMeta(dbVersionMeta); } public boolean isEnabled() { return (lockMeta.getLockBusy() != null && lockMeta.getLockBusy() != DBVersionMeta.LockBusy.No); } public void lock(JdbcDatabase database) { if (lockMeta.getLockBusy() == DBVersionMeta.LockBusy.No) return; DBVersionMeta lockMeta = createLockMeta(this.lockMeta); try { tryLock(database); } catch (SQLException ex) { if (lockMeta.getLockBusy() == DBVersionMeta.LockBusy.Fail) { fail(ex); } else if (lockMeta.getLockBusy() == DBVersionMeta.LockBusy.Wait) { waitAndRetry(database, 1, ex); } } } public DBVersionMeta getLockMeta() { return lockMeta; } protected DBVersionMeta createLockMeta(DBVersionMeta dbVersionMeta) { DBVersionMeta lockMeta = dbVersionMeta.copy(); lockMeta.setInsertOnly(false); lockMeta.setTableName(lockMeta.getLockTableName()); return lockMeta; } private void tryLock(JdbcDatabase database) throws SQLException { Statement stmt = database.getConnection().createStatement(); SQLCursor cursor = null; try { cursor = new SQLCursor(stmt, stmt.executeQuery(lockMeta.toSQLSelectVersion())); cursor.next(); cursor.close(); } catch (SQLException ex) { // assume that lock-table does not yet exist, auto-create if(cursor != null) cursor.close(); log.warn("Cannot access " + lockMeta.getTableName() + ": " + ex.getMessage()); try { UpdateVersionScriptVisitor.createTable(database, lockMeta); } catch(SQLException ex2) { log.warn("Read exception was: ", ex); log.warn("Cannot create " + lockMeta.getTableName() + " write exception was: ", ex2); throw ex2; } } finally { stmt.close(); } int count = UpdateVersionScriptVisitor.insertVersion(database, BUSY_VERSION, lockMeta); if (count != 1) { log.warn( lockMeta.toSQLInsert() + " for busy-lock '" + BUSY_VERSION + "' affected " + count + " rows!"); } else { log.info("Aquired busy-lock '" + BUSY_VERSION + "' on table " + lockMeta.getTableName()); setOwnLock(true); } } public boolean isOwnLock() { return ownLock; } public void setOwnLock(boolean ownLock) { this.ownLock = ownLock; } private void fail(SQLException ex) { throw new HaltedException( "Could not aquire busy-lock '" + BUSY_VERSION + "' from table '" + lockMeta.getLockTableName() + "'. " + "Perhaps another instance of dbmigrate is currently running on the database or " + "the lock was not correctly removed from a previous execution of dbmigrate. " + "\nTo remove the lock, execute: " + "DELETE FROM " + lockMeta.getTableName() + " WHERE " + lockMeta.getColumn_version() + " = '" + BUSY_VERSION + "';", ex); } private void waitAndRetry(JdbcDatabase database, int attempt, SQLException ex) { while (maxAttempts < 0 || attempt < maxAttempts) { log.warn("Attempt " + attempt + " to aquire busy-lock failed. Waiting for " + delayBetweenAttempts + " millis to retry...", ex); if (delayBetweenAttempts > 0) { try { Thread.sleep(delayBetweenAttempts); } catch (InterruptedException e) { log.warn("Interrupted while waiting for retry", e); } } try { tryLock(database); return; } catch (SQLException e) { ex = e; attempt++; } } fail(ex); } public void unlock(JdbcDatabase database) { if (lockMeta.getLockBusy() == DBVersionMeta.LockBusy.No) return; if (!isOwnLock()) { log.info("Not deleting lock '" + BUSY_VERSION + "' on table " + lockMeta.getTableName() + " because this instance does not own it."); } else { try { int count = UpdateVersionScriptVisitor.deleteVersion(database, BUSY_VERSION, lockMeta); if (count != 1) { log.warn( lockMeta.toSQLDelete() + " for busy-lock '" + BUSY_VERSION + "' affected " + count + " rows!"); } else { log.info( "Deleted busy-lock '" + BUSY_VERSION + "' on table " + lockMeta.getTableName()); setOwnLock(false); } } catch (SQLException e) { log.error("Failed to delete busy-lock", e); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy