blob: 2e34681041d0ce694e14cbdf96d940564b5922fd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF 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
*
* 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.apache.geode.modules.session.catalina;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpSession;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.apache.geode.Statistics;
import org.apache.geode.cache.AttributesMutator;
import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.InterestResultPolicy;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.internal.InternalClientCache;
import org.apache.geode.cache.client.internal.PoolImpl;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.modules.session.catalina.callback.SessionExpirationCacheListener;
import org.apache.geode.modules.util.BootstrappingFunction;
import org.apache.geode.modules.util.CreateRegionFunction;
import org.apache.geode.modules.util.DebugCacheListener;
import org.apache.geode.modules.util.RegionConfiguration;
import org.apache.geode.modules.util.RegionStatus;
import org.apache.geode.modules.util.SessionCustomExpiry;
import org.apache.geode.modules.util.TouchPartitionedRegionEntriesFunction;
import org.apache.geode.modules.util.TouchReplicatedRegionEntriesFunction;
public class ClientServerSessionCacheJUnitTest extends AbstractSessionCacheJUnitTest {
private List<RegionStatus> regionStatusResultList = new ArrayList<>();
private ClientCache cache = mock(GemFireCacheImpl.class);
private ResultCollector collector = mock(ResultCollector.class);
private Statistics stats = mock(Statistics.class);
@SuppressWarnings("unchecked")
private ClientRegionFactory<String, HttpSession> regionFactory = mock(ClientRegionFactory.class);
@SuppressWarnings("unchecked")
private RegionAttributes<String, HttpSession> attributes = mock(RegionAttributes.class);
@Before
public void setUp() {
sessionCache = spy(new ClientServerSessionCache(sessionManager, cache));
doReturn(emptyExecution).when((ClientServerSessionCache) sessionCache)
.getExecutionForFunctionOnServers();
doReturn(emptyExecution).when((ClientServerSessionCache) sessionCache)
.getExecutionForFunctionOnServersWithArguments(any());
doReturn(emptyExecution).when((ClientServerSessionCache) sessionCache)
.getExecutionForFunctionOnServerWithRegionConfiguration(any());
doReturn(emptyExecution).when((ClientServerSessionCache) sessionCache)
.getExecutionForFunctionOnRegionWithFilter(any());
when(sessionManager.getLogger()).thenReturn(logger);
when(sessionManager.getEnableLocalCache()).thenReturn(true);
when(sessionManager.getRegionName()).thenReturn(sessionRegionName);
when(sessionManager.getMaxInactiveInterval())
.thenReturn(RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL);
when(cache.getDistributedSystem()).thenReturn(distributedSystem);
doReturn(regionFactory).when(cache)
.createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU);
when(((InternalClientCache) cache).isClient()).thenReturn(true);
when(emptyExecution.execute(any(Function.class))).thenReturn(collector);
when(emptyExecution.execute(any(String.class))).thenReturn(collector);
when(collector.getResult()).thenReturn(regionStatusResultList);
when(distributedSystem.createAtomicStatistics(any(), any())).thenReturn(stats);
regionStatusResultList.clear();
regionStatusResultList.add(RegionStatus.VALID);
}
@Test
public void initializeSessionCacheSucceeds() {
sessionCache.initialize();
verify(emptyExecution).execute(any(BootstrappingFunction.class));
verify(emptyExecution).execute(CreateRegionFunction.ID);
verify(cache).createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU);
verify(regionFactory, times(0)).setStatisticsEnabled(true);
verify(regionFactory, times(0)).setCustomEntryIdleTimeout(any(SessionCustomExpiry.class));
verify(regionFactory, times(0)).addCacheListener(any(SessionExpirationCacheListener.class));
verify(regionFactory).create(sessionRegionName);
}
@Test
public void bootstrappingFunctionThrowsException() {
FunctionException exception = new FunctionException();
ResultCollector exceptionCollector = mock(ResultCollector.class);
when(emptyExecution.execute(new BootstrappingFunction())).thenReturn(exceptionCollector);
when(exceptionCollector.getResult()).thenThrow(exception);
sessionCache.initialize();
verify(logger).warn("Caught unexpected exception:", exception);
}
@Test
public void createOrRetrieveRegionThrowsException() {
RuntimeException exception = new RuntimeException();
doThrow(exception).when((ClientServerSessionCache) sessionCache).createLocalSessionRegion();
assertThatThrownBy(() -> sessionCache.initialize()).hasCause(exception)
.isInstanceOf(IllegalStateException.class);
verify(logger).fatal("Unable to create or retrieve region", exception);
}
@Test
public void createRegionFunctionFailsOnServer() {
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
regionStatusResultList.clear();
regionStatusResultList.add(RegionStatus.INVALID);
assertThatThrownBy(() -> sessionCache.initialize()).isInstanceOf(IllegalStateException.class)
.hasCauseInstanceOf(IllegalStateException.class).hasMessageContaining(
"An exception occurred on the server while attempting to create or validate region named "
+ sessionRegionName
+ ". See the server log for additional details.");
verify(logger).fatal(stringCaptor.capture(), any(Exception.class));
assertThat(stringCaptor.getValue()).isEqualTo("Unable to create or retrieve region");
}
@Test
public void nonDefaultMaxTimeoutIntervalSetsExpirationDetails() {
// Setting the mocked return value of getMaxInactiveInterval to something distinctly not equal
// to the default
when(sessionManager.getMaxInactiveInterval())
.thenReturn(RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL + 1);
sessionCache.initialize();
verify(regionFactory).setStatisticsEnabled(true);
verify(regionFactory).setCustomEntryIdleTimeout(any(SessionCustomExpiry.class));
verify(regionFactory).addCacheListener(any(SessionExpirationCacheListener.class));
}
@Test
public void createLocalSessionRegionWithoutEnableLocalCache() {
when(sessionManager.getEnableLocalCache()).thenReturn(false);
doReturn(regionFactory).when(cache).createClientRegionFactory(ClientRegionShortcut.PROXY);
when(regionFactory.create(sessionRegionName)).thenReturn(sessionRegion);
sessionCache.initialize();
verify(regionFactory).addCacheListener(any(SessionExpirationCacheListener.class));
verify(sessionRegion).registerInterest("ALL_KEYS", InterestResultPolicy.KEYS);
}
@Test
public void createOrRetrieveRegionWithNonNullSessionRegionDoesNotCreateRegion() {
@SuppressWarnings("unchecked")
CacheListener<String, HttpSession>[] cacheListeners =
new CacheListener[] {new SessionExpirationCacheListener()};
doReturn(sessionRegion).when(cache).getRegion(sessionRegionName);
doReturn(attributes).when(sessionRegion).getAttributes();
doReturn(cacheListeners).when(attributes).getCacheListeners();
sessionCache.initialize();
verify((ClientServerSessionCache) sessionCache, times(0)).createSessionRegionOnServers();
verify((ClientServerSessionCache) sessionCache, times(0)).createLocalSessionRegion();
}
@Test
public void createOrRetrieveRegionWithNonNullSessionRegionAndNoSessionExpirationCacheListenerCreatesListener() {
@SuppressWarnings("unchecked")
CacheListener<String, HttpSession>[] cacheListeners =
new CacheListener[] {new DebugCacheListener()};
@SuppressWarnings("unchecked")
AttributesMutator<String, HttpSession> attributesMutator = mock(AttributesMutator.class);
doReturn(sessionRegion).when(cache).getRegion(sessionRegionName);
doReturn(attributes).when(sessionRegion).getAttributes();
doReturn(cacheListeners).when(attributes).getCacheListeners();
doReturn(attributesMutator).when(sessionRegion).getAttributesMutator();
sessionCache.initialize();
verify(attributesMutator).addCacheListener(any(SessionExpirationCacheListener.class));
}
@Test
public void createOrRetrieveRegionWithNonNullSessionProxyRegionRegistersInterestForAllKeys() {
@SuppressWarnings("unchecked")
CacheListener<String, HttpSession>[] cacheListeners =
new CacheListener[] {new SessionExpirationCacheListener()};
doReturn(sessionRegion).when(cache).getRegion(sessionRegionName);
doReturn(attributes).when(sessionRegion).getAttributes();
doReturn(cacheListeners).when(attributes).getCacheListeners();
when(attributes.getDataPolicy()).thenReturn(DataPolicy.EMPTY);
sessionCache.initialize();
verify(sessionRegion).registerInterest("ALL_KEYS", InterestResultPolicy.KEYS);
}
@Test
public void touchSessionsInvokesPRFunctionForPRAndDoesNotThrowExceptionWhenFunctionDoesNotThrowException() {
Set<String> sessionIds = new HashSet<>();
when(sessionManager.getRegionAttributesId()).thenReturn(RegionShortcut.PARTITION.toString());
sessionCache.touchSessions(sessionIds);
verify(emptyExecution).execute(TouchPartitionedRegionEntriesFunction.ID);
}
@Test
public void touchSessionsInvokesPRFunctionForPRAndThrowsExceptionWhenFunctionThrowsException() {
Set<String> sessionIds = new HashSet<>();
FunctionException exception = new FunctionException();
ResultCollector exceptionCollector = mock(ResultCollector.class);
when(sessionManager.getRegionAttributesId()).thenReturn(RegionShortcut.PARTITION.toString());
when(emptyExecution.execute(TouchPartitionedRegionEntriesFunction.ID))
.thenReturn(exceptionCollector);
when(exceptionCollector.getResult()).thenThrow(exception);
sessionCache.touchSessions(sessionIds);
verify(logger).warn("Caught unexpected exception:", exception);
}
@Test
public void touchSessionsInvokesRRFunctionForRRAndDoesNotThrowExceptionWhenFunctionDoesNotThrowException() {
// Need to invoke this to set the session region
when(regionFactory.create(sessionRegionName)).thenReturn(sessionRegion);
sessionCache.initialize();
Set<String> sessionIds = new HashSet<>();
when(sessionRegion.getFullPath()).thenReturn("/" + sessionRegionName);
when(sessionManager.getRegionAttributesId()).thenReturn(RegionShortcut.REPLICATE.toString());
sessionCache.touchSessions(sessionIds);
verify(emptyExecution).execute(TouchReplicatedRegionEntriesFunction.ID);
}
@Test
public void touchSessionsInvokesRRFunctionForRRAndThrowsExceptionWhenFunctionThrowsException() {
// Need to invoke this to set the session region
when(regionFactory.create(sessionRegionName)).thenReturn(sessionRegion);
sessionCache.initialize();
Set<String> sessionIds = new HashSet<>();
FunctionException exception = new FunctionException();
ResultCollector exceptionCollector = mock(ResultCollector.class);
when(sessionRegion.getFullPath()).thenReturn("/" + sessionRegionName);
when(sessionManager.getRegionAttributesId()).thenReturn(RegionShortcut.REPLICATE.toString());
when(emptyExecution.execute(TouchReplicatedRegionEntriesFunction.ID))
.thenReturn(exceptionCollector);
when(exceptionCollector.getResult()).thenThrow(exception);
sessionCache.touchSessions(sessionIds);
verify(logger).warn("Caught unexpected exception:", exception);
}
@Test
public void isBackingCacheEnabledReturnsTrueWhenCommitValveFailfastDisabled() {
assertThat(sessionCache.isBackingCacheAvailable()).isTrue();
}
@Test
public void isBackingCacheEnabledReturnsValueWhenCommitValveFailfastEnabled() {
boolean backingCacheEnabled = false;
PoolImpl pool = mock(PoolImpl.class);
when(sessionManager.isCommitValveFailfastEnabled()).thenReturn(true);
doReturn(pool).when((ClientServerSessionCache) sessionCache).findPoolInPoolManager();
when(pool.isPrimaryUpdaterAlive()).thenReturn(backingCacheEnabled);
assertThat(sessionCache.isBackingCacheAvailable()).isEqualTo(backingCacheEnabled);
}
}