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