parent
4eb0e433b1
commit
7ea845ebca
|
@ -5,6 +5,7 @@ public interface SessionConstants {
|
||||||
byte[] NULL_SESSION = "null".getBytes();
|
byte[] NULL_SESSION = "null".getBytes();
|
||||||
String CATALINA_BASE = "catalina.base";
|
String CATALINA_BASE = "catalina.base";
|
||||||
String CONF = "conf";
|
String CONF = "conf";
|
||||||
|
String SESSION_PERSISTENT_POLICIES = "session.persistent.policies";
|
||||||
|
|
||||||
enum SessionPolicy {
|
enum SessionPolicy {
|
||||||
DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;
|
DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;
|
||||||
|
|
|
@ -1,64 +1,27 @@
|
||||||
package tomcat.request.session.data.cache;
|
package tomcat.request.session.data.cache;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import tomcat.request.session.SessionConstants;
|
|
||||||
import tomcat.request.session.data.cache.impl.StandardDataCache;
|
import tomcat.request.session.data.cache.impl.StandardDataCache;
|
||||||
import tomcat.request.session.data.cache.impl.redis.RedisCache;
|
import tomcat.request.session.data.cache.impl.redis.RedisCache;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
/** author: Ranjith Manickam @ 3 Dec' 2018 */
|
/** author: Ranjith Manickam @ 3 Dec' 2018 */
|
||||||
public class DataCacheFactory {
|
public class DataCacheFactory {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(DataCacheFactory.class);
|
private final Properties properties;
|
||||||
|
|
||||||
private final int sessionExpiryTime;
|
private final int sessionExpiryTime;
|
||||||
|
|
||||||
public DataCacheFactory(int sessionExpiryTime) {
|
public DataCacheFactory(Properties properties, int sessionExpiryTime) {
|
||||||
|
this.properties = properties;
|
||||||
this.sessionExpiryTime = sessionExpiryTime;
|
this.sessionExpiryTime = sessionExpiryTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** To get data cache. */
|
/** To get data cache. */
|
||||||
public DataCache getDataCache() {
|
public DataCache getDataCache() {
|
||||||
Properties properties = getApplicationProperties();
|
if (Boolean.parseBoolean(getProperty(this.properties, DataCacheConstants.LB_STICKY_SESSION_ENABLED))) {
|
||||||
|
return new StandardDataCache(this.properties, this.sessionExpiryTime);
|
||||||
if (Boolean.valueOf(getProperty(properties, DataCacheConstants.LB_STICKY_SESSION_ENABLED))) {
|
|
||||||
return new StandardDataCache(properties, this.sessionExpiryTime);
|
|
||||||
}
|
}
|
||||||
|
return new RedisCache(this.properties);
|
||||||
return new RedisCache(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** To get redis data cache properties. */
|
|
||||||
private Properties getApplicationProperties() {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
try {
|
|
||||||
String filePath = System.getProperty(SessionConstants.CATALINA_BASE).concat(File.separator)
|
|
||||||
.concat(SessionConstants.CONF).concat(File.separator)
|
|
||||||
.concat(DataCacheConstants.APPLICATION_PROPERTIES_FILE);
|
|
||||||
|
|
||||||
InputStream resourceStream = null;
|
|
||||||
try {
|
|
||||||
resourceStream = (!filePath.isEmpty() && new File(filePath).exists()) ? new FileInputStream(filePath) : null;
|
|
||||||
if (resourceStream == null) {
|
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
resourceStream = loader.getResourceAsStream(DataCacheConstants.APPLICATION_PROPERTIES_FILE);
|
|
||||||
}
|
|
||||||
properties.load(resourceStream);
|
|
||||||
} finally {
|
|
||||||
if (resourceStream != null) {
|
|
||||||
resourceStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LOGGER.error("Error while retrieving application properties", ex);
|
|
||||||
}
|
|
||||||
return properties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class StandardDataCache extends RedisCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Session data. */
|
/** Session data. */
|
||||||
private class SessionData implements Serializable {
|
private static class SessionData implements Serializable {
|
||||||
private byte[] value;
|
private byte[] value;
|
||||||
private long lastAccessedOn;
|
private long lastAccessedOn;
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ public class StandardDataCache extends RedisCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Session data redis sync thread. */
|
/** Session data redis sync thread. */
|
||||||
private class SessionDataSyncThread implements Runnable {
|
private static class SessionDataSyncThread implements Runnable {
|
||||||
|
|
||||||
private final Logger LOGGER = LoggerFactory.getLogger(SessionDataSyncThread.class);
|
private final Logger LOGGER = LoggerFactory.getLogger(SessionDataSyncThread.class);
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ public class StandardDataCache extends RedisCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Session data expiry thread. This will takes care of the session expired data removal. */
|
/** Session data expiry thread. This will takes care of the session expired data removal. */
|
||||||
private class SessionDataExpiryThread implements Runnable {
|
private static class SessionDataExpiryThread implements Runnable {
|
||||||
|
|
||||||
private final Logger LOGGER = LoggerFactory.getLogger(SessionDataExpiryThread.class);
|
private final Logger LOGGER = LoggerFactory.getLogger(SessionDataExpiryThread.class);
|
||||||
|
|
||||||
|
|
|
@ -56,9 +56,9 @@ public class RedisCache implements DataCache {
|
||||||
|
|
||||||
private void initialize(Properties properties) {
|
private void initialize(Properties properties) {
|
||||||
RedisConfigType configType;
|
RedisConfigType configType;
|
||||||
if (Boolean.valueOf(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_CLUSTER_ENABLED))) {
|
if (Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_CLUSTER_ENABLED))) {
|
||||||
configType = RedisConfigType.CLUSTER;
|
configType = RedisConfigType.CLUSTER;
|
||||||
} else if (Boolean.valueOf(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_SENTINEL_ENABLED))) {
|
} else if (Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_SENTINEL_ENABLED))) {
|
||||||
configType = RedisConfigType.SENTINEL;
|
configType = RedisConfigType.SENTINEL;
|
||||||
} else {
|
} else {
|
||||||
configType = RedisConfigType.DEFAULT;
|
configType = RedisConfigType.DEFAULT;
|
||||||
|
@ -73,7 +73,7 @@ public class RedisCache implements DataCache {
|
||||||
int database = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_DATABASE));
|
int database = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_DATABASE));
|
||||||
|
|
||||||
int timeout = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TIMEOUT));
|
int timeout = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TIMEOUT));
|
||||||
timeout = (timeout < Protocol.DEFAULT_TIMEOUT) ? Protocol.DEFAULT_TIMEOUT : timeout;
|
timeout = Math.max(timeout, Protocol.DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
JedisPoolConfig poolConfig = getPoolConfig(properties);
|
JedisPoolConfig poolConfig = getPoolConfig(properties);
|
||||||
switch (configType) {
|
switch (configType) {
|
||||||
|
@ -107,7 +107,7 @@ public class RedisCache implements DataCache {
|
||||||
boolean testOnReturn = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_ONRETURN));
|
boolean testOnReturn = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_ONRETURN));
|
||||||
poolConfig.setTestOnReturn(testOnReturn);
|
poolConfig.setTestOnReturn(testOnReturn);
|
||||||
|
|
||||||
int maxIdle = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MAX_ACTIVE));
|
int maxIdle = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MAX_IDLE));
|
||||||
poolConfig.setMaxIdle(maxIdle);
|
poolConfig.setMaxIdle(maxIdle);
|
||||||
|
|
||||||
int minIdle = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MIN_IDLE));
|
int minIdle = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MIN_IDLE));
|
||||||
|
@ -143,14 +143,14 @@ public class RedisCache implements DataCache {
|
||||||
switch (configType) {
|
switch (configType) {
|
||||||
case CLUSTER:
|
case CLUSTER:
|
||||||
nodes = (nodes == null) ? new HashSet<>() : nodes;
|
nodes = (nodes == null) ? new HashSet<>() : nodes;
|
||||||
nodes.add(new HostAndPort(hostPortArr[0], Integer.valueOf(hostPortArr[1])));
|
nodes.add(new HostAndPort(hostPortArr[0], Integer.parseInt(hostPortArr[1])));
|
||||||
break;
|
break;
|
||||||
case SENTINEL:
|
case SENTINEL:
|
||||||
nodes = (nodes == null) ? new HashSet<>() : nodes;
|
nodes = (nodes == null) ? new HashSet<>() : nodes;
|
||||||
nodes.add(new HostAndPort(hostPortArr[0], Integer.valueOf(hostPortArr[1])).toString());
|
nodes.add(new HostAndPort(hostPortArr[0], Integer.parseInt(hostPortArr[1])).toString());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
int port = Integer.valueOf(hostPortArr[1]);
|
int port = Integer.parseInt(hostPortArr[1]);
|
||||||
if (!hostPortArr[0].isEmpty() && port > 0) {
|
if (!hostPortArr[0].isEmpty() && port > 0) {
|
||||||
List<String> node = new ArrayList<>();
|
List<String> node = new ArrayList<>();
|
||||||
node.add(hostPortArr[0]);
|
node.add(hostPortArr[0]);
|
||||||
|
|
|
@ -16,29 +16,37 @@ import tomcat.request.session.SessionConstants.SessionPolicy;
|
||||||
import tomcat.request.session.SessionContext;
|
import tomcat.request.session.SessionContext;
|
||||||
import tomcat.request.session.SessionMetadata;
|
import tomcat.request.session.SessionMetadata;
|
||||||
import tomcat.request.session.data.cache.DataCache;
|
import tomcat.request.session.data.cache.DataCache;
|
||||||
|
import tomcat.request.session.data.cache.DataCacheConstants;
|
||||||
import tomcat.request.session.data.cache.DataCacheFactory;
|
import tomcat.request.session.data.cache.DataCacheFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
|
/** author: Ranjith Manickam @ 12 Jul' 2018 */
|
||||||
public class SessionManager extends ManagerBase implements Lifecycle {
|
public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
|
||||||
|
|
||||||
private DataCache dataCache;
|
private DataCache dataCache;
|
||||||
private SerializationUtil serializer;
|
private SerializationUtil serializer;
|
||||||
|
|
||||||
private ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
|
private ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
|
||||||
private Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
|
private Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
|
|
||||||
|
|
||||||
public boolean getSaveOnChange() {
|
public boolean getSaveOnChange() {
|
||||||
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
|
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean getAlwaysSaveAfterRequest() {
|
||||||
|
return this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public void addLifecycleListener(LifecycleListener listener) {
|
public void addLifecycleListener(LifecycleListener listener) {
|
||||||
|
@ -203,11 +211,15 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
/** To initialize the session manager. */
|
/** To initialize the session manager. */
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
try {
|
try {
|
||||||
this.dataCache = new DataCacheFactory(getSessionTimeout(null)).getDataCache();
|
Properties properties = getApplicationProperties();
|
||||||
|
this.dataCache = new DataCacheFactory(properties, getSessionTimeout(null)).getDataCache();
|
||||||
this.serializer = new SerializationUtil();
|
this.serializer = new SerializationUtil();
|
||||||
|
|
||||||
Context context = getContextIns();
|
Context context = getContextIns();
|
||||||
ClassLoader loader = (context != null && context.getLoader() != null) ? context.getLoader().getClassLoader() : null;
|
ClassLoader loader = (context != null && context.getLoader() != null) ? context.getLoader().getClassLoader() : null;
|
||||||
this.serializer.setClassLoader(loader);
|
this.serializer.setClassLoader(loader);
|
||||||
|
|
||||||
|
setSessionPersistentPolicies(properties);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOGGER.error("Error occurred while initializing the session manager..", ex);
|
LOGGER.error("Error occurred while initializing the session manager..", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
|
@ -252,7 +264,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
session = (this.sessionContext.get() != null) ? this.sessionContext.get().getSession() : null;
|
session = (this.sessionContext.get() != null) ? this.sessionContext.get().getSession() : null;
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
if (session.isValid()) {
|
if (session.isValid()) {
|
||||||
save(session, this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST));
|
save(session, getAlwaysSaveAfterRequest());
|
||||||
} else {
|
} else {
|
||||||
remove(session);
|
remove(session);
|
||||||
}
|
}
|
||||||
|
@ -270,7 +282,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
private int getSessionTimeout(Session session) {
|
private int getSessionTimeout(Session session) {
|
||||||
int timeout = getContextIns().getSessionTimeout() * 60;
|
int timeout = getContextIns().getSessionTimeout() * 60;
|
||||||
int sessionTimeout = (session == null) ? 0 : session.getMaxInactiveInterval();
|
int sessionTimeout = (session == null) ? 0 : session.getMaxInactiveInterval();
|
||||||
return (sessionTimeout < timeout) ? ((timeout < 1800) ? 1800 : timeout) : sessionTimeout;
|
return (sessionTimeout < timeout) ? (Math.max(timeout, 1800)) : sessionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** To set values to session context. */
|
/** To set values to session context. */
|
||||||
|
@ -312,4 +324,45 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Error occurred while creating container instance");
|
throw new RuntimeException("Error occurred while creating container instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** To set session persistent policies */
|
||||||
|
private void setSessionPersistentPolicies(Properties properties) {
|
||||||
|
String sessionPolicies = properties.getProperty(SessionConstants.SESSION_PERSISTENT_POLICIES);
|
||||||
|
if (sessionPolicies == null || sessionPolicies.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionPolicies = sessionPolicies.replaceAll("\\s", "");
|
||||||
|
String[] sessionPolicyNames = sessionPolicies.split(",");
|
||||||
|
for (String sessionPolicyName : sessionPolicyNames) {
|
||||||
|
this.sessionPolicy.add(SessionPolicy.fromName(sessionPolicyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** To get redis data cache properties. */
|
||||||
|
private Properties getApplicationProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try {
|
||||||
|
String filePath = System.getProperty(SessionConstants.CATALINA_BASE).concat(File.separator)
|
||||||
|
.concat(SessionConstants.CONF).concat(File.separator)
|
||||||
|
.concat(DataCacheConstants.APPLICATION_PROPERTIES_FILE);
|
||||||
|
|
||||||
|
InputStream resourceStream = null;
|
||||||
|
try {
|
||||||
|
resourceStream = (!filePath.isEmpty() && new File(filePath).exists()) ? new FileInputStream(filePath) : null;
|
||||||
|
if (resourceStream == null) {
|
||||||
|
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
resourceStream = loader.getResourceAsStream(DataCacheConstants.APPLICATION_PROPERTIES_FILE);
|
||||||
|
}
|
||||||
|
properties.load(resourceStream);
|
||||||
|
} finally {
|
||||||
|
if (resourceStream != null) {
|
||||||
|
resourceStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOGGER.error("Error while retrieving application properties", ex);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
#-- Redis data-cache configuration
|
#-- Redis data-cache configuration
|
||||||
|
|
||||||
#- redis hosts ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
|
#- redis hosts. ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
|
||||||
redis.hosts=127.0.0.1:6379
|
redis.hosts=127.0.0.1:6379
|
||||||
|
|
||||||
#- redis password
|
#- redis password.
|
||||||
#redis.password=
|
#redis.password=
|
||||||
|
|
||||||
#- set true to enable redis cluster mode (default value: false)
|
#- set true to enable redis cluster mode. (default value: false)
|
||||||
redis.cluster.enabled=false
|
redis.cluster.enabled=false
|
||||||
|
|
||||||
#- set true to enable redis sentinel mode (default value: false)
|
#- set true to enable redis sentinel mode. (default value: false)
|
||||||
redis.sentinel.enabled=false
|
redis.sentinel.enabled=false
|
||||||
# redis sentinel master name (default value: mymaster)
|
# redis sentinel master name. (default value: mymaster)
|
||||||
redis.sentinel.master=mymaster
|
redis.sentinel.master=mymaster
|
||||||
|
|
||||||
#- redis database (default value: 0)
|
#- redis database. (default value: 0)
|
||||||
#redis.database=0
|
#redis.database=0
|
||||||
|
|
||||||
#- redis connection timeout (default value: 2000 ms)
|
#- redis connection timeout. (default value: 2000 ms)
|
||||||
#redis.timeout=2000
|
#redis.timeout=2000
|
||||||
|
|
||||||
#- enable redis and standard session mode. (default value: false)
|
#- enable redis and standard session mode. (default value: false)
|
||||||
|
@ -26,3 +26,9 @@ redis.sentinel.master=mymaster
|
||||||
# 2. Session values are stored in local jvm and redis.
|
# 2. Session values are stored in local jvm and redis.
|
||||||
# 3. If redis is down/not responding, requests uses jvm stored session values to process user requests. Redis comes back the values will be synced.
|
# 3. If redis is down/not responding, requests uses jvm stored session values to process user requests. Redis comes back the values will be synced.
|
||||||
lb.sticky-session.enabled=false
|
lb.sticky-session.enabled=false
|
||||||
|
|
||||||
|
#- session persistent policies. (default value: DEFAULT) ex: DEFAULT, SAVE_ON_CHANGE
|
||||||
|
# policies - DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST
|
||||||
|
# 1. SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.
|
||||||
|
# 2. ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.
|
||||||
|
session.persistent.policies=DEFAULT
|
Loading…
Reference in New Issue