Include impacted changes for APPC-346,APPC-348
[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 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.optimistic;
26
27 import org.junit.Assert;
28 import org.junit.Test;
29 import org.onap.appc.lockmanager.api.LockException;
30 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
31 import org.onap.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests;
32 import org.onap.appc.lockmanager.impl.sql.Synchronizer;
33
34 import java.util.concurrent.*;
35
36 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
37
38     @Override
39     protected JdbcLockManager createJdbcLockManager(boolean useReal) {
40         return new MySqlLockManagerMock(useReal);
41     }
42
43     @Test
44     public void testConcurrentLockDifferentOwners() throws LockException, InterruptedException, ExecutionException, TimeoutException {
45
46         final int participantsNo = 2;
47         Synchronizer synchronizer = new Synchronizer(participantsNo) {
48
49             private boolean wait = true;
50
51             @Override
52             public void preAddLockRecord(String resource, String owner) {
53                 if(Owner.A.name().equals(owner)) {
54                     synchronized(this) {
55                         if(wait) {
56                             waitOn(this);
57                         }
58                     }
59                 }
60             }
61
62             @Override
63             public void postAddLockRecord(String resource, String owner) {
64                 if(!Owner.A.name().equals(owner)) {
65                     synchronized(this) {
66                         notifyAll();
67                         wait = false;
68                     }
69                 }
70             }
71
72             @Override
73             public void preUpdateLockRecord(String resource, String owner) {
74                 preAddLockRecord(resource, owner);
75             }
76
77             @Override
78             public void postUpdateLockRecord(String resource, String owner) {
79                 postAddLockRecord(resource, owner);
80             }
81         };
82         if(!setSynchronizer(synchronizer)) {
83             return;
84         }
85         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
86         // acquireLock by owner A should fail as it will wait for acquireLock by owner B
87         Future<Boolean> future1 = executor.submit(new Callable<Boolean>() {
88             @Override
89             public Boolean call() throws Exception {
90                 try {
91                     lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
92                     return false;
93                 } catch(LockException e) {
94                     // this call should fail as Synchronizer delays its lock to make sure the second call locks the resource first
95                     Assert.assertEquals("VNF : [" + Resource.Resource1.name() + "] is locked by request id : [" + Owner.B.name() + "]", e.getMessage());
96                     return true;
97                 }
98             }
99         });
100         try {
101             // acquireLock by owner B should success
102             Future<Boolean> future2 = executor.submit(new Callable<Boolean>() {
103                 @Override
104                 public Boolean call() throws Exception {
105                     // this call should success as Synchronizer delays the above lock to make sure this call success to lock the resource
106                     return lockManager.acquireLock(Resource.Resource1.name(), Owner.B.name());
107                 }
108             });
109             try {
110                 Assert.assertTrue(future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
111                 Assert.assertTrue(future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
112             } finally {
113                 future2.cancel(true);
114             }
115         } finally {
116             future1.cancel(true);
117         }
118     }
119
120     @Test
121     public void testConcurrentLockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
122         final int participantsNo = 2;
123         Synchronizer synchronizer = new Synchronizer(participantsNo) {
124
125             private boolean wait = true;
126
127             @Override
128             public void preAddLockRecord(String resource, String owner) {
129                 synchronized(this) {
130                     if(wait) {
131                         wait = false;
132                         waitOn(this);
133                     }
134                 }
135             }
136
137             @Override
138             public void postAddLockRecord(String resource, String owner) {
139                 synchronized(this) {
140                     notifyAll();
141                 }
142             }
143         };
144         if(!setSynchronizer(synchronizer)) {
145             return;
146         }
147         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
148         // one acquireLock should return true and the other should return false
149         Callable<Boolean> callable = new Callable<Boolean>() {
150             @Override
151             public Boolean call() throws Exception {
152                 return lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
153             }
154         };
155         Future<Boolean> future1 = executor.submit(callable);
156         try {
157             Future<Boolean> future2 = executor.submit(callable);
158             try {
159                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
160                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
161                 // one of the lock requests should return true, the other one false as lock is requested simultaneously from 2 threads by same owner
162                 Assert.assertNotEquals(future1Res, future2Res);
163             } finally {
164                 future2.cancel(true);
165             }
166         } finally {
167             future1.cancel(true);
168         }
169     }
170
171     @Test
172     public void testConcurrentUnlockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
173         lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
174         final int participantsNo = 2;
175         Synchronizer synchronizer = new Synchronizer(participantsNo) {
176
177             private boolean wait = true;
178
179             @Override
180             public void preUpdateLockRecord(String resource, String owner) {
181                 synchronized(this) {
182                     // make sure second call updates the LockRecord first
183                     if(wait) {
184                         wait = false;
185                         waitOn(this);
186                     }
187                 }
188             }
189
190             @Override
191             public void postUpdateLockRecord(String resource, String owner) {
192                 synchronized(this) {
193                     notifyAll();
194                 }
195             }
196         };
197         if(!setSynchronizer(synchronizer)) {
198             return;
199         }
200         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
201         Callable<Boolean> callable = new Callable<Boolean>() {
202             @Override
203             public Boolean call() throws Exception {
204                 try {
205                     lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
206                     // one of the unlock calls should success
207                     return true;
208                 } catch(LockException e) {
209                     // one of the unlock calls should throw the LockException as the resource should already be unlocked by other call
210                     Assert.assertEquals("Error unlocking resource [" + Resource.Resource1.name() + "]: resource is not locked", e.getMessage());
211                     return false;
212                 }
213             }
214         };
215         Future<Boolean> future1 = executor.submit(callable);
216         try {
217             Future<Boolean> future2 = executor.submit(callable);
218             try {
219                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
220                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
221                 // one of the unlock calls should return true, the other one false as unlock is requested simultaneously from 2 threads by same owner
222                 Assert.assertNotEquals(future1Res, future2Res);
223             } finally {
224                 future2.cancel(true);
225             }
226         } finally {
227             future1.cancel(true);
228         }
229     }
230 }