Coverage for lockmanager.sql.optimistic package
[appc.git] / appc-dispatcher / appc-dispatcher-common / lock-manager-lib / lock-manager-impl / src / test / java / org / onap / appc / lockmanager / impl / sql / optimistic / TestMySqlLockManager.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  * Modifications Copyright (C) 2019 Ericsson
10  * =============================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  * 
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  * 
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * 
23  * ============LICENSE_END=========================================================
24  */
25
26 package org.onap.appc.lockmanager.impl.sql.optimistic;
27
28 import org.onap.appc.dao.util.api.JdbcConnectionFactory;
29 import org.junit.Assert;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.rules.ExpectedException;
33 import org.mockito.Mockito;
34 import org.onap.appc.lockmanager.api.LockException;
35 import org.onap.appc.lockmanager.api.LockRuntimeException;
36 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
37 import org.onap.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests;
38 import org.onap.appc.lockmanager.impl.sql.Synchronizer;
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertFalse;
41 import static org.junit.Assert.assertNull;
42 import static org.junit.Assert.assertTrue;
43 import java.sql.Timestamp;
44 import java.sql.Connection;
45 import java.sql.PreparedStatement;
46 import java.sql.ResultSet;
47 import java.sql.SQLException;
48 import java.util.concurrent.*;
49
50 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
51
52     @Override
53     protected JdbcLockManager createJdbcLockManager(boolean useReal) {
54         return new MySqlLockManagerMock(useReal);
55     }
56
57     @Test
58     public void testConcurrentLockDifferentOwners() throws LockException, InterruptedException, ExecutionException, TimeoutException {
59
60         final int participantsNo = 2;
61         Synchronizer synchronizer = new Synchronizer(participantsNo) {
62
63             private boolean wait = true;
64
65             @Override
66             public void preAddLockRecord(String resource, String owner) {
67                 if(Owner.A.name().equals(owner)) {
68                     synchronized(this) {
69                         if(wait) {
70                             waitOn(this);
71                         }
72                     }
73                 }
74             }
75
76             @Override
77             public void postAddLockRecord(String resource, String owner) {
78                 if(!Owner.A.name().equals(owner)) {
79                     synchronized(this) {
80                         notifyAll();
81                         wait = false;
82                     }
83                 }
84             }
85
86             @Override
87             public void preUpdateLockRecord(String resource, String owner) {
88                 preAddLockRecord(resource, owner);
89             }
90
91             @Override
92             public void postUpdateLockRecord(String resource, String owner) {
93                 postAddLockRecord(resource, owner);
94             }
95         };
96         if(!setSynchronizer(synchronizer)) {
97             return;
98         }
99         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
100         // acquireLock by owner A should fail as it will wait for acquireLock by owner B
101         Future<Boolean> future1 = executor.submit(new Callable<Boolean>() {
102             @Override
103             public Boolean call() throws Exception {
104                 try {
105                     lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
106                     return false;
107                 } catch(LockException e) {
108                     // this call should fail as Synchronizer delays its lock to make sure the second call locks the resource first
109                     Assert.assertEquals("VNF : [" + Resource.Resource1.name() + "] is locked by request id : [" + Owner.B.name() + "]", e.getMessage());
110                     return true;
111                 }
112             }
113         });
114         try {
115             // acquireLock by owner B should success
116             Future<Boolean> future2 = executor.submit(new Callable<Boolean>() {
117                 @Override
118                 public Boolean call() throws Exception {
119                     // this call should success as Synchronizer delays the above lock to make sure this call success to lock the resource
120                     return lockManager.acquireLock(Resource.Resource1.name(), Owner.B.name());
121                 }
122             });
123             try {
124                 Assert.assertTrue(future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
125                 Assert.assertTrue(future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
126             } finally {
127                 future2.cancel(true);
128             }
129         } finally {
130             future1.cancel(true);
131         }
132     }
133
134     @Test
135     public void testConcurrentLockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
136         final int participantsNo = 2;
137         Synchronizer synchronizer = new Synchronizer(participantsNo) {
138
139             private boolean wait = true;
140
141             @Override
142             public void preAddLockRecord(String resource, String owner) {
143                 synchronized(this) {
144                     if(wait) {
145                         wait = false;
146                         waitOn(this);
147                     }
148                 }
149             }
150
151             @Override
152             public void postAddLockRecord(String resource, String owner) {
153                 synchronized(this) {
154                     notifyAll();
155                 }
156             }
157         };
158         if(!setSynchronizer(synchronizer)) {
159             return;
160         }
161         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
162         // one acquireLock should return true and the other should return false
163         Callable<Boolean> callable = new Callable<Boolean>() {
164             @Override
165             public Boolean call() throws Exception {
166                 return lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
167             }
168         };
169         Future<Boolean> future1 = executor.submit(callable);
170         try {
171             Future<Boolean> future2 = executor.submit(callable);
172             try {
173                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
174                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
175                 // one of the lock requests should return true, the other one false as lock is requested simultaneously from 2 threads by same owner
176                 Assert.assertNotEquals(future1Res, future2Res);
177             } finally {
178                 future2.cancel(true);
179             }
180         } finally {
181             future1.cancel(true);
182         }
183     }
184
185     @Test
186     public void testConcurrentUnlockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
187         lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
188         final int participantsNo = 2;
189         Synchronizer synchronizer = new Synchronizer(participantsNo) {
190
191             private boolean wait = true;
192
193             @Override
194             public void preUpdateLockRecord(String resource, String owner) {
195                 synchronized(this) {
196                     // make sure second call updates the LockRecord first
197                     if(wait) {
198                         wait = false;
199                         waitOn(this);
200                     }
201                 }
202             }
203
204             @Override
205             public void postUpdateLockRecord(String resource, String owner) {
206                 synchronized(this) {
207                     notifyAll();
208                 }
209             }
210         };
211         if(!setSynchronizer(synchronizer)) {
212             return;
213         }
214         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
215         Callable<Boolean> callable = new Callable<Boolean>() {
216             @Override
217             public Boolean call() throws Exception {
218                 try {
219                     lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
220                     // one of the unlock calls should success
221                     return true;
222                 } catch(LockException e) {
223                     // one of the unlock calls should throw the LockException as the resource should already be unlocked by other call
224                     Assert.assertEquals("Error unlocking resource [" + Resource.Resource1.name() + "]: resource is not locked", e.getMessage());
225                     return false;
226                 }
227             }
228         };
229         Future<Boolean> future1 = executor.submit(callable);
230         try {
231             Future<Boolean> future2 = executor.submit(callable);
232             try {
233                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
234                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
235                 // one of the unlock calls should return true, the other one false as unlock is requested simultaneously from 2 threads by same owner
236                 Assert.assertNotEquals(future1Res, future2Res);
237             } finally {
238                 future2.cancel(true);
239             }
240         } finally {
241             future1.cancel(true);
242         }
243     }
244
245     @Rule
246     public ExpectedException expectedEx = ExpectedException.none();
247
248     @Test
249     public void testAcquireLockNullOwner() throws LockException {
250         MySqlLockManager lockManager = new MySqlLockManager();
251         expectedEx.expect(LockRuntimeException.class);
252         lockManager.acquireLock(null, null, 0);
253     }
254
255     @Test
256     public void testIsLocked() throws SQLException {
257         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
258         Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
259         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
260         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
261         lockManager.setConnectionFactory(connectionFactory);
262         expectedEx.expect(LockRuntimeException.class);
263         lockManager.isLocked(" ");
264     }
265
266     @Test
267     public void testGetLockOwnerExceptionFlow() throws SQLException {
268         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
269         Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
270         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
271         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
272         lockManager.setConnectionFactory(connectionFactory);
273         expectedEx.expect(LockRuntimeException.class);
274         lockManager.getLockOwner(" ");
275     }
276
277     @Test
278     public void testGetLockOwnerNull() throws SQLException {
279         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
280         Mockito.doReturn(null).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
281         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
282         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
283         lockManager.setConnectionFactory(connectionFactory);
284         assertNull(lockManager.getLockOwner(" "));
285     }
286
287     @Test
288     public void testGetLockOwnerExpired() throws SQLException {
289         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
290         LockRecord lockRecord = Mockito.mock(LockRecord.class);
291         Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
292         Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()-100);
293         Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
294         Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
295         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
296         Connection connection = Mockito.mock(Connection.class);
297         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
298         ResultSet resultSet = Mockito.mock(ResultSet.class);
299         Mockito.when(resultSet.next()).thenReturn(true);
300         Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
301         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
302         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
303         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
304         lockManager.setConnectionFactory(connectionFactory);
305         assertNull(lockManager.getLockOwner(" "));
306     }
307
308     @Test
309     public void testGetLockOwnerNotExpired() throws SQLException {
310         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
311         LockRecord lockRecord = Mockito.mock(LockRecord.class);
312         Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
313         Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()+10000);
314         Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
315         Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
316         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
317         Connection connection = Mockito.mock(Connection.class);
318         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
319         ResultSet resultSet = Mockito.mock(ResultSet.class);
320         Mockito.when(resultSet.next()).thenReturn(true);
321         Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
322         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
323         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
324         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
325         lockManager.setConnectionFactory(connectionFactory);
326         assertEquals("OWNER", lockManager.getLockOwner(" "));
327     }
328
329     @Test
330     public void testIsDuplicatePkError() throws SQLException {
331         SqlLockManager lockManager = new SqlLockManager() {};
332         SQLException sqlException = Mockito.mock(SQLException.class);
333         Mockito.when(sqlException.getSQLState()).thenReturn("23xxx");
334         assertTrue(lockManager.isDuplicatePkError(sqlException));
335     }
336
337     @Test
338     public void testLoadLockRecord() throws SQLException {
339         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
340         lockManager.setTableName("TABLE_NAME");
341         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
342         Connection connection = Mockito.mock(Connection.class);
343         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
344         ResultSet resultSet = Mockito.mock(ResultSet.class);
345         Mockito.when(resultSet.next()).thenReturn(true);
346         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
347         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
348         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
349         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
350         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
351         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_LOAD_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
352         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
353         lockManager.setConnectionFactory(connectionFactory);
354         assertTrue(lockManager.loadLockRecord(connection, "") instanceof LockRecord);
355     }
356
357     @Test
358     public void testLoadLockRecord3arg() throws SQLException {
359         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
360         lockManager.setTableName("TABLE_NAME");
361         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
362         Connection connection = Mockito.mock(Connection.class);
363         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
364         ResultSet resultSet = Mockito.mock(ResultSet.class);
365         Mockito.when(resultSet.next()).thenReturn(true);
366         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
367         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
368         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
369         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
370         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
371         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_LOAD_LOCK_RECORD_WITH_OWNER)).thenReturn(statement);
372         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
373         lockManager.setConnectionFactory(connectionFactory);
374         assertTrue(lockManager.loadLockRecord(connection, "", "") instanceof LockRecord);
375     }
376
377     @Test
378     public void testAddLockRecord() throws SQLException {
379         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
380         lockManager.setTableName("TABLE_NAME");
381         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
382         Connection connection = Mockito.mock(Connection.class);
383         PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
384         ResultSet resultSet2 = Mockito.mock(ResultSet.class);
385         Mockito.when(resultSet2.next()).thenReturn(true);
386         Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
387         Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
388         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
389
390         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
391         ResultSet resultSet = Mockito.mock(ResultSet.class);
392         Mockito.when(resultSet.next()).thenReturn(true);
393         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
394         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
395         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
396         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
397         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
398         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_INSERT_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
399         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
400         lockManager.setConnectionFactory(connectionFactory);
401         lockManager.addLockRecord(connection, "", "", 0L);
402         Mockito.verify(statement).executeUpdate();
403     }
404
405     @Test
406     public void testUpdateLockRecord() throws SQLException {
407         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
408         lockManager.setTableName("TABLE_NAME");
409         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
410         Connection connection = Mockito.mock(Connection.class);
411         PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
412         ResultSet resultSet2 = Mockito.mock(ResultSet.class);
413         Mockito.when(resultSet2.next()).thenReturn(true);
414         Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
415         Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
416         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
417
418         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
419         ResultSet resultSet = Mockito.mock(ResultSet.class);
420         Mockito.when(resultSet.next()).thenReturn(true);
421         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
422         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
423         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
424         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
425         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
426         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_UPDATE_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
427         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
428         lockManager.setConnectionFactory(connectionFactory);
429         assertFalse(lockManager.updateLockRecord(connection, "", "", 0L, 0L));
430     }
431 }