├── .gitignore ├── src ├── test │ └── resources │ │ ├── log4j.properties │ │ ├── applicationContext.xml │ │ ├── applicationContext1.xml │ │ └── applicationContext2.xml └── main │ └── java │ └── cn │ └── uncode │ └── session │ ├── SessionListener.java │ ├── data │ ├── SessionCache.java │ ├── SerializeUtil.java │ ├── SessionCacheManager.java │ ├── redis │ │ ├── RedisSessionCache.java │ │ └── RedisSentinelPool.java │ ├── memcached │ │ ├── MemcachedSessionCache.java │ │ └── MemcachedPool.java │ └── SessionMap.java │ ├── SessionSharingFilter.java │ ├── SessionHttpServletRequestWrapper.java │ ├── MapSession.java │ └── HttpSessionWrapper.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # except for .gitignore 3 | !.gitignore 4 | 5 | # Ignore working directory # 6 | /target/ 7 | .classpath 8 | .project 9 | .settings/ 10 | *.iml 11 | .idea/ 12 | target/ -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootCategory=INFO, stdout 2 | 3 | log4j.appender.stdout.encoding=UTF-8 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss}\:%p(%L)%t %C - %M - %m%n 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/SessionListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright@xiaocong.tv 2012 3 | */ 4 | package cn.uncode.session; 5 | 6 | import javax.servlet.http.HttpSessionEvent; 7 | import javax.servlet.http.HttpSessionListener; 8 | 9 | /** 10 | * @author weijun.ye 11 | * @version 12 | * @date 2012-5-2 13 | */ 14 | public class SessionListener implements HttpSessionListener{ 15 | 16 | private static final int MAX_INACTIVE_INTERVAL = 1000*60*60*8; 17 | 18 | public void sessionCreated(HttpSessionEvent event) { 19 | //ignore 20 | } 21 | 22 | 23 | public void sessionDestroyed(HttpSessionEvent event) { 24 | //ignore 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/SessionCache.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data; 2 | 3 | 4 | 5 | /** 6 | * 7 | * @author yeweijun 8 | */ 9 | public interface SessionCache { 10 | 11 | /** 12 | * 存储session到分布式缓存 13 | * @param sessionId 当前会话id 14 | * @param sessionMap 值对象 15 | * @param timeout 过期时间 16 | */ 17 | public void put(String sessionId, SessionMap sessionMap, int timeout); 18 | 19 | 20 | /** 21 | * 从分布式缓存获取会话 22 | * @param sessionId 当前会话id 23 | * @return 会话对象 24 | */ 25 | public SessionMap get(String sessionId); 26 | 27 | 28 | /** 29 | * 设置会话有效时间 30 | * @param sessionId 当前会话id 31 | * @param interval 有效时间,单位秒 32 | */ 33 | public void setMaxInactiveInterval(String sessionId, int interval); 34 | 35 | 36 | /** 37 | * 销毁当前会话 38 | * @param sessionId 当前会话id 39 | */ 40 | public void destroy(String sessionId); 41 | 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/SerializeUtil.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.ObjectInputStream; 6 | import java.io.ObjectOutputStream; 7 | 8 | public class SerializeUtil { 9 | 10 | public static byte[] serialize(Object object) { 11 | ObjectOutputStream oos = null; 12 | ByteArrayOutputStream baos = null; 13 | try { 14 | // 序列化 15 | baos = new ByteArrayOutputStream(); 16 | oos = new ObjectOutputStream(baos); 17 | oos.writeObject(object); 18 | byte[] bytes = baos.toByteArray(); 19 | return bytes; 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | return null; 24 | } 25 | 26 | public static Object unserialize(byte[] bytes) { 27 | ByteArrayInputStream bais = null; 28 | try { 29 | // 反序列化 30 | bais = new ByteArrayInputStream(bytes); 31 | ObjectInputStream ois = new ObjectInputStream(bais); 32 | return ois.readObject(); 33 | } catch (Exception e) { 34 | 35 | } 36 | return null; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 127.0.0.1:11211 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /src/test/resources/applicationContext1.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/SessionSharingFilter.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletContext; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | import org.apache.commons.lang3.StringUtils; 15 | 16 | public class SessionSharingFilter implements Filter { 17 | 18 | private static final int MAX_ACTIVE_TIME = 60*30;//秒 19 | private static final String MAX_ACTIVE_TIME_KEY = "maxActiveTime"; 20 | 21 | private ServletContext servletContext; 22 | private int maxActiveTime = MAX_ACTIVE_TIME; 23 | 24 | @Override 25 | public void init(FilterConfig filterConfig) throws ServletException { 26 | servletContext = filterConfig.getServletContext(); 27 | String matString = filterConfig.getInitParameter(MAX_ACTIVE_TIME_KEY); 28 | if(StringUtils.isNotEmpty(matString)){ 29 | maxActiveTime = Integer.valueOf(matString); 30 | } 31 | } 32 | 33 | @Override 34 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 35 | throws IOException, ServletException { 36 | HttpServletRequest httpServletRequest = (HttpServletRequest)request; 37 | SessionHttpServletRequestWrapper sessionHttpServletRequestWrapper = new SessionHttpServletRequestWrapper(httpServletRequest, maxActiveTime, servletContext); 38 | chain.doFilter(sessionHttpServletRequestWrapper, response); 39 | } 40 | 41 | @Override 42 | public void destroy() { 43 | // TODO Auto-generated method stub 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/SessionCacheManager.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data; 2 | 3 | import cn.uncode.session.data.memcached.MemcachedPool; 4 | import cn.uncode.session.data.memcached.MemcachedSessionCache; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.context.ApplicationContext; 8 | import org.springframework.context.ApplicationContextAware; 9 | 10 | import cn.uncode.session.data.redis.RedisSessionCache; 11 | 12 | public class SessionCacheManager implements ApplicationContextAware { 13 | 14 | private static ApplicationContext applicationContext; 15 | private static final SessionCacheManager instance = new SessionCacheManager(); 16 | 17 | private SessionCache sessionCache; 18 | 19 | private String beanName; 20 | 21 | public static SessionCache getSessionCache() { 22 | if(instance.sessionCache != null){ 23 | return instance.sessionCache; 24 | }else{ 25 | if(StringUtils.isNotEmpty(instance.beanName)){ 26 | instance.sessionCache = (SessionCache) applicationContext.getBean(instance.beanName); 27 | }else{ 28 | if (applicationContext.isTypeMatch("cachePool", MemcachedPool.class)){ 29 | instance.sessionCache = new MemcachedSessionCache(); 30 | } else { 31 | instance.sessionCache = new RedisSessionCache(); 32 | } 33 | } 34 | } 35 | return instance.sessionCache; 36 | } 37 | 38 | @Override 39 | public void setApplicationContext(ApplicationContext applicationContext) 40 | throws BeansException { 41 | SessionCacheManager.applicationContext = applicationContext; 42 | } 43 | 44 | public void setSessionCache(SessionCache sessionCache) { 45 | instance.sessionCache = sessionCache; 46 | } 47 | 48 | public void setBeanName(String beanName) { 49 | instance.beanName = beanName; 50 | } 51 | 52 | public static ApplicationContext getApplicationContext(){ 53 | return applicationContext; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/resources/applicationContext2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 0/3 * * * * ? 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/SessionHttpServletRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session; 2 | 3 | import javax.servlet.ServletContext; 4 | import javax.servlet.http.Cookie; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletRequestWrapper; 7 | import javax.servlet.http.HttpSession; 8 | 9 | import cn.uncode.session.data.SessionCacheManager; 10 | import cn.uncode.session.data.SessionMap; 11 | 12 | public class SessionHttpServletRequestWrapper extends HttpServletRequestWrapper { 13 | 14 | public static final String CURRENT_SESSION_ATTR = SessionHttpServletRequestWrapper.class.getName(); 15 | 16 | 17 | private ServletContext servletContext; 18 | private int maxActiveTime; 19 | 20 | 21 | 22 | public SessionHttpServletRequestWrapper(HttpServletRequest request, int maxActiveTime, ServletContext servletContext) { 23 | super(request); 24 | this.servletContext = servletContext; 25 | this.maxActiveTime = maxActiveTime; 26 | } 27 | 28 | @Override 29 | public HttpSession getSession() { 30 | return getSession(true); 31 | } 32 | 33 | @Override 34 | public HttpSession getSession(boolean create) { 35 | 36 | HttpSessionWrapper currentSession = getCurrentSession(); 37 | if(currentSession != null) { 38 | return currentSession; 39 | } 40 | 41 | String sessionId = null; 42 | Cookie[] cookies = this.getCookies(); 43 | 44 | if(cookies != null){ 45 | for(Cookie cookie:cookies){ 46 | if("JSESSIONID".equals(cookie.getName().toUpperCase())){ 47 | sessionId = cookie.getValue(); 48 | } 49 | } 50 | } 51 | 52 | if(sessionId != null) { 53 | SessionMap sessionMap = SessionCacheManager.getSessionCache().get(sessionId); 54 | if(sessionMap != null) { 55 | currentSession = new HttpSessionWrapper(sessionMap, SessionCacheManager.getSessionCache(), maxActiveTime, servletContext); 56 | currentSession.setNew(false); 57 | setCurrentSession(currentSession); 58 | return currentSession; 59 | } 60 | } 61 | 62 | if(!create) { 63 | return null; 64 | } 65 | 66 | HttpSession httpSession = super.getSession(); 67 | SessionMap sessionMap = new SessionMap(httpSession); 68 | currentSession = new HttpSessionWrapper(sessionMap, SessionCacheManager.getSessionCache(), maxActiveTime, servletContext); 69 | SessionCacheManager.getSessionCache().put(sessionMap.getId(), sessionMap, maxActiveTime); 70 | setCurrentSession(currentSession); 71 | return currentSession; 72 | } 73 | 74 | private HttpSessionWrapper getCurrentSession() { 75 | return (HttpSessionWrapper) getAttribute(CURRENT_SESSION_ATTR); 76 | } 77 | 78 | private void setCurrentSession(HttpSessionWrapper currentSession) { 79 | if(currentSession == null) { 80 | removeAttribute(CURRENT_SESSION_ATTR); 81 | } else { 82 | setAttribute(CURRENT_SESSION_ATTR, currentSession); 83 | } 84 | } 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/redis/RedisSessionCache.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data.redis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import cn.uncode.session.data.SerializeUtil; 7 | import cn.uncode.session.data.SessionCache; 8 | import cn.uncode.session.data.SessionMap; 9 | import redis.clients.jedis.Jedis; 10 | 11 | public class RedisSessionCache implements SessionCache{ 12 | 13 | private static final Logger LOG =LoggerFactory.getLogger(RedisSessionCache.class); 14 | 15 | @Override 16 | public void put(String sessionId, SessionMap sessionMap, int timeout) { 17 | RedisSentinelPool jedisPool = null; 18 | Jedis jedis = null; 19 | try { 20 | jedisPool = RedisSentinelPool.getPool(); 21 | jedis = jedisPool.getResource(); 22 | jedis.set(sessionId.getBytes(), SerializeUtil.serialize(sessionMap)); 23 | jedis.expire(sessionId, timeout); 24 | } catch (Exception e) { 25 | LOG.error("Put session to redis error", e); 26 | } finally { 27 | jedisPool.returnResource(jedis); 28 | } 29 | } 30 | 31 | @Override 32 | public SessionMap get(String sessionId) { 33 | RedisSentinelPool jedisPool = null; 34 | Jedis jedis = null; 35 | SessionMap sessionMap = null; 36 | byte[] reslut = null; 37 | try { 38 | jedisPool = RedisSentinelPool.getPool(); 39 | jedis = jedisPool.getResource(); 40 | if (jedis.exists(sessionId)) { 41 | reslut = jedis.get(sessionId.getBytes()); 42 | sessionMap = (SessionMap) SerializeUtil.unserialize(reslut); 43 | } 44 | } catch (Exception e) { 45 | LOG.error("Read session from redis error", e); 46 | return null; 47 | } finally { 48 | jedisPool.returnResource(jedis); 49 | } 50 | return sessionMap; 51 | } 52 | 53 | @Override 54 | public void setMaxInactiveInterval(String sessionId, int interval) { 55 | RedisSentinelPool jedisPool = null; 56 | Jedis jedis = null; 57 | try { 58 | jedisPool = RedisSentinelPool.getPool(); 59 | jedis = jedisPool.getResource(); 60 | if (jedis.exists(sessionId)) { 61 | jedis.expire(sessionId, interval); 62 | } 63 | } catch (Exception e) { 64 | LOG.error("Set session max inactive interval to redis error", e); 65 | } finally { 66 | jedisPool.returnResource(jedis); 67 | } 68 | } 69 | 70 | @Override 71 | public void destroy(String sessionId) { 72 | RedisSentinelPool jedisPool = null; 73 | Jedis jedis = null; 74 | try { 75 | jedisPool = RedisSentinelPool.getPool(); 76 | jedis = jedisPool.getResource(); 77 | if (jedis.exists(sessionId)) { 78 | jedis.expire(sessionId, 0); 79 | } 80 | } catch (Exception e) { 81 | LOG.error("Destroy session from redis error", e); 82 | } finally { 83 | jedisPool.returnResource(jedis); 84 | } 85 | 86 | } 87 | 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/memcached/MemcachedSessionCache.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data.memcached; 2 | 3 | import cn.uncode.session.data.SerializeUtil; 4 | import cn.uncode.session.data.SessionCache; 5 | import cn.uncode.session.data.SessionMap; 6 | import com.whalin.MemCached.MemCachedClient; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * User: Antergone 14 | * Date: 16/2/25 15 | */ 16 | public class MemcachedSessionCache implements SessionCache { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(MemcachedSessionCache.class); 19 | 20 | @Override 21 | public void put(String sessionId, SessionMap sessionMap, int timeout) { 22 | MemcachedPool memcachedPool = null; 23 | MemCachedClient memCachedClient = null; 24 | try { 25 | memcachedPool = MemcachedPool.getPool(); 26 | memCachedClient = memcachedPool.getClient(); 27 | memCachedClient.set(sessionId, SerializeUtil.serialize(sessionMap), new Date(timeout * 1000)); 28 | } catch (Exception e) { 29 | LOG.error("Put session to memcached error", e); 30 | } 31 | } 32 | 33 | @Override 34 | public SessionMap get(String sessionId) { 35 | MemcachedPool memcachedPool = null; 36 | MemCachedClient memCachedClient = null; 37 | SessionMap sessionMap = null; 38 | try { 39 | memcachedPool = MemcachedPool.getPool(); 40 | memCachedClient = memcachedPool.getClient(); 41 | if (memCachedClient.keyExists(sessionId)) { 42 | sessionMap = (SessionMap) memCachedClient.get(sessionId); 43 | } 44 | } catch (Exception e) { 45 | LOG.error("Read session from memcached error", e); 46 | return null; 47 | } 48 | return sessionMap; 49 | } 50 | 51 | @Override 52 | public void setMaxInactiveInterval(String sessionId, int interval) { 53 | 54 | MemcachedPool memcachedPool = null; 55 | MemCachedClient memCachedClient = null; 56 | try { 57 | memcachedPool = MemcachedPool.getPool(); 58 | memCachedClient = memcachedPool.getClient(); 59 | if (memCachedClient.keyExists(sessionId)) { 60 | memCachedClient.set(sessionId, memCachedClient.get(sessionId), new Date(interval * 1000)); 61 | } 62 | } catch (Exception e) { 63 | LOG.error("Set session max inactive interval to memcached error", e); 64 | } 65 | 66 | } 67 | 68 | @Override 69 | public void destroy(String sessionId) { 70 | 71 | MemcachedPool memcachedPool = null; 72 | MemCachedClient memCachedClient = null; 73 | try { 74 | memcachedPool = MemcachedPool.getPool(); 75 | memCachedClient = memcachedPool.getClient(); 76 | memCachedClient.delete(sessionId); 77 | } catch (Exception e) { 78 | LOG.error("Destroy session from memcached error", e); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #uncode-session 2 | 3 | 4 | 非常小巧的集群session共享组件,代码千行以内,避免使用应用容器插件的多种烦恼。 5 | 6 | 7 | 8 | # 功能概述 9 | 10 | 1. 非常小巧的集群session公享组件,类似于spring-session。 11 | 2. 总代码不超过1000行。 12 | 3. 易于使用和扩展。 13 | 14 | 15 | 16 | 17 | ------------------------------------------------------------------------ 18 | 19 | # 配置 20 | 21 | ## 1. web.xml 22 | 23 | 24 | 25 | SessionSharingFilter 26 | cn.uncode.session.SessionSharingFilter 27 | 28 | 29 | SessionSharingFilter 30 | /* 31 | 32 | 33 | ## 2. 基于Redis的Spring配置 34 | 35 | 36 | 37 | <<<<<<< HEAD 38 | 39 | 40 | ======= 41 | 42 | >>>>>>> 58c22ae632334d37423c07f378e242ffea23648e 43 | 44 | 45 | 127.0.0.1:26379 46 | 127.0.0.2:26379 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | ## 3. 基于Memcached的Spring配置 57 | 58 | 59 | 60 | 61 | 127.0.0.1:11211 62 | 63 | 64 | 65 | ------------------------------------------------------------------------ 66 | 67 | # 自定义扩展 68 | 69 | 70 | ## 1. 自定义实现类 71 | 72 | public class CustomSessionCache implements SessionCache{ 73 | 74 | @Override 75 | public void put(String sessionId, SessionMap sessionMap, int timeout) { 76 | 77 | } 78 | 79 | @Override 80 | public SessionMap get(String sessionId) { 81 | 82 | } 83 | 84 | @Override 85 | public void setMaxInactiveInterval(String sessionId, int interval) { 86 | 87 | } 88 | 89 | @Override 90 | public void destroy(String sessionId) { 91 | 92 | } 93 | } 94 | 95 | 96 | ## 2. 配置管理器 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 109 | 110 | 111 | ------------------------------------------------------------------------ 112 | 113 | 114 | # 版权 115 | 116 | 作者:冶卫军(ywj_316@qq.com) 117 | 贡献开发:马煜 118 | 119 | 技术支持QQ群:47306892 120 | 121 | Copyright 2016 www.uncode.cn 122 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/MapSession.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session; 2 | 3 | import java.io.Serializable; 4 | import java.util.Enumeration; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.UUID; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import javax.servlet.http.HttpSession; 12 | 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | public class MapSession implements Serializable { 16 | 17 | private static final long serialVersionUID = 3455295045889076281L; 18 | 19 | private String id = UUID.randomUUID().toString(); 20 | private Map sessionAttrs = new HashMap(); 21 | private long creationTime = System.currentTimeMillis(); 22 | private long lastAccessedTime = creationTime; 23 | private int maxInactiveInterval; 24 | 25 | 26 | public MapSession(HttpSession session) { 27 | if(session == null) { 28 | throw new IllegalArgumentException("session cannot be null"); 29 | } 30 | this.id = session.getId(); 31 | this.sessionAttrs = new HashMap(); 32 | Enumeration names = session.getAttributeNames(); 33 | while (names.hasMoreElements()) { 34 | String name = (String) names.nextElement(); 35 | Object attrValue = session.getAttribute(name); 36 | if(StringUtils.isNotEmpty(name) && attrValue != null){ 37 | this.sessionAttrs.put(name, attrValue); 38 | } 39 | } 40 | this.lastAccessedTime = session.getLastAccessedTime(); 41 | this.creationTime = session.getCreationTime(); 42 | this.maxInactiveInterval = session.getMaxInactiveInterval(); 43 | } 44 | 45 | 46 | public String getId() { 47 | return id; 48 | } 49 | 50 | public Map getSessionAttrs() { 51 | return sessionAttrs; 52 | } 53 | 54 | public long getCreationTime() { 55 | return creationTime; 56 | } 57 | 58 | public long getLastAccessedTime() { 59 | return lastAccessedTime; 60 | } 61 | 62 | public int getMaxInactiveInterval() { 63 | return maxInactiveInterval; 64 | } 65 | 66 | public boolean isExpired() { 67 | return isExpired(System.currentTimeMillis()); 68 | } 69 | 70 | boolean isExpired(long now) { 71 | if(maxInactiveInterval < 0) { 72 | return false; 73 | } 74 | return now - TimeUnit.SECONDS.toMillis(maxInactiveInterval) >= lastAccessedTime; 75 | } 76 | 77 | public boolean equals(Object obj) { 78 | return obj instanceof MapSession && id.equals(((MapSession) obj).getId()); 79 | } 80 | 81 | public int hashCode() { 82 | return id.hashCode(); 83 | } 84 | 85 | public Object getAttribute(String attributeName) { 86 | return sessionAttrs.get(attributeName); 87 | } 88 | 89 | public Set getAttributeNames() { 90 | return sessionAttrs.keySet(); 91 | } 92 | 93 | public void setAttribute(String attributeName, Object attributeValue) { 94 | sessionAttrs.put(attributeName, attributeValue); 95 | 96 | } 97 | 98 | public void removeAttribute(String attributeName) { 99 | sessionAttrs.remove(attributeName); 100 | } 101 | 102 | public void setMaxInactiveInterval(int interval) { 103 | this.maxInactiveInterval = interval; 104 | } 105 | 106 | public void setId(String id) { 107 | this.id = id; 108 | } 109 | 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/SessionMap.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data; 2 | 3 | import java.io.Serializable; 4 | import java.util.Enumeration; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.UUID; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import javax.servlet.http.HttpSession; 12 | 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | public class SessionMap implements Serializable { 16 | 17 | private static final long serialVersionUID = 3455295045889076281L; 18 | 19 | private String id = UUID.randomUUID().toString(); 20 | private Map sessionAttrs = new HashMap(); 21 | private long creationTime = System.currentTimeMillis(); 22 | private long lastAccessedTime = creationTime; 23 | private int maxInactiveInterval; 24 | 25 | 26 | public SessionMap(HttpSession session) { 27 | if(session == null) { 28 | throw new IllegalArgumentException("session cannot be null"); 29 | } 30 | this.id = session.getId(); 31 | this.sessionAttrs = new HashMap(); 32 | Enumeration names = session.getAttributeNames(); 33 | while (names.hasMoreElements()) { 34 | String name = (String) names.nextElement(); 35 | Object attrValue = session.getAttribute(name); 36 | if(StringUtils.isNotEmpty(name) && attrValue != null){ 37 | this.sessionAttrs.put(name, attrValue); 38 | } 39 | } 40 | this.lastAccessedTime = session.getLastAccessedTime(); 41 | this.creationTime = session.getCreationTime(); 42 | this.maxInactiveInterval = session.getMaxInactiveInterval(); 43 | } 44 | 45 | 46 | public String getId() { 47 | return id; 48 | } 49 | 50 | public Map getSessionAttrs() { 51 | return sessionAttrs; 52 | } 53 | 54 | public long getCreationTime() { 55 | return creationTime; 56 | } 57 | 58 | public long getLastAccessedTime() { 59 | return lastAccessedTime; 60 | } 61 | 62 | public int getMaxInactiveInterval() { 63 | return maxInactiveInterval; 64 | } 65 | 66 | public boolean isExpired() { 67 | return isExpired(System.currentTimeMillis()); 68 | } 69 | 70 | boolean isExpired(long now) { 71 | if(maxInactiveInterval < 0) { 72 | return false; 73 | } 74 | return now - TimeUnit.SECONDS.toMillis(maxInactiveInterval) >= lastAccessedTime; 75 | } 76 | 77 | public boolean equals(Object obj) { 78 | return obj instanceof SessionMap && id.equals(((SessionMap) obj).getId()); 79 | } 80 | 81 | public int hashCode() { 82 | return id.hashCode(); 83 | } 84 | 85 | public Object getAttribute(String attributeName) { 86 | return sessionAttrs.get(attributeName); 87 | } 88 | 89 | public Set getAttributeNames() { 90 | return sessionAttrs.keySet(); 91 | } 92 | 93 | public void setAttribute(String attributeName, Object attributeValue) { 94 | sessionAttrs.put(attributeName, attributeValue); 95 | 96 | } 97 | 98 | public void removeAttribute(String attributeName) { 99 | sessionAttrs.remove(attributeName); 100 | } 101 | 102 | public void setMaxInactiveInterval(int interval) { 103 | this.maxInactiveInterval = interval; 104 | } 105 | 106 | public void setId(String id) { 107 | this.id = id; 108 | } 109 | 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/redis/RedisSentinelPool.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data.redis; 2 | 3 | import java.util.HashSet; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 9 | import org.springframework.beans.factory.DisposableBean; 10 | import org.springframework.beans.factory.InitializingBean; 11 | 12 | import cn.uncode.session.data.SessionCacheManager; 13 | import redis.clients.jedis.Jedis; 14 | import redis.clients.jedis.JedisSentinelPool; 15 | 16 | public class RedisSentinelPool implements InitializingBean,DisposableBean{ 17 | 18 | private JedisSentinelPool jedisSentinelPool; 19 | 20 | private List hosts; 21 | private String auth; 22 | private int maxIdle = 5; 23 | private int maxTotal = 20; 24 | private int maxWaitMillis = 10000; 25 | private boolean testOnBorrow = true; 26 | private String name = "redis_master"; 27 | 28 | public static RedisSentinelPool getPool(){ 29 | return (RedisSentinelPool) SessionCacheManager.getApplicationContext().getBean(RedisSentinelPool.class); 30 | } 31 | 32 | public void setHosts(List hosts) { 33 | this.hosts = hosts; 34 | } 35 | 36 | public void setAuth(String auth) { 37 | this.auth = auth; 38 | } 39 | 40 | public void setName(String name) { 41 | if(StringUtils.isNotEmpty(name)){ 42 | this.name = name; 43 | } 44 | } 45 | 46 | public void setMaxIdle(int maxIdle) { 47 | this.maxIdle = maxIdle; 48 | } 49 | 50 | public void setMaxTotal(int maxTotal) { 51 | this.maxTotal = maxTotal; 52 | } 53 | 54 | public void setMaxWaitMillis(int maxWaitMillis) { 55 | this.maxWaitMillis = maxWaitMillis; 56 | } 57 | 58 | public void setTestOnBorrow(boolean testOnBorrow) { 59 | this.testOnBorrow = testOnBorrow; 60 | } 61 | 62 | public Jedis getResource() { 63 | if (jedisSentinelPool != null) 64 | return jedisSentinelPool.getResource(); 65 | return null; 66 | } 67 | 68 | public void returnResource(final Jedis jedis) { 69 | if (jedis != null) { 70 | if(jedisSentinelPool !=null) 71 | jedisSentinelPool.returnResource(jedis); 72 | } 73 | } 74 | 75 | public String getCurrentHostMaster(){ 76 | if(jedisSentinelPool !=null){ 77 | return jedisSentinelPool.getCurrentHostMaster().toString(); 78 | } 79 | return ""; 80 | } 81 | 82 | @Override 83 | public void destroy() throws Exception { 84 | if(jedisSentinelPool != null){ 85 | jedisSentinelPool.close(); 86 | } 87 | } 88 | 89 | @Override 90 | public void afterPropertiesSet() throws Exception { 91 | Set set = new HashSet(); 92 | if(null != hosts){ 93 | for(String host:hosts){ 94 | set.add(host); 95 | } 96 | GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 97 | poolConfig.setMaxIdle(maxIdle); 98 | poolConfig.setMaxTotal(maxTotal); 99 | poolConfig.setMaxWaitMillis(maxWaitMillis); 100 | poolConfig.setTestOnBorrow(testOnBorrow); 101 | jedisSentinelPool = new JedisSentinelPool(name, set,poolConfig, auth); 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/HttpSessionWrapper.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session; 2 | 3 | import java.util.Collections; 4 | import java.util.Enumeration; 5 | import java.util.Set; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.http.HttpSession; 9 | import javax.servlet.http.HttpSessionContext; 10 | 11 | import cn.uncode.session.data.SessionCache; 12 | import cn.uncode.session.data.SessionMap; 13 | 14 | public class HttpSessionWrapper implements HttpSession { 15 | 16 | private final ServletContext servletContext; 17 | private SessionMap sessionMap; 18 | private SessionCache sessionCache; 19 | private boolean invalidated; 20 | private boolean old; 21 | private int maxActiveTime; 22 | 23 | 24 | 25 | public HttpSessionWrapper(SessionMap sessionMap, SessionCache sessionCache, int maxActiveTime, ServletContext servletContext) { 26 | this.sessionMap = sessionMap; 27 | this.sessionCache = sessionCache; 28 | this.maxActiveTime = maxActiveTime; 29 | this.servletContext = servletContext; 30 | } 31 | 32 | public void setMaxInactiveInterval(int interval) { 33 | sessionMap.setMaxInactiveInterval(interval); 34 | sessionCache.setMaxInactiveInterval(sessionMap.getId(), interval); 35 | } 36 | 37 | public void setAttribute(String name, Object value) { 38 | sessionMap.setAttribute(name, value); 39 | sessionCache.put(sessionMap.getId(), sessionMap, maxActiveTime); 40 | } 41 | 42 | public void removeAttribute(String name) { 43 | sessionMap.removeAttribute(name); 44 | sessionCache.put(sessionMap.getId(), sessionMap, maxActiveTime); 45 | } 46 | 47 | public void putValue(String name, Object value) { 48 | setAttribute(name, value); 49 | } 50 | 51 | public void removeValue(String name) { 52 | removeAttribute(name); 53 | } 54 | 55 | public long getCreationTime() { 56 | return sessionMap.getCreationTime(); 57 | } 58 | 59 | public String getId() { 60 | return sessionMap.getId(); 61 | } 62 | 63 | public long getLastAccessedTime() { 64 | return sessionMap.getLastAccessedTime(); 65 | } 66 | 67 | public ServletContext getServletContext() { 68 | return servletContext; 69 | } 70 | 71 | 72 | public int getMaxInactiveInterval() { 73 | return sessionMap.getMaxInactiveInterval(); 74 | } 75 | 76 | 77 | public Object getAttribute(String name) { 78 | return sessionMap.getAttribute(name); 79 | } 80 | 81 | public Object getValue(String name) { 82 | return getAttribute(name); 83 | } 84 | 85 | public Enumeration getAttributeNames() { 86 | return Collections.enumeration(sessionMap.getAttributeNames()); 87 | } 88 | 89 | public String[] getValueNames() { 90 | Set attrs = sessionMap.getAttributeNames(); 91 | return attrs.toArray(new String[0]); 92 | } 93 | 94 | public void invalidate() { 95 | this.invalidated = true; 96 | sessionCache.destroy(sessionMap.getId()); 97 | setCurrentSession(null); 98 | } 99 | 100 | public void setNew(boolean isNew) { 101 | this.old = !isNew; 102 | } 103 | 104 | public boolean isNew() { 105 | return !old; 106 | } 107 | 108 | @Override 109 | public HttpSessionContext getSessionContext() { 110 | return null; 111 | } 112 | 113 | private void setCurrentSession(HttpSessionWrapper currentSession) { 114 | if(currentSession == null) { 115 | removeAttribute(SessionHttpServletRequestWrapper.CURRENT_SESSION_ATTR); 116 | } else { 117 | setAttribute(SessionHttpServletRequestWrapper.CURRENT_SESSION_ATTR, currentSession); 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/cn/uncode/session/data/memcached/MemcachedPool.java: -------------------------------------------------------------------------------- 1 | package cn.uncode.session.data.memcached; 2 | 3 | import cn.uncode.session.data.SessionCacheManager; 4 | import com.whalin.MemCached.MemCachedClient; 5 | import com.whalin.MemCached.SockIOPool; 6 | import org.springframework.beans.factory.DisposableBean; 7 | import org.springframework.beans.factory.InitializingBean; 8 | 9 | /** 10 | * User: Antergone 11 | * Date: 16/2/25 12 | */ 13 | public class MemcachedPool implements InitializingBean, DisposableBean { 14 | 15 | private SockIOPool sockIOPool; 16 | private MemCachedClient memCachedClient; 17 | 18 | private String[] hosts; 19 | private Integer[] weights; 20 | private boolean failOver = true; 21 | private int initConn = 5; 22 | private int minConn = 5; 23 | private int maxConn = 200; 24 | private int maxIdle = 30 * 30 * 1000; 25 | private int mainThreadSleep = 30; 26 | private boolean nagle = false; 27 | private boolean aliveCheck = true; 28 | private int socketTO = 30; 29 | private int socketConnectTO = 0; 30 | 31 | public static MemcachedPool getPool() { 32 | return (MemcachedPool) SessionCacheManager.getApplicationContext().getBean(MemcachedPool.class); 33 | } 34 | 35 | public void setHosts(String[] hosts) { 36 | this.hosts = hosts; 37 | } 38 | 39 | public void setWeights(Integer[] weights) { 40 | this.weights = weights; 41 | } 42 | 43 | public void setFailOver(boolean failOver) { 44 | this.failOver = failOver; 45 | } 46 | 47 | public void setInitConn(int initConn) { 48 | this.initConn = initConn; 49 | } 50 | 51 | public void setMinConn(int minConn) { 52 | this.minConn = minConn; 53 | } 54 | 55 | public void setMaxConn(int maxConn) { 56 | this.maxConn = maxConn; 57 | } 58 | 59 | public void setMaxIdle(int maxIdle) { 60 | this.maxIdle = maxIdle; 61 | } 62 | 63 | public void setMainThreadSleep(int mainThreadSleep) { 64 | this.mainThreadSleep = mainThreadSleep; 65 | } 66 | 67 | public void setNagle(boolean nagle) { 68 | this.nagle = nagle; 69 | } 70 | 71 | public void setAliveCheck(boolean aliveCheck) { 72 | this.aliveCheck = aliveCheck; 73 | } 74 | 75 | public void setSocketTO(int socketTO) { 76 | this.socketTO = socketTO; 77 | } 78 | 79 | public void setSocketConnectTO(int socketConnectTO) { 80 | this.socketConnectTO = socketConnectTO; 81 | } 82 | 83 | public MemCachedClient getClient() { 84 | if (memCachedClient != null) 85 | return memCachedClient; 86 | return null; 87 | } 88 | 89 | 90 | @Override 91 | public void destroy() throws Exception { 92 | if (sockIOPool != null) { 93 | sockIOPool.shutDown(); 94 | } 95 | } 96 | 97 | @Override 98 | public void afterPropertiesSet() throws Exception { 99 | if (null != hosts) { 100 | this.sockIOPool = SockIOPool.getInstance(); 101 | System.out.println("server are" + hosts); 102 | sockIOPool.setServers(hosts); 103 | sockIOPool.setWeights(weights); 104 | sockIOPool.setFailover(failOver); 105 | sockIOPool.setInitConn(initConn); 106 | sockIOPool.setMinConn(minConn); 107 | sockIOPool.setMaxConn(maxConn); 108 | sockIOPool.setMaxIdle(maxIdle); 109 | sockIOPool.setMaintSleep(mainThreadSleep); 110 | sockIOPool.setNagle(nagle); 111 | sockIOPool.setSocketTO(socketTO); 112 | sockIOPool.setAliveCheck(aliveCheck); 113 | sockIOPool.setSocketConnectTO(socketConnectTO); 114 | sockIOPool.initialize(); 115 | memCachedClient = new MemCachedClient(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | cn.uncode 6 | uncode-session 7 | 1.0.0 8 | jar 9 | 10 | uncode-session 11 | Java session sharing components. 12 | http://git.oschina.net/uncode/uncode-session 13 | 14 | 15 | 16 | The Apache Software License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | 23 | yeweijun 24 | ywj_316@qq.com 25 | http://www.uncode.cn 26 | +8 27 | 28 | 29 | 30 | 31 | scm:git:git@git.oschina.net:uncode/uncode-session.git 32 | scm:git:git@git.oschina.net:uncode/uncode-session.git 33 | git@git.oschina.net:uncode/uncode-session.git 34 | 35 | 36 | 37 | true 38 | true 39 | 3.1 40 | 2.3 41 | 2.9.1 42 | 1.5 43 | UTF-8 44 | UTF-8 45 | 1.7 46 | 3.2.0.RELEASE 47 | 2.4.1 48 | 1.2.16 49 | 4.8.1 50 | 5.1.23 51 | 1.6.4 52 | 3.1 53 | 1.9.2 54 | 3.0.4 55 | 1.0.6 56 | 3.2.2 57 | 2.5 58 | 59 | 1.1.1 60 | 3.3.2 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.springframework 70 | spring-core 71 | ${spring.version} 72 | 73 | 74 | org.springframework 75 | spring-context 76 | ${spring.version} 77 | 78 | 79 | org.springframework 80 | spring-context-support 81 | ${spring.version} 82 | 83 | 84 | org.springframework 85 | spring-beans 86 | ${spring.version} 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | javax.servlet 96 | servlet-api 97 | ${servlet.version} 98 | provided 99 | 100 | 101 | 102 | 103 | 104 | 105 | log4j 106 | log4j 107 | ${log4j.version} 108 | 109 | 110 | org.slf4j 111 | slf4j-api 112 | ${slf4j.version} 113 | 114 | 115 | org.slf4j 116 | slf4j-log4j12 117 | ${slf4j.version} 118 | 119 | 120 | 121 | 122 | 123 | redis.clients 124 | jedis 125 | 2.7.3 126 | 127 | 128 | 129 | 130 | 131 | com.whalin 132 | Memcached-Java-Client 133 | 3.0.2 134 | 135 | 136 | 137 | 138 | 139 | org.apache.commons 140 | commons-lang3 141 | ${commons.lang3.version} 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | junit 150 | junit 151 | ${junit.version} 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | maven-compiler-plugin 160 | 161 | ${jdk.version} 162 | ${jdk.version} 163 | 164 | 165 | 166 | org.apache.maven.plugins 167 | maven-source-plugin 168 | ${version.source-plugin} 169 | 170 | 171 | 172 | jar-no-fork 173 | 174 | 175 | 176 | 177 | true 178 | 179 | 180 | 181 | org.apache.maven.plugins 182 | maven-javadoc-plugin 183 | ${version.javadoc-plugin} 184 | 185 | 186 | package 187 | 188 | jar 189 | 190 | 191 | 192 | 193 | 194 | http://docs.oracle.com/javase/7/docs/api 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | release 204 | 205 | 206 | 207 | 208 | 209 | org.apache.maven.plugins 210 | maven-compiler-plugin 211 | ${version.compiler-plugin} 212 | 213 | ${jdk.version} 214 | ${jdk.version} 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-source-plugin 221 | ${version.source-plugin} 222 | 223 | 224 | package 225 | 226 | jar-no-fork 227 | 228 | 229 | 230 | 231 | 232 | 233 | org.apache.maven.plugins 234 | maven-javadoc-plugin 235 | ${version.javadoc-plugin} 236 | 237 | 238 | package 239 | 240 | jar 241 | 242 | 243 | 244 | 245 | 246 | 247 | org.apache.maven.plugins 248 | maven-gpg-plugin 249 | ${version.gpg-plugin} 250 | 251 | 252 | sign-artifacts 253 | verify 254 | 255 | sign 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | uncode 265 | https://oss.sonatype.org/content/repositories/snapshots/ 266 | 267 | 268 | uncode 269 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 270 | 271 | 272 | 273 | 274 | 275 | 276 | --------------------------------------------------------------------------------