tomcat session persistent policy update

session-42 3.0.3
Ranjith Manickam 2019-08-12 19:29:32 +05:30
parent 4eb0e433b1
commit 7ea845ebca
6 changed files with 89 additions and 66 deletions

View File

@ -5,6 +5,7 @@ public interface SessionConstants {
byte[] NULL_SESSION = "null".getBytes();
String CATALINA_BASE = "catalina.base";
String CONF = "conf";
String SESSION_PERSISTENT_POLICIES = "session.persistent.policies";
enum SessionPolicy {
DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;

View File

@ -1,64 +1,27 @@
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.redis.RedisCache;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/** author: Ranjith Manickam @ 3 Dec' 2018 */
public class DataCacheFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(DataCacheFactory.class);
private final Properties properties;
private final int sessionExpiryTime;
public DataCacheFactory(int sessionExpiryTime) {
public DataCacheFactory(Properties properties, int sessionExpiryTime) {
this.properties = properties;
this.sessionExpiryTime = sessionExpiryTime;
}
/** To get data cache. */
public DataCache getDataCache() {
Properties properties = getApplicationProperties();
if (Boolean.valueOf(getProperty(properties, DataCacheConstants.LB_STICKY_SESSION_ENABLED))) {
return new StandardDataCache(properties, this.sessionExpiryTime);
if (Boolean.parseBoolean(getProperty(this.properties, DataCacheConstants.LB_STICKY_SESSION_ENABLED))) {
return new StandardDataCache(this.properties, this.sessionExpiryTime);
}
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;
return new RedisCache(this.properties);
}
/**

View File

@ -113,7 +113,7 @@ public class StandardDataCache extends RedisCache {
}
/** Session data. */
private class SessionData implements Serializable {
private static class SessionData implements Serializable {
private byte[] value;
private long lastAccessedOn;
@ -157,7 +157,7 @@ public class StandardDataCache extends RedisCache {
}
/** Session data redis sync thread. */
private class SessionDataSyncThread implements Runnable {
private static class SessionDataSyncThread implements Runnable {
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. */
private class SessionDataExpiryThread implements Runnable {
private static class SessionDataExpiryThread implements Runnable {
private final Logger LOGGER = LoggerFactory.getLogger(SessionDataExpiryThread.class);

View File

@ -56,9 +56,9 @@ public class RedisCache implements DataCache {
private void initialize(Properties properties) {
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;
} 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;
} else {
configType = RedisConfigType.DEFAULT;
@ -73,7 +73,7 @@ public class RedisCache implements DataCache {
int database = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_DATABASE));
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);
switch (configType) {
@ -107,7 +107,7 @@ public class RedisCache implements DataCache {
boolean testOnReturn = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_ONRETURN));
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);
int minIdle = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MIN_IDLE));
@ -143,14 +143,14 @@ public class RedisCache implements DataCache {
switch (configType) {
case CLUSTER:
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;
case SENTINEL:
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;
default:
int port = Integer.valueOf(hostPortArr[1]);
int port = Integer.parseInt(hostPortArr[1]);
if (!hostPortArr[0].isEmpty() && port > 0) {
List<String> node = new ArrayList<>();
node.add(hostPortArr[0]);

View File

@ -16,29 +16,37 @@ import tomcat.request.session.SessionConstants.SessionPolicy;
import tomcat.request.session.SessionContext;
import tomcat.request.session.SessionMetadata;
import tomcat.request.session.data.cache.DataCache;
import tomcat.request.session.data.cache.DataCacheConstants;
import tomcat.request.session.data.cache.DataCacheFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Properties;
import java.util.Set;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
public class SessionManager extends ManagerBase implements Lifecycle {
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
private DataCache dataCache;
private SerializationUtil serializer;
private ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
private Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
public boolean getSaveOnChange() {
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
}
private boolean getAlwaysSaveAfterRequest() {
return this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST);
}
/** {@inheritDoc} */
@Override
public void addLifecycleListener(LifecycleListener listener) {
@ -203,11 +211,15 @@ public class SessionManager extends ManagerBase implements Lifecycle {
/** To initialize the session manager. */
private void initialize() {
try {
this.dataCache = new DataCacheFactory(getSessionTimeout(null)).getDataCache();
Properties properties = getApplicationProperties();
this.dataCache = new DataCacheFactory(properties, getSessionTimeout(null)).getDataCache();
this.serializer = new SerializationUtil();
Context context = getContextIns();
ClassLoader loader = (context != null && context.getLoader() != null) ? context.getLoader().getClassLoader() : null;
this.serializer.setClassLoader(loader);
setSessionPersistentPolicies(properties);
} catch (Exception ex) {
LOGGER.error("Error occurred while initializing the session manager..", ex);
throw ex;
@ -252,7 +264,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
session = (this.sessionContext.get() != null) ? this.sessionContext.get().getSession() : null;
if (session != null) {
if (session.isValid()) {
save(session, this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST));
save(session, getAlwaysSaveAfterRequest());
} else {
remove(session);
}
@ -270,7 +282,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
private int getSessionTimeout(Session session) {
int timeout = getContextIns().getSessionTimeout() * 60;
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. */
@ -312,4 +324,45 @@ public class SessionManager extends ManagerBase implements Lifecycle {
}
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;
}
}

View File

@ -1,23 +1,23 @@
#-- 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 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
#- 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 master name (default value: mymaster)
# redis sentinel master name. (default value: mymaster)
redis.sentinel.master=mymaster
#- redis database (default value: 0)
#- redis database. (default value: 0)
#redis.database=0
#- redis connection timeout (default value: 2000 ms)
#- redis connection timeout. (default value: 2000 ms)
#redis.timeout=2000
#- 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.
# 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
#- 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