Add Hazelcast

Fixes gh-276
This commit is contained in:
Tommy Ludwig
2015-08-23 23:57:08 +09:00
committed by Rob Winch
parent a48864bf20
commit d1c00c6080
16 changed files with 889 additions and 213 deletions

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2015 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;
import org.springframework.context.ApplicationListener;
import org.springframework.session.events.AbstractSessionEvent;
public class SessionEventRegistry implements ApplicationListener<AbstractSessionEvent> {
private AbstractSessionEvent event;
private Object lock = new Object();
public void onApplicationEvent(AbstractSessionEvent event) {
this.event = event;
synchronized (lock) {
lock.notifyAll();
}
}
public void setLock(Object lock) {
this.lock = lock;
}
public void clear() {
this.event = null;
}
public boolean receivedEvent() throws InterruptedException {
return waitForEvent() != null;
}
@SuppressWarnings("unchecked")
public <E extends AbstractSessionEvent> E getEvent() throws InterruptedException {
return (E) waitForEvent();
}
@SuppressWarnings("unchecked")
private <E extends AbstractSessionEvent> E waitForEvent() throws InterruptedException {
synchronized(lock) {
if(event == null) {
lock.wait(3000);
}
}
return (E) event;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2002-2015 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.hazelcast;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.SocketUtils;
import com.hazelcast.config.Config;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
/**
* Integration tests that check the underlying data source - in this case
* Hazelcast.
*
* @author Tommy Ludwig
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class HazelcastRepositoryITests<S extends ExpiringSession> {
@Autowired
private HazelcastInstance hazelcast;
@Autowired
private SessionRepository<S> repository;
@Test
public void createAndDestorySession() {
S sessionToSave = repository.createSession();
String sessionId = sessionToSave.getId();
IMap<String, S> hazelcastMap = hazelcast.getMap("spring:session:sessions");
assertThat(hazelcastMap.size()).isEqualTo(0);
repository.save(sessionToSave);
assertThat(hazelcastMap.size()).isEqualTo(1);
assertThat(hazelcastMap.get(sessionId)).isEqualTo(sessionToSave);
repository.delete(sessionId);
assertThat(hazelcastMap.size()).isEqualTo(0);
}
@EnableHazelcastHttpSession
@Configuration
static class HazelcastSessionConfig {
@Bean
public HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
hazelcastConfig.setNetworkConfig(netConfig);
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2002-2015 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.hazelcast.config.annotation.web.http;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.ExpiringSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.SessionEventRegistry;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.SocketUtils;
import com.hazelcast.config.Config;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
/**
* Ensure that the appropriate SessionEvents are fired at the expected times.
* Additionally ensure that the interactions with the {@link SessionRepository}
* abstraction behave as expected after each SessionEvent.
*
* @author Tommy Ludwig
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
private final static String MAX_INACTIVE_INTERVAL_IN_SECONDS_STR = "1";
private final static int MAX_INACTIVE_INTERVAL_IN_SECONDS = Integer.valueOf(MAX_INACTIVE_INTERVAL_IN_SECONDS_STR);
@Autowired
private SessionRepository<S> repository;
@Autowired
private SessionEventRegistry registry;
private final Object lock = new Object();
@Before
public void setup() {
registry.clear();
registry.setLock(lock);
}
@Test
public void saveSessionTest() throws InterruptedException {
String username = "saves-"+System.currentTimeMillis();
S sessionToSave = repository.createSession();
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
sessionToSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
sessionToSave.setAttribute("SPRING_SECURITY_CONTEXT", toSaveContext);
sessionToSave.setAttribute(Session.PRINCIPAL_NAME_ATTRIBUTE_NAME, username);
repository.save(sessionToSave);
assertThat(registry.receivedEvent()).isTrue();
assertThat(registry.getEvent()).isInstanceOf(SessionCreatedEvent.class);
Session session = repository.getSession(sessionToSave.getId());
assertThat(session.getId()).isEqualTo(sessionToSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(sessionToSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(sessionToSave.getAttribute(expectedAttributeName));
}
@Test
public void expiredSessionTest() throws InterruptedException {
S sessionToSave = repository.createSession();
repository.save(sessionToSave);
assertThat(registry.receivedEvent()).isTrue();
assertThat(registry.getEvent()).isInstanceOf(SessionCreatedEvent.class);
registry.clear();
assertThat(sessionToSave.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
synchronized (lock) {
lock.wait((sessionToSave.getMaxInactiveIntervalInSeconds() * 1000) + 1);
}
assertThat(registry.receivedEvent()).isTrue();
assertThat(registry.getEvent()).isInstanceOf(SessionExpiredEvent.class);
assertThat(repository.getSession(sessionToSave.getId())).isNull();
}
@Test
public void deletedSessionTest() throws InterruptedException {
S sessionToSave = repository.createSession();
repository.save(sessionToSave);
assertThat(registry.receivedEvent()).isTrue();
assertThat(registry.getEvent()).isInstanceOf(SessionCreatedEvent.class);
registry.clear();
repository.delete(sessionToSave.getId());
assertThat(registry.receivedEvent()).isTrue();
assertThat(registry.getEvent()).isInstanceOf(SessionDeletedEvent.class);
assertThat(repository.getSession(sessionToSave.getId())).isNull();
}
@Configuration
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS_STR)
static class HazelcastSessionConfig {
@Bean
public HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
hazelcastConfig.setNetworkConfig(netConfig);
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
@Bean
public SessionEventRegistry sessionEventRegistry() {
return new SessionEventRegistry();
}
}
}

View File

@@ -24,7 +24,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@@ -35,9 +34,9 @@ import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.Session;
import org.springframework.session.data.SessionEventRegistry;
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.test.context.ContextConfiguration;
@@ -88,7 +87,7 @@ public class RedisOperationsSessionRepositoryITests {
Session session = repository.getSession(toSave.getId());
assertThat(session.getId()).isEqualTo(toSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(session.getAttributeNames());
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(toSave.getAttribute(expectedAttributeName));
registry.clear();
@@ -146,41 +145,6 @@ public class RedisOperationsSessionRepositoryITests {
assertThat(findByPrincipalName.keySet()).excludes(toSave.getId());
}
static class SessionEventRegistry implements ApplicationListener<AbstractSessionEvent> {
private AbstractSessionEvent event;
private final Object lock = new Object();
public void onApplicationEvent(AbstractSessionEvent event) {
this.event = event;
synchronized (lock) {
lock.notifyAll();
}
}
public void clear() {
this.event = null;
}
public boolean receivedEvent() throws InterruptedException {
return waitForEvent() != null;
}
@SuppressWarnings("unchecked")
public <E extends AbstractSessionEvent> E getEvent() throws InterruptedException {
return (E) waitForEvent();
}
@SuppressWarnings("unchecked")
private <E extends AbstractSessionEvent> E waitForEvent() throws InterruptedException {
synchronized(lock) {
if(event == null) {
lock.wait(3000);
}
}
return (E) event;
}
}
@Configuration
@EnableRedisHttpSession(redisNamespace = "RedisOperationsSessionRepositoryITests")
static class Config {