Applying license changes to all files
[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  * 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.pessimistic;
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) VALUES (?, ?, ?, ?)";
41         private static final String SQL_UPDATE_LOCK_RECORD = "UPDATE %s SET OWNER_ID=?, UPDATED=?, TIMEOUT=? WHERE RESOURCE_ID=?";
42         private static final String SQL_CURRENT_TIMESTAMP = "SELECT CURRENT_TIMESTAMP()";
43
44         private String sqlLoadLockRecord;
45         private String sqlInsertLockRecord;
46         private String sqlUpdateLockRecord;
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                         enterCriticalSection(connection, resource);
62                         try {
63                                 res = lockResource(connection, resource, owner, timeout);
64                         } finally {
65                                 leaveCriticalSection(connection, resource);
66                         }
67                 } finally {
68                         closeDbConnection(connection);
69                 }
70                 return res;
71         }
72
73         @Override
74         public void releaseLock(String resource, String owner) throws LockException {
75                 Connection connection = openDbConnection();
76                 try {
77                         enterCriticalSection(connection, resource);
78                         try {
79                                 unlockResource(connection, resource, owner);
80                         } finally {
81                                 leaveCriticalSection(connection, resource);
82                         }
83                 } finally {
84                         closeDbConnection(connection);
85                 }
86         }
87
88         @Override
89         public boolean isLocked(String resource) {
90                 Connection connection=openDbConnection();
91                 try {
92                         LockRecord lockRecord=loadLockRecord(connection,resource);
93                         if(lockRecord==null){
94                                 return false;
95                         }else{
96                                 if(lockRecord.getOwner()==null){
97                                         return false;
98                                 }else if(isLockExpired(lockRecord, connection)){
99                                         return false;
100                                 }else{
101                                         return true;
102                                 }
103                         }
104                 } catch (SQLException e) {
105                         throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
106                 }finally {
107                         closeDbConnection(connection);
108                 }
109         }
110
111         private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
112                 try {
113                         boolean res = false;
114                         LockRecord lockRecord = loadLockRecord(connection, resource);
115                         if(lockRecord != null) {
116                                 // lock record already exists
117                                 String currentOwner = lockRecord.getOwner();
118                                 if(currentOwner != null) {
119                                         if(isLockExpired(lockRecord, connection)) {
120                                                 currentOwner = null;
121                                         } else if(!owner.equals(currentOwner)) {
122                                                 throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
123                                         }
124                                 }
125                                 // set new owner on the resource lock record
126                                 updateLockRecord(connection, resource, owner, timeout);
127                                 if(currentOwner == null) {
128                                         // no one locked the resource before
129                                         res = true;
130                                 }
131                         } else {
132                                 // resource record does not exist in lock table => create new record
133                                 addLockRecord(connection, resource, owner, timeout);
134                                 res = true;
135                         }
136                         return res;
137                 } catch(SQLException e) {
138                         throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
139                 }
140         }
141
142         private void unlockResource(Connection connection, String resource, String owner) throws LockException {
143                 try {
144                         LockRecord lockRecord = loadLockRecord(connection, resource);
145                         if(lockRecord != null) {
146                                 // check if expired
147                                 if(isLockExpired(lockRecord, connection)) {
148                                         // lock is expired => no lock
149                                         lockRecord = null;
150                                 }
151                         }
152                         if((lockRecord == null) || (lockRecord.getOwner() == null)) {
153                                 // resource is not locked
154                                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
155                         }
156                         String currentOwner = lockRecord.getOwner();
157                         if(!owner.equals(currentOwner)) {
158                                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
159                         }
160                         updateLockRecord(connection, resource, null, 0);
161                         // TODO delete record from table on lock release?
162 //                      deleteLockRecord(connection, resource);
163                 } catch(SQLException e) {
164                         throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
165                 }
166         }
167
168         protected abstract void enterCriticalSection(Connection connection, String resource);
169
170         protected abstract void leaveCriticalSection(Connection connection, String resource);
171
172         protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
173                 LockRecord res = null;
174                 if(sqlLoadLockRecord == null) {
175                         sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
176                 }
177                 try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
178                         statement.setString(1, resource);
179                         try(ResultSet resultSet = statement.executeQuery()) {
180                                 if(resultSet.next()) {
181                                         res = new LockRecord(resource);
182                                         res.setOwner(resultSet.getString(2));
183                                         res.setUpdated(resultSet.getLong(3));
184                                         res.setTimeout(resultSet.getLong(4));
185                                 }
186                         }
187                 }
188                 return res;
189         }
190
191         protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
192                 if(sqlInsertLockRecord == null) {
193                         sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
194                 }
195                 try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
196                         statement.setString(1, resource);
197                         statement.setString(2, owner);
198                         statement.setLong(3, getCurrentTime(connection));
199                         statement.setLong(4, timeout);
200                         statement.executeUpdate();
201                 }
202         }
203
204         protected void updateLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
205                 if(sqlUpdateLockRecord == null) {
206                         sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
207                 }
208                 try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
209                         statement.setString(1, owner);
210                         statement.setLong(2, getCurrentTime(connection));
211                         statement.setLong(3, timeout);
212                         statement.setString(4, resource);
213                         statement.executeUpdate();
214                 }
215         }
216
217 //      protected void deleteLockRecord(Connection connection, String resource) throws SQLException {
218 //              if(sqlDeleteLockRecord == null) {
219 //                      sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
220 //              }
221 //              try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
222 //                      statement.setString(1, resource);
223 //                      statement.executeUpdate();
224 //              }
225 //      }
226
227         private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
228                 long timeout = lockRecord.getTimeout();
229                 if(timeout == 0) {
230                         return false;
231                 }
232                 long updated = lockRecord.getUpdated();
233                 long now = getCurrentTime(connection);
234                 long expiration = updated + timeout;
235                 return (now > expiration);
236         }
237
238         private long getCurrentTime(Connection connection) throws SQLException {
239                 long res = -1;
240                 if(connection != null) {
241                         try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
242                                 try(ResultSet resultSet = statement.executeQuery()) {
243                                         if(resultSet.next()) {
244                                                 res = resultSet.getTimestamp(1).getTime();
245                                         }
246                                 }
247                         }
248                 }
249                 if(res == -1) {
250                         res = System.currentTimeMillis();
251                 }
252                 return res;
253         }
254 }