application config system property support changes

pull/49/head
Ranjith Manickam 2020-02-05 17:29:47 +05:30
parent b73c1b78c2
commit f45e1b23a9
19 changed files with 519 additions and 211 deletions

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>tomcat-cluster-redis-session-manager</groupId>
<artifactId>tomcat-cluster-redis-session-manager</artifactId>
<version>3.0.3</version>
<version>3.0.4</version>
<packaging>jar</packaging>
<name>tomcat-cluster-redis-session-manager</name>

View File

@ -0,0 +1,24 @@
package tomcat.request.session.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** author: Ranjith Manickam @ 5 Feb' 2020 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Property {
String name() default "";
String defaultValue() default "";
PropertyType type() default PropertyType.STRING;
enum PropertyType {
STRING,
BOOLEAN,
INTEGER,
LONG
}
}

View File

@ -1,11 +1,8 @@
package tomcat.request.session;
package tomcat.request.session.constant;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
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,40 +0,0 @@
package tomcat.request.session.data.cache;
/** author: Ranjith Manickam @ 3 Dec' 2018 */
public interface DataCacheConstants {
// redis properties file name
String APPLICATION_PROPERTIES_FILE = "redis-data-cache.properties";
// redis properties
String REDIS_HOSTS = "redis.hosts";
String REDIS_CLUSTER_ENABLED = "redis.cluster.enabled:false";
String REDIS_SENTINEL_ENABLED = "redis.sentinel.enabled:false";
String LB_STICKY_SESSION_ENABLED = "lb.sticky-session.enabled:false";
String REDIS_MAX_ACTIVE = "redis.max.active:10";
String REDIS_TEST_ONBORROW = "redis.test.onBorrow:true";
String REDIS_TEST_ONRETURN = "redis.test.onReturn:true";
String REDIS_MAX_IDLE = "redis.max.idle:5";
String REDIS_MIN_IDLE = "redis.min.idle:1";
String REDIS_TEST_WHILEIDLE = "redis.test.whileIdle:true";
String REDIS_TEST_NUMPEREVICTION = "redis.test.numPerEviction:10";
String REDIS_TIME_BETWEENEVICTION = "redis.time.betweenEviction:60000";
String REDIS_PASSWORD = "redis.password";
String REDIS_DATABASE = "redis.database:0";
String REDIS_TIMEOUT = "redis.timeout:2000";
String REDIS_SENTINEL_MASTER = "redis.sentinel.master:mymaster";
String REDIS_CONN_FAILED_RETRY_MSG = "Jedis connection failed, retrying...";
String SESSION_EXPIRY_JOB_INTERVAL = "redis.session.expiry.job.interval:60";
String SESSION_DATA_SYNC_JOB_INTERVAL = "redis.session.data-sync.job.interval:10";
enum RedisConfigType {
DEFAULT,
SENTINEL,
CLUSTER
}
}

View File

@ -2,49 +2,24 @@ package tomcat.request.session.data.cache;
import tomcat.request.session.data.cache.impl.StandardDataCache;
import tomcat.request.session.data.cache.impl.redis.RedisCache;
import java.util.Properties;
import tomcat.request.session.model.Config;
/** author: Ranjith Manickam @ 3 Dec' 2018 */
public class DataCacheFactory {
private final Properties properties;
private final Config config;
private final int sessionExpiryTime;
public DataCacheFactory(Properties properties, int sessionExpiryTime) {
this.properties = properties;
public DataCacheFactory(Config config, int sessionExpiryTime) {
this.config = config;
this.sessionExpiryTime = sessionExpiryTime;
}
/** To get data cache. */
public DataCache getDataCache() {
if (Boolean.parseBoolean(getProperty(this.properties, DataCacheConstants.LB_STICKY_SESSION_ENABLED))) {
return new StandardDataCache(this.properties, this.sessionExpiryTime);
if (this.config.getLbStickySessionEnabled()) {
return new StandardDataCache(this.config, this.sessionExpiryTime);
}
return new RedisCache(this.properties);
}
/**
* To get property with the specified key in this properties list.
*
* @param properties - properties list.
* @param key - search key.
* @return - Returns the property value.
*/
public static String getProperty(Properties properties, String key) {
return getProperty(properties, key, null);
}
/**
* To get property with the specified key in this properties list.
*
* @param properties - properties list.
* @param key - search key.
* @param defaultValue - default value.
* @return - - Returns the property value.
*/
public static String getProperty(Properties properties, String key, String defaultValue) {
String[] keyValue = key.split(":");
return properties.getProperty(keyValue[0], (keyValue.length > 1 && defaultValue == null) ? keyValue[1] : defaultValue);
return new RedisCache(this.config);
}
}

View File

@ -3,14 +3,12 @@ package tomcat.request.session.data.cache.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.impl.redis.RedisCache;
import tomcat.request.session.model.Config;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@ -32,15 +30,15 @@ public class StandardDataCache extends RedisCache {
private final Executor expiryJobExecutor;
private final Executor dataSyncJobExecutor;
public StandardDataCache(Properties properties, int sessionExpiryTime) {
super(properties);
public StandardDataCache(Config config, int sessionExpiryTime) {
super(config);
this.sessionExpiryTime = sessionExpiryTime;
this.sessionData = new ConcurrentHashMap<>();
this.expiryJob = new Date().getTime();
this.dataSyncJob = new Date().getTime();
this.processDataSync = false;
this.expiryJobTriggerInterval = TimeUnit.MINUTES.toMillis(Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.SESSION_EXPIRY_JOB_INTERVAL)));
this.dataSyncJobTriggerInterval = TimeUnit.MINUTES.toMillis(Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.SESSION_DATA_SYNC_JOB_INTERVAL)));
this.expiryJobTriggerInterval = TimeUnit.MINUTES.toMillis(config.getRedisSessionExpiryJobInterval());
this.dataSyncJobTriggerInterval = TimeUnit.MINUTES.toMillis(config.getRedisSessionDataSyncJobInterval());
this.expiryJobExecutor = Executors.newSingleThreadExecutor();
this.dataSyncJobExecutor = Executors.newSingleThreadExecutor();
}

View File

@ -2,17 +2,14 @@ package tomcat.request.session.data.cache.impl.redis;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
import tomcat.request.session.data.cache.DataCache;
import tomcat.request.session.data.cache.DataCacheConstants;
import tomcat.request.session.data.cache.DataCacheConstants.RedisConfigType;
import tomcat.request.session.data.cache.DataCacheFactory;
import tomcat.request.session.model.Config;
import tomcat.request.session.model.Config.RedisConfigType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
@ -20,8 +17,8 @@ public class RedisCache implements DataCache {
private DataCache dataCache;
public RedisCache(Properties properties) {
initialize(properties);
public RedisCache(Config config) {
initialize(config);
}
/** {@inheritDoc} */
@ -54,38 +51,31 @@ public class RedisCache implements DataCache {
return dataCache.delete(key);
}
private void initialize(Properties properties) {
RedisConfigType configType;
if (Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_CLUSTER_ENABLED))) {
configType = RedisConfigType.CLUSTER;
} else if (Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_SENTINEL_ENABLED))) {
configType = RedisConfigType.SENTINEL;
} else {
configType = RedisConfigType.DEFAULT;
}
String hosts = DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_HOSTS, String.format("%s:%s", Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT));
Collection<?> nodes = getJedisNodes(hosts, configType);
String password = DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_PASSWORD);
password = (password != null && !password.isEmpty()) ? password : null;
int database = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_DATABASE));
int timeout = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TIMEOUT));
timeout = Math.max(timeout, Protocol.DEFAULT_TIMEOUT);
JedisPoolConfig poolConfig = getPoolConfig(properties);
switch (configType) {
private void initialize(Config config) {
Collection<?> nodes = getJedisNodes(config.getRedisHosts(), config.getRedisConfigType());
JedisPoolConfig poolConfig = getPoolConfig(config);
switch (config.getRedisConfigType()) {
case CLUSTER:
dataCache = new RedisClusterManager((Set<HostAndPort>) nodes, password, timeout, poolConfig);
this.dataCache = new RedisClusterManager((Set<HostAndPort>) nodes,
config.getRedisPassword(),
config.getRedisTimeout(),
poolConfig);
break;
case SENTINEL:
String masterName = String.valueOf(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_SENTINEL_MASTER));
dataCache = new RedisSentinelManager((Set<String>) nodes, masterName, password, database, timeout, poolConfig);
this.dataCache = new RedisSentinelManager((Set<String>) nodes,
config.getRedisSentinelMaster(),
config.getRedisPassword(),
config.getRedisDatabase(),
config.getRedisTimeout(),
poolConfig);
break;
default:
dataCache = new RedisStandardManager(((List<String>) nodes).get(0), Integer.parseInt(((List<String>) nodes).get(1)), password, database, timeout, poolConfig);
this.dataCache = new RedisStandardManager(((List<String>) nodes).get(0),
Integer.parseInt(((List<String>) nodes).get(1)),
config.getRedisPassword(),
config.getRedisDatabase(),
config.getRedisTimeout(),
poolConfig);
break;
}
}
@ -93,34 +83,19 @@ public class RedisCache implements DataCache {
/**
* To get redis pool config.
*
* @param properties - Redis data cache properties.
* @param config - Application config.
* @return - Returns the redis pool config.
*/
private JedisPoolConfig getPoolConfig(Properties properties) {
private JedisPoolConfig getPoolConfig(Config config) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
int maxActive = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_MAX_ACTIVE));
poolConfig.setMaxTotal(maxActive);
boolean testOnBorrow = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_ONBORROW));
poolConfig.setTestOnBorrow(testOnBorrow);
boolean testOnReturn = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_ONRETURN));
poolConfig.setTestOnReturn(testOnReturn);
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));
poolConfig.setMinIdle(minIdle);
boolean testWhileIdle = Boolean.parseBoolean(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_WHILEIDLE));
poolConfig.setTestWhileIdle(testWhileIdle);
int testNumPerEviction = Integer.parseInt(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TEST_NUMPEREVICTION));
poolConfig.setNumTestsPerEvictionRun(testNumPerEviction);
long timeBetweenEviction = Long.parseLong(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_TIME_BETWEENEVICTION));
poolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEviction);
poolConfig.setMaxTotal(config.getRedisMaxActive());
poolConfig.setTestOnBorrow(config.getRedisTestOnBorrow());
poolConfig.setTestOnReturn(config.getRedisTestOnReturn());
poolConfig.setMaxIdle(config.getRedisMaxIdle());
poolConfig.setMinIdle(config.getRedisMinIdle());
poolConfig.setTestWhileIdle(config.getRedisTestWhileIdle());
poolConfig.setNumTestsPerEvictionRun(config.getRedisTestNumPerEviction());
poolConfig.setTimeBetweenEvictionRunsMillis(config.getRedisTimeBetweenEviction());
return poolConfig;
}

View File

@ -15,15 +15,15 @@ class RedisClusterManager extends RedisManager {
private final JedisCluster cluster;
private static final int NUM_RETRIES = 30;
private static final int DEFAULT_MAX_REDIRECTIONS = 5;
private static final long FAILIURE_WAIT_TIME = 4000L;
private static final int DEFAULT_MAX_RE_DIRECTIONS = 5;
private static final long FAILURE_WAIT_TIME = 4000L;
RedisClusterManager(Set<HostAndPort> nodes,
String password,
int timeout,
JedisPoolConfig poolConfig) {
super(null, FAILIURE_WAIT_TIME);
this.cluster = new JedisCluster(nodes, timeout, Protocol.DEFAULT_TIMEOUT, DEFAULT_MAX_REDIRECTIONS, password, poolConfig);
super(null, FAILURE_WAIT_TIME);
this.cluster = new JedisCluster(nodes, timeout, Protocol.DEFAULT_TIMEOUT, DEFAULT_MAX_RE_DIRECTIONS, password, poolConfig);
}
/** {@inheritDoc} */

View File

@ -6,7 +6,6 @@ import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.util.Pool;
import tomcat.request.session.data.cache.DataCache;
import tomcat.request.session.data.cache.DataCacheConstants;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
abstract class RedisManager implements DataCache {
@ -14,12 +13,14 @@ abstract class RedisManager implements DataCache {
private static final int NUM_RETRIES = 3;
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
private final Pool<Jedis> pool;
private final long failiureWaitTime;
private static final String REDIS_CONN_FAILED_RETRY_MSG = "Jedis connection failed, retrying...";
RedisManager(Pool<Jedis> pool, long failiureWaitTime) {
private final Pool<Jedis> pool;
private final long failureWaitTime;
RedisManager(Pool<Jedis> pool, long failureWaitTime) {
this.pool = pool;
this.failiureWaitTime = failiureWaitTime;
this.failureWaitTime = failureWaitTime;
}
/** {@inheritDoc} */
@ -119,12 +120,12 @@ abstract class RedisManager implements DataCache {
* @param ex - jedis exception.
*/
void handleException(int tries, RuntimeException ex) {
LOGGER.error(DataCacheConstants.REDIS_CONN_FAILED_RETRY_MSG + tries);
LOGGER.error(REDIS_CONN_FAILED_RETRY_MSG + tries);
if (tries == NUM_RETRIES) {
throw ex;
}
try {
Thread.sleep(this.failiureWaitTime);
Thread.sleep(this.failureWaitTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

View File

@ -8,7 +8,7 @@ import java.util.Set;
/** author: Ranjith Manickam @ 3 Dec' 2018 */
class RedisSentinelManager extends RedisManager {
private static final long FAILIURE_WAIT_TIME = 2000L;
private static final long FAILURE_WAIT_TIME = 2000L;
RedisSentinelManager(Set<String> nodes,
String masterName,
@ -16,6 +16,6 @@ class RedisSentinelManager extends RedisManager {
int database,
int timeout,
JedisPoolConfig poolConfig) {
super(new JedisSentinelPool(masterName, nodes, poolConfig, timeout, password, database), FAILIURE_WAIT_TIME);
super(new JedisSentinelPool(masterName, nodes, poolConfig, timeout, password, database), FAILURE_WAIT_TIME);
}
}

View File

@ -6,7 +6,7 @@ import redis.clients.jedis.JedisPoolConfig;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
class RedisStandardManager extends RedisManager {
private static final long FAILIURE_WAIT_TIME = 2000L;
private static final long FAILURE_WAIT_TIME = 2000L;
RedisStandardManager(String host,
int port,
@ -14,6 +14,6 @@ class RedisStandardManager extends RedisManager {
int database,
int timeout,
JedisPoolConfig poolConfig) {
super(new JedisPool(poolConfig, host, port, timeout, password, database), FAILIURE_WAIT_TIME);
super(new JedisPool(poolConfig, host, port, timeout, password, database), FAILURE_WAIT_TIME);
}
}

View File

@ -0,0 +1,253 @@
package tomcat.request.session.model;
import redis.clients.jedis.Protocol;
import tomcat.request.session.annotation.Property;
import java.io.Serializable;
import static tomcat.request.session.annotation.Property.PropertyType.BOOLEAN;
import static tomcat.request.session.annotation.Property.PropertyType.INTEGER;
/** author: Ranjith Manickam @ 5 Feb' 2020 */
public class Config implements Serializable {
public static final String APPLICATION_PROPERTIES_FILE = "redis-data-cache.properties";
/** Redis config type. */
public enum RedisConfigType {
DEFAULT,
SENTINEL,
CLUSTER
}
@Property(name = "redis.hosts", defaultValue = "127.0.0.1:6379")
private String redisHosts;
@Property(name = "redis.cluster.enabled", type = BOOLEAN, defaultValue = "false")
private Boolean redisClusterEnabled;
@Property(name = "redis.sentinel.enabled", type = BOOLEAN, defaultValue = "false")
private Boolean redisSentinelEnabled;
@Property(name = "lb.sticky-session.enabled", type = BOOLEAN, defaultValue = "false")
private Boolean lbStickySessionEnabled;
@Property(name = "redis.max.active", type = INTEGER, defaultValue = "10")
private Integer redisMaxActive;
@Property(name = "redis.test.onBorrow", type = BOOLEAN, defaultValue = "true")
private Boolean redisTestOnBorrow;
@Property(name = "redis.test.onReturn", type = BOOLEAN, defaultValue = "true")
private Boolean redisTestOnReturn;
@Property(name = "redis.max.idle", type = INTEGER, defaultValue = "5")
private Integer redisMaxIdle;
@Property(name = "redis.min.idle", type = INTEGER, defaultValue = "1")
private Integer redisMinIdle;
@Property(name = "redis.test.whileIdle", type = BOOLEAN, defaultValue = "true")
private Boolean redisTestWhileIdle;
@Property(name = "redis.test.numPerEviction", type = INTEGER, defaultValue = "10")
private Integer redisTestNumPerEviction;
@Property(name = "redis.time.betweenEviction", type = INTEGER, defaultValue = "60000")
private Integer redisTimeBetweenEviction;
@Property(name = "redis.password")
private String redisPassword;
@Property(name = "redis.database", type = INTEGER, defaultValue = "0")
private Integer redisDatabase;
@Property(name = "redis.timeout", type = INTEGER, defaultValue = "2000")
private Integer redisTimeout;
@Property(name = "redis.sentinel.master", defaultValue = "mymaster")
private String redisSentinelMaster;
@Property(name = "redis.session.expiry.job.interval", type = INTEGER, defaultValue = "60")
private Integer redisSessionExpiryJobInterval;
@Property(name = "redis.session.data-sync.job.interval", type = INTEGER, defaultValue = "10")
private Integer redisSessionDataSyncJobInterval;
@Property(name = "session.persistent.policies", defaultValue = "DEFAULT")
private String sessionPersistentPolicies;
public Config() {
}
public Config(String redisHosts,
Boolean redisClusterEnabled,
Boolean redisSentinelEnabled,
Boolean lbStickySessionEnabled,
Integer redisMaxActive,
Boolean redisTestOnBorrow,
Boolean redisTestOnReturn,
Integer redisMaxIdle,
Integer redisMinIdle,
Boolean redisTestWhileIdle,
Integer redisTestNumPerEviction,
Integer redisTimeBetweenEviction,
String redisPassword,
Integer redisDatabase,
Integer redisTimeout,
String redisSentinelMaster,
Integer redisSessionExpiryJobInterval,
Integer redisSessionDataSyncJobInterval,
String sessionPersistentPolicies) {
this.redisHosts = redisHosts;
this.redisClusterEnabled = redisClusterEnabled;
this.redisSentinelEnabled = redisSentinelEnabled;
this.lbStickySessionEnabled = lbStickySessionEnabled;
this.redisMaxActive = redisMaxActive;
this.redisTestOnBorrow = redisTestOnBorrow;
this.redisTestOnReturn = redisTestOnReturn;
this.redisMaxIdle = redisMaxIdle;
this.redisMinIdle = redisMinIdle;
this.redisTestWhileIdle = redisTestWhileIdle;
this.redisTestNumPerEviction = redisTestNumPerEviction;
this.redisTimeBetweenEviction = redisTimeBetweenEviction;
this.redisPassword = redisPassword;
this.redisDatabase = redisDatabase;
this.redisTimeout = redisTimeout;
this.redisSentinelMaster = redisSentinelMaster;
this.redisSessionExpiryJobInterval = redisSessionExpiryJobInterval;
this.redisSessionDataSyncJobInterval = redisSessionDataSyncJobInterval;
this.sessionPersistentPolicies = sessionPersistentPolicies;
}
/** To get 'redis.hosts' value. */
public String getRedisHosts() {
return redisHosts;
}
/** To get 'redis.cluster.enabled' value. */
public Boolean getRedisClusterEnabled() {
return redisClusterEnabled;
}
/** To get 'redis.sentinel.enabled' value. */
public Boolean getRedisSentinelEnabled() {
return redisSentinelEnabled;
}
/** To get 'lb.sticky-session.enabled' value. */
public Boolean getLbStickySessionEnabled() {
return lbStickySessionEnabled;
}
/** To get 'redis.max.active' value. */
public Integer getRedisMaxActive() {
return redisMaxActive;
}
/** To get 'redis.test.onBorrow' value. */
public Boolean getRedisTestOnBorrow() {
return redisTestOnBorrow;
}
/** To get 'redis.test.onReturn' value. */
public Boolean getRedisTestOnReturn() {
return redisTestOnReturn;
}
/** To get 'redis.max.idle' value. */
public Integer getRedisMaxIdle() {
return redisMaxIdle;
}
/** To get 'redis.min.idle' value. */
public Integer getRedisMinIdle() {
return redisMinIdle;
}
/** To get 'redis.test.whileIdle' value. */
public Boolean getRedisTestWhileIdle() {
return redisTestWhileIdle;
}
/** To get 'redis.test.numPerEviction' value. */
public Integer getRedisTestNumPerEviction() {
return redisTestNumPerEviction;
}
/** To get 'redis.time.betweenEviction' value. */
public Integer getRedisTimeBetweenEviction() {
return redisTimeBetweenEviction;
}
/** To get 'redis.password' value. */
public String getRedisPassword() {
return (redisPassword == null || redisPassword.isEmpty()) ? null : redisPassword;
}
/** To get 'redis.database' value. */
public Integer getRedisDatabase() {
return redisDatabase;
}
/** To get 'redis.timeout' value. */
public Integer getRedisTimeout() {
return Math.max(redisTimeout, Protocol.DEFAULT_TIMEOUT);
}
/** To get 'redis.sentinel.master' value. */
public String getRedisSentinelMaster() {
return redisSentinelMaster;
}
/** To get 'redis.session.expiry.job.interval' value. */
public Integer getRedisSessionExpiryJobInterval() {
return redisSessionExpiryJobInterval;
}
/** To get 'redis.session.data-sync.job.interval' value. */
public Integer getRedisSessionDataSyncJobInterval() {
return redisSessionDataSyncJobInterval;
}
/** To get 'session.persistent.policies' value */
public String getSessionPersistentPolicies() {
return sessionPersistentPolicies;
}
/** {@inheritDoc} */
@Override
public String toString() {
return "Config{" +
"redisHosts='" + redisHosts + '\'' +
", redisClusterEnabled=" + redisClusterEnabled +
", redisSentinelEnabled=" + redisSentinelEnabled +
", lbStickySessionEnabled=" + lbStickySessionEnabled +
", redisMaxActive=" + redisMaxActive +
", redisTestOnBorrow=" + redisTestOnBorrow +
", redisTestOnReturn=" + redisTestOnReturn +
", redisMaxIdle=" + redisMaxIdle +
", redisMinIdle=" + redisMinIdle +
", redisTestWhileIdle=" + redisTestWhileIdle +
", redisTestNumPerEviction=" + redisTestNumPerEviction +
", redisTimeBetweenEviction=" + redisTimeBetweenEviction +
", redisPassword='" + redisPassword + '\'' +
", redisDatabase=" + redisDatabase +
", redisTimeout=" + redisTimeout +
", redisSentinelMaster='" + redisSentinelMaster + '\'' +
", redisSessionExpiryJobInterval=" + redisSessionExpiryJobInterval +
", redisSessionDataSyncJobInterval=" + redisSessionDataSyncJobInterval +
", sessionPersistentPolicies='" + sessionPersistentPolicies + '\'' +
'}';
}
/** To get redis config type. */
public RedisConfigType getRedisConfigType() {
if (this.getRedisClusterEnabled()) {
return RedisConfigType.CLUSTER;
} else if (this.getRedisSentinelEnabled()) {
return RedisConfigType.SENTINEL;
}
return RedisConfigType.DEFAULT;
}
}

View File

@ -1,4 +1,8 @@
package tomcat.request.session;
package tomcat.request.session.model;
import org.apache.catalina.Manager;
import org.apache.catalina.session.StandardSession;
import tomcat.request.session.redis.SessionManager;
import java.io.IOException;
import java.io.ObjectInputStream;
@ -8,11 +12,6 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.apache.catalina.Manager;
import org.apache.catalina.session.StandardSession;
import tomcat.request.session.redis.SessionManager;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
public class Session extends StandardSession {

View File

@ -1,4 +1,4 @@
package tomcat.request.session;
package tomcat.request.session.model;
/** author: Ranjith Manickam @ 12 Jul' 2018 */
public class SessionContext {

View File

@ -1,4 +1,4 @@
package tomcat.request.session;
package tomcat.request.session.model;
import java.io.IOException;
import java.io.ObjectInputStream;

View File

@ -31,7 +31,7 @@ public class SessionHandlerValve extends ValveBase {
LOGGER.error("Error processing request", ex);
throw new BackendException();
} finally {
manager.afterRequest();
this.manager.afterRequest();
}
}
}

View File

@ -9,24 +9,21 @@ import org.apache.catalina.Valve;
import org.apache.catalina.session.ManagerBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tomcat.request.session.SerializationUtil;
import tomcat.request.session.Session;
import tomcat.request.session.SessionConstants;
import tomcat.request.session.SessionConstants.SessionPolicy;
import tomcat.request.session.SessionContext;
import tomcat.request.session.SessionMetadata;
import tomcat.request.session.constant.SessionConstants;
import tomcat.request.session.constant.SessionConstants.SessionPolicy;
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.model.Config;
import tomcat.request.session.model.Session;
import tomcat.request.session.model.SessionContext;
import tomcat.request.session.model.SessionMetadata;
import tomcat.request.session.util.ConfigUtil;
import tomcat.request.session.util.SerializationUtil;
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 */
@ -211,15 +208,15 @@ public class SessionManager extends ManagerBase implements Lifecycle {
/** To initialize the session manager. */
private void initialize() {
try {
Properties properties = getApplicationProperties();
this.dataCache = new DataCacheFactory(properties, getSessionTimeout(null)).getDataCache();
Config config = ConfigUtil.getConfig();
this.dataCache = new DataCacheFactory(config, 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);
setSessionPersistentPolicies(config);
} catch (Exception ex) {
LOGGER.error("Error occurred while initializing the session manager..", ex);
throw ex;
@ -326,8 +323,8 @@ public class SessionManager extends ManagerBase implements Lifecycle {
}
/** To set session persistent policies */
private void setSessionPersistentPolicies(Properties properties) {
String sessionPolicies = properties.getProperty(SessionConstants.SESSION_PERSISTENT_POLICIES);
private void setSessionPersistentPolicies(Config config) {
String sessionPolicies = config.getSessionPersistentPolicies();
if (sessionPolicies == null || sessionPolicies.isEmpty()) {
return;
}
@ -338,31 +335,4 @@ public class SessionManager extends ManagerBase implements Lifecycle {
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

@ -0,0 +1,154 @@
package tomcat.request.session.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tomcat.request.session.annotation.Property;
import tomcat.request.session.model.Config;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
/** author: Ranjith Manickam @ 5 Feb' 2020 */
public class ConfigUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);
private static final String CONF = "conf";
private static final String CATALINA_BASE = "catalina.base";
/** To get application config. */
public static Config getConfig() {
Properties properties = getApplicationProperties();
Config config = new Config();
for (Field field : Config.class.getDeclaredFields()) {
Property property = field.getAnnotation(Property.class);
if (property == null) {
continue;
}
String propertyName = property.name();
Property.PropertyType propertyType = property.type();
if (propertyName.isEmpty()) {
continue;
}
String value = properties.getProperty(propertyName);
if (isSystemProperty(value)) {
value = getSystemProperty(value);
}
if (value == null || value.isEmpty()) {
value = property.defaultValue();
if (value.isEmpty()) {
continue;
}
}
field.setAccessible(true);
try {
switch (propertyType) {
case BOOLEAN:
field.set(config, Boolean.parseBoolean(value));
break;
case INTEGER:
field.set(config, Integer.parseInt(value));
break;
case LONG:
field.set(config, Long.parseLong(value));
break;
case STRING:
default:
field.set(config, value);
break;
}
} catch (Exception ex) {
LOGGER.error("Error while initializing application properties", ex);
}
}
return config;
}
/** To get redis data cache properties. */
private static Properties getApplicationProperties() {
Properties properties = new Properties();
try {
String filePath = System.getProperty(CATALINA_BASE)
.concat(File.separator)
.concat(CONF).concat(File.separator)
.concat(Config.APPLICATION_PROPERTIES_FILE);
InputStream resourceStream = null;
try {
resourceStream = (!filePath.isEmpty() && new File(filePath).exists()) ? new FileInputStream(filePath) : null;
if (resourceStream == null) {
LOGGER.info("Initializing tomcat redis session manager with default properties");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
resourceStream = loader.getResourceAsStream(Config.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;
}
/**
* To get property with the specified key from system property.
*
* @param key - search key.
* @return - Returns the system property value.
*/
private static String getSystemProperty(String key) {
int fromIndex = 0;
while (true) {
int beginIndex = key.indexOf("${", fromIndex);
int endIndex = key.indexOf("}", fromIndex);
if (beginIndex < 0 || endIndex < 0) {
break;
}
String expression = key.substring(beginIndex + 2, endIndex);
String value = System.getProperty(expression);
if (value == null || value.isEmpty()) {
fromIndex = endIndex + 1;
continue;
}
key = key.replace(String.format("${%s}", expression), value);
}
return key;
}
/**
* To check if the value is from system property.
*
* @param key - search key.
* @return - Returns true if the key start with '${' and ends with '}'.
*/
private static boolean isSystemProperty(String key) {
if (key == null || key.isEmpty()) {
return false;
}
int beginIndex = key.indexOf("${");
int endIndex = key.indexOf("}");
return beginIndex >= 0 && endIndex >= 0 && beginIndex < endIndex;
}
}

View File

@ -1,6 +1,8 @@
package tomcat.request.session;
package tomcat.request.session.util;
import org.apache.catalina.util.CustomObjectInputStream;
import tomcat.request.session.model.Session;
import tomcat.request.session.model.SessionMetadata;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;