summaryrefslogtreecommitdiffstats
path: root/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl
diff options
context:
space:
mode:
Diffstat (limited to 'appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl')
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/pom.xml11
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java1
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java2
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/LockRecord.java62
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManager.java85
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/SqlLockManager.java251
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/MySqlLockManagerMock.java150
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/pessimistic/TestMySqlLockManager.java121
8 files changed, 676 insertions, 7 deletions
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 @@
<groupId>org.openecomp.appc</groupId>
<artifactId>appc-data-access-lib</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>com.att.eelf</groupId>
@@ -49,16 +48,16 @@
<configuration>
<instructions>
<Export-Service>org.openecomp.appc.lockmanager.api.LockManager</Export-Service>
- <Import-Package>org.openecomp.appc.lockmanager.api.*,*</Import-Package>
+ <Import-Package>
+ org.openecomp.appc.lockmanager.api.*,
+ org.openecomp.appc.dao.util,com.att.eelf.configuration, *;resolution:=optional
+ </Import-Package>
<Private-Package>org.openecomp.appc.lockmanager.impl.*</Private-Package>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
- <Import-Package>!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.*,*</Import-Package>
- <Embed-Dependency>appc-common,appc-data-access-lib,eelf-core,logback-core,logback-classic;scope=compile|runtime;inline=false</Embed-Dependency>
- <Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
-</project> \ No newline at end of file
+</project>
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<String, LockRecord> 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<Boolean>() {
+ @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<Boolean>() {
+ @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<Boolean> 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<Boolean> 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<Boolean> 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);
+ }
+ }
+}