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