From c7d0075d223eab9f89fd28853c4b138792059be9 Mon Sep 17 00:00:00 2001 From: Patrick Brady Date: Thu, 1 Jun 2017 10:45:37 -0700 Subject: Merge of new rebased code Change-Id: I9b8d1f69eb3e0af1935ed8304fea4bf54c1aac47 Signed-off-by: Patrick Brady --- .../appc-data-access-lib/pom.xml | 21 +- .../appc/domainmodel/lcm/VNFOperation.java | 5 +- .../execution-queue-management-lib/pom.xml | 7 +- .../impl/ExecutionQueueServiceFactory.java | 15 +- .../src/main/resources/features.xml | 4 + .../lock-manager-lib/lock-manager-impl/pom.xml | 11 +- .../appc/lockmanager/impl/sql/Messages.java | 1 + .../impl/sql/optimistic/SqlLockManager.java | 2 +- .../impl/sql/pessimistic/LockRecord.java | 62 +++++ .../impl/sql/pessimistic/MySqlLockManager.java | 85 +++++++ .../impl/sql/pessimistic/SqlLockManager.java | 251 +++++++++++++++++++++ .../impl/sql/pessimistic/MySqlLockManagerMock.java | 150 ++++++++++++ .../impl/sql/pessimistic/TestMySqlLockManager.java | 121 ++++++++++ 13 files changed, 711 insertions(+), 24 deletions(-) create mode 100644 appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/LockRecord.java create mode 100644 appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManager.java create mode 100644 appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/SqlLockManager.java create mode 100644 appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManagerMock.java create mode 100644 appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/TestMySqlLockManager.java (limited to 'appc-dispatcher/appc-dispatcher-common') diff --git a/appc-dispatcher/appc-dispatcher-common/appc-data-access-lib/pom.xml b/appc-dispatcher/appc-dispatcher-common/appc-data-access-lib/pom.xml index cae7eedb1..94a45e31a 100644 --- a/appc-dispatcher/appc-dispatcher-common/appc-data-access-lib/pom.xml +++ b/appc-dispatcher/appc-dispatcher-common/appc-data-access-lib/pom.xml @@ -6,7 +6,7 @@ 1.1.0-SNAPSHOT appc-data-access-lib - jar + bundle appc-data-access-lib http://maven.apache.org @@ -35,7 +35,24 @@ org.openecomp.appc appc-common ${project.version} - compile + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + org.openecomp.appc.dao.util + org.openecomp.appc.configuration,*;resolution:=optional + mysql-connector-java + true + + + + + diff --git a/appc-dispatcher/appc-dispatcher-common/domain-model-lib/src/main/java/org/openecomp/appc/domainmodel/lcm/VNFOperation.java b/appc-dispatcher/appc-dispatcher-common/domain-model-lib/src/main/java/org/openecomp/appc/domainmodel/lcm/VNFOperation.java index c61901eb3..904722fae 100644 --- a/appc-dispatcher/appc-dispatcher-common/domain-model-lib/src/main/java/org/openecomp/appc/domainmodel/lcm/VNFOperation.java +++ b/appc-dispatcher/appc-dispatcher-common/domain-model-lib/src/main/java/org/openecomp/appc/domainmodel/lcm/VNFOperation.java @@ -23,8 +23,9 @@ package org.openecomp.appc.domainmodel.lcm; public enum VNFOperation { - Configure, Test, HealthCheck, Start, Terminate, Restart, Rebuild, Stop, ModifyConfig, Backup, Snapshot, - SoftwareUpload, LiveUpgrade, Rollback, Sync, Audit, Test_lic, Migrate, Evacuate, + Configure, Test, HealthCheck, Start, Terminate, Restart, Rebuild, Stop, ConfigModify, + ConfigScaleOut,ConfigRestore,Backup, Snapshot, + SoftwareUpload, LiveUpgrade, Rollback, Sync, Audit, Test_lic, Migrate, Evacuate,ConfigBackup,ConfigBackupDelete,ConfigExport, Lock(true), Unlock(true), CheckLock(true); private boolean builtIn; diff --git a/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/pom.xml b/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/pom.xml index f8b1e5631..d20759f30 100644 --- a/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/pom.xml +++ b/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/pom.xml @@ -26,7 +26,7 @@ eelf-core - + @@ -36,10 +36,11 @@ ${project.artifactId} ${project.version} - appc-common,eelf-core,logback-core,logback-classic;scope=compile|runtime;inline=false true org.openecomp.appc.executionqueue,org.openecomp.appc.executionqueue.impl - !groovy.lang,!javax.*,!org.apache.log,!org.apache.log4j.*,!org.codehaus.jackson.*,!org.codehaus.jackson.map.*,!org.codehaus.commons.compiler,!org.codehaus.groovy.*,!org.apache.commons.lang3,!org.codehaus.janino,!org.jasypt.*,!com.ibm.icu.*,!com.sun.faces.*,* + + *;resolution:=optional + diff --git a/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/src/main/java/org/openecomp/appc/executionqueue/impl/ExecutionQueueServiceFactory.java b/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/src/main/java/org/openecomp/appc/executionqueue/impl/ExecutionQueueServiceFactory.java index 01e4358e9..29de597b1 100644 --- a/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/src/main/java/org/openecomp/appc/executionqueue/impl/ExecutionQueueServiceFactory.java +++ b/appc-dispatcher/appc-dispatcher-common/execution-queue-management-lib/src/main/java/org/openecomp/appc/executionqueue/impl/ExecutionQueueServiceFactory.java @@ -25,16 +25,11 @@ import org.openecomp.appc.executionqueue.ExecutionQueueService; public class ExecutionQueueServiceFactory { - private static ExecutionQueueService executionQueueService =null; - - public static ExecutionQueueService getExecutionQueueService(){ - if(executionQueueService == null){ - synchronized (ExecutionQueueServiceFactory.class){ - if(executionQueueService == null) - executionQueueService = new ExecutionQueueServiceImpl(); - } - } - return executionQueueService; + private static class ExecutionQueueServiceHolder { + public static final ExecutionQueueService executionQueueService = new ExecutionQueueServiceImpl(); } + public static ExecutionQueueService getExecutionQueueService() { + return ExecutionQueueServiceHolder.executionQueueService; + } } diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-features/src/main/resources/features.xml b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-features/src/main/resources/features.xml index ed51ca775..8b2131829 100644 --- a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-features/src/main/resources/features.xml +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-features/src/main/resources/features.xml @@ -26,6 +26,10 @@ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> + + mvn:org.openecomp.appc/appc-common/${project.version} + mvn:org.openecomp.appc/appc-data-access-lib/${project.version} + mvn:org.openecomp.appc/lock-manager-api/${project.version} mvn:org.openecomp.appc/lock-manager-impl/${project.version} diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/pom.xml b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/pom.xml index 48befceb8..8da65195d 100644 --- a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/pom.xml +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/pom.xml @@ -28,7 +28,6 @@ org.openecomp.appc appc-data-access-lib ${project.version} - provided com.att.eelf @@ -49,16 +48,16 @@ org.openecomp.appc.lockmanager.api.LockManager - org.openecomp.appc.lockmanager.api.*,* + + org.openecomp.appc.lockmanager.api.*, + org.openecomp.appc.dao.util,com.att.eelf.configuration, *;resolution:=optional + org.openecomp.appc.lockmanager.impl.* ${project.artifactId} ${project.version} - !org.apache.log,!org.apache.commons.logging,!groovy.lang,!javax.jms,!javax.mail.*,!org.codehaus.commons.compiler,!org.codehaus.groovy.*,!org.codehaus.janino,!com.ibm.icu.*,!com.sun.faces.*,!org.jasypt.*,* - appc-common,appc-data-access-lib,eelf-core,logback-core,logback-classic;scope=compile|runtime;inline=false - true - \ No newline at end of file + diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java index 0399726b5..62181d4c6 100644 --- a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java @@ -28,6 +28,7 @@ public enum Messages { ERR_UNLOCK_NOT_LOCKED("Error unlocking resource [%s]: resource is not locked"), ERR_UNLOCK_LOCKED_BY_OTHER("Error unlocking resource [%s] by [%s]: resource is locked by [%s]"), EXP_LOCK("Error locking resource [%s]."), + EXP_CHECK_LOCK("Error check locking resource [%s]."),//for checklock operation EXP_UNLOCK("Error unlocking resource [%s]."), ; diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java index 52c75d2b2..54622c422 100644 --- a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java @@ -91,7 +91,7 @@ abstract class SqlLockManager extends JdbcLockManager { } } } catch (SQLException e) { - throw new LockRuntimeException(Messages.EXP_LOCK.format(resource)); + throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource)); }finally { closeDbConnection(connection); } diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/LockRecord.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/LockRecord.java new file mode 100644 index 000000000..7d537379b --- /dev/null +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/LockRecord.java @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.lockmanager.impl.sql.pessimistic; + +class LockRecord { + + private String resource; + private String owner; + private long updated; + private long timeout; + + LockRecord(String resource) { + this.resource = resource; + } + + public String getResource() { + return resource; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public long getUpdated() { + return updated; + } + + public void setUpdated(long updated) { + this.updated = updated; + } + + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } +} diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManager.java new file mode 100644 index 000000000..79d0f6cca --- /dev/null +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManager.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.lockmanager.impl.sql.pessimistic; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.openecomp.appc.lockmanager.api.LockRuntimeException; + +public class MySqlLockManager extends SqlLockManager { + + private static final int DEF_CRITICAL_SECTION_WAIT_TIMEOUT = 3; + + protected int criticalSectionWaitTimeoutSecs = DEF_CRITICAL_SECTION_WAIT_TIMEOUT; + + public void setCriticalSectionWaitTimeoutSecs(int criticalSectionWaitTimeoutSecs) { + this.criticalSectionWaitTimeoutSecs = criticalSectionWaitTimeoutSecs; + } + + @Override + protected void enterCriticalSection(Connection connection, String resource) { + try { + CallableStatement statement = connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)"); + try { + statement.setString(1, resource); + statement.setInt(2, criticalSectionWaitTimeoutSecs); + boolean execRes = statement.execute(); + int result = 0; + if(execRes) { + ResultSet resultSet = statement.getResultSet(); + try { + if(resultSet.next()) { + result = resultSet.getInt(1); + } + } finally { + resultSet.close(); + } + } + if(result != 1) { // lock is not obtained + throw new LockRuntimeException("Cannot obtain critical section lock for resource [" + resource + "]."); + } + } finally { + statement.close(); + } + } catch(SQLException e) { + throw new LockRuntimeException("Cannot obtain critical section lock for resource [" + resource + "].", e); + } + } + + @Override + protected void leaveCriticalSection(Connection connection, String resource) { + try { + CallableStatement statement = connection.prepareCall("SELECT RELEASE_LOCK(?)"); + try { + statement.setString(1, resource); + statement.execute(); + } finally { + statement.close(); + } + } catch(SQLException e) { + throw new LockRuntimeException("Error releasing critical section lock.", e); + } + } +} diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/SqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/SqlLockManager.java new file mode 100644 index 000000000..71f649274 --- /dev/null +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/SqlLockManager.java @@ -0,0 +1,251 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.lockmanager.impl.sql.pessimistic; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.openecomp.appc.lockmanager.api.LockException; +import org.openecomp.appc.lockmanager.api.LockRuntimeException; +import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager; +import org.openecomp.appc.lockmanager.impl.sql.Messages; + +abstract class SqlLockManager extends JdbcLockManager { + + private static final String SQL_LOAD_LOCK_RECORD = "SELECT * FROM %s WHERE RESOURCE_ID=?"; + private static final String SQL_INSERT_LOCK_RECORD = "INSERT INTO %s (RESOURCE_ID, OWNER_ID, UPDATED, TIMEOUT) VALUES (?, ?, ?, ?)"; + private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=? WHERE RESOURCE_ID=?"; + private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()"; + + private String sqlLoadLockRecord; + private String sqlInsertLockRecord; + private String sqlUpdateLockRecord; + + @Override + public boolean acquireLock(String resource, String owner) throws LockException { + return acquireLock(resource, owner, 0); + } + + @Override + public boolean acquireLock(String resource, String owner, long timeout) throws LockException { + if(owner == null) { + throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource)); + } + boolean res = false; + Connection connection = openDbConnection(); + try { + enterCriticalSection(connection, resource); + try { + res = lockResource(connection, resource, owner, timeout); + } finally { + leaveCriticalSection(connection, resource); + } + } finally { + closeDbConnection(connection); + } + return res; + } + + @Override + public void releaseLock(String resource, String owner) throws LockException { + Connection connection = openDbConnection(); + try { + enterCriticalSection(connection, resource); + try { + unlockResource(connection, resource, owner); + } finally { + leaveCriticalSection(connection, resource); + } + } finally { + closeDbConnection(connection); + } + } + + @Override + public boolean isLocked(String resource) { + Connection connection=openDbConnection(); + try { + LockRecord lockRecord=loadLockRecord(connection,resource); + if(lockRecord==null){ + return false; + }else{ + if(lockRecord.getOwner()==null){ + return false; + }else if(isLockExpired(lockRecord, connection)){ + return false; + }else{ + return true; + } + } + } catch (SQLException e) { + throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource)); + }finally { + closeDbConnection(connection); + } + } + + private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException { + try { + boolean res = false; + LockRecord lockRecord = loadLockRecord(connection, resource); + if(lockRecord != null) { + // lock record already exists + String currentOwner = lockRecord.getOwner(); + if(currentOwner != null) { + if(isLockExpired(lockRecord, connection)) { + currentOwner = null; + } else if(!owner.equals(currentOwner)) { + throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner)); + } + } + // set new owner on the resource lock record + updateLockRecord(connection, resource, owner, timeout); + if(currentOwner == null) { + // no one locked the resource before + res = true; + } + } else { + // resource record does not exist in lock table => create new record + addLockRecord(connection, resource, owner, timeout); + res = true; + } + return res; + } catch(SQLException e) { + throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e); + } + } + + private void unlockResource(Connection connection, String resource, String owner) throws LockException { + try { + LockRecord lockRecord = loadLockRecord(connection, resource); + if(lockRecord != null) { + // check if expired + if(isLockExpired(lockRecord, connection)) { + // lock is expired => no lock + lockRecord = null; + } + } + if((lockRecord == null) || (lockRecord.getOwner() == null)) { + // resource is not locked + throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource)); + } + String currentOwner = lockRecord.getOwner(); + if(!owner.equals(currentOwner)) { + throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner)); + } + updateLockRecord(connection, resource, null, 0); + // TODO delete record from table on lock release? +// deleteLockRecord(connection, resource); + } catch(SQLException e) { + throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e); + } + } + + protected abstract void enterCriticalSection(Connection connection, String resource); + + protected abstract void leaveCriticalSection(Connection connection, String resource); + + protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException { + LockRecord res = null; + if(sqlLoadLockRecord == null) { + sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName); + } + try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) { + statement.setString(1, resource); + try(ResultSet resultSet = statement.executeQuery()) { + if(resultSet.next()) { + res = new LockRecord(resource); + res.setOwner(resultSet.getString(2)); + res.setUpdated(resultSet.getLong(3)); + res.setTimeout(resultSet.getLong(4)); + } + } + } + return res; + } + + protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException { + if(sqlInsertLockRecord == null) { + sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName); + } + try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) { + statement.setString(1, resource); + statement.setString(2, owner); + statement.setLong(3, getCurrentTime(connection)); + statement.setLong(4, timeout); + statement.executeUpdate(); + } + } + + protected void updateLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException { + if(sqlUpdateLockRecord == null) { + sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName); + } + try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) { + statement.setString(1, owner); + statement.setLong(2, getCurrentTime(connection)); + statement.setLong(3, timeout); + statement.setString(4, resource); + statement.executeUpdate(); + } + } + +// protected void deleteLockRecord(Connection connection, String resource) throws SQLException { +// if(sqlDeleteLockRecord == null) { +// sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName); +// } +// try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) { +// statement.setString(1, resource); +// statement.executeUpdate(); +// } +// } + + private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException { + long timeout = lockRecord.getTimeout(); + if(timeout == 0) { + return false; + } + long updated = lockRecord.getUpdated(); + long now = getCurrentTime(connection); + long expiration = updated + timeout; + return (now > expiration); + } + + private long getCurrentTime(Connection connection) throws SQLException { + long res = -1; + if(connection != null) { + try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) { + try(ResultSet resultSet = statement.executeQuery()) { + if(resultSet.next()) { + res = resultSet.getTimestamp(1).getTime(); + } + } + } + } + if(res == -1) { + res = System.currentTimeMillis(); + } + return res; + } +} diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManagerMock.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManagerMock.java new file mode 100644 index 000000000..a94dc18e4 --- /dev/null +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManagerMock.java @@ -0,0 +1,150 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.lockmanager.impl.sql.pessimistic; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.openecomp.appc.lockmanager.api.LockRuntimeException; +import org.openecomp.appc.lockmanager.impl.sql.Synchronizer; +import org.openecomp.appc.lockmanager.impl.sql.SynchronizerReceiver; +import org.openecomp.appc.lockmanager.impl.sql.pessimistic.LockRecord; +import org.openecomp.appc.lockmanager.impl.sql.pessimistic.MySqlLockManager; + +class MySqlLockManagerMock extends MySqlLockManager implements SynchronizerReceiver { + + private final Map locks = new HashMap<>(); + private final Lock lock = new ReentrantLock(); + private boolean useReal; + private Synchronizer synchronizer; + + MySqlLockManagerMock(boolean useReal) { + this.useReal = useReal; + } + + @Override + public void setSynchronizer(Synchronizer synchronizer) { + this.synchronizer = synchronizer; + } + + @Override + protected Connection openDbConnection() { + if(useReal) { + return super.openDbConnection(); + } + return null; + } + + @Override + protected void closeDbConnection(Connection connection) { + if(useReal) { + super.closeDbConnection(connection); + } + } + + @Override + protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException { + LockRecord res; + if(useReal) { + res = super.loadLockRecord(connection, resource); + } else { + res = locks.get(resource); + } + if(synchronizer != null) { + synchronizer.postLoadLockRecord(resource, (res == null) ? null : res.getOwner()); + } + return res; + } + + @Override + protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException { + if(synchronizer != null) { + synchronizer.preAddLockRecord(resource, owner); + } + try { + if(useReal) { + super.addLockRecord(connection, resource, owner, timeout); + return; + } + LockRecord lockRecord = new LockRecord(resource); + lockRecord.setOwner(owner); + lockRecord.setUpdated(System.currentTimeMillis()); + lockRecord.setTimeout(timeout); + locks.put(resource, lockRecord); + } finally { + if(synchronizer != null) { + synchronizer.postAddLockRecord(resource, owner); + } + } + } + + @Override + protected void updateLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException { + if(synchronizer != null) { + synchronizer.preUpdateLockRecord(resource, owner); + } + try { + if(useReal) { + super.updateLockRecord(connection, resource, owner, timeout); + return; + } + LockRecord lockRecord = loadLockRecord(connection, resource); + lockRecord.setOwner(owner); + lockRecord.setUpdated(System.currentTimeMillis()); + lockRecord.setTimeout(timeout); + locks.put(resource, lockRecord); + } finally { + if(synchronizer != null) { + synchronizer.postUpdateLockRecord(resource, owner); + } + } + } + + @Override + protected void enterCriticalSection(Connection connection, String resource) { + if(useReal) { + super.enterCriticalSection(connection, resource); + return; + } + try { + if(!lock.tryLock(criticalSectionWaitTimeoutSecs, TimeUnit.SECONDS)) { + throw new LockRuntimeException("Cannot obtain critical section lock for resource [" + resource + "]."); + } + } catch(InterruptedException e) { + throw new LockRuntimeException("Cannot obtain critical section lock.", e); + } + } + + @Override + protected void leaveCriticalSection(Connection connection, String resource) { + if(useReal) { + super.leaveCriticalSection(connection, resource); + return; + } + lock.unlock(); + } +} diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/TestMySqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/TestMySqlLockManager.java new file mode 100644 index 000000000..cf8781b13 --- /dev/null +++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/TestMySqlLockManager.java @@ -0,0 +1,121 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.lockmanager.impl.sql.pessimistic; + +import org.junit.Assert; +import org.junit.Test; +import org.openecomp.appc.lockmanager.api.LockException; +import org.openecomp.appc.lockmanager.api.LockRuntimeException; +import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager; +import org.openecomp.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests; +import org.openecomp.appc.lockmanager.impl.sql.Synchronizer; +import org.openecomp.appc.lockmanager.impl.sql.pessimistic.MySqlLockManager; + +import java.util.concurrent.*; + +public class TestMySqlLockManager extends MySqlLockManagerBaseTests { + + private static int CRITICAL_SECTION_WAIT_TIMEOUT = 1; // in secs + + @Override + protected JdbcLockManager createJdbcLockManager(boolean useReal) { + return new MySqlLockManagerMock(useReal); + } + + @Test + public void testConcurrentLock() throws LockException, InterruptedException, ExecutionException, TimeoutException { + try { + callConcurrentTest(new Callable() { + @Override + public Boolean call() throws Exception { + try { + Assert.assertTrue(lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name())); + return true; + } catch(LockRuntimeException e) { + Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage()); + return false; + } + } + }); + } finally { + lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name()); + } + } + + @Test + public void testConcurrentUnlock() throws LockException, InterruptedException, ExecutionException, TimeoutException { + lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name()); + callConcurrentTest(new Callable() { + @Override + public Boolean call() throws Exception { + try { + lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name()); + return true; + } catch(LockRuntimeException e) { + Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage()); + return false; + } + } + }); + } + + private void callConcurrentTest(Callable callable) throws LockException, InterruptedException, ExecutionException, TimeoutException { + final int participantsNo = 2; + Synchronizer synchronizer = new Synchronizer(participantsNo) { + + @Override + protected void waitForAllParticipants(Object waitObj, int totalParticipantsNo, int currentParticipantsNo) { + waitOn(this, TimeUnit.MILLISECONDS.convert(1 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS)); // add 1 sec to make sure timeout occured + } + }; + if(!setSynchronizer(synchronizer)) { + return; + } + ((MySqlLockManager)lockManager).setCriticalSectionWaitTimeoutSecs(CRITICAL_SECTION_WAIT_TIMEOUT); + ExecutorService executor = Executors.newFixedThreadPool(participantsNo); + Future future1 = executor.submit(callable); + try { + for(int i = 0; i < 10; i++) { + Thread.sleep(100); + if(synchronizer.getParticipantCount() > 0) { + break; + } + } + // make sure 1st thread gets inside critical section + if(synchronizer.getParticipantCount() < 1) { + Assert.fail(getClass().getName() + " first thread failed to acquireLock()"); + } + Future future2 = executor.submit(callable); + try { + // 1st thread should acquire the lock + Assert.assertTrue(future1.get(3 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS)); + // 2nd thread should fail waiting for critical section + Assert.assertFalse(future2.get(2 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS)); + } finally { + future2.cancel(true); + } + } finally { + future1.cancel(true); + setSynchronizer(null); + } + } +} -- cgit 1.2.3-korg