f5eddb52dc9266b4c1d791901384f7cdbaf99919
[appc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22
23 package org.openecomp.appc.lockmanager.impl.sql.optimistic;
24
25 import org.junit.Assert;
26 import org.junit.Test;
27 import org.openecomp.appc.lockmanager.api.LockException;
28 import org.openecomp.appc.lockmanager.impl.sql.JdbcLockManager;
29 import org.openecomp.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests;
30 import org.openecomp.appc.lockmanager.impl.sql.Synchronizer;
31
32 import java.util.concurrent.*;
33
34 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
35
36         @Override
37         protected JdbcLockManager createJdbcLockManager(boolean useReal) {
38                 return new MySqlLockManagerMock(useReal);
39         }
40
41         @Test
42         public void testConcurrentLockDifferentOwners() throws LockException, InterruptedException, ExecutionException, TimeoutException {
43
44                 final int participantsNo = 2;
45                 Synchronizer synchronizer = new Synchronizer(participantsNo) {
46
47                         private boolean wait = true;
48
49                         @Override
50                         public void preAddLockRecord(String resource, String owner) {
51                                 if(Owner.A.name().equals(owner)) {
52                                         synchronized(this) {
53                                                 if(wait) {
54                                                         waitOn(this);
55                                                 }
56                                         }
57                                 }
58                         }
59
60                         @Override
61                         public void postAddLockRecord(String resource, String owner) {
62                                 if(!Owner.A.name().equals(owner)) {
63                                         synchronized(this) {
64                                                 notifyAll();
65                                                 wait = false;
66                                         }
67                                 }
68                         }
69
70                         @Override
71                         public void preUpdateLockRecord(String resource, String owner) {
72                                 preAddLockRecord(resource, owner);
73                         }
74
75                         @Override
76                         public void postUpdateLockRecord(String resource, String owner) {
77                                 postAddLockRecord(resource, owner);
78                         }
79                 };
80                 if(!setSynchronizer(synchronizer)) {
81                         return;
82                 }
83                 ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
84                 // acquireLock by owner A should fail as it will wait for acquireLock by owner B
85                 Future<Boolean> future1 = executor.submit(new Callable<Boolean>() {
86                         @Override
87                         public Boolean call() throws Exception {
88                                 try {
89                                         lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
90                                         return false;
91                                 } catch(LockException e) {
92                                         // this call should fail as Synchronizer delays its lock to make sure the second call locks the resource first
93                                         Assert.assertEquals("Cannot lock resource [" + Resource.Resource1.name() + "] for [" + Owner.A.name() + "]: already locked by [" + Owner.B.name() + "]", e.getMessage());
94                                         return true;
95                                 }
96                         }
97                 });
98                 try {
99                         // acquireLock by owner B should success
100                         Future<Boolean> future2 = executor.submit(new Callable<Boolean>() {
101                                 @Override
102                                 public Boolean call() throws Exception {
103                                         // this call should success as Synchronizer delays the above lock to make sure this call success to lock the resource
104                                         return lockManager.acquireLock(Resource.Resource1.name(), Owner.B.name());
105                                 }
106                         });
107                         try {
108                                 Assert.assertTrue(future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
109                                 Assert.assertTrue(future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS));
110                         } finally {
111                                 future2.cancel(true);
112                         }
113                 } finally {
114                         future1.cancel(true);
115                 }
116         }
117
118         @Test
119         public void testConcurrentLockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
120                 final int participantsNo = 2;
121                 Synchronizer synchronizer = new Synchronizer(participantsNo) {
122
123                         private boolean wait = true;
124
125                         @Override
126                         public void preAddLockRecord(String resource, String owner) {
127                                 synchronized(this) {
128                                         if(wait) {
129                                                 wait = false;
130                                                 waitOn(this);
131                                         }
132                                 }
133                         }
134
135                         @Override
136                         public void postAddLockRecord(String resource, String owner) {
137                                 synchronized(this) {
138                                         notifyAll();
139                                 }
140                         }
141                 };
142                 if(!setSynchronizer(synchronizer)) {
143                         return;
144                 }
145                 ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
146                 // one acquireLock should return true and the other should return false
147                 Callable<Boolean> callable = new Callable<Boolean>() {
148                         @Override
149                         public Boolean call() throws Exception {
150                                 return lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
151                         }
152                 };
153                 Future<Boolean> future1 = executor.submit(callable);
154                 try {
155                         Future<Boolean> future2 = executor.submit(callable);
156                         try {
157                                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
158                                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
159                                 // one of the lock requests should return true, the other one false as lock is requested simultaneously from 2 threads by same owner
160                                 Assert.assertNotEquals(future1Res, future2Res);
161                         } finally {
162                                 future2.cancel(true);
163                         }
164                 } finally {
165                         future1.cancel(true);
166                 }
167         }
168
169         @Test
170         public void testConcurrentUnlockSameOwner() throws LockException, InterruptedException, ExecutionException, TimeoutException {
171                 lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
172                 final int participantsNo = 2;
173                 Synchronizer synchronizer = new Synchronizer(participantsNo) {
174
175                         private boolean wait = true;
176
177                         @Override
178                         public void preUpdateLockRecord(String resource, String owner) {
179                                 synchronized(this) {
180                                         // make sure second call updates the LockRecord first
181                                         if(wait) {
182                                                 wait = false;
183                                                 waitOn(this);
184                                         }
185                                 }
186                         }
187
188                         @Override
189                         public void postUpdateLockRecord(String resource, String owner) {
190                                 synchronized(this) {
191                                         notifyAll();
192                                 }
193                         }
194                 };
195                 if(!setSynchronizer(synchronizer)) {
196                         return;
197                 }
198                 ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
199                 Callable<Boolean> callable = new Callable<Boolean>() {
200                         @Override
201                         public Boolean call() throws Exception {
202                                 try {
203                                         lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
204                                         // one of the unlock calls should success
205                                         return true;
206                                 } catch(LockException e) {
207                                         // one of the unlock calls should throw the LockException as the resource should already be unlocked by other call
208                                         Assert.assertEquals("Error unlocking resource [" + Resource.Resource1.name() + "]: resource is not locked", e.getMessage());
209                                         return false;
210                                 }
211                         }
212                 };
213                 Future<Boolean> future1 = executor.submit(callable);
214                 try {
215                         Future<Boolean> future2 = executor.submit(callable);
216                         try {
217                                 boolean future1Res = future1.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
218                                 boolean future2Res = future2.get(CONCURRENT_TEST_WAIT_TIME, TimeUnit.SECONDS);
219                                 // one of the unlock calls should return true, the other one false as unlock is requested simultaneously from 2 threads by same owner
220                                 Assert.assertNotEquals(future1Res, future2Res);
221                         } finally {
222                                 future2.cancel(true);
223                         }
224                 } finally {
225                         future1.cancel(true);
226                 }
227         }
228 }