52c75d2b224f9943b7c0dfd917533de5a7401cb1
[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  * 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.optimistic;
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, 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()";
41
42         private String sqlLoadLockRecord;
43         private String sqlInsertLockRecord;
44         private String sqlUpdateLockRecord;
45 //      private String sqlDeleteLockRecord;
46
47         @Override
48         public boolean acquireLock(String resource, String owner) throws LockException {
49                 return acquireLock(resource, owner, 0);
50         }
51
52         @Override
53         public boolean acquireLock(String resource, String owner, long timeout) throws LockException {
54                 if(owner == null) {
55                         throw new LockRuntimeException(Messages.ERR_NULL_LOCK_OWNER.format(resource));
56                 }
57                 boolean res = false;
58                 Connection connection = openDbConnection();
59                 try {
60                         res = lockResource(connection, resource, owner, timeout);
61                 } finally {
62                         closeDbConnection(connection);
63                 }
64                 return res;
65         }
66
67         @Override
68         public void releaseLock(String resource, String owner) throws LockException {
69                 Connection connection = openDbConnection();
70                 try {
71                         unlockResource(connection, resource, owner);
72                 } finally {
73                         closeDbConnection(connection);
74                 }
75         }
76
77         @Override
78         public boolean isLocked(String resource) {
79         Connection connection=openDbConnection();
80                 try {
81                         LockRecord lockRecord=loadLockRecord(connection,resource);
82                         if(lockRecord==null){
83                                 return false;
84                         }else{
85                                 if(lockRecord.getOwner()==null){
86                                         return false;
87                                 }else if(isLockExpired(lockRecord, connection)){
88                                         return false;
89                                 }else{
90                     return true;
91                 }
92                         }
93                 } catch (SQLException e) {
94                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource));
95                 }finally {
96             closeDbConnection(connection);
97         }
98     }
99
100         private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
101                 try {
102                         boolean res = false;
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)) {
109                                                 currentOwner = null;
110                                         } else if(!owner.equals(currentOwner)) {
111                                                 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
112                                         }
113                                 }
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);
118                                 }
119                                 if(currentOwner == null) {
120                                         // no one locked the resource before
121                                         res = true;
122                                 }
123                         } else {
124                                 // resource record does not exist in lock table => create new record
125                                 try {
126                                         addLockRecord(connection, resource, owner, timeout);
127                                         res = true;
128                                 } catch(SQLException e) {
129                                         if(isDuplicatePkError(e)) {
130                                                 // try again - maybe same owner inserted the record
131                                                 lockResource(connection, resource, owner, timeout);
132                                         } else {
133                                                 throw e;
134                                         }
135                                 }
136                         }
137                         return res;
138                 } catch(SQLException e) {
139                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
140                 }
141         }
142
143         protected boolean isDuplicatePkError(SQLException e) {
144                 return e.getSQLState().startsWith("23");
145         }
146
147         private void unlockResource(Connection connection, String resource, String owner) throws LockException {
148                 try {
149                         LockRecord lockRecord = loadLockRecord(connection, resource);
150                         if(lockRecord != null) {
151                                 // check if expired
152                                 if(isLockExpired(lockRecord, connection)) {
153                                         // lock is expired => no lock
154                                         lockRecord = null;
155                                 }
156                         }
157                         if((lockRecord == null) || (lockRecord.getOwner() == null)) {
158                                 // resource is not locked
159                                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
160                         }
161                         String currentOwner = lockRecord.getOwner();
162                         if(!owner.equals(currentOwner)) {
163                                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
164             }
165             if (!updateLockRecord(connection, resource, null, 0, lockRecord.getVer())) {
166                                 unlockResource(connection, resource, owner);
167                         }
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);
172                 }
173         }
174
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);
179                 }
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));
189                                 }
190                         }
191                 }
192                 return res;
193         }
194
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);
198                 }
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();
206                 }
207         }
208
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);
212                 }
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);
222                 }
223         }
224
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);
228 //              }
229 //              try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
230 //                      statement.setString(1, resource);
231 //                      statement.setLong(2, ver);
232 //                      statement.executeUpdate();
233 //              }
234 //      }
235
236         private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
237                 long timeout = lockRecord.getTimeout();
238                 if(timeout == 0) {
239                         return false;
240                 }
241                 long updated = lockRecord.getUpdated();
242         long now = getCurrentTime(connection);
243                 long expiration = updated + timeout;
244                 return (now > expiration);
245         }
246
247         private long getCurrentTime(Connection connection) throws SQLException {
248                 long res = -1;
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();
254                                         }
255                                 }
256                         }
257                 }
258                 if(res == -1) {
259                         res = System.currentTimeMillis();
260                 }
261                 return res;
262         }
263 }