Tomcat session manager implemented. #590
parent
7c0ac961e7
commit
c378299009
@ -0,0 +1,61 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-parent</artifactId>
|
||||
<version>2.6.1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>redisson-tomcat</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Redisson/Tomcat</name>
|
||||
|
||||
<modules>
|
||||
<module>redisson-tomcat-6</module>
|
||||
<module>redisson-tomcat-7</module>
|
||||
<module>redisson-tomcat-8</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>fluent-hc</artifactId>
|
||||
<version>4.5.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,64 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-tomcat</artifactId>
|
||||
<version>2.6.1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>redisson-tomcat-6</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Redisson/Tomcat-6</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>catalina</artifactId>
|
||||
<version>6.0.48</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<basedir>${basedir}</basedir>
|
||||
<header>${basedir}/../../header.txt</header>
|
||||
<quiet>false</quiet>
|
||||
<failIfMissing>true</failIfMissing>
|
||||
<aggregate>false</aggregate>
|
||||
<includes>
|
||||
<include>src/main/java/org/redisson/</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>target/**</exclude>
|
||||
</excludes>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<mapping>
|
||||
<java>JAVADOC_STYLE</java>
|
||||
</mapping>
|
||||
<strictCheck>true</strictCheck>
|
||||
<useDefaultMapping>true</useDefaultMapping>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.catalina.session.StandardSession;
|
||||
import org.redisson.api.RMap;
|
||||
|
||||
/**
|
||||
* Redisson Session object for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSession extends StandardSession {
|
||||
|
||||
private final RedissonSessionManager redissonManager;
|
||||
private RMap<String, Object> map;
|
||||
|
||||
public RedissonSession(RedissonSessionManager manager) {
|
||||
super(manager);
|
||||
this.redissonManager = manager;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -2518607181636076487L;
|
||||
|
||||
@Override
|
||||
public void setId(String id, boolean notify) {
|
||||
super.setId(id, notify);
|
||||
map = redissonManager.getMap(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(long time) {
|
||||
super.setCreationTime(time);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void access() {
|
||||
super.access();
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
if (getMaxInactiveInterval() >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
super.setMaxInactiveInterval(interval);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
if (maxInactiveInterval >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValid(boolean isValid) {
|
||||
super.setValid(isValid);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isValid", isValid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNew(boolean isNew) {
|
||||
super.setNew(isNew);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAccess() {
|
||||
boolean oldValue = isNew;
|
||||
super.endAccess();
|
||||
|
||||
if (isNew != oldValue) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value, boolean notify) {
|
||||
super.setAttribute(name, value, notify);
|
||||
|
||||
if (map != null && value != null) {
|
||||
map.fastPut(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeAttributeInternal(String name, boolean notify) {
|
||||
super.removeAttributeInternal(name, notify);
|
||||
|
||||
if (getId() != null) {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastRemove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
map.fastPut("session:isValid", isValid);
|
||||
map.fastPut("session:isNew", isNew);
|
||||
|
||||
for (Entry<String, Object> entry : attributes.entrySet()) {
|
||||
map.fastPut(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
if (!map.isExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Entry<String, Object>> entrySet = map.readAllEntrySet();
|
||||
for (Entry<String, Object> entry : entrySet) {
|
||||
if ("session:creationTime".equals(entry.getKey())) {
|
||||
creationTime = (Long) entry.getValue();
|
||||
} else if ("session:lastAccessedTime".equals(entry.getKey())) {
|
||||
lastAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:thisAccessedTime".equals(entry.getKey())) {
|
||||
thisAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:maxInactiveInterval".equals(entry.getKey())) {
|
||||
maxInactiveInterval = (Integer) entry.getValue();
|
||||
} else if ("session:isValid".equals(entry.getKey())) {
|
||||
isValid = (Boolean) entry.getValue();
|
||||
} else if ("session:isNew".equals(entry.getKey())) {
|
||||
isNew = (Boolean) entry.getValue();
|
||||
} else {
|
||||
setAttribute(entry.getKey(), entry.getValue(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.Session;
|
||||
import org.apache.catalina.session.ManagerBase;
|
||||
import org.apache.catalina.util.LifecycleSupport;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
/**
|
||||
* Redisson Session Manager for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSessionManager extends ManagerBase implements Lifecycle {
|
||||
|
||||
private final Log log = LogFactory.getLog(RedissonSessionManager.class);
|
||||
|
||||
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
|
||||
|
||||
private RedissonClient redisson;
|
||||
private String configPath;
|
||||
|
||||
public void setConfigPath(String configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
public String getConfigPath() {
|
||||
return configPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRejectedSessions() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws ClassNotFoundException, IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRejectedSessions(int sessions) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.addLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleListener[] findLifecycleListeners() {
|
||||
return lifecycle.findLifecycleListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.removeLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createSession(String sessionId) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
|
||||
session.setNew(true);
|
||||
session.setValid(true);
|
||||
session.setCreationTime(System.currentTimeMillis());
|
||||
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
|
||||
|
||||
if (sessionId == null) {
|
||||
sessionId = generateSessionId();
|
||||
}
|
||||
|
||||
session.setId(sessionId);
|
||||
session.save();
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
public RMap<String, Object> getMap(String sessionId) {
|
||||
return redisson.getMap("redisson_tomcat_session:" + sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session findSession(String id) throws IOException {
|
||||
Session result = super.findSession(id);
|
||||
if (result == null && id != null) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
session.setId(id);
|
||||
session.load();
|
||||
return session;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createEmptySession() {
|
||||
return new RedissonSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Session session) {
|
||||
super.remove(session);
|
||||
|
||||
getMap(session.getId()).delete();
|
||||
}
|
||||
|
||||
public RedissonClient getRedisson() {
|
||||
return redisson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws LifecycleException {
|
||||
Config config = null;
|
||||
try {
|
||||
config = Config.fromJSON(new File(configPath));
|
||||
} catch (IOException e) {
|
||||
// trying next format
|
||||
try {
|
||||
config = Config.fromYAML(new File(configPath));
|
||||
} catch (IOException e1) {
|
||||
log.error("Can't parse json config " + configPath, e);
|
||||
throw new LifecycleException("Can't parse yaml config " + configPath, e1);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
redisson = Redisson.create(config);
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
lifecycle.fireLifecycleEvent(START_EVENT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws LifecycleException {
|
||||
try {
|
||||
if (redisson != null) {
|
||||
redisson.shutdown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.fluent.Executor;
|
||||
import org.apache.http.client.fluent.Request;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RedissonSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void testSwitchServer() throws LifecycleException, InterruptedException, ClientProtocolException, IOException {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
|
||||
server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteReadRemove() throws LifecycleException, InterruptedException, ClientProtocolException, IOException {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1234");
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreate() throws LifecycleException, InterruptedException, ClientProtocolException, IOException {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
recreate(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws LifecycleException, InterruptedException, ClientProtocolException, IOException {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
read(executor, "test", "1");
|
||||
write(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvalidate() throws LifecycleException, InterruptedException, ClientProtocolException, IOException {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "/src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
invalidate(executor);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private void write(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/write?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void read(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/read?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void remove(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/remove?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void invalidate(Executor executor) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/invalidate";
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void recreate(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/recreate?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
public class TestServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1243830648280853203L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
HttpSession session = req.getSession();
|
||||
|
||||
if (req.getPathInfo().equals("/write")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/read")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
Object attr = session.getAttribute(key);
|
||||
resp.getWriter().print(attr);
|
||||
} else if (req.getPathInfo().equals("/remove")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
session.removeAttribute(key);
|
||||
resp.getWriter().print(String.valueOf(session.getAttribute(key)));
|
||||
} else if (req.getPathInfo().equals("/invalidate")) {
|
||||
session.invalidate();
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/recreate")) {
|
||||
session.invalidate();
|
||||
|
||||
session = req.getSession();
|
||||
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import org.apache.catalina.Engine;
|
||||
import org.apache.catalina.Host;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.startup.Embedded;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TomcatServer {
|
||||
|
||||
private Embedded server;
|
||||
private int port;
|
||||
private boolean isRunning;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TomcatServer.class);
|
||||
private static final boolean isInfo = LOG.isInfoEnabled();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new Tomcat embedded server instance. Setup looks like:
|
||||
* <pre><Server>
|
||||
* <Service>
|
||||
* <Connector />
|
||||
* <Engine>
|
||||
* <Host>
|
||||
* <Context />
|
||||
* </Host>
|
||||
* </Engine>
|
||||
* </Service>
|
||||
*</Server></pre>
|
||||
* <Server> & <Service> will be created automcatically. We need to hook the remaining to an {@link Embedded} instnace
|
||||
* @param contextPath Context path for the application
|
||||
* @param port Port number to be used for the embedded Tomcat server
|
||||
* @param appBase Path to the Application files (for Maven based web apps, in general: <code>/src/main/</code>)
|
||||
* @param shutdownHook If true, registers a server' shutdown hook with JVM. This is useful to shutdown the server
|
||||
* in erroneous cases.
|
||||
* @throws Exception
|
||||
*/
|
||||
public TomcatServer(String contextPath, int port, String appBase) {
|
||||
if(contextPath == null || appBase == null || appBase.length() == 0) {
|
||||
throw new IllegalArgumentException("Context path or appbase should not be null");
|
||||
}
|
||||
if(!contextPath.startsWith("/")) {
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
|
||||
this.port = port;
|
||||
|
||||
server = new Embedded();
|
||||
server.setName("TomcatEmbeddedServer");
|
||||
|
||||
Host localHost = server.createHost("localhost", appBase);
|
||||
localHost.setAutoDeploy(false);
|
||||
|
||||
StandardContext rootContext = (StandardContext) server.createContext(contextPath, "webapp");
|
||||
rootContext.setDefaultWebXml("web.xml");
|
||||
localHost.addChild(rootContext);
|
||||
|
||||
Engine engine = server.createEngine();
|
||||
engine.setDefaultHost(localHost.getName());
|
||||
engine.setName("TomcatEngine");
|
||||
engine.addChild(localHost);
|
||||
|
||||
server.addEngine(engine);
|
||||
|
||||
Connector connector = server.createConnector(localHost.getName(), port, false);
|
||||
server.addConnector(connector);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tomcat embedded server
|
||||
*/
|
||||
public void start() throws LifecycleException {
|
||||
if(isRunning) {
|
||||
LOG.warn("Tomcat server is already running @ port={}; ignoring the start", port);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isInfo) LOG.info("Starting the Tomcat server @ port={}", port);
|
||||
|
||||
server.setAwait(true);
|
||||
server.start();
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the tomcat embedded server
|
||||
*/
|
||||
public void stop() throws LifecycleException {
|
||||
if(!isRunning) {
|
||||
LOG.warn("Tomcat server is not running @ port={}", port);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isInfo) LOG.info("Stopping the Tomcat server");
|
||||
|
||||
server.stop();
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Context>
|
||||
|
||||
<Manager className="org.redisson.tomcat.RedissonSessionManager"
|
||||
configPath="${catalina.base}/src/test/webapp/WEB-INF/redisson.yaml" />
|
||||
|
||||
</Context>
|
@ -0,0 +1,3 @@
|
||||
singleServerConfig:
|
||||
address:
|
||||
- "//127.0.0.1:6379"
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
version="2.5">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<servlet-class>org.redisson.tomcat.TestServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>30</session-timeout>
|
||||
</session-config>
|
||||
|
||||
</web-app>
|
@ -0,0 +1,82 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-tomcat</artifactId>
|
||||
<version>2.6.1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>redisson-tomcat-7</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Redisson/Tomcat-7</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>7.0.73</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
<version>7.0.73</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-jasper</artifactId>
|
||||
<version>7.0.73</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jasper</artifactId>
|
||||
<version>7.0.73</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<basedir>${basedir}</basedir>
|
||||
<header>${basedir}/../../header.txt</header>
|
||||
<quiet>false</quiet>
|
||||
<failIfMissing>true</failIfMissing>
|
||||
<aggregate>false</aggregate>
|
||||
<includes>
|
||||
<include>src/main/java/org/redisson/</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>target/**</exclude>
|
||||
</excludes>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<mapping>
|
||||
<java>JAVADOC_STYLE</java>
|
||||
</mapping>
|
||||
<strictCheck>true</strictCheck>
|
||||
<useDefaultMapping>true</useDefaultMapping>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.catalina.session.StandardSession;
|
||||
import org.redisson.api.RMap;
|
||||
|
||||
/**
|
||||
* Redisson Session object for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSession extends StandardSession {
|
||||
|
||||
private final RedissonSessionManager redissonManager;
|
||||
private RMap<String, Object> map;
|
||||
|
||||
public RedissonSession(RedissonSessionManager manager) {
|
||||
super(manager);
|
||||
this.redissonManager = manager;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -2518607181636076487L;
|
||||
|
||||
@Override
|
||||
public void setId(String id, boolean notify) {
|
||||
super.setId(id, notify);
|
||||
map = redissonManager.getMap(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(long time) {
|
||||
super.setCreationTime(time);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void access() {
|
||||
super.access();
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
if (getMaxInactiveInterval() >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
super.setMaxInactiveInterval(interval);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
if (maxInactiveInterval >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValid(boolean isValid) {
|
||||
super.setValid(isValid);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isValid", isValid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNew(boolean isNew) {
|
||||
super.setNew(isNew);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAccess() {
|
||||
boolean oldValue = isNew;
|
||||
super.endAccess();
|
||||
|
||||
if (isNew != oldValue) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value, boolean notify) {
|
||||
super.setAttribute(name, value, notify);
|
||||
|
||||
if (map != null && value != null) {
|
||||
map.fastPut(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeAttributeInternal(String name, boolean notify) {
|
||||
super.removeAttributeInternal(name, notify);
|
||||
|
||||
if (getId() != null) {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastRemove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
map.fastPut("session:isValid", isValid);
|
||||
map.fastPut("session:isNew", isNew);
|
||||
|
||||
for (Entry<String, Object> entry : attributes.entrySet()) {
|
||||
map.fastPut(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
if (!map.isExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Entry<String, Object>> entrySet = map.readAllEntrySet();
|
||||
for (Entry<String, Object> entry : entrySet) {
|
||||
if ("session:creationTime".equals(entry.getKey())) {
|
||||
creationTime = (Long) entry.getValue();
|
||||
} else if ("session:lastAccessedTime".equals(entry.getKey())) {
|
||||
lastAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:thisAccessedTime".equals(entry.getKey())) {
|
||||
thisAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:maxInactiveInterval".equals(entry.getKey())) {
|
||||
maxInactiveInterval = (Integer) entry.getValue();
|
||||
} else if ("session:isValid".equals(entry.getKey())) {
|
||||
isValid = (Boolean) entry.getValue();
|
||||
} else if ("session:isNew".equals(entry.getKey())) {
|
||||
isNew = (Boolean) entry.getValue();
|
||||
} else {
|
||||
setAttribute(entry.getKey(), entry.getValue(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.Session;
|
||||
import org.apache.catalina.session.ManagerBase;
|
||||
import org.apache.catalina.util.LifecycleSupport;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
/**
|
||||
* Redisson Session Manager for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSessionManager extends ManagerBase implements Lifecycle {
|
||||
|
||||
private final Log log = LogFactory.getLog(RedissonSessionManager.class);
|
||||
|
||||
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
|
||||
|
||||
private RedissonClient redisson;
|
||||
private String configPath;
|
||||
|
||||
public void setConfigPath(String configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
public String getConfigPath() {
|
||||
return configPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RedissonSessionManager.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRejectedSessions() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws ClassNotFoundException, IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.addLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleListener[] findLifecycleListeners() {
|
||||
return lifecycle.findLifecycleListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.removeLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createSession(String sessionId) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
|
||||
session.setNew(true);
|
||||
session.setValid(true);
|
||||
session.setCreationTime(System.currentTimeMillis());
|
||||
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
|
||||
|
||||
if (sessionId == null) {
|
||||
sessionId = generateSessionId();
|
||||
}
|
||||
|
||||
session.setId(sessionId);
|
||||
session.save();
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
public RMap<String, Object> getMap(String sessionId) {
|
||||
return redisson.getMap("redisson_tomcat_session:" + sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session findSession(String id) throws IOException {
|
||||
Session result = super.findSession(id);
|
||||
if (result == null && id != null) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
session.setId(id);
|
||||
session.load();
|
||||
return session;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createEmptySession() {
|
||||
return new RedissonSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Session session) {
|
||||
super.remove(session);
|
||||
|
||||
getMap(session.getId()).delete();
|
||||
}
|
||||
|
||||
public RedissonClient getRedisson() {
|
||||
return redisson;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startInternal() throws LifecycleException {
|
||||
super.startInternal();
|
||||
Config config = null;
|
||||
try {
|
||||
config = Config.fromJSON(new File(configPath));
|
||||
} catch (IOException e) {
|
||||
// trying next format
|
||||
try {
|
||||
config = Config.fromYAML(new File(configPath));
|
||||
} catch (IOException e1) {
|
||||
log.error("Can't parse json config " + configPath, e);
|
||||
throw new LifecycleException("Can't parse yaml config " + configPath, e1);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
redisson = Redisson.create(config);
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
setState(LifecycleState.STARTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopInternal() throws LifecycleException {
|
||||
super.stopInternal();
|
||||
|
||||
setState(LifecycleState.STOPPING);
|
||||
|
||||
try {
|
||||
if (redisson != null) {
|
||||
redisson.shutdown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.fluent.Executor;
|
||||
import org.apache.http.client.fluent.Request;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RedissonSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void testSwitchServer() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
|
||||
server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteReadRemove() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1234");
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
recreate(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
read(executor, "test", "1");
|
||||
write(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvalidate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
invalidate(executor);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private void write(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/write?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void read(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/read?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void remove(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/remove?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void invalidate(Executor executor) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/invalidate";
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void recreate(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/recreate?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
public class TestServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1243830648280853203L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
HttpSession session = req.getSession();
|
||||
|
||||
if (req.getPathInfo().equals("/write")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/read")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
Object attr = session.getAttribute(key);
|
||||
resp.getWriter().print(attr);
|
||||
} else if (req.getPathInfo().equals("/remove")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
session.removeAttribute(key);
|
||||
resp.getWriter().print(String.valueOf(session.getAttribute(key)));
|
||||
} else if (req.getPathInfo().equals("/invalidate")) {
|
||||
session.invalidate();
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/recreate")) {
|
||||
session.invalidate();
|
||||
|
||||
session = req.getSession();
|
||||
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TomcatServer {
|
||||
|
||||
private Tomcat tomcat = new Tomcat();
|
||||
private int port;
|
||||
private boolean isRunning;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TomcatServer.class);
|
||||
private static final boolean isInfo = LOG.isInfoEnabled();
|
||||
|
||||
public TomcatServer(String contextPath, int port, String appBase) throws MalformedURLException, ServletException {
|
||||
if(contextPath == null || appBase == null || appBase.length() == 0) {
|
||||
throw new IllegalArgumentException("Context path or appbase should not be null");
|
||||
}
|
||||
if(!contextPath.startsWith("/")) {
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
|
||||
tomcat.setBaseDir("."); // location where temp dir is created
|
||||
tomcat.setPort(port);
|
||||
tomcat.getHost().setAppBase(".");
|
||||
|
||||
tomcat.addWebapp(contextPath, appBase + "webapp");
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tomcat embedded server
|
||||
*/
|
||||
public void start() throws LifecycleException {
|
||||
tomcat.start();
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the tomcat embedded server
|
||||
*/
|
||||
public void stop() throws LifecycleException {
|
||||
if(!isRunning) {
|
||||
LOG.warn("Tomcat server is not running @ port={}", port);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isInfo) LOG.info("Stopping the Tomcat server");
|
||||
|
||||
tomcat.stop();
|
||||
tomcat.destroy();
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Context>
|
||||
|
||||
<Manager className="org.redisson.tomcat.RedissonSessionManager"
|
||||
configPath="${catalina.base}/src/test/webapp/WEB-INF/redisson.yaml" />
|
||||
|
||||
</Context>
|
@ -0,0 +1,3 @@
|
||||
singleServerConfig:
|
||||
address:
|
||||
- "//127.0.0.1:6379"
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
version="2.5">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<servlet-class>org.redisson.tomcat.TestServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>30</session-timeout>
|
||||
</session-config>
|
||||
|
||||
</web-app>
|
@ -0,0 +1,81 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-tomcat</artifactId>
|
||||
<version>2.6.1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>redisson-tomcat-8</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Redisson/Tomcat-8</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>8.0.39</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
<version>8.0.39</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-jasper</artifactId>
|
||||
<version>8.0.39</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jasper</artifactId>
|
||||
<version>8.0.39</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<basedir>${basedir}</basedir>
|
||||
<header>${basedir}/../../header.txt</header>
|
||||
<quiet>false</quiet>
|
||||
<failIfMissing>true</failIfMissing>
|
||||
<aggregate>false</aggregate>
|
||||
<includes>
|
||||
<include>src/main/java/org/redisson/</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>target/**</exclude>
|
||||
</excludes>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<mapping>
|
||||
<java>JAVADOC_STYLE</java>
|
||||
</mapping>
|
||||
<strictCheck>true</strictCheck>
|
||||
<useDefaultMapping>true</useDefaultMapping>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.catalina.session.StandardSession;
|
||||
import org.redisson.api.RMap;
|
||||
|
||||
/**
|
||||
* Redisson Session object for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSession extends StandardSession {
|
||||
|
||||
private final RedissonSessionManager redissonManager;
|
||||
private RMap<String, Object> map;
|
||||
|
||||
public RedissonSession(RedissonSessionManager manager) {
|
||||
super(manager);
|
||||
this.redissonManager = manager;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -2518607181636076487L;
|
||||
|
||||
@Override
|
||||
public void setId(String id, boolean notify) {
|
||||
super.setId(id, notify);
|
||||
map = redissonManager.getMap(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(long time) {
|
||||
super.setCreationTime(time);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void access() {
|
||||
super.access();
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
if (getMaxInactiveInterval() >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
super.setMaxInactiveInterval(interval);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
if (maxInactiveInterval >= 0) {
|
||||
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValid(boolean isValid) {
|
||||
super.setValid(isValid);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isValid", isValid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNew(boolean isNew) {
|
||||
super.setNew(isNew);
|
||||
|
||||
if (map != null) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAccess() {
|
||||
boolean oldValue = isNew;
|
||||
super.endAccess();
|
||||
|
||||
if (isNew != oldValue) {
|
||||
map.fastPut("session:isNew", isNew);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value, boolean notify) {
|
||||
super.setAttribute(name, value, notify);
|
||||
|
||||
if (map != null && value != null) {
|
||||
map.fastPut(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeAttributeInternal(String name, boolean notify) {
|
||||
super.removeAttributeInternal(name, notify);
|
||||
|
||||
if (getId() != null) {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastRemove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
map.fastPut("session:creationTime", creationTime);
|
||||
map.fastPut("session:lastAccessedTime", lastAccessedTime);
|
||||
map.fastPut("session:thisAccessedTime", thisAccessedTime);
|
||||
map.fastPut("session:maxInactiveInterval", maxInactiveInterval);
|
||||
map.fastPut("session:isValid", isValid);
|
||||
map.fastPut("session:isNew", isNew);
|
||||
|
||||
for (Entry<String, Object> entry : attributes.entrySet()) {
|
||||
map.fastPut(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
RMap<String, Object> map = redissonManager.getMap(getId());
|
||||
if (!map.isExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Entry<String, Object>> entrySet = map.readAllEntrySet();
|
||||
for (Entry<String, Object> entry : entrySet) {
|
||||
if ("session:creationTime".equals(entry.getKey())) {
|
||||
creationTime = (Long) entry.getValue();
|
||||
} else if ("session:lastAccessedTime".equals(entry.getKey())) {
|
||||
lastAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:thisAccessedTime".equals(entry.getKey())) {
|
||||
thisAccessedTime = (Long) entry.getValue();
|
||||
} else if ("session:maxInactiveInterval".equals(entry.getKey())) {
|
||||
maxInactiveInterval = (Integer) entry.getValue();
|
||||
} else if ("session:isValid".equals(entry.getKey())) {
|
||||
isValid = (Boolean) entry.getValue();
|
||||
} else if ("session:isNew".equals(entry.getKey())) {
|
||||
isNew = (Boolean) entry.getValue();
|
||||
} else {
|
||||
setAttribute(entry.getKey(), entry.getValue(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright 2016 Nikita Koksharov
|
||||
*
|
||||
* 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.redisson.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.Session;
|
||||
import org.apache.catalina.session.ManagerBase;
|
||||
import org.apache.catalina.util.LifecycleSupport;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
|
||||
/**
|
||||
* Redisson Session Manager for Apache Tomcat
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonSessionManager extends ManagerBase implements Lifecycle {
|
||||
|
||||
private final Log log = LogFactory.getLog(RedissonSessionManager.class);
|
||||
|
||||
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
|
||||
|
||||
private RedissonClient redisson;
|
||||
private String configPath;
|
||||
|
||||
public void setConfigPath(String configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
public String getConfigPath() {
|
||||
return configPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RedissonSessionManager.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRejectedSessions() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws ClassNotFoundException, IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.addLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleListener[] findLifecycleListeners() {
|
||||
return lifecycle.findLifecycleListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.removeLifecycleListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createSession(String sessionId) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
|
||||
session.setNew(true);
|
||||
session.setValid(true);
|
||||
session.setCreationTime(System.currentTimeMillis());
|
||||
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
|
||||
|
||||
if (sessionId == null) {
|
||||
sessionId = generateSessionId();
|
||||
}
|
||||
|
||||
session.setId(sessionId);
|
||||
session.save();
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
public RMap<String, Object> getMap(String sessionId) {
|
||||
return redisson.getMap("redisson_tomcat_session:" + sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session findSession(String id) throws IOException {
|
||||
Session result = super.findSession(id);
|
||||
if (result == null && id != null) {
|
||||
RedissonSession session = (RedissonSession) createEmptySession();
|
||||
session.setId(id);
|
||||
session.load();
|
||||
return session;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session createEmptySession() {
|
||||
return new RedissonSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Session session) {
|
||||
super.remove(session);
|
||||
|
||||
getMap(session.getId()).delete();
|
||||
}
|
||||
|
||||
public RedissonClient getRedisson() {
|
||||
return redisson;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startInternal() throws LifecycleException {
|
||||
super.startInternal();
|
||||
Config config = null;
|
||||
try {
|
||||
config = Config.fromJSON(new File(configPath));
|
||||
} catch (IOException e) {
|
||||
// trying next format
|
||||
try {
|
||||
config = Config.fromYAML(new File(configPath));
|
||||
} catch (IOException e1) {
|
||||
log.error("Can't parse json config " + configPath, e);
|
||||
throw new LifecycleException("Can't parse yaml config " + configPath, e1);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
redisson = Redisson.create(config);
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
setState(LifecycleState.STARTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopInternal() throws LifecycleException {
|
||||
super.stopInternal();
|
||||
|
||||
setState(LifecycleState.STOPPING);
|
||||
|
||||
try {
|
||||
if (redisson != null) {
|
||||
redisson.shutdown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new LifecycleException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.fluent.Executor;
|
||||
import org.apache.http.client.fluent.Request;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RedissonSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void testSwitchServer() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
|
||||
server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteReadRemove() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1234");
|
||||
read(executor, "test", "1234");
|
||||
remove(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
recreate(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
|
||||
write(executor, "test", "1");
|
||||
read(executor, "test", "1");
|
||||
write(executor, "test", "2");
|
||||
read(executor, "test", "2");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvalidate() throws Exception {
|
||||
// start the server at http://localhost:8080/myapp
|
||||
TomcatServer server = new TomcatServer("myapp", 8080, "src/test/");
|
||||
server.start();
|
||||
|
||||
Executor executor = Executor.newInstance();
|
||||
BasicCookieStore cookieStore = new BasicCookieStore();
|
||||
executor.use(cookieStore);
|
||||
|
||||
write(executor, "test", "1234");
|
||||
Cookie cookie = cookieStore.getCookies().get(0);
|
||||
invalidate(executor);
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
|
||||
executor = Executor.newInstance();
|
||||
cookieStore = new BasicCookieStore();
|
||||
cookieStore.addCookie(cookie);
|
||||
executor.use(cookieStore);
|
||||
read(executor, "test", "null");
|
||||
|
||||
Executor.closeIdleConnections();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private void write(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/write?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void read(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/read?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void remove(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/remove?key=" + key;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals(value, response);
|
||||
}
|
||||
|
||||
private void invalidate(Executor executor) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/invalidate";
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
private void recreate(Executor executor, String key, String value) throws IOException, ClientProtocolException {
|
||||
String url = "http://localhost:8080/myapp/recreate?key=" + key + "&value=" + value;
|
||||
String response = executor.execute(Request.Get(url)).returnContent().asString();
|
||||
Assert.assertEquals("OK", response);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
public class TestServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1243830648280853203L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
HttpSession session = req.getSession();
|
||||
|
||||
if (req.getPathInfo().equals("/write")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/read")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
Object attr = session.getAttribute(key);
|
||||
resp.getWriter().print(attr);
|
||||
} else if (req.getPathInfo().equals("/remove")) {
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
for (String param : params) {
|
||||
String[] line = param.split("=");
|
||||
String keyParam = line[0];
|
||||
if ("key".equals(keyParam)) {
|
||||
key = line[1];
|
||||
}
|
||||
}
|
||||
|
||||
session.removeAttribute(key);
|
||||
resp.getWriter().print(String.valueOf(session.getAttribute(key)));
|
||||
} else if (req.getPathInfo().equals("/invalidate")) {
|
||||
session.invalidate();
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
} else if (req.getPathInfo().equals("/recreate")) {
|
||||
session.invalidate();
|
||||
|
||||
session = req.getSession();
|
||||
|
||||
String[] params = req.getQueryString().split("&");
|
||||
String key = null;
|
||||
String value = null;
|
||||
for (String param : params) {
|
||||
String[] paramLine = param.split("=");
|
||||
String keyParam = paramLine[0];
|
||||
String valueParam = paramLine[1];
|
||||
|
||||
if ("key".equals(keyParam)) {
|
||||
key = valueParam;
|
||||
}
|
||||
if ("value".equals(keyParam)) {
|
||||
value = valueParam;
|
||||
}
|
||||
}
|
||||
session.setAttribute(key, value);
|
||||
|
||||
resp.getWriter().print("OK");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package org.redisson.tomcat;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TomcatServer {
|
||||
|
||||
private Tomcat tomcat = new Tomcat();
|
||||
private int port;
|
||||
private boolean isRunning;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TomcatServer.class);
|
||||
private static final boolean isInfo = LOG.isInfoEnabled();
|
||||
|
||||
public TomcatServer(String contextPath, int port, String appBase) throws MalformedURLException, ServletException {
|
||||
if(contextPath == null || appBase == null || appBase.length() == 0) {
|
||||
throw new IllegalArgumentException("Context path or appbase should not be null");
|
||||
}
|
||||
if(!contextPath.startsWith("/")) {
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
|
||||
tomcat.setBaseDir("."); // location where temp dir is created
|
||||
tomcat.setPort(port);
|
||||
tomcat.getHost().setAppBase(".");
|
||||
|
||||
tomcat.addWebapp(contextPath, appBase + "webapp");
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tomcat embedded server
|
||||
*/
|
||||
public void start() throws LifecycleException {
|
||||
tomcat.start();
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the tomcat embedded server
|
||||
*/
|
||||
public void stop() throws LifecycleException {
|
||||
if(!isRunning) {
|
||||
LOG.warn("Tomcat server is not running @ port={}", port);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isInfo) LOG.info("Stopping the Tomcat server");
|
||||
|
||||
tomcat.stop();
|
||||
tomcat.destroy();
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Context>
|
||||
|
||||
<Manager className="org.redisson.tomcat.RedissonSessionManager"
|
||||
configPath="${catalina.base}/src/test/webapp/WEB-INF/redisson.yaml" />
|
||||
|
||||
</Context>
|
@ -0,0 +1,3 @@
|
||||
singleServerConfig:
|
||||
address:
|
||||
- "//127.0.0.1:6379"
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
version="2.5">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<servlet-class>org.redisson.tomcat.TestServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>testServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>30</session-timeout>
|
||||
</session-config>
|
||||
|
||||
</web-app>
|
@ -1,13 +0,0 @@
|
||||
Copyright 2016 Nikita Koksharov
|
||||
|
||||
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.
|
Loading…
Reference in New Issue