/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.concurrency.locking; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.common.base.Function; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apereo.portal.IPortalInfoProvider; import org.apereo.portal.concurrency.locking.IClusterLockService.LockStatus; import org.apereo.portal.concurrency.locking.IClusterLockService.TryLockFunctionResult; import org.joda.time.Duration; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; //@Ignore @RunWith(MockitoJUnitRunner.class) public class ClusterLockServiceImplTest { @InjectMocks private final ClusterLockServiceImpl clusterLockService = new ClusterLockServiceImpl(); @Mock private IClusterLockDao clusterLockDao; @Mock private IPortalInfoProvider portalInfoProvider; private ExecutorService lockMonitorExecutorService; @Before public void setup() { lockMonitorExecutorService = Executors.newSingleThreadExecutor(); clusterLockService.setLockMonitorExecutorService(this.lockMonitorExecutorService); } @After public void teardown() { this.lockMonitorExecutorService.shutdownNow(); } @Test public void testWithinLastRunDelay() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock(serverName); clusterMutex.unlock(); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().lastRunDelay(1000), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.SKIPPED_LAST_RUN, result.getLockStatus()); assertNull(result.getResult()); assertFalse(result.isExecuted()); } @Test public void testOutsideLastRunDelay() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock(serverName); clusterMutex.unlock(); Thread.sleep(10); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().lastRunDelay(10), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.EXECUTED, result.getLockStatus()); assertTrue(result.getResult()); assertTrue(result.isExecuted()); } @Test public void testWithinServerBiasDelayDifferentServer() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock("server_2"); clusterMutex.unlock(); when(portalInfoProvider.getUniqueServerName()).thenReturn(serverName); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().serverBiasDelay(1000), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.SKIPPED_SERVER_BIAS, result.getLockStatus()); assertNull(result.getResult()); assertFalse(result.isExecuted()); } @Test public void testWithinServerBiasDelaySameServer() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock(serverName); clusterMutex.unlock(); when(portalInfoProvider.getUniqueServerName()).thenReturn(serverName); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().serverBiasDelay(1000), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.EXECUTED, result.getLockStatus()); assertTrue(result.getResult()); assertTrue(result.isExecuted()); } @Test public void testOutsideServerBiasDelayDifferentServer() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock("server_2"); clusterMutex.unlock(); Thread.sleep(10); when(portalInfoProvider.getUniqueServerName()).thenReturn(serverName); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().serverBiasDelay(10), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.EXECUTED, result.getLockStatus()); assertTrue(result.getResult()); assertTrue(result.isExecuted()); } @Test public void testOutsideServerBiasDelaySameServer() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock(serverName); clusterMutex.unlock(); Thread.sleep(10); when(portalInfoProvider.getUniqueServerName()).thenReturn(serverName); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().serverBiasDelay(10), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.EXECUTED, result.getLockStatus()); assertTrue(result.getResult()); assertTrue(result.isExecuted()); } @Test public void testLockThreadPoolDead() throws InterruptedException { final String mutexName = "TEST"; final String serverName = "server_1"; final ClusterMutex clusterMutex = new ClusterMutex(mutexName); clusterMutex.lock(serverName); clusterMutex.unlock(); when(portalInfoProvider.getUniqueServerName()).thenReturn(serverName); when(clusterLockDao.getClusterMutex(mutexName)).thenReturn(clusterMutex); when(clusterLockDao.getLock(mutexName)) .thenAnswer( new Answer<ClusterMutex>() { @Override public ClusterMutex answer(InvocationOnMock invocation) throws Throwable { clusterMutex.lock(serverName); return clusterMutex; } }); ExecutorService fakeLockMonitorExecutorService = mock(ExecutorService.class); final Future dbLockWorkerFuture = mock(Future.class); when(fakeLockMonitorExecutorService.submit(any(Callable.class))) .thenReturn(dbLockWorkerFuture); this.clusterLockService.setLockMonitorExecutorService(fakeLockMonitorExecutorService); this.clusterLockService.setDbLockTimeout(Duration.millis(500)); final TryLockFunctionResult<Boolean> result = this.clusterLockService.doInTryLock( mutexName, LockOptions.builder().serverBiasDelay(10), new Function<ClusterMutex, Boolean>() { @Override public Boolean apply(ClusterMutex input) { return Boolean.TRUE; } }); assertNotNull(result); assertEquals(LockStatus.SKIPPED_LOCKED, result.getLockStatus()); assertNull(result.getResult()); assertFalse(result.isExecuted()); } //test exec serv not actually execing }