Define o.s.session.data.gemfire.expiration.SessionExpirationPolicy strategy interface and supporting classes to enable users to implement custom expiration policies.

Resolve gh-5.
This commit is contained in:
John Blum
2018-10-22 19:02:20 -07:00
parent 9d9a5d0b1a
commit 65d9ff8f42
10 changed files with 830 additions and 154 deletions

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration;
import java.time.Duration;
import org.springframework.lang.NonNull;
import org.springframework.session.Session;
/**
* The {@link SessionExpirationPolicy} interface is a Strategy interface defining a contract for users to implement
* different {@link Session} expiration policies and rules.
*
* Examples of different {@link Session} expiration strategies might include, but are not limited to:
* idle expiration timeout or fixed duration expiration timeouts, and so on.
*
* @author John Blum
* @see java.time.Duration
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.expiration.support.FixedTimeoutSessionExpirationPolicy
* @see org.springframework.session.data.gemfire.expiration.support.IdleTimeoutSessionExpirationPolicy
* @since 2.1.0
*/
@FunctionalInterface
@SuppressWarnings("unused")
public interface SessionExpirationPolicy {
/**
* Defines the {@link Duration length of time} until the given {@link Session} will expire.
*
* @param session {@link Session} to evaluate.
* @return a {@link Duration} specifying the length of time until the {@link Session} will expire.
* @see org.springframework.session.Session
* @see java.time.Duration
*/
@NonNull
Duration expireAfter(Session session);
/**
* Defines the {@link ExpirationAction action} to take when the {@link Session} expires.
*
* Defaults to {@link ExpirationAction#INVALIDATE}.
*
* @return an {@link ExpirationAction} specifying the action to take when the {@link Session} expires.
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy.ExpirationAction
*/
default ExpirationAction getAction() {
return ExpirationAction.INVALIDATE;
}
/**
* Enumeration of different actions to take when the {@link Session} expires.
*/
enum ExpirationAction {
DESTROY,
INVALIDATE;
public static ExpirationAction defaultIfNull(ExpirationAction expirationAction) {
return expirationAction != null ? expirationAction : INVALIDATE;
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.config;
import java.time.Duration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.gemfire.expiration.repository.FixedDurationExpirationSessionRepository;
/**
* The {@link FixedDurationExpirationSessionRepositoryBeanPostProcessor} class wraps an existing, data store specific,
* instance of {@link SessionRepository} in an instance of {@link FixedDurationExpirationSessionRepository} initialized
* with a provided {@link Duration} for the expiration timeout to implement lazy, fixed {@link Duration} expiration
* on all {@link Session Sessions}.
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.session.Session
* @see org.springframework.session.SessionRepository
* @see org.springframework.session.data.gemfire.expiration.repository.FixedDurationExpirationSessionRepository
* @see <a href="https://github.com/spring-projects/spring-session/issues/922">Absolute Session Timeouts</a>
* @since 2.1.0
*/
@SuppressWarnings("unused")
public class FixedDurationExpirationSessionRepositoryBeanPostProcessor implements BeanPostProcessor {
private final Duration expirationTimeout;
/**
* Constructs a new instance of {@link FixedDurationExpirationSessionRepositoryBeanPostProcessor} initialized with
* the given {@link Duration} to implement lazy, fixed {@link Duration} expiration policy
* on all {@link Session Sessions}.
*
* @param expirationTimeout {@link Duration} indicating the length of time until the {@link Session} expires.
* @see java.time.Duration
*/
public FixedDurationExpirationSessionRepositoryBeanPostProcessor(@Nullable Duration expirationTimeout) {
this.expirationTimeout = expirationTimeout;
}
/**
* Returns the configured {@link Session} {@link Duration expiration timeout}.
*
* @return the configured {@link Session} {@link Duration expiration timeout}.
* @see java.time.Duration
*/
protected Duration getExpirationTimeout() {
return this.expirationTimeout;
}
@Nullable @Override @SuppressWarnings("unchecked")
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean instanceof SessionRepository
? new FixedDurationExpirationSessionRepository<>((SessionRepository) bean, getExpirationTimeout())
: bean;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.config;
import java.time.Duration;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
/**
* The {@link SessionExpirationTimeoutAware} interface is a configuration callback interface allowing implementors
* to receive a callback with the configured {@link Session} {@link Duration expiration timeout} as set on the
* {@link EnableGemFireHttpSession} annotation, {@link EnableGemFireHttpSession#maxInactiveIntervalInSeconds()}
* attribute.
*
* @author John Blum
* @see java.time.Duration
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @since 2.1.0
*/
@SuppressWarnings("unused")
public interface SessionExpirationTimeoutAware {
/**
* Configures the {@link Session} {@link Duration expiration timeout} on this implementating object.
*
* @param expirationTimeout {@link Duration} specifying the expiration timeout fo the {@link Session}.
* @see java.time.Duration
*/
void setExpirationTimeout(Duration expirationTimeout);
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.config;
import java.time.Duration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.session.Session;
import org.springframework.util.Assert;
/**
* The {@link SessionExpirationTimeoutAwareBeanPostProcessor} class is a Spring {@link BeanPostProcessor} handling
* the post processing of all Spring beans defined in the Spring container implementing
* the {@link SessionExpirationTimeoutAware} interface.
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @since 2.1.0
*/
public class SessionExpirationTimeoutAwareBeanPostProcessor implements BeanPostProcessor {
private final Duration expirationTimeout;
/**
* Constructs a new {@link SessionExpirationTimeoutAwareBeanPostProcessor} initialized with
* the given {@link Session} {@link Duration expiration timeout}.
*
* @param expirationTimeout {@link Duration} specifying the length of time until {@link Session} expires.
* @throws IllegalArgumentException if {@link Duration} is {@literal null}.
* @see java.time.Duration
*/
public SessionExpirationTimeoutAwareBeanPostProcessor(Duration expirationTimeout) {
Assert.notNull(expirationTimeout, "Expiration timeout is required");
this.expirationTimeout = expirationTimeout;
}
/**
* Returns the configured {@link Session} {@link Duration expiration timeout}.
*
* @return the configured {@link Session} {@link Duration expiration timeout}.
* @see java.time.Duration
*/
protected Duration getExpirationTimeout() {
return this.expirationTimeout;
}
@Nullable @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SessionExpirationTimeoutAware) {
((SessionExpirationTimeoutAware) bean).setExpirationTimeout(getExpirationTimeout());
}
return bean;
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.repository;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.apache.shiro.util.Assert;
/**
* The {@link FixedDurationExpirationSessionRepository} class is a {@link SessionRepository} implementation wrapping
* an existing {@link SessionRepository}, data store specific, implementation in order to implement
* a fixed {@link Duration} expiration policy on the {@link Session}.
*
* That is, the {@link Session} will always expire (or be considered "expired") after a fixed amount of time. Even if
* the user {@link Session} is still actively being accessed up to the last moment right before the {@link Session}
* is about to expire, the {@link Session} will expire regardless.
*
* This may be useful in certain UCs where, for security reasons, the {@link Session} must expire no matter what.
*
* @author John Blum
* @see java.time.Duration
* @see java.time.Instant
* @see org.springframework.session.Session
* @see org.springframework.session.SessionRepository
* @see <a href="https://github.com/spring-projects/spring-session/issues/922">Absolute Session Timeouts</a>
* @since 2.1.0
*/
@SuppressWarnings("unused")
public class FixedDurationExpirationSessionRepository<S extends Session> implements SessionRepository<S> {
private final SessionRepository<S> delegate;
private final Duration expirationTimeout;
/**
* Constructs a new instance of {@link FixedDurationExpirationSessionRepository} initialized with the given
* data store specific {@link SessionRepository}.
*
* @param sessionRepository {@link SessionRepository} delegate.
* @param expirationTimeout {@link Duration} specifying the length of time until the {@link Session} expires.
* @throws IllegalArgumentException if {@link SessionRepository} is {@literal null}.
* @see org.springframework.session.SessionRepository
* @see java.time.Duration
*/
public FixedDurationExpirationSessionRepository(@NonNull SessionRepository<S> sessionRepository,
@Nullable Duration expirationTimeout) {
Assert.notNull(sessionRepository, "SessionRepository is required");
this.delegate = sessionRepository;
this.expirationTimeout = expirationTimeout;
}
/**
* Returns a reference to the data store specific {@link SessionRepository}.
*
* @return a reference to the data store specific {@link SessionRepository}.
* @see org.springframework.session.SessionRepository
*/
@NonNull
protected SessionRepository<S> getDelegate() {
return this.delegate;
}
/**
* Return an {@link Optional} {@link Duration expiraiton timeout}.
*
* @return an {@link Optional} {@link Duration expiraiton timeout}.
* @see java.time.Duration
* @see java.util.Optional
*/
public Optional<Duration> getExpirationTimeout() {
return Optional.ofNullable(this.expirationTimeout);
}
/**
* Creates a new instance of {@link Session}.
*
* The {@link Session} instance will be managed by Apache Geode or Pivotal GemFire.
*
* @return a new instance of {@link Session}.
* @see org.springframework.session.Session
*/
@Override
public S createSession() {
return getDelegate().createSession();
}
/**
* Finds a {@link Session} with the given {@link String ID}.
*
* This method will also perform a lazy expiration check to determine if the {@link Session} has already expired
* upon access, and if so, delete the {@link Session} with the given {@link String ID}.
*
* @param id {@link String} containing the ID identifying the {@link Session} to lookup.
* @return the {@link Session} with the given {@link String ID} or {@literal null} if no {@link Session}
* with {@link String ID} exists or the {@link Session} is expired.
* @see org.springframework.session.Session
* @see #handleExpired(Session, Duration)
* @see #getExpirationTimeout()
*/
@Override
@SuppressWarnings("unchecked")
public S findById(String id) {
return Optional.ofNullable(getDelegate().findById(id))
.map(session -> getExpirationTimeout()
.map(expirationDuration -> handleExpired(session, expirationDuration))
.orElseGet(()-> getExpirationTimeout().isPresent() ? null : session)
).orElse(null);
}
/**
* Handles the expiration event for the given {@link Session} if the {@link Session} has expired.
*
* @param session {@link Session} to evaluate for expiration.
* @param expirationDuration {@link Duration} indicating the length of time before an idle,
* unused or old {@link Session} expires.
* @return the given {@link Session} or {@literal null} if the {@link Session} has already expired.
* @see #isExpired(Session, Duration)
* @see #deleteById(String)
* @see org.springframework.session.Session
* @see java.time.Duration
*/
S handleExpired(S session, Duration expirationDuration) {
if (isExpired(session, expirationDuration)) {
deleteById(session.getId());
session = null;
}
return session;
}
/**
* Determines whether the given {@link Session} has expired.
*
* This {@link SessionRepository} implements fixed duration expiration, which means the {@link Session} will expire
* after a fixed length of time (i.e. a fixed {@link Duration}). Even if the user {@link Session} is active
* (i.e. not idle), the {@link Session} will still expire after the fixed {@link Duration} is exceeded.
*
* @param session {@link Session} to evaluate.
* @param expirationDuration {@link Duration} indicating the length of time before the {@link Session} expires.
* @return a boolean value indication whether the {@link Session} has expired.
* @see org.springframework.session.Session
* @see java.time.Duration
* @see java.time.Instant
*/
boolean isExpired(S session, Duration expirationDuration) {
Instant sessionCreationTime = session.getCreationTime();
Instant now = Instant.now();
return now.minusMillis(expirationDuration.toMillis()).isAfter(sessionCreationTime);
}
/**
* Saves the given {@link Session} to the underlying data (persistent) store.
*
* @param session {@link Session} to save.
* @see org.springframework.session.Session
*/
@Override
public void save(S session) {
getDelegate().save(session);
}
/**
* Deletes the {@link Session} identified by the given {@link String ID}.
*
* @param id {@link String} containing the ID of the {@link Session} to delete.
* @see org.springframework.session.Session
*/
@Override
public void deleteById(String id) {
getDelegate().deleteById(id);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.support;
import java.time.Duration;
import org.springframework.lang.NonNull;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy;
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.
*
* @author John Blum
* @see java.time.Duration
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
* @since 2.1.0
*/
@SuppressWarnings("unused")
public class FixedTimeoutSessionExpirationPolicy implements SessionExpirationPolicy {
private final Duration fixedExpirationTimeout;
/**
* 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}.
* @see java.time.Duration
*/
public FixedTimeoutSessionExpirationPolicy(@NonNull Duration fixedExpirationTimeout) {
Assert.notNull(fixedExpirationTimeout, "Fixed expiration timeout is required");
this.fixedExpirationTimeout = fixedExpirationTimeout;
}
/**
* Return the configured {@link Duration fixed expiration timeout}.
*
* @return the configured {@link Duration fixed expiration timeout}.
* @see java.time.Duration
*/
protected Duration getFixedExpirationTimeout() {
return this.fixedExpirationTimeout;
}
@NonNull @Override
public Duration expireAfter(@NonNull Session session) {
long currentTimeMinusCreationTime =
Math.max(System.currentTimeMillis() - session.getCreationTime().toEpochMilli(), 0);
Duration expirationDuration =
getFixedExpirationTimeout().minus(Duration.ofMillis(currentTimeMinusCreationTime));
return expirationDuration.isNegative() ? Duration.ZERO : expirationDuration;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.support;
import java.time.Duration;
import org.springframework.lang.NonNull;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy;
import org.springframework.util.Assert;
/**
* An implementation of the {@link SessionExpirationPolicy} interface that specifies an expiration policy
* based on inactive, idle {@link Session Sessions} exceeding a predefined time period for expiration.
*
* @author John Blum
* @see java.time.Duration
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
* @since 2.1.0
*/
@SuppressWarnings("unused")
public class IdleTimeoutSessionExpirationPolicy implements SessionExpirationPolicy {
private final Duration idleExpirationTimeout;
/**
* Constructs a new instance of {@link IdleTimeoutSessionExpirationPolicy} initialized with
* the given {@link Duration expiration timeout}.
*
* @param idleExpirationTimeout {@link Duration} specifying the length of time until the {@link Session} expires.
* @throws IllegalArgumentException if {@link Duration} is {@literal null}.
* @see java.time.Duration
*/
public IdleTimeoutSessionExpirationPolicy(@NonNull Duration idleExpirationTimeout) {
Assert.notNull(idleExpirationTimeout, "Idle expiration timeout is required");
this.idleExpirationTimeout = idleExpirationTimeout;
}
/**
* Return the configured {@link Duration idle expiration timeout}.
*
* @return the configured {@link Duration idle expiration timeout}.
* @see java.time.Duration
*/
protected Duration getIdleExpirationTimeout() {
return this.idleExpirationTimeout;
}
@NonNull @Override
public Duration expireAfter(@NonNull Session session) {
long currentTimeMinusLastAccessTime =
Math.max(System.currentTimeMillis() - session.getLastAccessedTime().toEpochMilli(), 0);
Duration expirationDuration =
getIdleExpirationTimeout().minus(Duration.ofMillis(currentTimeMinusLastAccessTime));
return expirationDuration.isNegative() ? Duration.ZERO : expirationDuration;
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.expiration.support;
import java.time.Duration;
import java.util.Optional;
import org.apache.geode.cache.CustomExpiry;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.pdx.PdxInstance;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy;
import org.springframework.util.Assert;
/**
* The {@link SessionExpirationPolicyCustomExpiryAdapter} class is an Apache Geode/Pivotal GemFire {@link CustomExpiry}
* implementation wrapping and adapting an instance of the {@link SessionExpirationPolicy} strategy interface
* to plugin to GemFire/Geode's expiration logistics.
*
* @author John Blum
* @see org.apache.geode.cache.CustomExpiry
* @see org.apache.geode.cache.ExpirationAction
* @see org.apache.geode.cache.ExpirationAttributes
* @see org.apache.geode.cache.Region
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class SessionExpirationPolicyCustomExpiryAdapter implements CustomExpiry<String, Object> {
private final SessionExpirationPolicy sessionExpirationPolicy;
/**
* Constructs a new instance of {@link SessionExpirationPolicyCustomExpiryAdapter} initialized with
* the given, required {@link SessionExpirationPolicy}.
*
* @param sessionExpirationPolicy {@link SessionExpirationPolicy} used to enforce the expiration policy
* on all {@link Session Sessions}.
* @throws IllegalArgumentException if the {@link SessionExpirationPolicy} is {@literal null}.
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
*/
public SessionExpirationPolicyCustomExpiryAdapter(@NonNull SessionExpirationPolicy sessionExpirationPolicy) {
Assert.notNull(sessionExpirationPolicy, "SessionExpirationPolicy is required");
this.sessionExpirationPolicy = sessionExpirationPolicy;
}
/**
* Returns a reference to the {@link SessionExpirationPolicy} defining the expiration policies
* for all managed {@link Session Sessions}.
*
* @return a reference to the {@link SessionExpirationPolicy}.
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy
*/
protected SessionExpirationPolicy getSessionExpirationPolicy() {
return this.sessionExpirationPolicy;
}
@Nullable @Override
public ExpirationAttributes getExpiry(@Nullable Region.Entry<String, Object> regionEntry) {
return Optional.ofNullable(resolveSession(regionEntry))
.map(this::newExpirationAttributes)
.orElse(null);
}
/**
* Constructs {@link ExpirationAttributes} from the given {@link Session}.
*
* @param session {@link Session} used to construct the {@link ExpirationAttributes}.
* @return a new {@link ExpirationAttributes} constructed from the given {@link Session}.
* @see #newExpirationAttributes(Duration, SessionExpirationPolicy.ExpirationAction)
* @see org.apache.geode.cache.ExpirationAttributes
* @see org.springframework.session.Session
*/
private ExpirationAttributes newExpirationAttributes(Session session) {
SessionExpirationPolicy sessionExpirationPolicy = getSessionExpirationPolicy();
Duration sessionExpirationDuration = sessionExpirationPolicy.expireAfter(session);
SessionExpirationPolicy.ExpirationAction sessionExpirationAction = sessionExpirationPolicy.getAction();
return newExpirationAttributes(sessionExpirationDuration, sessionExpirationAction);
}
/**
* Constructs a new {@link ExpirationAttributes} initialized with the given {@link Duration expiration timeout}
* and {@link SessionExpirationPolicy.ExpirationAction} to take when the {@link Session} expires.
*
* @param duration {@link Duration} specifying the expiration timeout.
* @param expirationAction {@link SessionExpirationPolicy.ExpirationAction} to take when
* the {@link Session} expires.
* @return the new {@link ExpirationAttributes}.
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy.ExpirationAction
* @see #newExpirationAttributes(int, ExpirationAction)
* @see org.apache.geode.cache.ExpirationAttributes
* @see java.time.Duration
*/
private ExpirationAttributes newExpirationAttributes(Duration duration,
SessionExpirationPolicy.ExpirationAction expirationAction) {
int expirationTimeout = (int) Math.min(Integer.MAX_VALUE, Math.max(duration.getSeconds(), 1));
return newExpirationAttributes(expirationTimeout, resolveExpirationAction(expirationAction));
}
/**
* Constructs new {@link ExpirationAttributes} with the given {@link Integer expiration timeout}
* and {@link ExpirationAction} to take when the {@link Session} expires.
*
* @param expirationTimeInSeconds length of time in seconds until the {@link Session} expires.
* @param expirationAction {@link ExpirationAction} to take when the {@link Session} expires.
* @return the new {@link ExpirationAttributes}.
* @see org.apache.geode.cache.ExpirationAttributes
*/
private ExpirationAttributes newExpirationAttributes(int expirationTimeInSeconds,
ExpirationAction expirationAction) {
return new ExpirationAttributes(expirationTimeInSeconds, expirationAction);
}
/**
* Resolves the {@link org.apache.geode.cache.ExpirationAction} from the given
* {@link SessionExpirationPolicy.ExpirationAction}.
*
* Defaults to {@link ExpirationAction#INVALIDATE} if {@link SessionExpirationPolicy.ExpirationAction}
* is {@literal null}.
*
* @param expirationAction {@link SessionExpirationPolicy.ExpirationAction} to convert into a
* {@link org.apache.geode.cache.ExpirationAction}.
* @return an {@link org.apache.geode.cache.ExpirationAction} from the given
* {@link SessionExpirationPolicy.ExpirationAction}.
* @see org.springframework.session.data.gemfire.expiration.SessionExpirationPolicy.ExpirationAction
* @see org.apache.geode.cache.ExpirationAction
*/
private ExpirationAction resolveExpirationAction(SessionExpirationPolicy.ExpirationAction expirationAction) {
switch (SessionExpirationPolicy.ExpirationAction.defaultIfNull(expirationAction)) {
case DESTROY:
return ExpirationAction.DESTROY;
default:
return ExpirationAction.INVALIDATE;
}
}
/**
* Resolves a {@link Session} object from the given {@link Region.Entry#getValue() Region Entry Value}.
*
* @param regionEntry {@link Region.Entry} from which to extract the {@link Session} value.
* @return a {@link Session} object from the given {@link Region.Entry#getValue()}.
* @see org.springframework.session.Session
* @see org.apache.geode.cache.Region.Entry
* @see #resolveSession(Object)
*/
@Nullable
private Session resolveSession(Region.Entry<String, Object> regionEntry) {
return resolveSession(Optional.ofNullable(regionEntry).map(Region.Entry::getValue).orElse(null));
}
/**
* Resolves a {@link Session} object from the given {@link Object} value.
*
* The {@link Object} may already be a {@link Session} or may possibly be a {@link PdxInstance} if GemFire/Geode
* PDX serialization is enabled.
*
* @param regionEntryValue {@link Object} to evaluate as a {@link Session}.
* @return a {@link Session} from the given {@link Object}.
* @see org.springframework.session.Session
* @see org.apache.geode.pdx.PdxInstance
* @see java.lang.Object
*/
@Nullable
private Session resolveSession(Object regionEntryValue) {
return regionEntryValue instanceof Session ? (Session) regionEntryValue
: regionEntryValue instanceof PdxInstance ? (Session) ((PdxInstance) regionEntryValue).getObject()
: null;
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.support;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* The {@link FixedDurationExpirationSessionRepository} class...
*
* @author John Blum
* @see org.springframework.session.Session
* @see org.springframework.session.SessionRepository
* @see <a href="https://github.com/spring-projects/spring-session/issues/922">Absolute Session Timeouts</a>
* @since 2.0.0
*/
@SuppressWarnings("unused")
public class FixedDurationExpirationSessionRepository<S extends Session> implements SessionRepository<S> {
private final SessionRepository<S> delegate;
private final Duration expirationDuration;
public FixedDurationExpirationSessionRepository(SessionRepository<S> sessionRepository, Duration expirationDuration) {
this.delegate = Optional.ofNullable(sessionRepository)
.orElseThrow(() -> new IllegalArgumentException("SessionRepository is required"));
this.expirationDuration = expirationDuration;
}
protected SessionRepository<S> getDelegate() {
return this.delegate;
}
public Optional<Duration> getExpirationDuration() {
return Optional.ofNullable(this.expirationDuration);
}
@Override
public S createSession() {
return getDelegate().createSession();
}
@Override
@SuppressWarnings("unchecked")
public S findById(String id) {
return Optional.ofNullable(getDelegate().findById(id))
.map(session ->
getExpirationDuration()
.map(expirationDuration -> handleExpired(session, expirationDuration))
.orElse(session)
).orElse(null);
}
private S handleExpired(S session, Duration expirationDuration) {
if (isExpired(session, expirationDuration)) {
deleteById(session.getId());
session = null;
}
return session;
}
private boolean isExpired(S session, Duration expirationDuration) {
Instant sessionCreationTime = session.getCreationTime();
Instant now = Instant.now();
return now.minusMillis(expirationDuration.toMillis()).isAfter(sessionCreationTime);
}
@Override
public void save(S session) {
getDelegate().save(session);
}
@Override
public void deleteById(String id) {
getDelegate().deleteById(id);
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.springframework.session.data.gemfire.support;
import java.time.Duration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.session.SessionRepository;
/**
* The {@link FixedDurationExpirationSessionRepositoryBeanPostProcessor} class...
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.session.SessionRepository
* @see <a href="https://github.com/spring-projects/spring-session/issues/922">Absolute Session Timeouts</a>
* @since 2.0.0
*/
@SuppressWarnings("unused")
public class FixedDurationExpirationSessionRepositoryBeanPostProcessor implements BeanPostProcessor {
private final Duration expirationDuration;
public FixedDurationExpirationSessionRepositoryBeanPostProcessor(Duration expirationDuration) {
this.expirationDuration = expirationDuration;
}
@Nullable @Override @SuppressWarnings("unchecked")
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return (bean instanceof SessionRepository
? new FixedDurationExpirationSessionRepository<>((SessionRepository) bean, this.expirationDuration)
: bean);
}
}