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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user