added session details manager.

session-42
Ranjith Manickam 2019-08-12 22:44:14 +05:30
parent 8902534782
commit 7212b4a559
9 changed files with 124 additions and 9 deletions

View File

@ -6,6 +6,8 @@ public interface SessionConstants {
String CATALINA_BASE = "catalina.base"; String CATALINA_BASE = "catalina.base";
String CONF = "conf"; String CONF = "conf";
String SESSION_PERSISTENT_POLICIES = "session.persistent.policies"; String SESSION_PERSISTENT_POLICIES = "session.persistent.policies";
String SESSION_ID_PREFIX = "session.id.prefix";
String EMPTY_STRING = "";
enum SessionPolicy { enum SessionPolicy {
DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST; DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;

View File

@ -1,5 +1,7 @@
package tomcat.request.session.data.cache; package tomcat.request.session.data.cache;
import java.util.Set;
/** author: Ranjith Manickam @ 12 Jul' 2018 */ /** author: Ranjith Manickam @ 12 Jul' 2018 */
public interface DataCache { public interface DataCache {
@ -45,4 +47,12 @@ public interface DataCache {
* @return - Returns the number of keys that were removed. * @return - Returns the number of keys that were removed.
*/ */
Long delete(String key); Long delete(String key);
/**
* Returns the keys from data-cache.
*
* @param pattern - key pattern.
* @return - Returns the keys list.
*/
Set<String> keys(String pattern);
} }

View File

@ -11,6 +11,7 @@ import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -112,6 +113,21 @@ public class StandardDataCache extends RedisCache {
return (value == null) ? 0L : 1L; return (value == null) ? 0L : 1L;
} }
/** {@inheritDoc} */
@Override
public Set<String> keys(String pattern) {
handleSessionData();
if (this.sessionData.isEmpty()) {
try {
return super.keys(pattern);
} catch (RuntimeException ex) {
this.processDataSync = true;
throw ex;
}
}
return this.sessionData.keySet();
}
/** Session data. */ /** Session data. */
private static class SessionData implements Serializable { private static class SessionData implements Serializable {
private byte[] value; private byte[] value;

View File

@ -3,6 +3,7 @@ package tomcat.request.session.data.cache.impl.redis;
import redis.clients.jedis.HostAndPort; import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol;
import tomcat.request.session.SessionConstants;
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.DataCacheConstants;
import tomcat.request.session.data.cache.DataCacheConstants.RedisConfigType; import tomcat.request.session.data.cache.DataCacheConstants.RedisConfigType;
@ -19,39 +20,48 @@ import java.util.Set;
public class RedisCache implements DataCache { public class RedisCache implements DataCache {
private DataCache dataCache; private DataCache dataCache;
private final String sessionIdPrefix;
public RedisCache(Properties properties) { public RedisCache(Properties properties) {
initialize(properties); initialize(properties);
String sessionKeyPrefix = properties.getProperty(SessionConstants.SESSION_ID_PREFIX, SessionConstants.EMPTY_STRING);
this.sessionIdPrefix = sessionKeyPrefix.equals(SessionConstants.EMPTY_STRING) ? sessionKeyPrefix : sessionKeyPrefix.concat("-");
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public byte[] set(String key, byte[] value) { public byte[] set(String key, byte[] value) {
return dataCache.set(key, value); return this.dataCache.set(this.sessionIdPrefix.concat(key), value);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Long setnx(String key, byte[] value) { public Long setnx(String key, byte[] value) {
return dataCache.setnx(key, value); return this.dataCache.setnx(this.sessionIdPrefix.concat(key), value);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Long expire(String key, int seconds) { public Long expire(String key, int seconds) {
return dataCache.expire(key, seconds); return this.dataCache.expire(this.sessionIdPrefix.concat(key), seconds);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public byte[] get(String key) { public byte[] get(String key) {
return dataCache.get(key); return this.dataCache.get(this.sessionIdPrefix.concat(key));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Long delete(String key) { public Long delete(String key) {
return dataCache.delete(key); return this.dataCache.delete(this.sessionIdPrefix.concat(key));
}
/** {@inheritDoc} */
@Override
public Set<String> keys(String pattern) {
return this.dataCache.keys(pattern);
} }
private void initialize(Properties properties) { private void initialize(Properties properties) {
@ -78,14 +88,14 @@ public class RedisCache implements DataCache {
JedisPoolConfig poolConfig = getPoolConfig(properties); JedisPoolConfig poolConfig = getPoolConfig(properties);
switch (configType) { switch (configType) {
case CLUSTER: case CLUSTER:
dataCache = new RedisClusterManager((Set<HostAndPort>) nodes, password, timeout, poolConfig); this.dataCache = new RedisClusterManager((Set<HostAndPort>) nodes, password, timeout, poolConfig);
break; break;
case SENTINEL: case SENTINEL:
String masterName = String.valueOf(DataCacheFactory.getProperty(properties, DataCacheConstants.REDIS_SENTINEL_MASTER)); 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, masterName, password, database, timeout, poolConfig);
break; break;
default: 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)), password, database, timeout, poolConfig);
break; break;
} }
} }

View File

@ -115,4 +115,22 @@ class RedisClusterManager extends RedisManager {
} while (retry && tries <= NUM_RETRIES); } while (retry && tries <= NUM_RETRIES);
return retVal; return retVal;
} }
/** {@inheritDoc} */
@Override
public Set<String> keys(String pattern) {
int tries = 0;
boolean retry = true;
Set<String> retVal = null;
do {
tries++;
try {
retVal = cluster.keys(pattern);
retry = false;
} catch (JedisRedirectionException | JedisConnectionException ex) {
handleException(tries, ex);
}
} while (retry && tries <= NUM_RETRIES);
return retVal;
}
} }

View File

@ -8,6 +8,8 @@ import redis.clients.jedis.util.Pool;
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.DataCacheConstants;
import java.util.Set;
/** author: Ranjith Manickam @ 12 Jul' 2018 */ /** author: Ranjith Manickam @ 12 Jul' 2018 */
abstract class RedisManager implements DataCache { abstract class RedisManager implements DataCache {
@ -112,6 +114,24 @@ abstract class RedisManager implements DataCache {
return retVal; return retVal;
} }
/** {@inheritDoc} */
@Override
public Set<String> keys(String pattern) {
int tries = 0;
boolean retry = true;
Set<String> retVal = null;
do {
tries++;
try (Jedis jedis = this.pool.getResource()) {
retVal = jedis.keys(pattern);
retry = false;
} catch (JedisConnectionException ex) {
handleException(tries, ex);
}
} while (retry && tries <= NUM_RETRIES);
return retVal;
}
/** /**
* To handle jedis exception. * To handle jedis exception.
* *

View File

@ -0,0 +1,29 @@
package tomcat.request.session.manager;
import tomcat.request.session.SessionConstants;
import tomcat.request.session.data.cache.DataCache;
import java.util.Set;
/** author: Ranjith Manickam @ 12 Aug' 2019 */
public class SessionDetailsManager {
private final DataCache dataCache;
private final String sessionIdPrefix;
public SessionDetailsManager(DataCache dataCache, String sessionIdPrefix) {
this.dataCache = dataCache;
this.sessionIdPrefix = sessionIdPrefix.equals(SessionConstants.EMPTY_STRING) ? sessionIdPrefix : sessionIdPrefix.concat("-*");
}
/** Returns active session count. */
public int getActiveSessionCount() {
Set<String> activeSessionIds = getActiveSessionIds();
return activeSessionIds == null ? 0 : activeSessionIds.size();
}
/** Returns active session ids. */
private Set<String> getActiveSessionIds() {
return this.dataCache.keys(this.sessionIdPrefix);
}
}

View File

@ -18,6 +18,7 @@ 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.DataCacheConstants;
import tomcat.request.session.data.cache.DataCacheFactory; import tomcat.request.session.data.cache.DataCacheFactory;
import tomcat.request.session.manager.SessionDetailsManager;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -33,12 +34,17 @@ import java.util.Set;
public class SessionManager extends ManagerBase implements Lifecycle { public class SessionManager extends ManagerBase implements Lifecycle {
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class); private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
private static SessionDetailsManager sessionDetailsManager;
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);
public static SessionDetailsManager getSessionDetailsManagerInstance() {
return sessionDetailsManager;
}
public boolean getSaveOnChange() { public boolean getSaveOnChange() {
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE); return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
} }
@ -220,6 +226,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
this.serializer.setClassLoader(loader); this.serializer.setClassLoader(loader);
setSessionPersistentPolicies(properties); setSessionPersistentPolicies(properties);
sessionDetailsManager = new SessionDetailsManager(this.dataCache, properties.getProperty(SessionConstants.SESSION_ID_PREFIX, SessionConstants.EMPTY_STRING));
} 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;

View File

@ -32,3 +32,6 @@ lb.sticky-session.enabled=false
# 1. SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved. # 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. # 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 session.persistent.policies=DEFAULT
#- session id prefix. By default, the JSESSIONID value is used as the key. And this prefix value is mandatory to monitor the active sessions. (ex: jsessionid)
#session.id.prefix=jsessionid