d4fb7d15585131ef18cf29db6fb3e59f624cd537
[appc.git] / appc-dispatcher / appc-dispatcher-common / lock-manager-lib / lock-manager-impl / src / main / java / org / openecomp / appc / lockmanager / impl / sql / optimistic / SqlLockManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
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
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
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.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.openecomp.appc.lockmanager.impl.sql.optimistic;
26
27 import java.sql.Connection;
28 import java.sql.PreparedStatement;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31
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;
36
37 abstract class SqlLockManager extends JdbcLockManager {
38
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
45         private String sqlLoadLockRecord;
46         private String sqlInsertLockRecord;
47         private String sqlUpdateLockRecord;
48 //      private String sqlDeleteLockRecord;
49
50         @Override
51         public boolean acquireLock(String resource, String owner) throws LockException {
52                 return acquireLock(resource, owner, 0);
53         }
54
55         @Override
56         public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
57                 if(owner == null) {
58                         throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
59                 }
60                 boolean res = false;
61                 Connection connection = openDbConnection();
62                 try {
63                         res = lockResource(connection, resource, owner, timeout);
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                         unlockResource(connection, resource, owner);
75                 } finally {
76                         closeDbConnection(connection);
77                 }
78         }
79
80         @Override
81         public boolean isLocked(String resource) {
82         Connection connection=openDbConnection();
83                 try {
84                         LockRecord lockRecord=loadLockRecord(connection,resource);
85                         if(lockRecord==null){
86                                 return false;
87                         }else{
88                                 if(lockRecord.getOwner()==null){
89                                         return false;
90                                 }else if(isLockExpired(lockRecord, connection)){
91                                         return false;
92                                 }else{
93                     return true;
94                 }
95                         }
96                 } catch (SQLException e) {
97                         throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
98                 }finally {
99             closeDbConnection(connection);
100         }
101     }
102
103         private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
104                 try {
105                         boolean res = false;
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)) {
112                                                 currentOwner = null;
113                                         } else if(!owner.equals(currentOwner)) {
114                                                 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
115                                         }
116                                 }
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);
121                                 }
122                                 if(currentOwner == null) {
123                                         // no one locked the resource before
124                                         res = true;
125                                 }
126                         } else {
127                                 // resource record does not exist in lock table => create new record
128                                 try {
129                                         addLockRecord(connection, resource, owner, timeout);
130                                         res = true;
131                                 } catch(SQLException e) {
132                                         if(isDuplicatePkError(e)) {
133                                                 // try again - maybe same owner inserted the record
134                                                 lockResource(connection, resource, owner, timeout);
135                                         } else {
136                                                 throw e;
137                                         }
138                                 }
139                         }
140                         return res;
141                 } catch(SQLException e) {
142                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
143                 }
144         }
145
146         protected boolean isDuplicatePkError(SQLException e) {
147                 return e.getSQLState().startsWith("23");
148         }
149
150         private void unlockResource(Connection connection, String resource, String owner) throws LockException {
151                 try {
152                         LockRecord lockRecord = loadLockRecord(connection, resource);
153                         if(lockRecord != null) {
154                                 // check if expired
155                                 if(isLockExpired(lockRecord, connection)) {
156                                         // lock is expired => no lock
157                                         lockRecord = null;
158                                 }
159                         }
160                         if((lockRecord == null) || (lockRecord.getOwner() == null)) {
161                                 // resource is not locked
162                                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
163                         }
164                         String currentOwner = lockRecord.getOwner();
165                         if(!owner.equals(currentOwner)) {
166                                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
167             }
168             if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
169                                 unlockResource(connection, resource, owner);
170                         }
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);
175                 }
176         }
177
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);
182                 }
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));
192                                 }
193                         }
194                 }
195                 return res;
196         }
197
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);
201                 }
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();
209                 }
210         }
211
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);
215                 }
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);
225                 }
226         }
227
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);
231 //              }
232 //              try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
233 //                      statement.setString(1, resource);
234 //                      statement.setLong(2, ver);
235 //                      statement.executeUpdate();
236 //              }
237 //      }
238
239         private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
240                 long timeout = lockRecord.getTimeout();
241                 if(timeout == 0) {
242                         return false;
243                 }
244                 long updated = lockRecord.getUpdated();
245         long now = getCurrentTime(connection);
246                 long expiration = updated + timeout;
247                 return (now > expiration);
248         }
249
250         private long getCurrentTime(Connection connection) throws SQLException {
251                 long res = -1;
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();
257                                         }
258                                 }
259                         }
260                 }
261                 if(res == -1) {
262                         res = System.currentTimeMillis();
263                 }
264                 return res;
265         }
266 }