Compare commits
9 Commits
1.3.0.RC1
...
1.3.0.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3827ae1e72 | ||
|
|
9067f8235d | ||
|
|
19928e6b7f | ||
|
|
1df1a76069 | ||
|
|
17e397212d | ||
|
|
39503a21a7 | ||
|
|
ebbc10b2b4 | ||
|
|
9a51cb9ca7 | ||
|
|
5e0ee5077a |
@@ -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:
|
||||
|
||||
|
||||
@@ -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 task’s cron expression
|
||||
* Lots of performance improvemetns and bug fixes
|
||||
|
||||
[[samples]]
|
||||
== Samples and Guides (Start Here)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user