2 * ============LICENSE_START=======================================================
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
15 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 * ============LICENSE_END=========================================================
26 package org.onap.appc.lockmanager.impl.sql.optimistic;
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.*;
50 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
53 protected JdbcLockManager createJdbcLockManager(boolean useReal) {
54 return new MySqlLockManagerMock(useReal);
58 public void testConcurrentLockDifferentOwners() throws LockException, InterruptedException, ExecutionException, TimeoutException {
60 final int participantsNo = 2;
61 Synchronizer synchronizer = new Synchronizer(participantsNo) {
63 private boolean wait = true;
66 public void preAddLockRecord(String resource, String owner) {
67 if(Owner.A.name().equals(owner)) {
77 public void postAddLockRecord(String resource, String owner) {
78 if(!Owner.A.name().equals(owner)) {
87 public void preUpdateLockRecord(String resource, String owner) {
88 preAddLockRecord(resource, owner);
92 public void postUpdateLockRecord(String resource, String owner) {
93 postAddLockRecord(resource, owner);
96 if(!setSynchronizer(synchronizer)) {
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>() {
103 public Boolean call() throws Exception {
105 lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
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());
115 // acquireLock by owner B should success
116 Future<Boolean> future2 = executor.submit(new Callable<Boolean>() {
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());
124 Assert.assertTrue(future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
125 Assert.assertTrue(future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
127 future2.cancel(true);
130 future1.cancel(true);
135 public void testConcurrentLockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
136 final int participantsNo = 2;
137 Synchronizer synchronizer = new Synchronizer(participantsNo) {
139 private boolean wait = true;
142 public void preAddLockRecord(String resource, String owner) {
152 public void postAddLockRecord(String resource, String owner) {
158 if(!setSynchronizer(synchronizer)) {
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>() {
165 public Boolean call() throws Exception {
166 return lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
169 Future<Boolean> future1 = executor.submit(callable);
171 Future<Boolean> future2 = executor.submit(callable);
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);
178 future2.cancel(true);
181 future1.cancel(true);
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) {
191 private boolean wait = true;
194 public void preUpdateLockRecord(String resource, String owner) {
196 // make sure second call updates the LockRecord first
205 public void postUpdateLockRecord(String resource, String owner) {
211 if(!setSynchronizer(synchronizer)) {
214 ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
215 Callable<Boolean> callable = new Callable<Boolean>() {
217 public Boolean call() throws Exception {
219 lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
220 // one of the unlock calls should success
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());
229 Future<Boolean> future1 = executor.submit(callable);
231 Future<Boolean> future2 = executor.submit(callable);
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);
238 future2.cancel(true);
241 future1.cancel(true);
246 public ExpectedException expectedEx = ExpectedException.none();
249 public void testAcquireLockNullOwner() throws LockException {
250 MySqlLockManager lockManager = new MySqlLockManager();
251 expectedEx.expect(LockRuntimeException.class);
252 lockManager.acquireLock(null, null, 0);
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(" ");
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(" ");
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(" "));
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(" "));
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(" "));
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));
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);
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);
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);
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();
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);
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));