Compare commits

..

9 Commits

Author SHA1 Message Date
Spring Buildmaster
3827ae1e72 Release version 1.3.0.RELEASE 2016-12-15 22:30:19 +00:00
Rob Winch
9067f8235d Add What's New in 1.3
Fixes gh-627
2016-12-15 15:56:16 -06:00
Rob Winch
19928e6b7f Add MongoSession.isExpired interval < 0 Test
Issue gh-629
2016-12-14 08:52:43 -06:00
Joe Atkins
1df1a76069 Prevent expiration on RedisSession interval < 0
Since a negative maxInactiveInterval is supposed to disable
expiration, if it is negative, use persist on the session's
spring:session:session and spring:session:expires keys to
prevent the expiration of the RedisSession.

Issue gh-629
2016-12-14 08:51:19 -06:00
Joe Atkins
17e397212d Fix MongoExpiringSession.isExpired interval < 0
Verify that interval is non-negative, as negative values for
maxInactiveInterval disables expiration.

Issue gh-629
2016-12-14 08:49:26 -06:00
Vedran Pavic
39503a21a7 Refactor JdbcOperationsSessionRepository session clean up query to prevent overflow
Fixes gh-679
2016-12-02 21:29:47 +01:00
Gabor Csizmadia
ebbc10b2b4 Fix misleading comment about SessionCreatedEvent
Fixes gh-678
2016-11-28 22:54:06 +01:00
John Blum
9a51cb9ca7 Minor changes to improve the timing between Spring Boot-based GemFire client and server connections
Fixes gh-672
2016-11-22 22:48:37 -08:00
Spring Buildmaster
5e0ee5077a Next development version 2016-11-23 04:15:45 +00:00
12 changed files with 104 additions and 40 deletions

View File

@@ -232,6 +232,9 @@ or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
NOTE: In order to run the following, you must uncomment the lines in the `GemFireServer` class, `gemfireProperties` bean
for the following GemFire System properties: `jmx-manager` and `jmx-manager-start`.
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -23,16 +23,21 @@ Additional features include:
* <<websocket,WebSocket>> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
== What's New in 1.2
== What's New in 1.3
Below are the highlights of what is new in Spring Session 1.2. You can find a complete list of what's new in https://github.com/spring-projects/spring-session/issues?utf8=%E2%9C%93&q=milestone%3A%221.2.0+RC1%22[1.2.0 RC1] by referring to the changelog.
Below are the highlights of what is new in Spring Session 1.3. You can find a complete list of what's new by referring to the changelogs of
https://github.com/spring-projects/spring-session/milestone/6?closed=1[1.3.0.M1],
https://github.com/spring-projects/spring-session/milestone/18?closed=1[1.3.0.M2],
https://github.com/spring-projects/spring-session/milestone/16?closed=1[1.3.0.RC1], and
https://github.com/spring-projects/spring-session/milestone/19?closed=1[1.3.0.RELEASE].
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#httpsession-hazelcast[Hazelcast]
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#spring-security-concurrent-sessions-how[Spring Security's concurrent session management]
* Added https://github.com/maseev/spring-session-orientdb[OrientDB Community Extension]
* Added <<httpsession-jdbc,JdbcOperationsSessionRepository>> (See https://github.com/spring-projects/spring-session/issues/364[#364]).
* Added <<httpsession-mongo,MongoOperationsSessionRepository>> (See https://github.com/spring-projects/spring-session/pull/371[#371]).
* SessionRepositoryFilter caches null session lookup (See https://github.com/spring-projects/spring-session/issues/423[#423])
* link:guides/grails3.html[Grails 3 Sample & Guide]
* Improved Workspace Setup (See https://github.com/spring-projects/spring-session/pull/417[#417])
* https://github.com/spring-projects/spring-session/tree/1.3.0.RELEASE/samples/httpsession-redis-json[GenericJackson2JsonRedisSerializer sample] with Spring Security's new Jackson Support
* Guides now https://github.com/spring-projects/spring-session/pull/652[use Lettuce]
* `spring.session.cleanup.cron.expression` can be used to override the cleanup tasks cron expression
* Lots of performance improvemetns and bug fixes
[[samples]]
== Samples and Guides (Start Here)

View File

@@ -4,7 +4,7 @@ jacksonVersion=2.6.5
jspApiVersion=2.0
servletApiVersion=3.0.1
jstlelVersion=1.2.5
version=1.3.0.RC1
version=1.3.0.RELEASE
springDataRedisVersion=1.7.1.RELEASE
html5ShivVersion=3.7.3
commonsLoggingVersion=1.2

View File

@@ -72,7 +72,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class Application {
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(60);
static final CountDownLatch latch = new CountDownLatch(1);
@@ -127,7 +127,7 @@ public class Application {
gemfirePool.setKeepAlive(false);
gemfirePool.setPingInterval(TimeUnit.SECONDS.toMillis(5));
gemfirePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(2)).intValue());
gemfirePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(15)).intValue());
gemfirePool.setRetryAttempts(1);
gemfirePool.setSubscriptionEnabled(true);
gemfirePool.setThreadLocalConnections(false);

View File

@@ -66,15 +66,14 @@ public class GemFireServer {
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("jmx-manager", "true");
gemfireProperties.setProperty("jmx-manager-start", "true");
//gemfireProperties.setProperty("jmx-manager", "true");
//gemfireProperties.setProperty("jmx-manager-start", "true");
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-boot:"
.concat(getClass().getSimpleName());
return "samples:httpsession-gemfire-boot:".concat(getClass().getSimpleName());
}
String logLevel() {

View File

@@ -116,7 +116,7 @@ public class MongoExpiringSession implements ExpiringSession {
}
public boolean isExpired() {
return new Date().after(this.expireAt);
return this.interval >= 0 && new Date().after(this.expireAt);
}
public Date getExpireAt() {

View File

@@ -80,15 +80,23 @@ final class RedisSessionExpirationPolicy {
}
}
long sessionExpireInSeconds = session.getMaxInactiveIntervalInSeconds();
String sessionKey = getSessionKey(keyToExpire);
if (sessionExpireInSeconds < 0) {
this.redis.boundValueOps(sessionKey).append("");
this.redis.boundValueOps(sessionKey).persist();
this.redis.boundHashOps(getSessionKey(session.getId())).persist();
return;
}
String expireKey = getExpirationKey(toExpire);
BoundSetOperations<Object, Object> expireOperations = this.redis
.boundSetOps(expireKey);
expireOperations.add(keyToExpire);
long sessionExpireInSeconds = session.getMaxInactiveIntervalInSeconds();
long fiveMinutesAfterExpires = sessionExpireInSeconds
+ TimeUnit.MINUTES.toSeconds(5);
String sessionKey = getSessionKey(keyToExpire);
expireOperations.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
if (sessionExpireInSeconds == 0) {

View File

@@ -21,7 +21,7 @@ import org.springframework.session.SessionRepository;
/**
* For {@link SessionRepository} implementations that support it, this event is fired when
* a {@link Session} is destroyed either explicitly or via expiration.
* a {@link Session} is created.
*
* @author Rob Winch
* @since 1.0

View File

@@ -176,7 +176,7 @@ public class JdbcOperationsSessionRepository implements
private static final String DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY =
"DELETE FROM %TABLE_NAME% " +
"WHERE LAST_ACCESS_TIME < ? - MAX_INACTIVE_INTERVAL * 1000";
"WHERE MAX_INACTIVE_INTERVAL < (? - LAST_ACCESS_TIME) / 1000";
private static final Log logger = LogFactory
.getLog(JdbcOperationsSessionRepository.class);

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.session.jdbc.config.annotation.web.http;
import java.util.Arrays;
import java.util.Map;
import javax.sql.DataSource;
@@ -36,8 +35,6 @@ import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
@@ -88,19 +85,14 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
@Bean
public JdbcOperationsSessionRepository sessionRepository(
@Qualifier("springSessionJdbcOperations") JdbcTemplate jdbcTemplate,
@Qualifier("springSessionJdbcOperations") JdbcOperations jdbcOperations,
PlatformTransactionManager transactionManager) {
JdbcOperationsSessionRepository sessionRepository =
new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
new JdbcOperationsSessionRepository(jdbcOperations, transactionManager);
String tableName = getTableName();
if (StringUtils.hasText(tableName)) {
sessionRepository.setTableName(tableName);
}
String databaseName = getDatabaseName(jdbcTemplate.getDataSource());
if (Arrays.asList("Apache Derby", "H2").contains(databaseName)) {
sessionRepository.setDeleteSessionsByLastAccessTimeQuery("DELETE FROM " + tableName +
" WHERE LAST_ACCESS_TIME < ? - CAST(MAX_INACTIVE_INTERVAL AS BIGINT) * 1000");
}
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (this.lobHandler != null) {
@@ -163,17 +155,6 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
private String getDatabaseName(DataSource dataSource) {
try {
String databaseProductName = JdbcUtils.extractDatabaseMetaData(dataSource,
"getDatabaseProductName").toString();
return JdbcUtils.commonDatabaseName(databaseProductName);
}
catch (MetaDataAccessException e) {
return null;
}
}
private String getTableName() {
String systemProperty = System.getProperty("spring.session.jdbc.tableName", "");
if (StringUtils.hasText(systemProperty)) {

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2014-2016 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.mongo;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*/
public class MongoExpiringSessionTests {
@Test
public void isExpiredWhenIntervalNegativeThenFalse() {
MongoExpiringSession session = new MongoExpiringSession();
session.setMaxInactiveIntervalInSeconds(-1);
session.setLastAccessedTime(0L);
assertThat(session.isExpired()).isFalse();
}
}

View File

@@ -137,4 +137,36 @@ public class RedisSessionExpirationPolicyTests {
verify(this.hashOperations).expire(this.session.getMaxInactiveIntervalInSeconds()
+ TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedDeleteOnZero() throws Exception {
String sessionKey = this.policy.getSessionKey("expires:" + this.session.getId());
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
this.session.setMaxInactiveIntervalInSeconds(0);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
// verify the original is removed
verify(this.setOperations).remove("expires:" + this.session.getId());
verify(this.setOperations).add("expires:" + this.session.getId());
verify(this.sessionRedisOperations).delete(sessionKey);
verify(this.setOperations).expire(this.session.getMaxInactiveIntervalInSeconds()
+ TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedPersistOnNegativeExpiration() throws Exception {
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
this.session.setMaxInactiveIntervalInSeconds(-1);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
verify(this.setOperations).remove("expires:" + this.session.getId());
verify(this.valueOperations).append("");
verify(this.valueOperations).persist();
verify(this.hashOperations).persist();
}
}