From 149c76f8b1fd077543452e76657867752459ae1f Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 23 Oct 2018 20:13:25 -0700 Subject: [PATCH] Extend IdleTimeoutSessionExpirationPolicy. Determine expiration timeout in terms of both fixed timeout and idle timeout. If the remaining idle timeout is less than the remaining fixed timeout then return Optional.empty(). Edit Javadoc. Resolves gh-5. --- .../FixedTimeoutSessionExpirationPolicy.java | 53 +++--- ...meoutSessionExpirationPolicyUnitTests.java | 159 ++++++++++++++++-- 2 files changed, 175 insertions(+), 37 deletions(-) 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();