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.pessimistic;
28 import static org.hamcrest.CoreMatchers.isA;
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertTrue;
32 import java.sql.CallableStatement;
33 import java.sql.Connection;
34 import java.sql.PreparedStatement;
35 import java.sql.ResultSet;
36 import java.sql.SQLException;
37 import java.sql.Timestamp;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Executors;
42 import java.util.concurrent.Future;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.TimeoutException;
45 import org.junit.Assert;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.rules.ExpectedException;
49 import org.mockito.Mockito;
50 import org.onap.appc.dao.util.api.JdbcConnectionFactory;
51 import org.onap.appc.lockmanager.api.LockException;
52 import org.onap.appc.lockmanager.api.LockRuntimeException;
53 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
54 import org.onap.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests;
55 import org.onap.appc.lockmanager.impl.sql.Synchronizer;
58 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
60 private static int CRITICAL_SECTION_WAIT_TIMEOUT = 1; // in secs
63 protected JdbcLockManager createJdbcLockManager(boolean useReal) {
64 return new MySqlLockManagerMock(useReal);
68 public void testConcurrentLock() throws LockException, InterruptedException, ExecutionException, TimeoutException {
70 callConcurrentTest(new Callable<Boolean>() {
72 public Boolean call() throws Exception {
74 Assert.assertTrue(lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name()));
76 } catch(LockRuntimeException e) {
77 Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage());
83 lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
88 public void testConcurrentUnlock() throws LockException, InterruptedException, ExecutionException, TimeoutException {
89 lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
90 callConcurrentTest(new Callable<Boolean>() {
92 public Boolean call() throws Exception {
94 lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
96 } catch(LockRuntimeException e) {
97 Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage());
104 private void callConcurrentTest(Callable<Boolean> callable) throws LockException, InterruptedException, ExecutionException, TimeoutException {
105 final int participantsNo = 2;
106 Synchronizer synchronizer = new Synchronizer(participantsNo) {
109 protected void waitForAllParticipants(Object waitObj, int totalParticipantsNo, int currentParticipantsNo) {
110 waitOn(this, TimeUnit.MILLISECONDS.convert(1 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS)); // add 1 sec to make sure timeout occured
113 if(!setSynchronizer(synchronizer)) {
116 ((MySqlLockManager)lockManager).setCriticalSectionWaitTimeoutSecs(CRITICAL_SECTION_WAIT_TIMEOUT);
117 ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
118 Future<Boolean> future1 = executor.submit(callable);
120 for(int i = 0; i < 10; i++) {
122 if(synchronizer.getParticipantCount() > 0) {
126 // make sure 1st thread gets inside critical section
127 if(synchronizer.getParticipantCount() < 1) {
128 Assert.fail(getClass().getName() + " first thread failed to acquireLock()");
130 Future<Boolean> future2 = executor.submit(callable);
132 // 1st thread should acquire the lock
133 Assert.assertTrue(future1.get(3 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS));
134 // 2nd thread should fail waiting for critical section
135 Assert.assertFalse(future2.get(2 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS));
137 future2.cancel(true);
140 future1.cancel(true);
141 setSynchronizer(null);
146 public ExpectedException expectedEx = ExpectedException.none();
149 public void testAcquireLockNullOwner() throws LockException {
150 MySqlLockManager lockManager = new MySqlLockManager();
151 expectedEx.expect(LockRuntimeException.class);
152 lockManager.acquireLock(null, null, 0);
156 public void testIsLocked() throws SQLException {
157 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
158 Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
159 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
160 Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
161 lockManager.setConnectionFactory(connectionFactory);
162 expectedEx.expect(LockRuntimeException.class);
163 lockManager.isLocked(" ");
167 public void testGetLockOwnerExceptionFlow() throws SQLException {
168 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
169 Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
170 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
171 Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
172 lockManager.setConnectionFactory(connectionFactory);
173 expectedEx.expect(LockRuntimeException.class);
174 lockManager.getLockOwner(" ");
178 public void testGetLockOwnerNull() throws SQLException {
179 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
180 Mockito.doReturn(null).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
181 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
182 Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
183 lockManager.setConnectionFactory(connectionFactory);
184 assertNull(lockManager.getLockOwner(" "));
188 public void testGetLockOwnerExpired() throws SQLException {
189 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
190 LockRecord lockRecord = Mockito.mock(LockRecord.class);
191 Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
192 Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()-100);
193 Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
194 Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
195 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
196 Connection connection = Mockito.mock(Connection.class);
197 PreparedStatement statement = Mockito.mock(PreparedStatement.class);
198 ResultSet resultSet = Mockito.mock(ResultSet.class);
199 Mockito.when(resultSet.next()).thenReturn(true);
200 Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
201 Mockito.when(statement.executeQuery()).thenReturn(resultSet);
202 Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
203 Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
204 lockManager.setConnectionFactory(connectionFactory);
205 assertNull(lockManager.getLockOwner(" "));
209 public void testGetLockOwnerNotExpired() throws SQLException {
210 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
211 LockRecord lockRecord = Mockito.mock(LockRecord.class);
212 Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
213 Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()+10000);
214 Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
215 Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
216 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
217 Connection connection = Mockito.mock(Connection.class);
218 PreparedStatement statement = Mockito.mock(PreparedStatement.class);
219 ResultSet resultSet = Mockito.mock(ResultSet.class);
220 Mockito.when(resultSet.next()).thenReturn(true);
221 Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
222 Mockito.when(statement.executeQuery()).thenReturn(resultSet);
223 Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
224 Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
225 lockManager.setConnectionFactory(connectionFactory);
226 assertEquals("OWNER", lockManager.getLockOwner(" "));
230 public void testLoadLockRecord() throws SQLException {
231 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
232 lockManager.setTableName("TABLE_NAME");
233 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
234 Connection connection = Mockito.mock(Connection.class);
235 PreparedStatement statement = Mockito.mock(PreparedStatement.class);
236 ResultSet resultSet = Mockito.mock(ResultSet.class);
237 Mockito.when(resultSet.next()).thenReturn(true);
238 Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
239 Mockito.when(resultSet.getLong(3)).thenReturn(0L);
240 Mockito.when(resultSet.getLong(4)).thenReturn(0L);
241 Mockito.when(resultSet.getLong(5)).thenReturn(0L);
242 Mockito.when(statement.executeQuery()).thenReturn(resultSet);
243 Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_LOAD_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
244 Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
245 lockManager.setConnectionFactory(connectionFactory);
246 assertTrue(lockManager.loadLockRecord(connection, "") instanceof LockRecord);
250 public void testAddLockRecord() throws SQLException {
251 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
252 lockManager.setTableName("TABLE_NAME");
253 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
254 Connection connection = Mockito.mock(Connection.class);
255 PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
256 ResultSet resultSet2 = Mockito.mock(ResultSet.class);
257 Mockito.when(resultSet2.next()).thenReturn(true);
258 Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
259 Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
260 Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
262 PreparedStatement statement = Mockito.mock(PreparedStatement.class);
263 ResultSet resultSet = Mockito.mock(ResultSet.class);
264 Mockito.when(resultSet.next()).thenReturn(true);
265 Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
266 Mockito.when(resultSet.getLong(3)).thenReturn(0L);
267 Mockito.when(resultSet.getLong(4)).thenReturn(0L);
268 Mockito.when(resultSet.getLong(5)).thenReturn(0L);
269 Mockito.when(statement.executeQuery()).thenReturn(resultSet);
270 Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_INSERT_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
271 Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
272 lockManager.setConnectionFactory(connectionFactory);
273 lockManager.addLockRecord(connection, "", "", 0L);
274 Mockito.verify(statement).executeUpdate();
278 public void testUpdateLockRecord() throws SQLException {
279 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
280 lockManager.setTableName("TABLE_NAME");
281 JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
282 Connection connection = Mockito.mock(Connection.class);
283 PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
284 ResultSet resultSet2 = Mockito.mock(ResultSet.class);
285 Mockito.when(resultSet2.next()).thenReturn(true);
286 Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(-1));
287 Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
288 Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
290 PreparedStatement statement = Mockito.mock(PreparedStatement.class);
291 ResultSet resultSet = Mockito.mock(ResultSet.class);
292 Mockito.when(resultSet.next()).thenReturn(true);
293 Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
294 Mockito.when(resultSet.getLong(3)).thenReturn(0L);
295 Mockito.when(resultSet.getLong(4)).thenReturn(0L);
296 Mockito.when(resultSet.getLong(5)).thenReturn(0L);
297 Mockito.when(statement.executeQuery()).thenReturn(resultSet);
298 Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_UPDATE_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
299 Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
300 lockManager.setConnectionFactory(connectionFactory);
301 lockManager.updateLockRecord(connection, "", "", 0L);
302 Mockito.verify(statement).executeUpdate();
306 public void testEnterCriticalSectionLockRuntimeException() throws SQLException {
307 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
308 Connection connection = Mockito.mock(Connection.class);
309 CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
310 Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenReturn(callableStatement);
311 expectedEx.expect(LockRuntimeException.class);
312 expectedEx.expectMessage("Cannot obtain critical section lock for resource [null].");
313 lockManager.enterCriticalSection(connection, null);
317 public void testEnterCriticalSectionLockRuntimeException2() throws SQLException {
318 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
319 Connection connection = Mockito.mock(Connection.class);
320 Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenThrow(new SQLException());
321 expectedEx.expect(LockRuntimeException.class);
322 expectedEx.expectMessage("Cannot obtain critical section lock for resource [null].");
323 expectedEx.expectCause(isA(SQLException.class));
324 lockManager.enterCriticalSection(connection, null);
328 public void testEnterCriticalSection() throws SQLException {
329 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
330 Connection connection = Mockito.mock(Connection.class);
331 CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
332 Mockito.when(callableStatement.execute()).thenReturn(true);
333 ResultSet resultSet = Mockito.mock(ResultSet.class);
334 Mockito.when(resultSet.getInt(1)).thenReturn(1);
335 Mockito.when(resultSet.next()).thenReturn(true);
336 Mockito.when(callableStatement.getResultSet()).thenReturn(resultSet);
337 Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenReturn(callableStatement);
338 lockManager.enterCriticalSection(connection, null);
339 Mockito.verify(callableStatement).close();
343 public void testLeaveCriticalSection() throws SQLException {
344 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
345 Connection connection = Mockito.mock(Connection.class);
346 CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
347 Mockito.when(connection.prepareCall("SELECT RELEASE_LOCK(?)")).thenReturn(callableStatement);
348 lockManager.leaveCriticalSection(connection, null);
349 Mockito.verify(callableStatement).close();
353 public void testLeaveCriticalSectionExceptionFlow() throws SQLException {
354 MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
355 Connection connection = Mockito.mock(Connection.class);
356 Mockito.when(connection.prepareCall("SELECT RELEASE_LOCK(?)")).thenThrow(new SQLException());
357 expectedEx.expect(LockRuntimeException.class);
358 lockManager.leaveCriticalSection(connection, null);
359 expectedEx.expectMessage("Error releasing critical section lock.");
360 expectedEx.expectCause(isA(SQLException.class));