Created
March 31, 2015 04:03
-
-
Save caoxudong/87c54be9105bc8cf18b7 to your computer and use it in GitHub Desktop.
Memcached-based shared session
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beans xmlns="http://www.springframework.org/schema/beans" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:context="http://www.springframework.org/schema/context" | |
xsi:schemaLocation=" | |
http://www.springframework.org/schema/beans | |
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd | |
http://www.springframework.org/schema/context | |
http://www.springframework.org/schema/context/spring-context-4.1.xsd"> | |
<bean id="memcachedPlainCallbackHandler" | |
class="net.spy.memcached.auth.PlainCallbackHandler"> | |
<constructor-arg><value>${ocs.username}</value></constructor-arg> | |
<constructor-arg><value>${ocs.password}</value></constructor-arg> | |
</bean> | |
<bean id="authDescriptor" class="net.spy.memcached.auth.AuthDescriptor"> | |
<constructor-arg> | |
<array><value>PLAIN</value></array> | |
</constructor-arg> | |
<constructor-arg><ref bean="memcachedPlainCallbackHandler"/></constructor-arg> | |
</bean> | |
<bean id="memcachedClient" | |
class="net.spy.memcached.spring.MemcachedClientFactoryBean"> | |
<property name="servers" value="${ocs.host}:${ocs.port}"/> | |
<property name="protocol" value="BINARY"/> | |
<property name="transcoder"> | |
<bean class="net.spy.memcached.transcoders.SerializingTranscoder"> | |
<property name="compressionThreshold" value="1024"/> | |
</bean> | |
</property> | |
<property name="opTimeout" value="3000"/> | |
<property name="timeoutExceptionThreshold" value="1998"/> | |
<property name="hashAlg"> | |
<value type="net.spy.memcached.DefaultHashAlgorithm">KETAMA_HASH</value> | |
</property> | |
<property name="locatorType" value="CONSISTENT"/> | |
<property name="failureMode" value="Redistribute"/> | |
<property name="useNagleAlgorithm" value="false"/> | |
<property name="authDescriptor" ref="authDescriptor"/> | |
<property name="needAuth" value="${ocs.needAuth}"/> | |
</bean> | |
<bean id="configMemcachedPlainCallbackHandler" | |
class="net.spy.memcached.auth.PlainCallbackHandler"> | |
<constructor-arg><value>${config.data.ocs.username}</value></constructor-arg> | |
<constructor-arg><value>${config.data.ocs.password}</value></constructor-arg> | |
</bean> | |
<bean id="configAuthDescriptor" class="net.spy.memcached.auth.AuthDescriptor"> | |
<constructor-arg> | |
<array><value>PLAIN</value></array> | |
</constructor-arg> | |
<constructor-arg><ref bean="configMemcachedPlainCallbackHandler"/></constructor-arg> | |
</bean> | |
<bean id="configMemcachedClient" | |
class="net.spy.memcached.spring.MemcachedClientFactoryBean"> | |
<property name="servers" value="${config.data.ocs.host}:${config.data.ocs.port}"/> | |
<property name="protocol" value="BINARY"/> | |
<property name="transcoder"> | |
<bean class="net.spy.memcached.transcoders.SerializingTranscoder"> | |
<property name="compressionThreshold" value="1024"/> | |
</bean> | |
</property> | |
<property name="opTimeout" value="3000"/> | |
<property name="timeoutExceptionThreshold" value="1998"/> | |
<property name="hashAlg"> | |
<value type="net.spy.memcached.DefaultHashAlgorithm">KETAMA_HASH</value> | |
</property> | |
<property name="locatorType" value="CONSISTENT"/> | |
<property name="failureMode" value="Redistribute"/> | |
<property name="useNagleAlgorithm" value="false"/> | |
<property name="authDescriptor" ref="configAuthDescriptor"/> | |
<property name="needAuth" value="${config.data.ocs.needAuth}"/> | |
</bean> | |
</beans> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session.sharedsession; | |
import java.io.IOException; | |
import javax.servlet.Filter; | |
import javax.servlet.FilterChain; | |
import javax.servlet.FilterConfig; | |
import javax.servlet.ServletContext; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import javax.servlet.http.Cookie; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import javax.servlet.http.HttpSession; | |
import org.apache.logging.log4j.LogManager; | |
import org.apache.logging.log4j.Logger; | |
import org.springframework.web.context.WebApplicationContext; | |
import org.springframework.web.context.support.WebApplicationContextUtils; | |
import com.alibaba.fastjson.JSON; | |
import net.spy.memcached.MemcachedClient; | |
/** | |
* <p>获取共享session的过滤器。应该在使用session对象之前,先经过该过滤器。 | |
* | |
* <p>共享session存储在memcached中。 | |
* | |
* <p>该过滤器会使用以下应用初始化参数: | |
* <ul> | |
* <li>sessionTimeout: 用于控制session的有效期,单位为秒,默认值为30分钟。</li> | |
* <li>memcachedClientBeanId: 配置于spring中的MemCachedClient对象的bean的id, | |
* 默认为"memcachedClient"</li> | |
* <li>sessionKeyPrefix: 用于设置memcached中session对象key的前缀,默认值为"session_"</li> | |
* </ul> | |
* | |
* <p>针对该过滤器,可以设置以下初始化参数: | |
* <ul> | |
* <li>sessionIdName: 指定用于记录sessionId的cookie的名字,默认值为"sessid"</li> | |
* </ul> | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
*/ | |
public class MemcachedSessionFilter implements Filter { | |
private static Logger logger = LogManager.getLogger(); | |
// session有效期,单位是秒 | |
private static final String CONFIG_PROP_NAME_SESSION_TIMEOUT = | |
"sessionTimeout"; | |
private int sessionTimeout = 30 * 60; | |
// session id属性的名字,使用该名字在cookie中查找对应的属性值 | |
private static final String CONFIG_PROP_NAME_SESSION_ID = "sessionIdName"; | |
private String sessionIdName = "sessid"; | |
// spring中memcachhedClient对象的id | |
private static final String CONFIG_PROP_NAME_MEMCACHED_CLIENT_BEAN_ID = | |
"memcachedClientBeanId"; | |
private String memcachedClientBeanId = "memcachedClient"; | |
// memcached中session缓存的key的前缀 | |
private static final String CONFIG_PROP_NAME_SESSION_KEY_PREFIX = | |
"sessionKeyPrefix"; | |
private String sessionKeyPrefix = "session_"; | |
// 通过memcached存储共享session | |
private MemcachedClient memcachedClient; | |
// 应用上下文 | |
private ServletContext servletContext; | |
@Override | |
public void init(FilterConfig filterConfig) throws ServletException { | |
// 解析session有效期 | |
this.servletContext = filterConfig.getServletContext(); | |
String interval = | |
this.servletContext.getInitParameter(CONFIG_PROP_NAME_SESSION_TIMEOUT); | |
if ((interval != null) && (!"".equals(interval))) { | |
sessionTimeout = Integer.parseInt(interval); | |
} | |
SessionWrapper.defaultSessionTimeout = sessionTimeout; | |
// 解析session缓存的key前缀 | |
String sessionKeyPre = | |
this.servletContext.getInitParameter( | |
CONFIG_PROP_NAME_SESSION_KEY_PREFIX); | |
if ((null != sessionKeyPre) && ("".equals(sessionKeyPre))) { | |
this.sessionKeyPrefix = sessionKeyPre; | |
} | |
// 获取memcachedClient对象的id | |
String initMemcachedClientBeanId = | |
this.servletContext.getInitParameter( | |
CONFIG_PROP_NAME_MEMCACHED_CLIENT_BEAN_ID); | |
if ((initMemcachedClientBeanId != null) | |
&& (!"".equals(initMemcachedClientBeanId))) { | |
this.memcachedClientBeanId = initMemcachedClientBeanId; | |
} | |
// 设置memcachedClient | |
WebApplicationContext wac = | |
WebApplicationContextUtils.getRequiredWebApplicationContext( | |
servletContext); | |
memcachedClient = (MemcachedClient)wac.getBean(this.memcachedClientBeanId); | |
MemcachedSessionListener.memcachedClient = memcachedClient; | |
// 解析sessionId的名字 | |
String tempSessionIdName = | |
filterConfig.getInitParameter(CONFIG_PROP_NAME_SESSION_ID); | |
if ((tempSessionIdName != null) && (!"".equals(tempSessionIdName))) { | |
sessionIdName = tempSessionIdName; | |
} | |
} | |
@Override | |
public void doFilter(ServletRequest req, ServletResponse resp, | |
FilterChain chain) throws IOException, ServletException { | |
HttpServletRequest request = (HttpServletRequest)req; | |
HttpServletResponse response = (HttpServletResponse)resp; | |
RequestWrapper requestWrapper = null; | |
// 查找是否已经设置过sessionID | |
Cookie[] cookies = request.getCookies(); | |
logger.debug("Received cookies: " + JSON.toJSONString(cookies)); | |
Cookie sessionIdCookie = null; | |
String sessionId = null; | |
if ((cookies != null) && (cookies.length > 0)) { | |
for (Cookie cookie: cookies) { | |
String name = cookie.getName(); | |
String value = cookie.getValue(); | |
if (sessionIdName.equals(name)) { | |
sessionIdCookie = cookie; | |
sessionId = value; | |
break; | |
} | |
} | |
} | |
/** | |
* 1. 若没设置过sessionId,则表示该用户之前没有访问过,不必理会session | |
* 2. 若设置过sessionId,则根据sessionId,从memcached中获取缓存的session数据 | |
* 2.1 若不存在(没有或已失效)session数据,则不必理会session | |
* 2.2 若存在session数据,则以sessionId和session数据创建SessionWrapper对象 | |
* | |
* 这里实际上是以memcached中是否存在数据来判断session是否失效,因而导致 | |
*/ | |
SessionWrapper session = null; | |
if (sessionIdCookie == null) { | |
// 之前没有访问过 | |
requestWrapper = new RequestWrapper(request); | |
} else { | |
// 之前已经访问过的用户 | |
// 先从memcache中获取session对象 | |
session = | |
(SessionWrapper)memcachedClient.get(sessionKeyPrefix + sessionId); | |
logger.debug("Received memcahed session: " + JSON.toJSONString(session)); | |
if (session == null) { | |
// 没有或者已经过期 | |
requestWrapper = new RequestWrapper(request); | |
} else { | |
session.setNew(false); | |
session.setLastAccessedTime(System.currentTimeMillis()); | |
requestWrapper = new RequestWrapper(request, session); | |
} | |
} | |
// 执行其他业务 | |
chain.doFilter(requestWrapper, response); | |
// 如果应用使用到了session,将session保存到memcache中,并将sessionId写入到cookie | |
HttpSession sessionAfterService = requestWrapper.getSession(false); | |
if (sessionAfterService != null) { | |
String sidAfterService = sessionAfterService.getId(); | |
Cookie sessionIdCookieAfterService = | |
new Cookie(this.sessionIdName, sidAfterService); | |
sessionIdCookieAfterService.setHttpOnly(true); | |
sessionIdCookieAfterService.setPath("/"); | |
sessionIdCookieAfterService.setMaxAge(this.sessionTimeout); | |
response.addCookie(sessionIdCookieAfterService); | |
memcachedClient.set( | |
sessionKeyPrefix + sidAfterService, | |
sessionTimeout, | |
sessionAfterService); | |
logger.debug("Stored memcached session: " + JSON.toJSONString(session)); | |
} | |
} | |
@Override | |
public void destroy() { | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session.sharedsession; | |
import net.spy.memcached.MemcachedClient; | |
import appcloud.common.util.web.session.SessionEvent; | |
import appcloud.common.util.web.session.SessionEventType; | |
import appcloud.common.util.web.session.SessionListener; | |
/** | |
* 根据不同的session事件,对memcached中缓存的session对象做处理 | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
*/ | |
public class MemcachedSessionListener implements SessionListener { | |
/** | |
* 该字段的值由{@link MemcachedSessionFilter}在初始化时设置 | |
* | |
* FIXME: 这个设置太别扭 | |
*/ | |
public static MemcachedClient memcachedClient; | |
@Override | |
public void fire(SessionEvent event) { | |
SessionEventType eventType = event.getType(); | |
switch (eventType) { | |
case INVALIDATE: { | |
/** | |
* 需要将memcached中的session置为失效 | |
*/ | |
SessionWrapper session = (SessionWrapper)event.getData(); | |
String sid = session.getId(); | |
memcachedClient.delete(sid); | |
break; | |
} | |
case CREATE: | |
break; | |
default: | |
break; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session.sharedsession; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletRequestWrapper; | |
import javax.servlet.http.HttpSession; | |
public class RequestWrapper extends HttpServletRequestWrapper { | |
private SessionWrapper internalSession; | |
public RequestWrapper(HttpServletRequest request, SessionWrapper session) { | |
super(request); | |
if ((request == null) || (session == null)) { | |
throw new IllegalArgumentException( | |
"Arguments request or session cannot be null."); | |
} | |
this.internalSession = session; | |
} | |
public RequestWrapper(HttpServletRequest request) { | |
super(request); | |
if (request == null) { | |
throw new IllegalArgumentException("Arguments request cannot be null."); | |
} | |
} | |
@Override | |
public HttpSession getSession(boolean create) { | |
SessionWrapper session = null; | |
if (create) { | |
session = new SessionWrapper(); | |
session.addSessionLister(new MemcachedSessionListener()); | |
this.internalSession = session; | |
} else { | |
if (internalSession != null) { | |
session = internalSession; | |
} | |
} | |
return session; | |
} | |
@Override | |
public HttpSession getSession() { | |
if (this.internalSession == null) { | |
return this.getSession(true); | |
} else { | |
return this.internalSession; | |
} | |
} | |
@Override | |
public String changeSessionId() { | |
String newSessionId = SessionWrapper.generateSessionId(); | |
this.internalSession.id = newSessionId; | |
return newSessionId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session; | |
import java.util.EventObject; | |
import javax.servlet.http.HttpSession; | |
import com.alibaba.fastjson.JSON; | |
/** | |
* session中的事件对象 | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
*/ | |
public final class SessionEvent extends EventObject { | |
private static final long serialVersionUID = 1L; | |
/** | |
* 事件数据 | |
*/ | |
private final Object data; | |
/** | |
* 发生事件的session对象 | |
*/ | |
private final HttpSession session; | |
/** | |
* 事件类型 | |
*/ | |
private final SessionEventType type; | |
public SessionEvent(HttpSession session, SessionEventType type, Object data) { | |
super(session); | |
this.session = session; | |
this.type = type; | |
this.data = data; | |
} | |
/** | |
* 获取事件数据 | |
*/ | |
public Object getData() { | |
return this.data; | |
} | |
/** | |
* 获取发生了事件的session对象 | |
*/ | |
public HttpSession getSession() { | |
return this.session; | |
} | |
/** | |
* 获取事件类型 | |
*/ | |
public SessionEventType getType() { | |
return this.type; | |
} | |
@Override | |
public String toString() { | |
return JSON.toJSONString(this); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session; | |
/** | |
* session事件类型 | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
*/ | |
public enum SessionEventType { | |
/** | |
* 创建session | |
*/ | |
CREATE, | |
/** | |
* 销毁session | |
*/ | |
//DESCTROY, | |
/** | |
* session属性变动 | |
*/ | |
//ATTRIBUTE_CHANGE, | |
/** | |
* 激活session | |
*/ | |
//ACTIVATE, | |
/** | |
* 换出,即将session暂时从内存中排出,进行持久化 | |
*/ | |
//PASSIVATE, | |
/** | |
* session失效 | |
*/ | |
INVALIDATE; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session; | |
import appcloud.common.exception.AppCloudException; | |
/** | |
* session相关的异常 | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
*/ | |
public class SessionException extends AppCloudException { | |
private static final long serialVersionUID = 1L; | |
public SessionException() { | |
super(); | |
} | |
public SessionException(String message) { | |
super(message); | |
} | |
public SessionException(String message, Throwable cause) { | |
super(message, cause); | |
} | |
public SessionException(Throwable cause) { | |
super(cause); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session; | |
import java.util.EventListener; | |
public interface SessionListener extends EventListener { | |
public void fire(SessionEvent event); | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package appcloud.common.util.web.session.sharedsession; | |
import java.io.Serializable; | |
import java.util.Collections; | |
import java.util.Enumeration; | |
import java.util.HashSet; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.UUID; | |
import java.util.concurrent.ConcurrentHashMap; | |
import javax.servlet.ServletContext; | |
import javax.servlet.http.HttpSession; | |
import javax.servlet.http.HttpSessionAttributeListener; | |
import javax.servlet.http.HttpSessionContext; | |
import org.apache.commons.codec.binary.Base64; | |
import appcloud.common.util.web.session.SessionEvent; | |
import appcloud.common.util.web.session.SessionEventType; | |
import appcloud.common.util.web.session.SessionListener; | |
/** | |
* <p>对{@link HttpSession}默认实现的包装, | |
* 将数据存储在内部的{@link SessionWrapper#data}中。 | |
* | |
* <p>目前,有一些session相关的特性还没有实现,包括: | |
* <ul> | |
* <li>不支持SessionContext</li> | |
* <li>监听session属性的变动,不支持{@link HttpSessionAttributeListener}</li> | |
* <li>监听session属性的变动,不支持{@link HttpSessionAttributeListener}</li> | |
* </ul> | |
* | |
* TODO: 1.实现对session属性变动的监听; 2. 多线程安全性还未考虑太多 | |
* | |
* @author caoxudong | |
* @since 0.1.0 | |
* @see SessionEventType | |
*/ | |
public class SessionWrapper implements HttpSession, Serializable { | |
private static final long serialVersionUID = 1L; | |
/** | |
* 默认情况下,session有效期为30分钟。可以在{@link MemcachedSessionFilter}中设置。 | |
* | |
* FIXME: 这里的设置方式太别扭。 | |
*/ | |
public static volatile int defaultSessionTimeout = 30 * 60; | |
protected Map<String, Object> data = new ConcurrentHashMap<>(); | |
protected transient List<SessionListener> listeners = new LinkedList<>(); | |
protected boolean isNew = true; | |
protected boolean isValid = true; | |
protected String id; | |
protected long creationTime = System.currentTimeMillis(); | |
protected long lastAccessedTime = this.creationTime; | |
protected int maxInactiveInterval = defaultSessionTimeout; | |
public SessionWrapper() { | |
super(); | |
this.id = generateSessionId(); | |
} | |
@Override | |
public long getCreationTime() { | |
checkValidity(); | |
return this.creationTime; | |
} | |
@Override | |
public String getId() { | |
return this.id; | |
} | |
@Override | |
public long getLastAccessedTime() { | |
checkValidity(); | |
return this.lastAccessedTime; | |
} | |
public void setLastAccessedTime(long lastAccessedTime) { | |
this.lastAccessedTime = lastAccessedTime; | |
} | |
@Override | |
public ServletContext getServletContext() { | |
return null; | |
} | |
@Override | |
public void setMaxInactiveInterval(int interval) { | |
this.maxInactiveInterval = interval; | |
} | |
@Override | |
public int getMaxInactiveInterval() { | |
return this.maxInactiveInterval; | |
} | |
@Override | |
public HttpSessionContext getSessionContext() { | |
return null; | |
} | |
@Override | |
public Object getAttribute(String name) { | |
checkValidity(); | |
if (null == name) { | |
return null; | |
} else { | |
return this.data.get(name); | |
} | |
} | |
@Override | |
public Object getValue(String name) { | |
checkValidity(); | |
if (null == name) { | |
return null; | |
} else { | |
return this.data.get(name); | |
} | |
} | |
@Override | |
public Enumeration<String> getAttributeNames() { | |
checkValidity(); | |
Set<String> names = new HashSet<>(); | |
names.addAll(this.data.keySet()); | |
return Collections.enumeration(names); | |
} | |
@Override | |
public String[] getValueNames() { | |
checkValidity(); | |
Set<String> keyNamesSet = this.data.keySet(); | |
String[] keyNames = new String[keyNamesSet.size()]; | |
keyNames = keyNamesSet.toArray(keyNames); | |
return keyNames; | |
} | |
@Override | |
public void setAttribute(String name, Object value) { | |
checkAttributeName(name); | |
checkValidity(); | |
if (null == value) { | |
this.removeAttribute(name); | |
} else { | |
this.data.put(name, value); | |
} | |
} | |
@Override | |
public void putValue(String name, Object value) { | |
checkAttributeName(name); | |
checkValidity(); | |
if (null == value) { | |
this.removeAttribute(name); | |
} else { | |
this.data.put(name, value); | |
} | |
} | |
@Override | |
public void removeAttribute(String name) { | |
checkValidity(); | |
this.data.remove(name); | |
} | |
@Override | |
public void removeValue(String name) { | |
checkValidity(); | |
this.data.remove(name); | |
} | |
@Override | |
public void invalidate() { | |
checkValidity(); | |
this.data.clear(); | |
this.isValid = true; | |
for (SessionListener listener: listeners) { | |
listener.fire(new SessionEvent(this, SessionEventType.INVALIDATE, this)); | |
} | |
} | |
@Override | |
public boolean isNew() { | |
checkValidity(); | |
return this.isNew; | |
} | |
public void setNew(boolean isNew) { | |
this.isNew = isNew; | |
} | |
/** | |
* 添加session事件监听器 | |
* | |
* @param sessionListener 事件监听器 | |
* @see SessionListener | |
* @see SessionEventType | |
*/ | |
public void addSessionLister(SessionListener sessionListener) { | |
this.listeners.add(sessionListener); | |
} | |
/** | |
* 检查session是否已经失效了 | |
*/ | |
private void checkValidity() { | |
if (!isValid) { | |
throw new IllegalStateException("Session is invalid."); | |
} | |
} | |
/** | |
* 检查session属性的名字 | |
* @param name session属性的名字 | |
*/ | |
private void checkAttributeName(String name) { | |
if (name == null) { | |
throw new IllegalArgumentException("Attribute name cannot be null."); | |
} | |
} | |
public static String generateSessionId() { | |
return Base64.encodeBase64String(UUID.randomUUID().toString().getBytes()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns="http://java.sun.com/xml/ns/javaee" | |
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" | |
id="WebApp_ID" version="3.0"> | |
<display-name>demo</display-name> | |
<!-- for shared session --> | |
<context-param> | |
<param-name>sessionTimeout</param-name> | |
<param-value>1800</param-value> | |
</context-param> | |
<context-param> | |
<param-name>memcachedClientBeanId</param-name> | |
<param-value>memcachedClient</param-value> | |
</context-param> | |
<!-- spring 配置文件 --> | |
<context-param> | |
<param-name>contextConfigLocation</param-name> | |
<param-value>classpath:applicationContext.xml</param-value> | |
</context-param> | |
<!-- 强制字符转码 UTF8 --> | |
<filter> | |
<filter-name>springUtf8Encoding</filter-name> | |
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> | |
<init-param> | |
<param-name>encoding</param-name> | |
<param-value>UTF-8</param-value> | |
</init-param> | |
<init-param> | |
<param-name>forceEncoding</param-name> | |
<param-value>true</param-value> | |
</init-param> | |
</filter> | |
<filter-mapping> | |
<filter-name>springUtf8Encoding</filter-name> | |
<url-pattern>/*</url-pattern> | |
</filter-mapping> | |
<!-- ocs-based shared session --> | |
<filter> | |
<filter-name>ocsSessionFilter</filter-name> | |
<filter-class>appcloud.common.util.web.session.sharedsession.MemcachedSessionFilter</filter-class> | |
<init-param> | |
<param-name>sessionIdName</param-name> | |
<param-value>sessid</param-value> | |
</init-param> | |
</filter> | |
<filter-mapping> | |
<filter-name>ocsSessionFilter</filter-name> | |
<url-pattern>/*</url-pattern> | |
</filter-mapping> | |
<!-- spring上下文启动监听 --> | |
<listener> | |
<listener-class> | |
org.springframework.web.context.ContextLoaderListener | |
</listener-class> | |
</listener> | |
<!--订单定时器--> | |
<listener> | |
<listener-class> | |
appcloud.common.util.web.listener.OrderListener | |
</listener-class> | |
</listener> | |
<!-- spring-mvc servlet --> | |
<servlet> | |
<servlet-name>springMVCServlet</servlet-name> | |
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> | |
<init-param> | |
<param-name>contextConfigLocation</param-name> | |
<param-value>classpath:spring-mvc.xml</param-value> | |
</init-param> | |
<load-on-startup>1</load-on-startup> | |
</servlet> | |
<servlet-mapping> | |
<servlet-name>springMVCServlet</servlet-name> | |
<url-pattern>/</url-pattern> | |
</servlet-mapping> | |
</web-app> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment