2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.openecomp.appc.lockmanager.impl.sql.optimistic;
24 import java.sql.Connection;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
29 import org.openecomp.appc.lockmanager.api.LockException;
30 import org.openecomp.appc.lockmanager.api.LockRuntimeException;
31 import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager;
32 import org.openecomp.appc.lockmanager.impl.sql.Messages;
34 abstract class SqlLockManager extends JdbcLockManager {
36 private static final String SQL_LOAD_LOCK_RECORD = "SELECT * FROM %s WHERE RESOURCE_ID=?";
37 private static final String SQL_INSERT_LOCK_RECORD = "INSERT INTO %s (RESOURCE_ID, OWNER_ID, UPDATED, TIMEOUT, VER) VALUES (?, ?, ?, ?, ?)";
38 private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=?, VER=? WHERE RESOURCE_ID=? AND VER=?";
39 // private static final String SQL_DELETE_LOCK_RECORD = "DELETE FROM %s WHERE RESOURCE_ID=? AND VER=?";
40 private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()";
42 private String sqlLoadLockRecord;
43 private String sqlInsertLockRecord;
44 private String sqlUpdateLockRecord;
45 // private String sqlDeleteLockRecord;
48 public boolean acquireLock(String resource, String owner) throws LockException {
49 return acquireLock(resource, owner, 0);
53 public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
55 throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
58 Connection connection = openDbConnection();
60 res = lockResource(connection, resource, owner, timeout);
62 closeDbConnection(connection);
68 public void releaseLock(String resource, String owner) throws LockException {
69 Connection connection = openDbConnection();
71 unlockResource(connection, resource, owner);
73 closeDbConnection(connection);
78 public boolean isLocked(String resource) {
79 Connection connection=openDbConnection();
81 LockRecord lockRecord=loadLockRecord(connection,resource);
85 if(lockRecord.getOwner()==null){
87 }else if(isLockExpired(lockRecord, connection)){
93 } catch (SQLException e) {
94 throw new LockRuntimeException(Messages.EXP_LOCK.format(resource));
96 closeDbConnection(connection);
100 private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
103 LockRecord lockRecord = loadLockRecord(connection, resource);
104 if(lockRecord != null) {
105 // lock record already exists
106 String currentOwner = lockRecord.getOwner();
107 if(currentOwner != null) {
108 if(isLockExpired(lockRecord, connection)) {
110 } else if(!owner.equals(currentOwner)) {
111 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
114 // set new owner on the resource lock record
115 if(!updateLockRecord(connection, resource, owner, timeout, lockRecord.getVer())) {
116 // try again - maybe same owner updated the record
117 lockResource(connection, resource, owner, timeout);
119 if(currentOwner == null) {
120 // no one locked the resource before
124 // resource record does not exist in lock table => create new record
126 addLockRecord(connection, resource, owner, timeout);
128 } catch(SQLException e) {
129 if(isDuplicatePkError(e)) {
130 // try again - maybe same owner inserted the record
131 lockResource(connection, resource, owner, timeout);
138 } catch(SQLException e) {
139 throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
143 protected boolean isDuplicatePkError(SQLException e) {
144 return e.getSQLState().startsWith("23");
147 private void unlockResource(Connection connection, String resource, String owner) throws LockException {
149 LockRecord lockRecord = loadLockRecord(connection, resource);
150 if(lockRecord != null) {
152 if(isLockExpired(lockRecord, connection)) {
153 // lock is expired => no lock
157 if((lockRecord == null) || (lockRecord.getOwner() == null)) {
158 // resource is not locked
159 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
161 String currentOwner = lockRecord.getOwner();
162 if(!owner.equals(currentOwner)) {
163 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
165 if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
166 unlockResource(connection, resource, owner);
168 // TODO delete record from table on lock release?
169 // deleteLockRecord(connection, resource, lockRecord.getVer());
170 } catch(SQLException e) {
171 throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
175 protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
176 LockRecord res = null;
177 if(sqlLoadLockRecord == null) {
178 sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
180 try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
181 statement.setString(1, resource);
182 try(ResultSet resultSet = statement.executeQuery()) {
183 if(resultSet.next()) {
184 res = new LockRecord(resource);
185 res.setOwner(resultSet.getString(2));
186 res.setUpdated(resultSet.getLong(3));
187 res.setTimeout(resultSet.getLong(4));
188 res.setVer(resultSet.getLong(5));
195 protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
196 if(sqlInsertLockRecord == null) {
197 sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
199 try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
200 statement.setString(1, resource);
201 statement.setString(2, owner);
202 statement.setLong(3, getCurrentTime(connection));
203 statement.setLong(4, timeout);
204 statement.setLong(5, 1);
205 statement.executeUpdate();
209 protected boolean updateLockRecord(Connection connection, String resource, String owner, long timeout, long ver) throws SQLException {
210 if(sqlUpdateLockRecord == null) {
211 sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
213 try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
214 long newVer = (ver >= Long.MAX_VALUE) ? 1 : (ver + 1);
215 statement.setString(1, owner);
216 statement.setLong(2, getCurrentTime(connection));
217 statement.setLong(3, timeout);
218 statement.setLong(4, newVer);
219 statement.setString(5, resource);
220 statement.setLong(6, ver);
221 return (statement.executeUpdate() != 0);
225 // protected void deleteLockRecord(Connection connection, String resource, long ver) throws SQLException {
226 // if(sqlDeleteLockRecord == null) {
227 // sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
229 // try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
230 // statement.setString(1, resource);
231 // statement.setLong(2, ver);
232 // statement.executeUpdate();
236 private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
237 long timeout = lockRecord.getTimeout();
241 long updated = lockRecord.getUpdated();
242 long now = getCurrentTime(connection);
243 long expiration = updated + timeout;
244 return (now > expiration);
247 private long getCurrentTime(Connection connection) throws SQLException {
249 if(connection != null) {
250 try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
251 try(ResultSet resultSet = statement.executeQuery()) {
252 if(resultSet.next()) {
253 res = resultSet.getTimestamp(1).getTime();
259 res = System.currentTimeMillis();