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.onap.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.onap.appc.lockmanager.api.LockException;
33 import org.onap.appc.lockmanager.api.LockRuntimeException;
34 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
35 import org.onap.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()";
44 private static final String SQL_LOAD_LOCK_RECORD_WITH_OWNER = "SELECT * FROM LOCK_MANAGEMENT WHERE RESOURCE_ID = ? AND OWNER = ? ";
46 private String sqlLoadLockRecord;
47 private String sqlInsertLockRecord;
48 private String sqlUpdateLockRecord;
49 // private String sqlDeleteLockRecord;
52 public boolean acquireLock(String resource, String owner) throws LockException {
53 return acquireLock(resource, owner, 0);
57 public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
59 throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
62 Connection connection = openDbConnection();
64 res = lockResource(connection, resource, owner, timeout);
66 closeDbConnection(connection);
72 public void releaseLock(String resource, String owner) throws LockException {
73 Connection connection = openDbConnection();
75 unlockResource(connection, resource, owner);
77 closeDbConnection(connection);
82 public boolean isLocked(String resource) {
83 Connection connection=openDbConnection();
85 LockRecord lockRecord=loadLockRecord(connection,resource);
89 if(lockRecord.getOwner()==null){
91 }else if(isLockExpired(lockRecord, connection)){
97 } catch (SQLException e) {
98 throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
100 closeDbConnection(connection);
105 public String getLockOwner(String resource) {
106 Connection connection=openDbConnection();
108 LockRecord lockRecord=loadLockRecord(connection,resource);
109 if(lockRecord==null || lockRecord.getOwner() ==null ){
112 if(isLockExpired(lockRecord, connection)){
115 return lockRecord.getOwner();
118 } catch (SQLException e) {
119 throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
121 closeDbConnection(connection);
125 private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
128 LockRecord lockRecord = loadLockRecord(connection, resource);
129 if(lockRecord != null) {
130 // lock record already exists
131 String currentOwner = lockRecord.getOwner();
132 if(currentOwner != null) {
133 if(isLockExpired(lockRecord, connection)) {
135 } else if(!owner.equals(currentOwner)) {
136 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, currentOwner));
139 // set new owner on the resource lock record
140 if(!updateLockRecord(connection, resource, owner, timeout, lockRecord.getVer())) {
141 // try again - maybe same owner updated the record
142 lockResource(connection, resource, owner, timeout);
144 if(currentOwner == null) {
145 // no one locked the resource before
149 // resource record does not exist in lock table => create new record
151 addLockRecord(connection, resource, owner, timeout);
153 } catch(SQLException e) {
154 if(isDuplicatePkError(e)) {
155 // try again - maybe same owner inserted the record
156 lockResource(connection, resource, owner, timeout);
163 } catch(SQLException e) {
164 throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
168 protected boolean isDuplicatePkError(SQLException e) {
169 return e.getSQLState().startsWith("23");
172 private void unlockResource(Connection connection, String resource, String owner) throws LockException {
174 LockRecord lockRecord = loadLockRecord(connection, resource);
175 if(lockRecord != null) {
177 if(isLockExpired(lockRecord, connection)) {
178 // lock is expired => no lock
182 if((lockRecord == null) || (lockRecord.getOwner() == null)) {
183 // resource is not locked
184 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
186 String currentOwner = lockRecord.getOwner();
187 if(!owner.equals(currentOwner)) {
188 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
190 if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
191 unlockResource(connection, resource, owner);
193 // TODO delete record from table on lock release?
194 // deleteLockRecord(connection, resource, lockRecord.getVer());
195 } catch(SQLException e) {
196 throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
200 protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
201 LockRecord res = null;
202 if(sqlLoadLockRecord == null) {
203 sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
205 try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
206 statement.setString(1, resource);
207 try(ResultSet resultSet = statement.executeQuery()) {
208 if(resultSet.next()) {
209 res = new LockRecord(resource);
210 res.setOwner(resultSet.getString(2));
211 res.setUpdated(resultSet.getLong(3));
212 res.setTimeout(resultSet.getLong(4));
213 res.setVer(resultSet.getLong(5));
220 protected LockRecord loadLockRecord(Connection connection, String resource,String owner) throws SQLException {
221 LockRecord res = null;
222 try(PreparedStatement statement = connection.prepareStatement(SQL_LOAD_LOCK_RECORD_WITH_OWNER)) {
223 statement.setString(1, resource);
224 statement.setString(2, owner);
225 try(ResultSet resultSet = statement.executeQuery()) {
226 if(resultSet.next()) {
227 res = new LockRecord(resource);
228 res.setOwner(resultSet.getString(2));
229 res.setUpdated(resultSet.getLong(3));
230 res.setTimeout(resultSet.getLong(4));
231 res.setVer(resultSet.getLong(5));
238 protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
239 if(sqlInsertLockRecord == null) {
240 sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
242 try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
243 statement.setString(1, resource);
244 statement.setString(2, owner);
245 statement.setLong(3, getCurrentTime(connection));
246 statement.setLong(4, timeout);
247 statement.setLong(5, 1);
248 statement.executeUpdate();
252 protected boolean updateLockRecord(Connection connection, String resource, String owner, long timeout, long ver) throws SQLException {
253 if(sqlUpdateLockRecord == null) {
254 sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
256 try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
257 long newVer = (ver >= Long.MAX_VALUE) ? 1 : (ver + 1);
258 statement.setString(1, owner);
259 statement.setLong(2, getCurrentTime(connection));
260 statement.setLong(3, timeout);
261 statement.setLong(4, newVer);
262 statement.setString(5, resource);
263 statement.setLong(6, ver);
264 return (statement.executeUpdate() != 0);
268 // protected void deleteLockRecord(Connection connection, String resource, long ver) throws SQLException {
269 // if(sqlDeleteLockRecord == null) {
270 // sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
272 // try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
273 // statement.setString(1, resource);
274 // statement.setLong(2, ver);
275 // statement.executeUpdate();
279 private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
280 long timeout = lockRecord.getTimeout();
284 long updated = lockRecord.getUpdated();
285 long now = getCurrentTime(connection);
286 long expiration = updated + timeout;
287 return (now > expiration);
290 private long getCurrentTime(Connection connection) throws SQLException {
292 if(connection != null) {
293 try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
294 try(ResultSet resultSet = statement.executeQuery()) {
295 if(resultSet.next()) {
296 res = resultSet.getTimestamp(1).getTime();
302 res = System.currentTimeMillis();