summaryrefslogtreecommitdiffstats
path: root/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src
diff options
context:
space:
mode:
Diffstat (limited to 'appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src')
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImpl.java130
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockValue.java43
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/JdbcLockManager.java51
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java47
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/MySqlConnectionFactory.java34
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/LockRecord.java71
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManager.java32
-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.java263
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml41
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/api/LockManagerBaseTests.java167
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImplTest.java36
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/MySqlLockManagerBaseTests.java93
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/Synchronizer.java80
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/SynchronizerReceiver.java27
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManagerMock.java132
-rw-r--r--appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/TestMySqlLockManager.java227
16 files changed, 1474 insertions, 0 deletions
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImpl.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImpl.java
new file mode 100644
index 000000000..309e2790c
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImpl.java
@@ -0,0 +1,130 @@
+/*-
+ * ============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.inmemory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.openecomp.appc.lockmanager.api.LockException;
+import org.openecomp.appc.lockmanager.api.LockManager;
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+
+public class LockManagerInMemoryImpl implements LockManager {
+
+ private static LockManagerInMemoryImpl instance = null;
+ private Map<String, LockValue> lockedVNFs;
+
+ private static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger();
+
+ private LockManagerInMemoryImpl() {
+ lockedVNFs = new ConcurrentHashMap<>();
+ }
+
+ public static LockManager getLockManager() {
+ if(instance == null) {
+ instance = new LockManagerInMemoryImpl();
+ }
+ return instance;
+ }
+
+ @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 {
+ debugLogger.debug("Try to acquire lock on resource " + resource + " with owner " + owner);
+ long now = System.currentTimeMillis();
+ LockValue lockValue = lockedVNFs.get(resource);
+ if (lockValue != null) {
+ if (lockIsMine(lockValue, owner, now) || hasExpired(lockValue, now)) {
+ setExpirationTime(resource, owner, timeout, now);
+ debugLogger.debug("Locked successfully resource " + resource + " with owner " + owner + " for " + timeout + " ms");
+ return hasExpired(lockValue, now);
+ }
+ else {
+ debugLogger.debug("Owner " + owner + " tried to lock resource " + resource + " but it is already locked by owner " + lockValue.getOwner());
+ throw new LockException("Owner " + owner + " tried to lock resource " + resource + " but it is already locked by owner " + lockValue.getOwner());
+ }
+ }
+ else {
+ setExpirationTime(resource, owner, timeout, now);
+ debugLogger.debug("Locked successfully resource " + resource + " with owner " + owner + " for " + timeout + " ms");
+ return true;
+ }
+ }
+
+ @Override
+ public void releaseLock(String resource, String owner) throws LockException {
+ debugLogger.debug("Try to release lock on resource " + resource + " with owner " + owner);
+ long now = System.currentTimeMillis();
+ LockValue lockValue = lockedVNFs.get(resource);
+ if (lockValue != null) {
+ if (!hasExpired(lockValue, now)) {
+ if (isOwner(lockValue, owner)) {
+ debugLogger.debug("Unlocked successfully resource " + resource + " with owner " + owner);
+ lockedVNFs.remove(resource);
+ }
+ else {
+ debugLogger.debug("Unlock failed. Tried to release lock on resource " + resource + " from owner " + owner + " but it is held by a different owner");
+ throw new LockException("Unlock failed. Tried to release lock on resource " + resource + " from owner " + owner + " but it is held by a different owner");
+ }
+ }
+ else {
+ lockedVNFs.remove(resource);
+ debugLogger.debug("Unlock failed. lock on resource " + resource + " has expired");
+ throw new LockException("Unlock failed. lock on resource " + resource + " has expired");
+ }
+
+ }
+ else {
+ debugLogger.debug("Tried to release lock on resource " + resource + " from owner " + owner + " but there is not lock on this resource");
+ throw new LockException("Tried to release lock on resource " + resource + " from owner " + owner + " but there is not lock on this resource");
+ }
+ }
+
+ @Override
+ public boolean isLocked(String resource) {
+ return lockedVNFs.get(resource)!=null?true:false;
+ }
+
+ private boolean lockIsMine(LockValue lockValue, String owner, long now) {
+ return isOwner(lockValue, owner) && !hasExpired(lockValue, now);
+ }
+
+ private boolean isOwner(LockValue lockValue, String owner) {
+ return lockValue.getOwner() != null && lockValue.getOwner().equals(owner);
+ }
+
+ private boolean hasExpired(LockValue lockValue, long now) {
+ return (lockValue.getExpirationTime() != 0 && now > lockValue.getExpirationTime());
+ }
+
+ private void setExpirationTime(String resource, String owner, long timeout, long now) {
+ long expirationTime = timeout == 0 ? 0 : now + timeout;
+ LockValue lockValue = new LockValue(owner, expirationTime);
+ lockedVNFs.put(resource, lockValue);
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockValue.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockValue.java
new file mode 100644
index 000000000..abdd494d9
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/inmemory/LockValue.java
@@ -0,0 +1,43 @@
+/*-
+ * ============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.inmemory;
+
+
+public class LockValue {
+
+ private String owner;
+ private long expirationTime;
+
+ LockValue(String owner, long expirationTime) {
+ this.owner = owner;
+ this.expirationTime = expirationTime;
+ }
+
+ String getOwner() {
+ return owner;
+ }
+
+ long getExpirationTime() {
+ return expirationTime;
+ }
+
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/JdbcLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/JdbcLockManager.java
new file mode 100644
index 000000000..97a5cc339
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/JdbcLockManager.java
@@ -0,0 +1,51 @@
+/*-
+ * ============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;
+
+import java.sql.Connection;
+
+import org.openecomp.appc.dao.util.JdbcConnectionFactory;
+import org.openecomp.appc.lockmanager.api.LockManager;
+
+public abstract class JdbcLockManager implements LockManager {
+
+ private static final String DEF_TABLE_LOCK_MANAGEMENT = "LOCK_MANAGEMENT";
+
+ private JdbcConnectionFactory connectionFactory;
+ protected String tableName = DEF_TABLE_LOCK_MANAGEMENT;
+
+ public void setConnectionFactory(JdbcConnectionFactory connectionFactory) {
+ this.connectionFactory = connectionFactory;
+ }
+
+ public void setTableName(String tableName) {
+ this.tableName = tableName;
+ }
+
+ protected Connection openDbConnection() {
+ return connectionFactory.openDbConnection();
+ }
+
+ protected void closeDbConnection(Connection connection) {
+ connectionFactory.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/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
new file mode 100644
index 000000000..0399726b5
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/Messages.java
@@ -0,0 +1,47 @@
+/*-
+ * ============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;
+
+public enum Messages {
+
+ ERR_NULL_LOCK_OWNER("Cannot acquire lock for resource [%s]: lock owner must be specified"),
+ ERR_LOCK_LOCKED_BY_OTHER("Cannot lock resource [%s] for [%s]: already locked by [%s]"),
+ 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_UNLOCK("Error unlocking resource [%s]."),
+ ;
+
+ private String message;
+
+ Messages(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String format(Object... s) {
+ return String.format(message, s);
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/MySqlConnectionFactory.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/MySqlConnectionFactory.java
new file mode 100644
index 000000000..4ccd0c453
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/MySqlConnectionFactory.java
@@ -0,0 +1,34 @@
+/*-
+ * ============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;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import org.openecomp.appc.dao.util.DefaultJdbcConnectionFactory;
+
+public class MySqlConnectionFactory extends DefaultJdbcConnectionFactory {
+
+ protected void registedDriver() throws SQLException {
+ DriverManager.registerDriver(new com.mysql.jdbc.Driver());
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/LockRecord.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/LockRecord.java
new file mode 100644
index 000000000..d66804488
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/LockRecord.java
@@ -0,0 +1,71 @@
+/*-
+ * ============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.optimistic;
+
+class LockRecord {
+
+ private String resource;
+ private String owner;
+ private long updated;
+ private long timeout;
+ private long ver;
+
+ 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;
+ }
+
+ public long getVer() {
+ return ver;
+ }
+
+ public void setVer(long ver) {
+ this.ver = ver;
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManager.java
new file mode 100644
index 000000000..e80133620
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManager.java
@@ -0,0 +1,32 @@
+/*-
+ * ============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.optimistic;
+
+import java.sql.SQLException;
+
+public class MySqlLockManager extends SqlLockManager {
+
+ @Override
+ protected boolean isDuplicatePkError(SQLException e) {
+ return (e.getErrorCode() == 1062);
+ }
+}
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
new file mode 100644
index 000000000..52c75d2b2
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/SqlLockManager.java
@@ -0,0 +1,263 @@
+/*-
+ * ============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.optimistic;
+
+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, VER) VALUES (?, ?, ?, ?, ?)";
+ private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=?, VER=? WHERE RESOURCE_ID=? AND VER=?";
+// private static final String SQL_DELETE_LOCK_RECORD = "DELETE FROM %s WHERE RESOURCE_ID=? AND VER=?";
+ private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()";
+
+ private String sqlLoadLockRecord;
+ private String sqlInsertLockRecord;
+ private String sqlUpdateLockRecord;
+// private String sqlDeleteLockRecord;
+
+ @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 {
+ res = lockResource(connection, resource, owner, timeout);
+ } finally {
+ closeDbConnection(connection);
+ }
+ return res;
+ }
+
+ @Override
+ public void releaseLock(String resource, String owner) throws LockException {
+ Connection connection = openDbConnection();
+ try {
+ unlockResource(connection, resource, owner);
+ } 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_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
+ if(!updateLockRecord(connection, resource, owner, timeout, lockRecord.getVer())) {
+ // try again - maybe same owner updated the record
+ lockResource(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
+ try {
+ addLockRecord(connection, resource, owner, timeout);
+ res = true;
+ } catch(SQLException e) {
+ if(isDuplicatePkError(e)) {
+ // try again - maybe same owner inserted the record
+ lockResource(connection, resource, owner, timeout);
+ } else {
+ throw e;
+ }
+ }
+ }
+ return res;
+ } catch(SQLException e) {
+ throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
+ }
+ }
+
+ protected boolean isDuplicatePkError(SQLException e) {
+ return e.getSQLState().startsWith("23");
+ }
+
+ 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));
+ }
+ if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
+ unlockResource(connection, resource, owner);
+ }
+ // TODO delete record from table on lock release?
+// deleteLockRecord(connection, resource, lockRecord.getVer());
+ } catch(SQLException e) {
+ throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
+ }
+ }
+
+ 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));
+ res.setVer(resultSet.getLong(5));
+ }
+ }
+ }
+ 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.setLong(5, 1);
+ statement.executeUpdate();
+ }
+ }
+
+ protected boolean updateLockRecord(Connection connection, String resource, String owner, long timeout, long ver) throws SQLException {
+ if(sqlUpdateLockRecord == null) {
+ sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
+ }
+ try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
+ long newVer = (ver >= Long.MAX_VALUE) ? 1 : (ver + 1);
+ statement.setString(1, owner);
+ statement.setLong(2, getCurrentTime(connection));
+ statement.setLong(3, timeout);
+ statement.setLong(4, newVer);
+ statement.setString(5, resource);
+ statement.setLong(6, ver);
+ return (statement.executeUpdate() != 0);
+ }
+ }
+
+// protected void deleteLockRecord(Connection connection, String resource, long ver) throws SQLException {
+// if(sqlDeleteLockRecord == null) {
+// sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
+// }
+// try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
+// statement.setString(1, resource);
+// statement.setLong(2, ver);
+// 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/main/resources/OSGI-INF/blueprint/blueprint.xml b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml
new file mode 100644
index 000000000..6ec23eeb0
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
+
+ <!--<bean id="lockManagerInMemoryBean" class="org.openecomp.appc.lockmanager.impl.inmemory.LockManagerInMemoryImpl" factory-method="getLockManager" scope="singleton"/>-->
+ <!--<service id="lockManagerInMemoryService" interface="org.openecomp.appc.lockmanager.api.LockManager" ref="lockManagerInMemoryBean"/>-->
+
+ <service id="lockManagerMySqlOptimisticService" interface="org.openecomp.appc.lockmanager.api.LockManager">
+ <bean class="org.openecomp.appc.lockmanager.impl.sql.optimistic.MySqlLockManager">
+ <property name="connectionFactory">
+ <bean class="org.openecomp.appc.dao.util.AppcJdbcConnectionFactory">
+ <property name="schema" value="sdnctl"/>
+ </bean>
+ </property>
+ <property name="tableName" value="VNF_LOCK_MANAGEMENT"/>
+ </bean>
+ </service>
+</blueprint>
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/api/LockManagerBaseTests.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/api/LockManagerBaseTests.java
new file mode 100644
index 000000000..47e27f79d
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/api/LockManagerBaseTests.java
@@ -0,0 +1,167 @@
+/*-
+ * ============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.api;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.appc.lockmanager.api.LockException;
+import org.openecomp.appc.lockmanager.api.LockManager;
+
+public abstract class LockManagerBaseTests {
+
+ protected enum Resource {Resource1, Resource2};
+ protected enum Owner {A, B};
+
+ protected LockManager lockManager;
+
+ @Before
+ public void beforeTest() {
+ lockManager = createLockManager();
+ }
+
+ protected abstract LockManager createLockManager();
+
+ @Test
+ public void testAcquireLock() throws LockException {
+ boolean lockRes = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ try {
+ Assert.assertTrue(lockRes);
+ } finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test
+ public void testAcquireLock_AlreadyLockedBySameOwner() throws LockException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ try {
+ Assert.assertTrue(lockRes1);
+ boolean lockRes2 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ Assert.assertFalse(lockRes2);
+ } finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test(expected = LockException.class)
+ public void testAcquireLock_AlreadyLockedByOtherOwner() throws LockException {
+ String owner2 = "B";
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ try {
+ Assert.assertTrue(lockRes1);
+ boolean lockRes2 = lockManager.acquireLock(Resource.Resource1.name(), owner2);
+ Assert.assertFalse(lockRes2);
+ } finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test
+ public void testAcquireLock_LockDifferentResources() throws LockException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ try {
+ Assert.assertTrue(lockRes1);
+ boolean lockRes2 = lockManager.acquireLock(Resource.Resource2.name(), Owner.B.name());
+ try {
+ Assert.assertTrue(lockRes2);
+ } finally {
+ lockManager.releaseLock(Resource.Resource2.name(), Owner.B.name());
+ }
+ } finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test(expected = LockException.class)
+ public void testReleaseLock_NotLockedResource() throws LockException {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+
+ @Test(expected = LockException.class)
+ public void testReleaseLock_LockedByOtherOwnerResource() throws LockException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ try {
+ Assert.assertTrue(lockRes1);
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.B.name());
+ } finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test(expected = LockException.class)
+ public void testAcquireLock_LockExpired() throws LockException, InterruptedException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name(), 50);
+ Assert.assertTrue(lockRes1);
+ Thread.sleep(1000);
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+
+ @Test
+ public void testAcquireLock_OtherLockExpired() throws LockException, InterruptedException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name(), 50);
+ Assert.assertTrue(lockRes1);
+ Thread.sleep(1000);
+ boolean lockRes2 = lockManager.acquireLock(Resource.Resource1.name(), Owner.B.name());
+ try {
+ Assert.assertTrue(lockRes2);
+ }finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.B.name());
+ }
+ }
+
+ @Test
+ public void testIsLocked_WhenLocked() throws LockException, InterruptedException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name(), 50);
+ try {
+ Assert.assertTrue(lockManager.isLocked(Resource.Resource1.name()));
+ }finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+
+ @Test(expected = LockException.class)
+ public void testIsLocked_LockExpired() throws LockException, InterruptedException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name(), 50);
+ Assert.assertTrue(lockRes1);
+ Assert.assertTrue(lockManager.isLocked(Resource.Resource1.name()));
+ Thread.sleep(1000);
+ try {
+ Assert.assertFalse(lockManager.isLocked(Resource.Resource1.name()));
+ }finally {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ }
+
+ @Test
+ public void testIsLocked_LockReleased() throws LockException, InterruptedException {
+ boolean lockRes1 = lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name(), 50);
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ Assert.assertFalse(lockManager.isLocked(Resource.Resource1.name()));
+ }
+
+ @Test
+ public void testIsLocked_NoLock() throws LockException, InterruptedException {
+ Assert.assertFalse(lockManager.isLocked(Resource.Resource1.name()));
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImplTest.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImplTest.java
new file mode 100644
index 000000000..99faec23d
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/inmemory/LockManagerInMemoryImplTest.java
@@ -0,0 +1,36 @@
+/*-
+ * ============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.inmemory;
+
+import org.openecomp.appc.lockmanager.api.LockManager;
+import org.openecomp.appc.lockmanager.api.LockManagerBaseTests;
+import org.openecomp.appc.lockmanager.impl.inmemory.LockManagerInMemoryImpl;
+
+
+public class LockManagerInMemoryImplTest extends LockManagerBaseTests {
+
+ @Override
+ protected LockManager createLockManager() {
+ return LockManagerInMemoryImpl.getLockManager();
+ }
+
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/MySqlLockManagerBaseTests.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/MySqlLockManagerBaseTests.java
new file mode 100644
index 000000000..da5607819
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/MySqlLockManagerBaseTests.java
@@ -0,0 +1,93 @@
+/*-
+ * ============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;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.appc.dao.util.DefaultJdbcConnectionFactory;
+import org.openecomp.appc.lockmanager.api.LockManager;
+import org.openecomp.appc.lockmanager.api.LockManagerBaseTests;
+import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager;
+import org.openecomp.appc.lockmanager.impl.sql.MySqlConnectionFactory;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+public abstract class MySqlLockManagerBaseTests extends LockManagerBaseTests {
+
+ private static final boolean USE_REAL_DB = Boolean.getBoolean("lockmanager.tests.useRealDb");
+ private static final String TABLE_LOCK_MANAGEMENT = "TEST_LOCK_MANAGEMENT";
+ private static final String JDBC_URL = System.getProperty("lockmanager.tests.jdbcUrl", "jdbc:mysql://192.168.1.2/test");
+ private static final String JDBC_USERNAME = System.getProperty("lockmanager.tests.jdbcUsername", "test");
+ private static final String JDBC_PASSWORD = System.getProperty("lockmanager.tests.jdbcPassword", "123456");
+
+ protected static final int CONCURRENT_TEST_WAIT_TIME = 10; // secs
+
+ @Rule
+ public TestName testName = new TestName();
+
+ @Override
+ protected LockManager createLockManager() {
+ JdbcLockManager jdbcLockManager = createJdbcLockManager(USE_REAL_DB);
+ DefaultJdbcConnectionFactory connectionFactory = new MySqlConnectionFactory();
+ connectionFactory.setJdbcURL(JDBC_URL);
+ connectionFactory.setJdbcUserName(JDBC_USERNAME);
+ connectionFactory.setJdbcPassword(JDBC_PASSWORD);
+ jdbcLockManager.setConnectionFactory(connectionFactory);
+ jdbcLockManager.setTableName(TABLE_LOCK_MANAGEMENT);
+ System.out.println("=> Running LockManager test [" + jdbcLockManager.getClass().getName() + "." + testName.getMethodName() + "]" + (USE_REAL_DB ? ". JDBC URL is [" + JDBC_URL + "]" : ""));
+ clearTestLocks(jdbcLockManager);
+ return jdbcLockManager;
+ }
+
+ protected abstract JdbcLockManager createJdbcLockManager(boolean useRealDb);
+
+ protected boolean setSynchronizer(Synchronizer synchronizer) {
+ if(!(lockManager instanceof SynchronizerReceiver)) {
+ System.err.println("Skipping concurrency test [" + testName.getMethodName() + "] for LockManager of type " + lockManager.getClass());
+ return false;
+ }
+ ((SynchronizerReceiver)lockManager).setSynchronizer(synchronizer);
+ return true;
+ }
+
+ private static final String SQL_DELETE_LOCK_RECORD = String.format("DELETE FROM %s WHERE RESOURCE_ID=?", TABLE_LOCK_MANAGEMENT);
+ private void clearTestLocks(JdbcLockManager jdbcLockManager) {
+ Connection connection = jdbcLockManager.openDbConnection();
+ if(connection == null) {
+ return;
+ }
+ try {
+ for(Resource resource: Resource.values()) {
+ try(PreparedStatement statement = connection.prepareStatement(SQL_DELETE_LOCK_RECORD)) {
+ statement.setString(1, resource.name());
+ statement.executeUpdate();
+ }
+ }
+ } catch(SQLException e) {
+ throw new RuntimeException("Cannot clear test resources in table", e);
+ } finally {
+ jdbcLockManager.closeDbConnection(connection);
+ }
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/Synchronizer.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/Synchronizer.java
new file mode 100644
index 000000000..8d1f0bbfd
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/Synchronizer.java
@@ -0,0 +1,80 @@
+/*-
+ * ============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;
+
+public class Synchronizer {
+
+ private int participantNo;
+ private int participantCount;
+
+ public Synchronizer(int participantNo) {
+ this.participantNo = participantNo;
+ }
+
+ public int getParticipantCount() {
+ return participantCount;
+ }
+
+ public void postLoadLockRecord(String resource, String owner) {
+ synchronized(this) {
+ waitForAllParticipants(this, participantNo, ++participantCount);
+ }
+ }
+
+ public void preAddLockRecord(String resource, String owner) {
+ }
+
+ public void postAddLockRecord(String resource, String owner) {
+ }
+
+ public void preUpdateLockRecord(String resource, String owner) {
+ }
+
+ public void postUpdateLockRecord(String resource, String owner) {
+ }
+
+ public void releaseWait() {
+ synchronized(this) {
+ this.notifyAll();
+ }
+ }
+
+ protected void waitOn(Object obj, long timeout) {
+ try {
+ obj.wait(timeout);
+ } catch(InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected void waitOn(Object obj) {
+ waitOn(obj, 0);
+ }
+
+ protected void waitForAllParticipants(Object waitObj, int totalParticipantsNo, int currentParticipantsNo) {
+ if(totalParticipantsNo > currentParticipantsNo) {
+ waitOn(waitObj);
+ } else {
+ waitObj.notifyAll();
+ }
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/SynchronizerReceiver.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/SynchronizerReceiver.java
new file mode 100644
index 000000000..945fed10b
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/SynchronizerReceiver.java
@@ -0,0 +1,27 @@
+/*-
+ * ============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;
+
+public interface SynchronizerReceiver {
+
+ void setSynchronizer(Synchronizer synchronizer);
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManagerMock.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManagerMock.java
new file mode 100644
index 000000000..3e6a236ba
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/MySqlLockManagerMock.java
@@ -0,0 +1,132 @@
+/*-
+ * ============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.optimistic;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.openecomp.appc.lockmanager.impl.sql.Synchronizer;
+import org.openecomp.appc.lockmanager.impl.sql.SynchronizerReceiver;
+import org.openecomp.appc.lockmanager.impl.sql.optimistic.LockRecord;
+import org.openecomp.appc.lockmanager.impl.sql.optimistic.MySqlLockManager;
+
+class MySqlLockManagerMock extends MySqlLockManager implements SynchronizerReceiver {
+
+ private final ConcurrentMap<String, LockRecord> locks = new ConcurrentHashMap<>();
+ 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);
+ lockRecord.setVer(1);
+ LockRecord prevLockRecord = locks.putIfAbsent(resource, lockRecord);
+ if(prevLockRecord != null) {
+ // simulate unique constraint violation
+ throw new SQLException("Duplicate PK exception", "23000", 1062);
+ }
+ } finally {
+ if(synchronizer != null) {
+ synchronizer.postAddLockRecord(resource, owner);
+ }
+ }
+ }
+
+ @Override
+ protected boolean updateLockRecord(Connection connection, String resource, String owner, long timeout, long ver) throws SQLException {
+ if(synchronizer != null) {
+ synchronizer.preUpdateLockRecord(resource, owner);
+ }
+ try {
+ if(useReal) {
+ return super.updateLockRecord(connection, resource, owner, timeout, ver);
+ }
+ LockRecord lockRecord = loadLockRecord(connection, resource);
+ synchronized(lockRecord) {
+ // should be atomic operation
+ if(ver != lockRecord.getVer()) {
+ return false;
+ }
+ lockRecord.setOwner(owner);
+ lockRecord.setUpdated(System.currentTimeMillis());
+ lockRecord.setTimeout(timeout);
+ lockRecord.setVer(ver + 1);
+ }
+ return true;
+ } finally {
+ if(synchronizer != null) {
+ synchronizer.postUpdateLockRecord(resource, owner);
+ }
+ }
+ }
+}
diff --git a/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/TestMySqlLockManager.java b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/TestMySqlLockManager.java
new file mode 100644
index 000000000..6106dd84b
--- /dev/null
+++ b/appc-dispatcher/appc-dispatcher-common/lock-manager-lib/lock-manager-impl/src/test/java/org/openecomp/appc/lockmanager/impl/sql/optimistic/TestMySqlLockManager.java
@@ -0,0 +1,227 @@
+/*-
+ * ============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.optimistic;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openecomp.appc.lockmanager.api.LockException;
+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 java.util.concurrent.*;
+
+public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
+
+ @Override
+ protected JdbcLockManager createJdbcLockManager(boolean useReal) {
+ return new MySqlLockManagerMock(useReal);
+ }
+
+ @Test
+ public void testConcurrentLockDifferentOwners() throws LockException, InterruptedException, ExecutionException, TimeoutException {
+
+ final int participantsNo = 2;
+ Synchronizer synchronizer = new Synchronizer(participantsNo) {
+
+ private boolean wait = true;
+
+ @Override
+ public void preAddLockRecord(String resource, String owner) {
+ if(Owner.A.name().equals(owner)) {
+ synchronized(this) {
+ if(wait) {
+ waitOn(this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void postAddLockRecord(String resource, String owner) {
+ if(!Owner.A.name().equals(owner)) {
+ synchronized(this) {
+ notifyAll();
+ wait = false;
+ }
+ }
+ }
+
+ @Override
+ public void preUpdateLockRecord(String resource, String owner) {
+ preAddLockRecord(resource, owner);
+ }
+
+ @Override
+ public void postUpdateLockRecord(String resource, String owner) {
+ postAddLockRecord(resource, owner);
+ }
+ };
+ if(!setSynchronizer(synchronizer)) {
+ return;
+ }
+ ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
+ // acquireLock by owner A should fail as it will wait for acquireLock by owner B
+ Future<Boolean> future1 = executor.submit(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ return false;
+ } catch(LockException e) {
+ // this call should fail as Synchronizer delays its lock to make sure the second call locks the resource first
+ Assert.assertEquals("Cannot lock resource [" + Resource.Resource1.name() + "] for [" + Owner.A.name() + "]: already locked by [" + Owner.B.name() + "]", e.getMessage());
+ return true;
+ }
+ }
+ });
+ try {
+ // acquireLock by owner B should success
+ Future<Boolean> future2 = executor.submit(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ // this call should success as Synchronizer delays the above lock to make sure this call success to lock the resource
+ return lockManager.acquireLock(Resource.Resource1.name(), Owner.B.name());
+ }
+ });
+ try {
+ Assert.assertTrue(future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
+ Assert.assertTrue(future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
+ } finally {
+ future2.cancel(true);
+ }
+ } finally {
+ future1.cancel(true);
+ }
+ }
+
+ @Test
+ public void testConcurrentLockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
+ final int participantsNo = 2;
+ Synchronizer synchronizer = new Synchronizer(participantsNo) {
+
+ private boolean wait = true;
+
+ @Override
+ public void preAddLockRecord(String resource, String owner) {
+ synchronized(this) {
+ if(wait) {
+ wait = false;
+ waitOn(this);
+ }
+ }
+ }
+
+ @Override
+ public void postAddLockRecord(String resource, String owner) {
+ synchronized(this) {
+ notifyAll();
+ }
+ }
+ };
+ if(!setSynchronizer(synchronizer)) {
+ return;
+ }
+ ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
+ // one acquireLock should return true and the other should return false
+ Callable<Boolean> callable = new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ }
+ };
+ Future<Boolean> future1 = executor.submit(callable);
+ try {
+ Future<Boolean> future2 = executor.submit(callable);
+ try {
+ boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
+ boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
+ // one of the lock requests should return true, the other one false as lock is requested simultaneously from 2 threads by same owner
+ Assert.assertNotEquals(future1Res, future2Res);
+ } finally {
+ future2.cancel(true);
+ }
+ } finally {
+ future1.cancel(true);
+ }
+ }
+
+ @Test
+ public void testConcurrentUnlockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
+ lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
+ final int participantsNo = 2;
+ Synchronizer synchronizer = new Synchronizer(participantsNo) {
+
+ private boolean wait = true;
+
+ @Override
+ public void preUpdateLockRecord(String resource, String owner) {
+ synchronized(this) {
+ // make sure second call updates the LockRecord first
+ if(wait) {
+ wait = false;
+ waitOn(this);
+ }
+ }
+ }
+
+ @Override
+ public void postUpdateLockRecord(String resource, String owner) {
+ synchronized(this) {
+ notifyAll();
+ }
+ }
+ };
+ if(!setSynchronizer(synchronizer)) {
+ return;
+ }
+ ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
+ Callable<Boolean> callable = new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
+ // one of the unlock calls should success
+ return true;
+ } catch(LockException e) {
+ // one of the unlock calls should throw the LockException as the resource should already be unlocked by other call
+ Assert.assertEquals("Error unlocking resource [" + Resource.Resource1.name() + "]: resource is not locked", e.getMessage());
+ return false;
+ }
+ }
+ };
+ Future<Boolean> future1 = executor.submit(callable);
+ try {
+ Future<Boolean> future2 = executor.submit(callable);
+ try {
+ boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
+ boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
+ // one of the unlock calls should return true, the other one false as unlock is requested simultaneously from 2 threads by same owner
+ Assert.assertNotEquals(future1Res, future2Res);
+ } finally {
+ future2.cancel(true);
+ }
+ } finally {
+ future1.cancel(true);
+ }
+ }
+}