2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
 
   6  * ================================================================================
 
   7  * Copyright (C) 2017 Amdocs
 
   8  * =============================================================================
 
   9  * Licensed under the Apache License, Version 2.0 (the "License");
 
  10  * you may not use this file except in compliance with the License.
 
  11  * You may obtain a copy of the License at
 
  13  *      http://www.apache.org/licenses/LICENSE-2.0
 
  15  * Unless required by applicable law or agreed to in writing, software
 
  16  * distributed under the License is distributed on an "AS IS" BASIS,
 
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  18  * See the License for the specific language governing permissions and
 
  19  * limitations under the License.
 
  21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
 
  22  * ============LICENSE_END=========================================================
 
  25 package org.openecomp.appc.lockmanager.impl.sql.optimistic;
 
  27 import java.sql.Connection;
 
  28 import java.sql.PreparedStatement;
 
  29 import java.sql.ResultSet;
 
  30 import java.sql.SQLException;
 
  32 import org.openecomp.appc.lockmanager.api.LockException;
 
  33 import org.openecomp.appc.lockmanager.api.LockRuntimeException;
 
  34 import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager;
 
  35 import org.openecomp.appc.lockmanager.impl.sql.Messages;
 
  37 abstract class SqlLockManager extends JdbcLockManager {
 
  39         private static final String SQL_LOAD_LOCK_RECORD = "SELECT * FROM %s WHERE RESOURCE_ID=?";
 
  40         private static final String SQL_INSERT_LOCK_RECORD = "INSERT INTO %s (RESOURCE_ID, OWNER_ID, UPDATED, TIMEOUT, VER) VALUES (?, ?, ?, ?, ?)";
 
  41         private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=?, VER=? WHERE RESOURCE_ID=? AND VER=?";
 
  42 //      private static final String SQL_DELETE_LOCK_RECORD = "DELETE FROM %s WHERE RESOURCE_ID=? AND VER=?";
 
  43         private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()";
 
  45         private String sqlLoadLockRecord;
 
  46         private String sqlInsertLockRecord;
 
  47         private String sqlUpdateLockRecord;
 
  48 //      private String sqlDeleteLockRecord;
 
  51         public boolean acquireLock(String resource, String owner) throws LockException {
 
  52                 return acquireLock(resource, owner, 0);
 
  56         public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
 
  58                         throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
 
  61                 Connection connection = openDbConnection();
 
  63                         res = lockResource(connection, resource, owner, timeout);
 
  65                         closeDbConnection(connection);
 
  71         public void releaseLock(String resource, String owner) throws LockException {
 
  72                 Connection connection = openDbConnection();
 
  74                         unlockResource(connection, resource, owner);
 
  76                         closeDbConnection(connection);
 
  81         public boolean isLocked(String resource) {
 
  82         Connection connection=openDbConnection();
 
  84                         LockRecord lockRecord=loadLockRecord(connection,resource);
 
  88                                 if(lockRecord.getOwner()==null){
 
  90                                 }else if(isLockExpired(lockRecord, connection)){
 
  96                 } catch (SQLException e) {
 
  97                         throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
 
  99             closeDbConnection(connection);
 
 103         private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
 
 106                         LockRecord lockRecord = loadLockRecord(connection, resource);
 
 107                         if(lockRecord != null) {
 
 108                                 // lock record already exists
 
 109                                 String currentOwner = lockRecord.getOwner();
 
 110                                 if(currentOwner != null) {
 
 111                                         if(isLockExpired(lockRecord, connection)) {
 
 113                                         } else if(!owner.equals(currentOwner)) {
 
 114                                                 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
 
 117                                 // set new owner on the resource lock record
 
 118                                 if(!updateLockRecord(connection, resource, owner, timeout, lockRecord.getVer())) {
 
 119                                         // try again - maybe same owner updated the record
 
 120                                         lockResource(connection, resource, owner, timeout);
 
 122                                 if(currentOwner == null) {
 
 123                                         // no one locked the resource before
 
 127                                 // resource record does not exist in lock table => create new record
 
 129                                         addLockRecord(connection, resource, owner, timeout);
 
 131                                 } catch(SQLException e) {
 
 132                                         if(isDuplicatePkError(e)) {
 
 133                                                 // try again - maybe same owner inserted the record
 
 134                                                 lockResource(connection, resource, owner, timeout);
 
 141                 } catch(SQLException e) {
 
 142                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
 
 146         protected boolean isDuplicatePkError(SQLException e) {
 
 147                 return e.getSQLState().startsWith("23");
 
 150         private void unlockResource(Connection connection, String resource, String owner) throws LockException {
 
 152                         LockRecord lockRecord = loadLockRecord(connection, resource);
 
 153                         if(lockRecord != null) {
 
 155                                 if(isLockExpired(lockRecord, connection)) {
 
 156                                         // lock is expired => no lock
 
 160                         if((lockRecord == null) || (lockRecord.getOwner() == null)) {
 
 161                                 // resource is not locked
 
 162                                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
 
 164                         String currentOwner = lockRecord.getOwner();
 
 165                         if(!owner.equals(currentOwner)) {
 
 166                                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
 
 168             if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
 
 169                                 unlockResource(connection, resource, owner);
 
 171                         // TODO delete record from table on lock release?
 
 172 //                      deleteLockRecord(connection, resource, lockRecord.getVer());
 
 173                 } catch(SQLException e) {
 
 174                         throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
 
 178         protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
 
 179                 LockRecord res = null;
 
 180                 if(sqlLoadLockRecord == null) {
 
 181                         sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
 
 183                 try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
 
 184                         statement.setString(1, resource);
 
 185                         try(ResultSet resultSet = statement.executeQuery()) {
 
 186                                 if(resultSet.next()) {
 
 187                                         res = new LockRecord(resource);
 
 188                                         res.setOwner(resultSet.getString(2));
 
 189                                         res.setUpdated(resultSet.getLong(3));
 
 190                                         res.setTimeout(resultSet.getLong(4));
 
 191                                         res.setVer(resultSet.getLong(5));
 
 198         protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
 
 199                 if(sqlInsertLockRecord == null) {
 
 200                         sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
 
 202                 try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
 
 203                         statement.setString(1, resource);
 
 204                         statement.setString(2, owner);
 
 205             statement.setLong(3, getCurrentTime(connection));
 
 206                         statement.setLong(4, timeout);
 
 207                         statement.setLong(5, 1);
 
 208                         statement.executeUpdate();
 
 212         protected boolean updateLockRecord(Connection connection, String resource, String owner, long timeout, long ver) throws SQLException {
 
 213                 if(sqlUpdateLockRecord == null) {
 
 214                         sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
 
 216                 try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
 
 217                         long newVer = (ver >= Long.MAX_VALUE) ? 1 : (ver + 1);
 
 218                         statement.setString(1, owner);
 
 219             statement.setLong(2, getCurrentTime(connection));
 
 220                         statement.setLong(3, timeout);
 
 221                         statement.setLong(4, newVer);
 
 222                         statement.setString(5, resource);
 
 223                         statement.setLong(6, ver);
 
 224                         return (statement.executeUpdate() != 0);
 
 228 //      protected void deleteLockRecord(Connection connection, String resource, long ver) throws SQLException {
 
 229 //              if(sqlDeleteLockRecord == null) {
 
 230 //                      sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
 
 232 //              try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
 
 233 //                      statement.setString(1, resource);
 
 234 //                      statement.setLong(2, ver);
 
 235 //                      statement.executeUpdate();
 
 239         private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
 
 240                 long timeout = lockRecord.getTimeout();
 
 244                 long updated = lockRecord.getUpdated();
 
 245         long now = getCurrentTime(connection);
 
 246                 long expiration = updated + timeout;
 
 247                 return (now > expiration);
 
 250         private long getCurrentTime(Connection connection) throws SQLException {
 
 252                 if(connection != null) {
 
 253                         try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
 
 254                                 try(ResultSet resultSet = statement.executeQuery()) {
 
 255                                         if(resultSet.next()) {
 
 256                                                 res = resultSet.getTimestamp(1).getTime();
 
 262                         res = System.currentTimeMillis();