locking fixes for H2 updates

This commit is contained in:
Jeremy Long
2017-09-15 17:34:46 -04:00
parent cb6287eacc
commit 1e96b43720
4 changed files with 31 additions and 11 deletions

View File

@@ -29,10 +29,13 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.exception.H2DBLockException;
import org.owasp.dependencycheck.utils.DBUtils; import org.owasp.dependencycheck.utils.DBUtils;
import org.owasp.dependencycheck.utils.DependencyVersion; import org.owasp.dependencycheck.utils.DependencyVersion;
import org.owasp.dependencycheck.utils.DependencyVersionUtil; import org.owasp.dependencycheck.utils.DependencyVersionUtil;
import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.H2DBLock;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -106,13 +109,20 @@ public final class ConnectionFactory {
* @throws DatabaseException thrown if we are unable to connect to the * @throws DatabaseException thrown if we are unable to connect to the
* database * database
*/ */
public void initialize() throws DatabaseException { public synchronized void initialize() throws DatabaseException {
//this only needs to be called once. //this only needs to be called once.
if (connectionString != null) { if (connectionString != null) {
return; return;
} }
Connection conn = null; Connection conn = null;
H2DBLock dblock = null;
try { try {
if (isH2Connection()) {
dblock = new H2DBLock(settings);
LOGGER.debug("locking for init");
dblock.lock();
}
//load the driver if necessary //load the driver if necessary
final String driverName = settings.getString(Settings.KEYS.DB_DRIVER_NAME, ""); final String driverName = settings.getString(Settings.KEYS.DB_DRIVER_NAME, "");
if (!driverName.isEmpty()) { if (!driverName.isEmpty()) {
@@ -174,7 +184,6 @@ public final class ConnectionFactory {
throw new DatabaseException("Unable to connect to the database", ex); throw new DatabaseException("Unable to connect to the database", ex);
} }
} }
if (shouldCreateSchema) { if (shouldCreateSchema) {
try { try {
createTables(conn); createTables(conn);
@@ -189,6 +198,8 @@ public final class ConnectionFactory {
LOGGER.debug("", dex); LOGGER.debug("", dex);
throw new DatabaseException("Database schema does not match this version of dependency-check", dex); throw new DatabaseException("Database schema does not match this version of dependency-check", dex);
} }
} catch (H2DBLockException ex) {
throw new DatabaseException("Unable to obtain an exclusive lock on the H2 database to perform initializataion", ex);
} finally { } finally {
if (conn != null) { if (conn != null) {
try { try {
@@ -197,6 +208,9 @@ public final class ConnectionFactory {
LOGGER.debug("An error occurred closing the connection", ex); LOGGER.debug("An error occurred closing the connection", ex);
} }
} }
if (dblock != null) {
dblock.release();
}
} }
} }
@@ -206,7 +220,7 @@ public final class ConnectionFactory {
* finalize method being called as during shutdown the class loader used to * finalize method being called as during shutdown the class loader used to
* load the driver may be unloaded prior to the driver being de-registered. * load the driver may be unloaded prior to the driver being de-registered.
*/ */
public void cleanup() { public synchronized void cleanup() {
if (driver != null) { if (driver != null) {
DriverLoader.cleanup(driver); DriverLoader.cleanup(driver);
driver = null; driver = null;
@@ -224,7 +238,7 @@ public final class ConnectionFactory {
* @throws DatabaseException thrown if there is an exception loading the * @throws DatabaseException thrown if there is an exception loading the
* database connection * database connection
*/ */
public Connection getConnection() throws DatabaseException { public synchronized Connection getConnection() throws DatabaseException {
initialize(); initialize();
Connection conn = null; Connection conn = null;
try { try {

View File

@@ -206,7 +206,6 @@ public final class CveDB implements AutoCloseable {
public CveDB(Settings settings) throws DatabaseException { public CveDB(Settings settings) throws DatabaseException {
this.settings = settings; this.settings = settings;
connectionFactory = new ConnectionFactory(settings); connectionFactory = new ConnectionFactory(settings);
connectionFactory.initialize();
open(); open();
} }

View File

@@ -113,11 +113,12 @@ public class NvdCveUpdater implements CachedWebDataSource {
if (isUpdateConfiguredFalse()) { if (isUpdateConfiguredFalse()) {
return; return;
} }
H2DBLock dbupdate = null; H2DBLock dblock = null;
try { try {
if (ConnectionFactory.isH2Connection(settings)) { if (ConnectionFactory.isH2Connection(settings)) {
dbupdate = new H2DBLock(settings); dblock = new H2DBLock(settings);
dbupdate.lock(); LOGGER.debug("locking for update");
dblock.lock();
} }
initializeExecutorServices(); initializeExecutorServices();
dbProperties = cveDb.getDatabaseProperties(); dbProperties = cveDb.getDatabaseProperties();
@@ -142,8 +143,8 @@ public class NvdCveUpdater implements CachedWebDataSource {
} catch (H2DBLockException ex) { } catch (H2DBLockException ex) {
throw new UpdateException("Unable to obtain an exclusive lock on the H2 database to perform updates", ex); throw new UpdateException("Unable to obtain an exclusive lock on the H2 database to perform updates", ex);
} finally { } finally {
if (dbupdate != null) { if (dblock != null) {
dbupdate.release(); dblock.release();
} }
shutdownExecutorServices(); shutdownExecutorServices();
} }

View File

@@ -82,6 +82,9 @@ public class H2DBLock {
try { try {
final File dir = settings.getDataDirectory(); final File dir = settings.getDataDirectory();
lockFile = new File(dir, "dc.update.lock"); lockFile = new File(dir, "dc.update.lock");
if (!lockFile.getParentFile().isDirectory() && !lockFile.mkdir()) {
throw new H2DBLockException("Unable to create path to data directory.");
}
if (lockFile.isFile() && getFileAge(lockFile) > 5 && !lockFile.delete()) { if (lockFile.isFile() && getFileAge(lockFile) > 5 && !lockFile.delete()) {
LOGGER.warn("An old db update lock file was found but the system was unable to delete " LOGGER.warn("An old db update lock file was found but the system was unable to delete "
+ "the file. Consider manually deleting {}", lockFile.getAbsolutePath()); + "the file. Consider manually deleting {}", lockFile.getAbsolutePath());
@@ -92,6 +95,7 @@ public class H2DBLock {
if (!lockFile.exists() && lockFile.createNewFile()) { if (!lockFile.exists() && lockFile.createNewFile()) {
file = new RandomAccessFile(lockFile, "rw"); file = new RandomAccessFile(lockFile, "rw");
lock = file.getChannel().lock(); lock = file.getChannel().lock();
LOGGER.debug("Lock file created ({})", Thread.currentThread().getName());
} }
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.trace("Expected error as another thread has likely locked the file", ex); LOGGER.trace("Expected error as another thread has likely locked the file", ex);
@@ -106,7 +110,7 @@ public class H2DBLock {
} }
if (lock == null || !lock.isValid()) { if (lock == null || !lock.isValid()) {
try { try {
LOGGER.debug("Sleeping thread {} for 5 seconds because we could not obtain the update lock.", LOGGER.debug("Sleeping thread {} for 5 seconds because an exclusive lock on the database could not be obtained",
Thread.currentThread().getName()); Thread.currentThread().getName());
Thread.sleep(5000); Thread.sleep(5000);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@@ -121,6 +125,7 @@ public class H2DBLock {
} catch (IOException ex) { } catch (IOException ex) {
throw new H2DBLockException(ex.getMessage(), ex); throw new H2DBLockException(ex.getMessage(), ex);
} }
} }
/** /**
@@ -148,6 +153,7 @@ public class H2DBLock {
lockFile.deleteOnExit(); lockFile.deleteOnExit();
} }
lockFile = null; lockFile = null;
LOGGER.debug("Lock released ({})", Thread.currentThread().getName());
} }
/** /**