提交 7a51fc3b 编写于 作者: N Nikita

Tomcat 9 compatible module added. #1211

上级 9212ac5d
......@@ -17,6 +17,7 @@
<module>redisson-tomcat-6</module>
<module>redisson-tomcat-7</module>
<module>redisson-tomcat-8</module>
<module>redisson-tomcat-9</module>
</modules>
<build>
......
<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.10.8-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>redisson-tomcat-9</artifactId>
<packaging>jar</packaging>
<name>Redisson/Tomcat-9</name>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>9.0.0.M6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.2</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>
/**
* 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.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap;
import org.redisson.tomcat.RedissonSessionManager.ReadMode;
/**
* Redisson Session object for Apache Tomcat
*
* @author Nikita Koksharov
*
*/
public class RedissonSession extends StandardSession {
private final RedissonSessionManager redissonManager;
private final Map<String, Object> attrs;
private RMap<String, Object> map;
private final RedissonSessionManager.ReadMode readMode;
public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode) {
super(manager);
this.redissonManager = manager;
this.readMode = readMode;
try {
Field attr = StandardSession.class.getDeclaredField("attributes");
attrs = (Map<String, Object>) attr.get(this);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private static final long serialVersionUID = -2518607181636076487L;
@Override
public Object getAttribute(String name) {
if (readMode == ReadMode.REDIS) {
return map.get(name);
}
return super.getAttribute(name);
}
@Override
public void setId(String id, boolean notify) {
super.setId(id, notify);
map = redissonManager.getMap(id);
}
public void delete() {
map.delete();
map = null;
}
@Override
public void setCreationTime(long time) {
super.setCreationTime(time);
if (map != null) {
Map<String, Object> newMap = new HashMap<String, Object>(3);
newMap.put("session:creationTime", creationTime);
newMap.put("session:lastAccessedTime", lastAccessedTime);
newMap.put("session:thisAccessedTime", thisAccessedTime);
map.putAll(newMap);
}
}
@Override
public void access() {
super.access();
if (map != null) {
Map<String, Object> newMap = new HashMap<String, Object>(2);
newMap.put("session:lastAccessedTime", lastAccessedTime);
newMap.put("session:thisAccessedTime", thisAccessedTime);
map.putAll(newMap);
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) {
if (!isValid && !map.isExists()) {
return;
}
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 (map != null) {
map.fastRemove(name);
}
}
public void save() {
Map<String, Object> newMap = new HashMap<String, Object>();
newMap.put("session:creationTime", creationTime);
newMap.put("session:lastAccessedTime", lastAccessedTime);
newMap.put("session:thisAccessedTime", thisAccessedTime);
newMap.put("session:maxInactiveInterval", maxInactiveInterval);
newMap.put("session:isValid", isValid);
newMap.put("session:isNew", isNew);
if (attrs != null) {
for (Entry<String, Object> entry : attrs.entrySet()) {
newMap.put(entry.getKey(), entry.getValue());
}
}
map.putAll(newMap);
if (maxInactiveInterval >= 0) {
map.expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
}
}
public void load(Map<String, Object> attrs) {
Long creationTime = (Long) attrs.remove("session:creationTime");
if (creationTime != null) {
this.creationTime = creationTime;
}
Long lastAccessedTime = (Long) attrs.remove("session:lastAccessedTime");
if (lastAccessedTime != null) {
this.lastAccessedTime = lastAccessedTime;
}
Long thisAccessedTime = (Long) attrs.remove("session:thisAccessedTime");
if (thisAccessedTime != null) {
this.thisAccessedTime = thisAccessedTime;
}
Boolean isValid = (Boolean) attrs.remove("session:isValid");
if (isValid != null) {
this.isValid = isValid;
}
Boolean isNew = (Boolean) attrs.remove("session:isNew");
if (isNew != null) {
this.isNew = isNew;
}
for (Entry<String, Object> entry : attrs.entrySet()) {
setAttribute(entry.getKey(), entry.getValue(), false);
}
}
}
/**
* 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 java.util.Map;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.session.ManagerBase;
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.client.codec.Codec;
import org.redisson.config.Config;
/**
* Redisson Session Manager for Apache Tomcat
*
* @author Nikita Koksharov
*
*/
public class RedissonSessionManager extends ManagerBase {
public enum ReadMode {REDIS, MEMORY}
private final Log log = LogFactory.getLog(RedissonSessionManager.class);
private RedissonClient redisson;
private String configPath;
private ReadMode readMode = ReadMode.MEMORY;
public String getReadMode() {
return readMode.toString();
}
public void setReadMode(String readMode) {
this.readMode = ReadMode.valueOf(readMode);
}
public void setConfigPath(String configPath) {
this.configPath = configPath;
}
public String getConfigPath() {
return configPath;
}
@Override
public String getName() {
return RedissonSessionManager.class.getSimpleName();
}
@Override
public void load() throws ClassNotFoundException, IOException {
}
@Override
public void unload() throws IOException {
}
@Override
public Session createSession(String sessionId) {
RedissonSession session = (RedissonSession) createEmptySession();
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(getContext().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) {
Map<String, Object> attrs = getMap(id).readAllMap();
if (attrs.isEmpty() || !Boolean.valueOf(String.valueOf(attrs.get("session:isValid")))) {
log.info("Session " + id + " can't be found");
return null;
}
RedissonSession session = (RedissonSession) createEmptySession();
session.setId(id);
session.load(attrs);
session.access();
session.endAccess();
return session;
}
return result;
}
@Override
public Session createEmptySession() {
return new RedissonSession(this, readMode);
}
@Override
public void remove(Session session, boolean update) {
super.remove(session, update);
if (session.getIdInternal() != null) {
((RedissonSession)session).delete();
}
}
public RedissonClient getRedisson() {
return redisson;
}
@Override
protected void startInternal() throws LifecycleException {
super.startInternal();
redisson = buildClient();
setState(LifecycleState.STARTING);
}
protected RedissonClient buildClient() throws LifecycleException {
Config config = null;
try {
config = Config.fromJSON(new File(configPath), getClass().getClassLoader());
} catch (IOException e) {
// trying next format
try {
config = Config.fromYAML(new File(configPath), getClass().getClassLoader());
} catch (IOException e1) {
log.error("Can't parse json config " + configPath, e);
throw new LifecycleException("Can't parse yaml config " + configPath, e1);
}
}
try {
Config c = new Config(config);
Codec codec = c.getCodec().getClass().getConstructor(ClassLoader.class)
.newInstance(Thread.currentThread().getContextClassLoader());
config.setCodec(codec);
return Redisson.create(config);
} catch (Exception e) {
throw new LifecycleException(e);
}
}
@Override
protected void stopInternal() throws LifecycleException {
super.stopInternal();
setState(LifecycleState.STOPPING);
try {
if (redisson != null) {
redisson.shutdown();
}
} catch (Exception e) {
throw new LifecycleException(e);
}
}
}
package org.redisson.tomcat;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
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;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
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 {
File f = Paths.get("").toAbsolutePath().resolve("src/test/webapp/WEB-INF/redisson.yaml").toFile();
Config config = Config.fromYAML(f);
RedissonClient r = Redisson.create(config);
r.getKeys().flushall();
// 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");
invalidate(executor);
Executor.closeIdleConnections();
server.stop();
Assert.assertEquals(0, r.getKeys().count());
}
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);
}
}
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");
}
}
}
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
* @throws InterruptedException
*/
public void start() throws LifecycleException, InterruptedException {
tomcat.start();
tomcat.getConnector();
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();
tomcat.getServer().await();
isRunning = false;
}
public boolean isRunning() {
return isRunning;
}
}
\ No newline at end of file
<?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>
\ No newline at end of file
singleServerConfig:
address: "redis://127.0.0.1:6379"
\ No newline at end of file
<?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_3_0.xsd"
version="3.0">
<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>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册