Include impacted changes for APPC-346,APPC-348
[appc.git] / appc-dispatcher / appc-dispatcher-common / lock-manager-lib / lock-manager-impl / src / main / java / org / onap / 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.onap.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.onap.appc.lockmanager.api.LockException;
33 import org.onap.appc.lockmanager.api.LockRuntimeException;
34 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
35 import org.onap.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     @Override
112     public String getLockOwner(String resource) {
113         Connection connection=openDbConnection();
114         try {
115             org.onap.appc.lockmanager.impl.sql.pessimistic.LockRecord lockRecord=loadLockRecord(connection,resource);
116             if(lockRecord==null || lockRecord.getOwner() ==null ){
117                 return null;
118             }else{
119                 if(isLockExpired(lockRecord, connection)){
120                     return null;
121                 }else{
122                     return lockRecord.getOwner();
123                 }
124             }
125         } catch (SQLException e) {
126             throw new LockRuntimeException(Messages.EXP_CHECK_LOCK.format(resource));
127         }finally {
128             closeDbConnection(connection);
129         }
130     }
131
132     private boolean lockResource(Connection connection, String resource, String owner, long timeout) throws LockException {
133         try {
134             boolean res = false;
135             LockRecord lockRecord = loadLockRecord(connection, resource);
136             if(lockRecord != null) {
137                 // lock record already exists
138                 String currentOwner = lockRecord.getOwner();
139                 if(currentOwner != null) {
140                     if(isLockExpired(lockRecord, connection)) {
141                         currentOwner = null;
142                     } else if(!owner.equals(currentOwner)) {
143                         throw new LockException(Messages.ERR_LOCK_LOCKED_BY_OTHER.format(resource, currentOwner));
144                     }
145                 }
146                 // set new owner on the resource lock record
147                 updateLockRecord(connection, resource, owner, timeout);
148                 if(currentOwner == null) {
149                     // no one locked the resource before
150                     res = true;
151                 }
152             } else {
153                 // resource record does not exist in lock table => create new record
154                 addLockRecord(connection, resource, owner, timeout);
155                 res = true;
156             }
157             return res;
158         } catch(SQLException e) {
159             throw new LockRuntimeException(Messages.EXP_LOCK.format(resource), e);
160         }
161     }
162
163     private void unlockResource(Connection connection, String resource, String owner) throws LockException {
164         try {
165             LockRecord lockRecord = loadLockRecord(connection, resource);
166             if(lockRecord != null) {
167                 // check if expired
168                 if(isLockExpired(lockRecord, connection)) {
169                     // lock is expired => no lock
170                     lockRecord = null;
171                 }
172             }
173             if((lockRecord == null) || (lockRecord.getOwner() == null)) {
174                 // resource is not locked
175                 throw new LockException(Messages.ERR_UNLOCK_NOT_LOCKED.format(resource));
176             }
177             String currentOwner = lockRecord.getOwner();
178             if(!owner.equals(currentOwner)) {
179                 throw new LockException(Messages.ERR_UNLOCK_LOCKED_BY_OTHER.format(resource, owner, currentOwner));
180             }
181             updateLockRecord(connection, resource, null, 0);
182             // TODO delete record from table on lock release?
183 //            deleteLockRecord(connection, resource);
184         } catch(SQLException e) {
185             throw new LockRuntimeException(Messages.EXP_UNLOCK.format(resource), e);
186         }
187     }
188
189     protected abstract void enterCriticalSection(Connection connection, String resource);
190
191     protected abstract void leaveCriticalSection(Connection connection, String resource);
192
193     protected LockRecord loadLockRecord(Connection connection, String resource) throws SQLException {
194         LockRecord res = null;
195         if(sqlLoadLockRecord == null) {
196             sqlLoadLockRecord = String.format(SQL_LOAD_LOCK_RECORD, tableName);
197         }
198         try(PreparedStatement statement = connection.prepareStatement(sqlLoadLockRecord)) {
199             statement.setString(1, resource);
200             try(ResultSet resultSet = statement.executeQuery()) {
201                 if(resultSet.next()) {
202                     res = new LockRecord(resource);
203                     res.setOwner(resultSet.getString(2));
204                     res.setUpdated(resultSet.getLong(3));
205                     res.setTimeout(resultSet.getLong(4));
206                 }
207             }
208         }
209         return res;
210     }
211
212     protected void addLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
213         if(sqlInsertLockRecord == null) {
214             sqlInsertLockRecord = String.format(SQL_INSERT_LOCK_RECORD, tableName);
215         }
216         try(PreparedStatement statement = connection.prepareStatement(sqlInsertLockRecord)) {
217             statement.setString(1, resource);
218             statement.setString(2, owner);
219             statement.setLong(3, getCurrentTime(connection));
220             statement.setLong(4, timeout);
221             statement.executeUpdate();
222         }
223     }
224
225     protected void updateLockRecord(Connection connection, String resource, String owner, long timeout) throws SQLException {
226         if(sqlUpdateLockRecord == null) {
227             sqlUpdateLockRecord = String.format(SQL_UPDATE_LOCK_RECORD, tableName);
228         }
229         try(PreparedStatement statement = connection.prepareStatement(sqlUpdateLockRecord)) {
230             statement.setString(1, owner);
231             statement.setLong(2, getCurrentTime(connection));
232             statement.setLong(3, timeout);
233             statement.setString(4, resource);
234             statement.executeUpdate();
235         }
236     }
237
238 //    protected void deleteLockRecord(Connection connection, String resource) throws SQLException {
239 //        if(sqlDeleteLockRecord == null) {
240 //            sqlDeleteLockRecord = String.format(SQL_DELETE_LOCK_RECORD, tableName);
241 //        }
242 //        try(PreparedStatement statement = connection.prepareStatement(sqlDeleteLockRecord)) {
243 //            statement.setString(1, resource);
244 //            statement.executeUpdate();
245 //        }
246 //    }
247
248     private boolean isLockExpired(LockRecord lockRecord, Connection connection) throws SQLException {
249         long timeout = lockRecord.getTimeout();
250         if(timeout == 0) {
251             return false;
252         }
253         long updated = lockRecord.getUpdated();
254         long now = getCurrentTime(connection);
255         long expiration = updated + timeout;
256         return (now > expiration);
257     }
258
259     private long getCurrentTime(Connection connection) throws SQLException {
260         long res = -1;
261         if(connection != null) {
262             try(PreparedStatement statement = connection.prepareStatement(SQL_CURRENT_TIMESTAMP)) {
263                 try(ResultSet resultSet = statement.executeQuery()) {
264                     if(resultSet.next()) {
265                         res = resultSet.getTimestamp(1).getTime();
266                     }
267                 }
268             }
269         }
270         if(res == -1) {
271             res = System.currentTimeMillis();
272         }
273         return res;
274     }
275 }