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

com.tacitknowledge.util.migration.jdbc.PatchTable Maven / Gradle / Ivy

There is a newer version: 1.4.2
Show newest version
/* Copyright 2004 Tacit Knowledge
 *  
 * 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 com.tacitknowledge.util.migration.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

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

import com.tacitknowledge.util.migration.MigrationException;
import com.tacitknowledge.util.migration.PatchInfoStore;
import com.tacitknowledge.util.migration.jdbc.util.SqlUtil;


/**
 * Manages interactions with the "patches" table.  The patches table stores
 * the current patch level for a given system, as well as a system-scoped lock
 * use to avoid concurrent patches to the system.  A system is defined as an
 * exclusive target of a patch.
 * 

* This class is responsible for: *

    *
  • Validating the existence of the patches table and creating it if it * doesn't exist
  • *
  • Determining if a patch is currently running on a given system
  • *
  • Obtaining and releasing patch locks for a given system
  • *
  • Obtaining and incrementing the patch level for a given system
  • *
*

* TRANSACTIONS: Transactions should be committed by the calling * class as needed. This class does not explictly commit or rollback transactions. * * @author Scott Askew ([email protected]) */ public class PatchTable implements PatchInfoStore { /** Class logger */ private static Log log = LogFactory.getLog(PatchTable.class); /** The migration configuration */ private JdbcMigrationContext context = null; /** Keeps track of table validation (see #createPatchesTableIfNeeded) */ private boolean tableExistenceValidated = false; /** * Create a new PatchTable. * * @param migrationContext the migration configuration and connection source */ public PatchTable(JdbcMigrationContext migrationContext) { this.context = migrationContext; if (context.getDatabaseType() == null) { throw new IllegalArgumentException("The JDBC database type is required"); } } /** {@inheritDoc} */ public void createPatchStoreIfNeeded() throws MigrationException { if (tableExistenceValidated) { return; } Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql("level.read")); stmt.setString(1, context.getSystemName()); rs = stmt.executeQuery(); log.debug("'patches' table already exists."); tableExistenceValidated = true; } catch (SQLException e) { // logging error in case it's not a simple patch table doesn't exist error log.debug(e.getMessage()); SqlUtil.close(null, stmt, rs); // check connection is valid before using, because the getConnection() call // could have thrown the SQLException if (null == conn) { throw new MigrationException("Unable to create a connection.", e); } log.info("'patches' table must not exist; creating...."); try { stmt = conn.prepareStatement(getSql("patches.create")); if (log.isDebugEnabled()) { log.debug("Creating patches table with SQL '" + getSql("patches.create") + "'"); } stmt.execute(); context.commit(); } catch (SQLException sqle) { throw new MigrationException("Unable to create patch table", sqle); } tableExistenceValidated = true; log.info("Created 'patches' table."); } catch (Exception ex) { throw new MigrationException("Unexpected exception while creating patch store.", ex); } finally { SqlUtil.close(conn, stmt, rs); } } /** {@inheritDoc} */ public int getPatchLevel() throws MigrationException { createPatchStoreIfNeeded(); Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql("level.read")); stmt.setString(1, context.getSystemName()); rs = stmt.executeQuery(); if (rs.next()) { return rs.getInt(1); } SqlUtil.close(conn, stmt, rs); conn = null; stmt = null; rs = null; // We don't yet have a patch record for this system; create one createSystemPatchRecord(); return 0; } catch (SQLException e) { throw new MigrationException("Unable to get patch level", e); } finally { SqlUtil.close(conn, stmt, rs); } } /** {@inheritDoc} */ public void updatePatchLevel(int level) throws MigrationException { // Make sure a patch record already exists for this system getPatchLevel(); Connection conn = null; PreparedStatement stmt = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql("level.update")); stmt.setInt(1, level); stmt.setString(2, context.getSystemName()); stmt.execute(); context.commit(); } catch (SQLException e) { throw new MigrationException("Unable to update patch level", e); } finally { SqlUtil.close(conn, stmt, null); } } /** {@inheritDoc} */ public boolean isPatchStoreLocked() throws MigrationException { createPatchStoreIfNeeded(); Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql("lock.read")); stmt.setString(1, context.getSystemName()); rs = stmt.executeQuery(); if (rs.next()) { return ("T".equals(rs.getString(1))); } else { return false; } } catch (SQLException e) { throw new MigrationException("Unable to determine if table is locked", e); } finally { SqlUtil.close(conn, stmt, rs); } } /** {@inheritDoc} */ public void lockPatchStore() throws MigrationException, IllegalStateException { if (isPatchStoreLocked()) { throw new IllegalStateException("Patch table is already locked!"); } updatePatchLock(true); } /** {@inheritDoc} */ public void unlockPatchStore() throws MigrationException { updatePatchLock(false); } /** * Returns the SQL to execute for the database type associated with this patch table. * * @param key the key within database.properties whose * SQL should be returned * @return the SQL to execute for the database type associated with this patch table */ protected String getSql(String key) { return context.getDatabaseType().getProperty(key); } /** * Creates an initial record in the patches table for this system. * * @exception SQLException if an unrecoverable database error occurs * @exception MigrationException if an unrecoverable database error occurs */ private void createSystemPatchRecord() throws MigrationException, SQLException { String systemName = context.getSystemName(); Connection conn = null; PreparedStatement stmt = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql("level.create")); stmt.setString(1, systemName); stmt.execute(); context.commit(); log.info("Created patch record for " + systemName); } catch (SQLException e) { log.error("Error creating patch record for system '" + systemName + "'", e); throw e; } finally { SqlUtil.close(conn, stmt, null); } } /** * Obtains or releases a lock for this system in the patches table. * * @param lock true if a lock is to be obtained, false * if it is to be removed * @throws MigrationException if an unrecoverable database error occurs */ private void updatePatchLock(boolean lock) throws MigrationException { String sqlkey = (lock) ? "lock.obtain" : "lock.release"; Connection conn = null; PreparedStatement stmt = null; try { conn = context.getConnection(); stmt = conn.prepareStatement(getSql(sqlkey)); if (log.isDebugEnabled()) { log.debug("Updating patch table lock: " + getSql(sqlkey)); } stmt.setString(1, context.getSystemName()); stmt.execute(); context.commit(); } catch (SQLException e) { throw new MigrationException("Unable to update patch lock to " + lock, e); } finally { SqlUtil.close(conn, stmt, null); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy