Merge of new rebased code
[appc.git] / appc-dispatcher / appc-dispatcher-common / lock-manager-lib / lock-manager-impl / src / main / java / org / openecomp / appc / lockmanager / impl / sql / pessimistic / SqlLockManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : APP-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                                              reserved.
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
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
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=========================================================
20  */
21
22 package org.openecomp.appc.lockmanager.impl.sql.pessimistic;
23
24 import java.sql.Connection;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28
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;
33
34 abstract class SqlLockManager extends JdbcLockManager {
35
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) VALUES (?, ?, ?, ?)";
38         private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=? WHERE RESOURCE_ID=?";
39         private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()";
40
41         private String sqlLoadLockRecord;
42         private String sqlInsertLockRecord;
43         private String sqlUpdateLockRecord;
44
45         @Override
46         public boolean acquireLock(String resource, String owner) throws LockException {
47                 return acquireLock(resource, owner, 0);
48         }
49
50         @Override
51         public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
52                 if(owner == null) {
53                         throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
54                 }
55                 boolean res = false;
56                 Connection connection = openDbConnection();
57                 try {
58                         enterCriticalSection(connection, resource);
59                         try {
60                                 res = lockResource(connection, resource, owner, timeout);
61                         } finally {
62                                 leaveCriticalSection(connection, resource);
63                         }
64                 } finally {
65                         closeDbConnection(connection);
66                 }
67                 return res;
68         }
69
70         @Override
71         public void releaseLock(String resource, String owner) throws LockException {
72                 Connection connection = openDbConnection();
73                 try {
74                         enterCriticalSection(connection, resource);
75                         try {
76                                 unlockResource(connection, resource, owner);
77                         } finally {
78                                 leaveCriticalSection(connection, resource);
79                         }
80                 } finally {
81                         closeDbConnection(connection);
82                 }
83         }
84
85         @Override
86         public boolean isLocked(String resource) {
87                 Connection connection=openDbConnection();
88                 try {
89                         LockRecord lockRecord=loadLockRecord(connection,resource);
90                         if(lockRecord==null){
91                                 return false;
92                         }else{
93                                 if(lockRecord.getOwner()==null){
94                                         return false;
95                                 }else if(isLockExpired(lockRecord, connection)){
96                                         return false;
97                                 }else{
98                                         return true;
99                                 }
100                         }
101                 } catch (SQLException e) {
102                         throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
103                 }finally {
104                         closeDbConnection(connection);
105                 }
106         }
107
108         private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
109                 try {
110                         boolean res = false;
111                         LockRecord lockRecord = loadLockRecord(connection, resource);
112                         if(lockRecord != null) {
113                                 // lock record already exists
114                                 String currentOwner = lockRecord.getOwner();
115                                 if(currentOwner != null) {
116                                         if(isLockExpired(lockRecord, connection)) {
117                                                 currentOwner = null;
118                                         } else if(!owner.equals(currentOwner)) {
119                                                 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
120                                         }
121                                 }
122                                 // set new owner on the resource lock record
123                                 updateLockRecord(connection, resource, owner, timeout);
124                                 if(currentOwner == null) {
125                                         // no one locked the resource before
126                                         res = true;
127                                 }
128                         } else {
129                                 // resource record does not exist in lock table => create new record
130                                 addLockRecord(connection, resource, owner, timeout);
131                                 res = true;
132                         }
133                         return res;
134                 } catch(SQLException e) {
135                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
136                 }
137         }
138
139         private void unlockResource(Connection connection, String resource, String owner) throws LockException {
140                 try {
141                         LockRecord lockRecord = loadLockRecord(connection, resource);
142                         if(lockRecord != null) {
143                                 // check if expired
144                                 if(isLockExpired(lockRecord, connection)) {
145                                         // lock is expired => no lock
146                                         lockRecord = null;
147                                 }
148                         }
149                         if((lockRecord == null) || (lockRecord.getOwner() == null)) {
150                                 // resource is not locked
151                                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
152                         }
153                         String currentOwner = lockRecord.getOwner();
154                         if(!owner.equals(currentOwner)) {
155                                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
156                         }
157                         updateLockRecord(connection, resource, null, 0);
158                         // TODO delete record from table on lock release?
159 //                      deleteLockRecord(connection, resource);
160                 } catch(SQLException e) {
161                         throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
162                 }
163         }
164
165         protected abstract void enterCriticalSection(Connection connection, String resource);
166
167         protected abstract void leaveCriticalSection(Connection connection, String resource);
168
169         protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
170                 LockRecord res = null;
171                 if(sqlLoadLockRecord == null) {
172                         sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
173                 }
174                 try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
175                         statement.setString(1, resource);
176                         try(ResultSet resultSet = statement.executeQuery()) {
177                                 if(resultSet.next()) {
178                                         res = new LockRecord(resource);
179                                         res.setOwner(resultSet.getString(2));
180                                         res.setUpdated(resultSet.getLong(3));
181                                         res.setTimeout(resultSet.getLong(4));
182                                 }
183                         }
184                 }
185                 return res;
186         }
187
188         protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
189                 if(sqlInsertLockRecord == null) {
190                         sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
191                 }
192                 try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
193                         statement.setString(1, resource);
194                         statement.setString(2, owner);
195                         statement.setLong(3, getCurrentTime(connection));
196                         statement.setLong(4, timeout);
197                         statement.executeUpdate();
198                 }
199         }
200
201         protected void updateLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
202                 if(sqlUpdateLockRecord == null) {
203                         sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
204                 }
205                 try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
206                         statement.setString(1, owner);
207                         statement.setLong(2, getCurrentTime(connection));
208                         statement.setLong(3, timeout);
209                         statement.setString(4, resource);
210                         statement.executeUpdate();
211                 }
212         }
213
214 //      protected void deleteLockRecord(Connection connection, String resource) throws SQLException {
215 //              if(sqlDeleteLockRecord == null) {
216 //                      sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
217 //              }
218 //              try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
219 //                      statement.setString(1, resource);
220 //                      statement.executeUpdate();
221 //              }
222 //      }
223
224         private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
225                 long timeout = lockRecord.getTimeout();
226                 if(timeout == 0) {
227                         return false;
228                 }
229                 long updated = lockRecord.getUpdated();
230                 long now = getCurrentTime(connection);
231                 long expiration = updated + timeout;
232                 return (now > expiration);
233         }
234
235         private long getCurrentTime(Connection connection) throws SQLException {
236                 long res = -1;
237                 if(connection != null) {
238                         try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
239                                 try(ResultSet resultSet = statement.executeQuery()) {
240                                         if(resultSet.next()) {
241                                                 res = resultSet.getTimestamp(1).getTime();
242                                         }
243                                 }
244                         }
245                 }
246                 if(res == -1) {
247                         res = System.currentTimeMillis();
248                 }
249                 return res;
250         }
251 }