/* * Copyright Terracotta, Inc. * * Licensed 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.ehcache.clustered.client.internal.service; import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.internal.InternalClusterTierManagerClientEntity; import org.ehcache.clustered.client.internal.MockConnectionService; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockClient; import org.ehcache.clustered.client.internal.store.InternalClusterTierClientEntity; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ClusterTierManagerConfiguration; import org.ehcache.clustered.common.internal.exceptions.DestroyInProgressException; import org.ehcache.clustered.common.internal.lock.LockMessaging; import org.ehcache.spi.service.MaintainableService; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.terracotta.connection.Connection; import org.terracotta.connection.entity.Entity; import org.terracotta.connection.entity.EntityRef; import org.terracotta.exception.EntityAlreadyExistsException; import java.net.URI; import java.util.HashSet; import java.util.Set; import static java.util.Collections.emptyMap; import static org.ehcache.clustered.common.EhcacheEntityVersion.ENTITY_VERSION; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * DefaultClusteringServiceDestroyTest */ public class DefaultClusteringServiceDestroyTest { @Mock private Connection connection; @Mock private EntityRef<InternalClusterTierManagerClientEntity, Object, Void> managerEntityRef; @Mock private EntityRef<InternalClusterTierClientEntity, Object, Void> tierEntityRef; @Mock private EntityRef<VoltronReadWriteLockClient, Object, Void> lockEntityRef; @Before public void setUp() { MockitoAnnotations.initMocks(this); MockConnectionService.mockConnection = connection; } @Test public void testDestroyAllFullyMocked() throws Exception { mockLockForWriteLockSuccess(); when(getEntityRef(InternalClusterTierManagerClientEntity.class)).thenReturn(managerEntityRef); InternalClusterTierManagerClientEntity managerEntity = mock(InternalClusterTierManagerClientEntity.class); when(managerEntityRef.fetchEntity(null)).thenReturn(managerEntity); Set<String> stores = new HashSet<>(); stores.add("store1"); stores.add("store2"); when(managerEntity.prepareForDestroy()).thenReturn(stores); when(getEntityRef(InternalClusterTierClientEntity.class)).thenReturn(tierEntityRef); when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI .create("mock://localhost/whatever"))); service.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); service.destroyAll(); verify(managerEntity).prepareForDestroy(); verify(tierEntityRef, times(2)).destroy(); verify(managerEntityRef).destroy(); } @Test public void testAutoCreateOnPartialDestroyState() throws Exception { ServerSideConfiguration serverConfig = new ServerSideConfiguration("default", emptyMap()); mockLockForWriteLockSuccess(); when(getEntityRef(InternalClusterTierManagerClientEntity.class)).thenReturn(managerEntityRef); InternalClusterTierManagerClientEntity managerEntity = mock(InternalClusterTierManagerClientEntity.class); // ClusterTierManager exists doThrow(new EntityAlreadyExistsException("className", "entityName")) // Next time simulate creation .doNothing() .when(managerEntityRef).create(new ClusterTierManagerConfiguration("whatever", serverConfig)); // And can be fetch when(managerEntityRef.fetchEntity(null)).thenReturn(managerEntity); // However validate indicates destroy in progress doThrow(new DestroyInProgressException("destroying")) // Next time validation succeeds .doNothing() .when(managerEntity).validate(serverConfig); Set<String> stores = new HashSet<>(); stores.add("store1"); stores.add("store2"); when(managerEntity.prepareForDestroy()).thenReturn(stores); when(getEntityRef(InternalClusterTierClientEntity.class)).thenReturn(tierEntityRef); when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI .create("mock://localhost/whatever"), true, serverConfig)); service.start(null); verify(managerEntity).prepareForDestroy(); verify(tierEntityRef, times(2)).destroy(); verify(managerEntityRef).destroy(); } @Test public void testFetchOnPartialDestroyState() throws Exception { mockLockForReadLockSuccess(); when(getEntityRef(InternalClusterTierManagerClientEntity.class)).thenReturn(managerEntityRef); InternalClusterTierManagerClientEntity managerEntity = mock(InternalClusterTierManagerClientEntity.class); // ClusterTierManager can be fetch when(managerEntityRef.fetchEntity(null)).thenReturn(managerEntity); // However validate indicates destroy in progress doThrow(new DestroyInProgressException("destroying")).when(managerEntity).validate(null); DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI .create("mock://localhost/whatever"))); try { service.start(null); fail("IllegalStateException expected"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString("does not exist")); } } @Test public void testDestroyOnPartialDestroyState() throws Exception { mockLockForWriteLockSuccess(); when(getEntityRef(InternalClusterTierManagerClientEntity.class)).thenReturn(managerEntityRef); InternalClusterTierManagerClientEntity managerEntity = mock(InternalClusterTierManagerClientEntity.class); when(managerEntityRef.fetchEntity(null)).thenReturn(managerEntity); doThrow(new DestroyInProgressException("destroying")).when(managerEntity).validate(any()); Set<String> stores = new HashSet<>(); stores.add("store1"); stores.add("store2"); when(managerEntity.prepareForDestroy()).thenReturn(stores); when(getEntityRef(InternalClusterTierClientEntity.class)).thenReturn(tierEntityRef); when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI .create("mock://localhost/whatever"))); service.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); service.destroyAll(); verify(managerEntity).prepareForDestroy(); verify(tierEntityRef, times(2)).destroy(); verify(managerEntityRef).destroy(); } private void mockLockForWriteLockSuccess() throws org.terracotta.exception.EntityNotProvidedException, org.terracotta.exception.EntityNotFoundException, org.terracotta.exception.EntityVersionMismatchException { when(connection.<VoltronReadWriteLockClient, Object, Void>getEntityRef(same(VoltronReadWriteLockClient.class), eq(1L), any())).thenReturn(lockEntityRef); VoltronReadWriteLockClient lockClient = mock(VoltronReadWriteLockClient.class); when(lockEntityRef.fetchEntity(null)).thenReturn(lockClient); when(lockClient.tryLock(LockMessaging.HoldType.WRITE)).thenReturn(true); } private void mockLockForReadLockSuccess() throws org.terracotta.exception.EntityNotProvidedException, org.terracotta.exception.EntityNotFoundException, org.terracotta.exception.EntityVersionMismatchException { when(connection.<VoltronReadWriteLockClient, Object, Void>getEntityRef(same(VoltronReadWriteLockClient.class), eq(1L), any())).thenReturn(lockEntityRef); VoltronReadWriteLockClient lockClient = mock(VoltronReadWriteLockClient.class); when(lockEntityRef.fetchEntity(null)).thenReturn(lockClient); when(lockClient.tryLock(LockMessaging.HoldType.READ)).thenReturn(true); } private <E extends Entity> EntityRef<E, Object, Void> getEntityRef(Class<E> value) throws org.terracotta.exception.EntityNotProvidedException { return connection.getEntityRef(same(value), eq(ENTITY_VERSION), any()); } }