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