diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicy.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicy.java index 73448d6..696a944 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicy.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicy.java @@ -17,6 +17,7 @@ package org.springframework.session.data.gemfire.expiration.support; import java.time.Duration; +import java.util.Optional; import org.springframework.lang.NonNull; import org.springframework.session.Session; @@ -25,34 +26,38 @@ import org.springframework.util.Assert; /** * An implementation of the {@link SessionExpirationPolicy} interface that specifies an expiration policy based on - * a fixed period of time. That is, the {@link Session} will timeout after a fixed {@link Duration} even if the - * {@link Session} is still active. + * a {@link Duration fixed duration of time}. + * + * In other words, the {@link Session} will timeout after a {@link Duration fixed duration of time} even if + * the {@link Session} is still active (i.e. not idle). * * @author John Blum * @see java.time.Duration * @see org.springframework.session.Session * @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy + * @see org.springframework.session.data.gemfire.expiration.support.IdleTimeoutSessionExpirationPolicy * @since 2.1.0 */ @SuppressWarnings("unused") -public class FixedTimeoutSessionExpirationPolicy implements SessionExpirationPolicy { +public class FixedTimeoutSessionExpirationPolicy extends IdleTimeoutSessionExpirationPolicy { - private final Duration fixedExpirationTimeout; + private final Duration fixedTimeout; /** - * Constructs a new {@link FixedTimeoutSessionExpirationPolicy} initialized with the given - * {@link Duration fixed expiration timeout}. + * Constructs a new {@link FixedTimeoutSessionExpirationPolicy} initialized with + * the given {@link Duration fixed, expiration timeout}. * - * @param fixedExpirationTimeout {@link Duration} specifying the fixed length of time until - * the {@link Session} expires. - * @throws IllegalArgumentException if {@link Duration} is {@literal null}. + * @param fixedTimeout {@link Duration fixed length of time} until the {@link Session} will expire. + * @throws IllegalArgumentException if the {@link Duration fixed timeout} is {@literal null}. * @see java.time.Duration */ - public FixedTimeoutSessionExpirationPolicy(@NonNull Duration fixedExpirationTimeout) { + public FixedTimeoutSessionExpirationPolicy(@NonNull Duration fixedTimeout) { - Assert.notNull(fixedExpirationTimeout, "Fixed expiration timeout is required"); + super(null); - this.fixedExpirationTimeout = fixedExpirationTimeout; + Assert.notNull(fixedTimeout, "Fixed expiration timeout is required"); + + this.fixedTimeout = fixedTimeout; } /** @@ -61,19 +66,25 @@ public class FixedTimeoutSessionExpirationPolicy implements SessionExpirationPol * @return the configured {@link Duration fixed expiration timeout}. * @see java.time.Duration */ - protected Duration getFixedExpirationTimeout() { - return this.fixedExpirationTimeout; + protected Duration getFixedTimeout() { + return this.fixedTimeout; } - @NonNull @Override - public Duration expireAfter(@NonNull Session session) { + @Override + public Optional determineExpirationTimeout(@NonNull Session session) { - long currentTimeMinusCreationTime = - Math.max(System.currentTimeMillis() - session.getCreationTime().toEpochMilli(), 0); + Optional idleTimeout = super.determineExpirationTimeout(session); - Duration expirationDuration = - getFixedExpirationTimeout().minus(Duration.ofMillis(currentTimeMinusCreationTime)); + Duration fixedExpirationTimeout = getFixedTimeout().minus(computeTimeSinceCreation(session)); - return expirationDuration.isNegative() ? Duration.ZERO : expirationDuration; + return idleTimeout.filter(it -> it.compareTo(fixedExpirationTimeout) <= 0).isPresent() + ? Optional.empty() : Optional.of(fixedExpirationTimeout); + } + + private Duration computeTimeSinceCreation(@NonNull Session session) { + + long timeSinceCreation = Math.max(System.currentTimeMillis() - session.getCreationTime().toEpochMilli(), 0L); + + return Duration.ofMillis(timeSinceCreation); } } diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicyUnitTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicyUnitTests.java index 3ccad93..12d4cc4 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicyUnitTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/expiration/support/FixedTimeoutSessionExpirationPolicyUnitTests.java @@ -34,6 +34,8 @@ import org.springframework.session.Session; * Unit tests for {@link FixedTimeoutSessionExpirationPolicy}. * * @author John Blum + * @see java.time.Duration + * @see java.time.Instant * @see org.junit.Test * @see org.mockito.Mockito * @see org.springframework.session.Session @@ -45,17 +47,18 @@ public class FixedTimeoutSessionExpirationPolicyUnitTests { @Test public void constructsFixedTimeoutSessionExpirationPolicy() { - Duration fixedExpirationTimeout = Duration.ofSeconds(60); + Duration fixedExpirationTimeout = Duration.ofSeconds(60L); FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = new FixedTimeoutSessionExpirationPolicy(fixedExpirationTimeout); assertThat(sessionExpirationPolicy).isNotNull(); - assertThat(sessionExpirationPolicy.getFixedExpirationTimeout()).isEqualTo(fixedExpirationTimeout); + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedExpirationTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isNull(); } @Test(expected = IllegalArgumentException.class) - public void constructsFixedTimeoutSessionExpirationPolicyWithNullDuration() { + public void constructsFixedTimeoutSessionExpirationPolicyWithNullFixedTimeout() { try { new FixedTimeoutSessionExpirationPolicy(null); @@ -70,43 +73,167 @@ public class FixedTimeoutSessionExpirationPolicyUnitTests { } @Test - public void expireAfterReturnsFutureDuration() { + public void determineExpirationTimeoutWithNoIdleTimeoutReturnsExpiredDuration() { - Duration expirationTimeout = Duration.ofSeconds(30); + Duration fixedTimeout = Duration.ofSeconds(30L); FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = - new FixedTimeoutSessionExpirationPolicy(expirationTimeout); + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isNull(); Session mockSession = mock(Session.class); when(mockSession.getCreationTime()) - .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(15).toMillis())); + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(60L).toMillis())); - Duration expireAfter = sessionExpirationPolicy.expireAfter(mockSession); + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); - assertThat(expireAfter).isNotNull(); - assertThat(expireAfter.getSeconds()).isLessThanOrEqualTo(15); + assertThat(expirationTimeout).isNotNull(); + assertThat(expirationTimeout).isLessThan(Duration.ZERO); verify(mockSession, times(1)).getCreationTime(); verify(mockSession, never()).getLastAccessedTime(); } @Test - public void expireAfterReturnsZero() { + public void determineExpirationTimeoutWithNoIdleTimeoutReturnsNonExpiredDuration() { - Duration expirationTimeout = Duration.ofSeconds(30); + Duration fixedTimeout = Duration.ofSeconds(30L); FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = - new FixedTimeoutSessionExpirationPolicy(expirationTimeout); + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isNull(); Session mockSession = mock(Session.class); when(mockSession.getCreationTime()) - .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(60).toMillis())); + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(15L).toMillis())); - Duration expireAfter = sessionExpirationPolicy.expireAfter(mockSession); + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); - assertThat(expireAfter).isEqualTo(Duration.ZERO); + assertThat(expirationTimeout).isNotNull(); + assertThat(expirationTimeout).isGreaterThan(Duration.ZERO); + + verify(mockSession, times(1)).getCreationTime(); + verify(mockSession, never()).getLastAccessedTime(); + } + + @Test + public void determineExpirationTimeoutWithLongerIdleTimeoutReturnsExpiredDuration() { + + Duration fixedTimeout = Duration.ofSeconds(60L); + Duration idleTimeout = Duration.ofSeconds(30L); + + FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + sessionExpirationPolicy.setExpirationTimeout(idleTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isEqualTo(idleTimeout); + + Session mockSession = mock(Session.class); + + when(mockSession.getCreationTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(90L).toMillis())); + + when(mockSession.getLastAccessedTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(15L).toMillis())); + + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); + + assertThat(expirationTimeout).isNotNull(); + assertThat(expirationTimeout).isLessThan(Duration.ZERO); + + verify(mockSession, times(1)).getCreationTime(); + verify(mockSession, times(1)).getLastAccessedTime(); + } + + @Test + public void determineExpirationTimeoutWithLongerIdleTimeoutReturnsNonExpiredDuration() { + + Duration fixedTimeout = Duration.ofSeconds(60L); + Duration idleTimeout = Duration.ofSeconds(30L); + + FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + sessionExpirationPolicy.setExpirationTimeout(idleTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isEqualTo(idleTimeout); + + Session mockSession = mock(Session.class); + + when(mockSession.getCreationTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(45L).toMillis())); + + when(mockSession.getLastAccessedTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(10L).toMillis())); + + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); + + assertThat(expirationTimeout).isNotNull(); + assertThat(expirationTimeout).isGreaterThan(Duration.ZERO); + + verify(mockSession, times(1)).getCreationTime(); + verify(mockSession, times(1)).getLastAccessedTime(); + } + + @Test + public void determineExpirationTimeoutWithShorterTimeoutReturnsNoDuration() { + + Duration fixedTimeout = Duration.ofSeconds(60L); + Duration idleTimeout = Duration.ofSeconds(30L); + + FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + sessionExpirationPolicy.setExpirationTimeout(idleTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isEqualTo(idleTimeout); + + Session mockSession = mock(Session.class); + + when(mockSession.getCreationTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(30L).toMillis())); + + when(mockSession.getLastAccessedTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() - Duration.ofSeconds(15L).toMillis())); + + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); + + assertThat(expirationTimeout).isNull(); + + verify(mockSession, times(1)).getCreationTime(); + verify(mockSession, times(1)).getLastAccessedTime(); + } + + @Test + public void expirationTimeoutIsNotGreaterThanFixedTimeout() { + + Duration fixedTimeout = Duration.ofSeconds(30L); + + FixedTimeoutSessionExpirationPolicy sessionExpirationPolicy = + new FixedTimeoutSessionExpirationPolicy(fixedTimeout); + + assertThat(sessionExpirationPolicy.getFixedTimeout()).isEqualTo(fixedTimeout); + assertThat(sessionExpirationPolicy.getIdleTimeout().orElse(null)).isNull(); + + Session mockSession = mock(Session.class); + + when(mockSession.getCreationTime()) + .thenReturn(Instant.ofEpochMilli(System.currentTimeMillis() + Duration.ofSeconds(30).toMillis())); + + Duration expirationTimeout = sessionExpirationPolicy.determineExpirationTimeout(mockSession).orElse(null); + + assertThat(expirationTimeout).isNotNull(); + assertThat(expirationTimeout).isEqualTo(fixedTimeout); verify(mockSession, times(1)).getCreationTime(); verify(mockSession, never()).getLastAccessedTime();