Bug fixes and added redis sentinel supportability changes.
							parent
							
								
									9069315d96
								
							
						
					
					
						commit
						d562a03d63
					
				
							
								
								
									
										134
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										134
									
								
								pom.xml
								
								
								
								
							| 
						 | 
				
			
			@ -1,68 +1,82 @@
 | 
			
		|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
	<modelVersion>4.0.0</modelVersion>
 | 
			
		||||
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
	<groupId>tomcat-cluster-redis-session-manager</groupId>
 | 
			
		||||
	<artifactId>tomcat-cluster-redis-session-manager</artifactId>
 | 
			
		||||
	<version>2.0.5</version>
 | 
			
		||||
	<packaging>jar</packaging>
 | 
			
		||||
    <groupId>tomcat-cluster-redis-session-manager</groupId>
 | 
			
		||||
    <artifactId>tomcat-cluster-redis-session-manager</artifactId>
 | 
			
		||||
    <version>2.0.5</version>
 | 
			
		||||
    <packaging>jar</packaging>
 | 
			
		||||
 | 
			
		||||
	<name>tomcat-cluster-redis-session-manager</name>
 | 
			
		||||
	<url>http://maven.apache.org</url>
 | 
			
		||||
    <name>tomcat-cluster-redis-session-manager</name>
 | 
			
		||||
    <url>http://maven.apache.org</url>
 | 
			
		||||
 | 
			
		||||
	<properties>
 | 
			
		||||
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
			
		||||
		<java.version>1.7</java.version>
 | 
			
		||||
	</properties>
 | 
			
		||||
    <properties>
 | 
			
		||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
			
		||||
        <java.version>1.7</java.version>
 | 
			
		||||
 | 
			
		||||
	<dependencies>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>redis.clients</groupId>
 | 
			
		||||
			<artifactId>jedis</artifactId>
 | 
			
		||||
			<version>2.9.0</version>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.apache.commons</groupId>
 | 
			
		||||
			<artifactId>commons-pool2</artifactId>
 | 
			
		||||
			<version>2.4.2</version>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>commons-logging</groupId>
 | 
			
		||||
			<artifactId>commons-logging</artifactId>
 | 
			
		||||
			<version>1.2</version>
 | 
			
		||||
		</dependency>
 | 
			
		||||
        <jedis.version>2.9.0</jedis.version>
 | 
			
		||||
        <commons-pool2.version>2.4.2</commons-pool2.version>
 | 
			
		||||
        <commons-logging.version>1.2</commons-logging.version>
 | 
			
		||||
 | 
			
		||||
		<!-- For local development -->
 | 
			
		||||
		<!-- <dependency>
 | 
			
		||||
			<groupId>apache-tomcat</groupId>
 | 
			
		||||
			<artifactId>catalina</artifactId>
 | 
			
		||||
			<version>apache-tomcat-8.5.20</version>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>apache-tomcat</groupId>
 | 
			
		||||
			<artifactId>servlet-api</artifactId>
 | 
			
		||||
			<version>apache-tomcat-8.5.20</version>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>apache-tomcat</groupId>
 | 
			
		||||
			<artifactId>tomcat-api</artifactId>
 | 
			
		||||
			<version>apache-tomcat-8.5.20</version>
 | 
			
		||||
		</dependency> -->
 | 
			
		||||
		<!-- For local development end.. -->
 | 
			
		||||
	</dependencies>
 | 
			
		||||
        <maven-compiler.version>3.6.1</maven-compiler.version>
 | 
			
		||||
        <source-java.version>1.7</source-java.version>
 | 
			
		||||
        <target-java.version>${source-java.version}</target-java.version>
 | 
			
		||||
 | 
			
		||||
	<build>
 | 
			
		||||
		<plugins>
 | 
			
		||||
			<plugin>
 | 
			
		||||
				<groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
				<artifactId>maven-compiler-plugin</artifactId>
 | 
			
		||||
				<version>3.6.1</version>
 | 
			
		||||
				<configuration>
 | 
			
		||||
					<source>1.7</source>
 | 
			
		||||
					<target>1.7</target>
 | 
			
		||||
				</configuration>
 | 
			
		||||
			</plugin>
 | 
			
		||||
		</plugins>
 | 
			
		||||
	</build>
 | 
			
		||||
        <!-- For local development properties begins.. -->
 | 
			
		||||
        <tomcat-catalina.version>apache-tomcat-8.5.32</tomcat-catalina.version>
 | 
			
		||||
        <tomcat-servlet-api.version>apache-tomcat-8.5.32</tomcat-servlet-api.version>
 | 
			
		||||
        <tomcat-api.version>apache-tomcat-8.5.32</tomcat-api.version>
 | 
			
		||||
        <!-- For local development properties end.. -->
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>redis.clients</groupId>
 | 
			
		||||
            <artifactId>jedis</artifactId>
 | 
			
		||||
            <version>${jedis.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.apache.commons</groupId>
 | 
			
		||||
            <artifactId>commons-pool2</artifactId>
 | 
			
		||||
            <version>${commons-pool2.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>commons-logging</groupId>
 | 
			
		||||
            <artifactId>commons-logging</artifactId>
 | 
			
		||||
            <version>${commons-logging.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- For local development dependency begins.. -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>apache-tomcat</groupId>
 | 
			
		||||
            <artifactId>catalina</artifactId>
 | 
			
		||||
            <version>${tomcat-catalina.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>apache-tomcat</groupId>
 | 
			
		||||
            <artifactId>servlet-api</artifactId>
 | 
			
		||||
            <version>${tomcat-servlet-api.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>apache-tomcat</groupId>
 | 
			
		||||
            <artifactId>tomcat-api</artifactId>
 | 
			
		||||
            <version>${tomcat-api.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!-- For local development dependency end.. -->
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
                <artifactId>maven-compiler-plugin</artifactId>
 | 
			
		||||
                <version>${maven-compiler.version}</version>
 | 
			
		||||
                <configuration>
 | 
			
		||||
                    <source>${source-java.version}</source>
 | 
			
		||||
                    <target>${target-java.version}</target>
 | 
			
		||||
                </configuration>
 | 
			
		||||
            </plugin>
 | 
			
		||||
        </plugins>
 | 
			
		||||
    </build>
 | 
			
		||||
</project>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
package tomcat.request.session;
 | 
			
		||||
 | 
			
		||||
import org.apache.catalina.util.CustomObjectInputStream;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedInputStream;
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
| 
						 | 
				
			
			@ -11,81 +13,66 @@ import java.security.MessageDigest;
 | 
			
		|||
import java.util.Enumeration;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import org.apache.catalina.util.CustomObjectInputStream;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Session serialization utility.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class SerializationUtil {
 | 
			
		||||
 | 
			
		||||
  private ClassLoader loader;
 | 
			
		||||
    private ClassLoader loader;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set class loader
 | 
			
		||||
   */
 | 
			
		||||
  public void setClassLoader(ClassLoader loader) {
 | 
			
		||||
    this.loader = loader;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session attributes hash code
 | 
			
		||||
   */
 | 
			
		||||
  public byte[] getSessionAttributesHashCode(Session session) throws IOException {
 | 
			
		||||
    byte[] serialized;
 | 
			
		||||
    Map<String, Object> attributes = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    for (Enumeration<String> enumerator = session.getAttributeNames();
 | 
			
		||||
        enumerator.hasMoreElements(); ) {
 | 
			
		||||
      String key = enumerator.nextElement();
 | 
			
		||||
      attributes.put(key, session.getAttribute(key));
 | 
			
		||||
    /** To set class loader. */
 | 
			
		||||
    public void setClassLoader(ClassLoader loader) {
 | 
			
		||||
        this.loader = loader;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
 | 
			
		||||
        ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos))) {
 | 
			
		||||
      oos.writeUnshared(attributes);
 | 
			
		||||
      oos.flush();
 | 
			
		||||
      serialized = bos.toByteArray();
 | 
			
		||||
    /** To get session attributes hash code. */
 | 
			
		||||
    public byte[] getSessionAttributesHashCode(Session session) throws IOException {
 | 
			
		||||
        byte[] serialized;
 | 
			
		||||
        Map<String, Object> attributes = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        for (Enumeration<String> enumerator = session.getAttributeNames();
 | 
			
		||||
             enumerator.hasMoreElements(); ) {
 | 
			
		||||
            String key = enumerator.nextElement();
 | 
			
		||||
            attributes.put(key, session.getAttribute(key));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
 | 
			
		||||
             ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos))) {
 | 
			
		||||
            oos.writeUnshared(attributes);
 | 
			
		||||
            oos.flush();
 | 
			
		||||
            serialized = bos.toByteArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MessageDigest digester;
 | 
			
		||||
        try {
 | 
			
		||||
            digester = MessageDigest.getInstance("MD5");
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            throw new RuntimeException("Unable to get MessageDigest instance for MD5", ex);
 | 
			
		||||
        }
 | 
			
		||||
        return digester.digest(serialized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MessageDigest digester;
 | 
			
		||||
    try {
 | 
			
		||||
      digester = MessageDigest.getInstance("MD5");
 | 
			
		||||
    } catch (Exception ex) {
 | 
			
		||||
      throw new RuntimeException("Unable to get MessageDigest instance for MD5", ex);
 | 
			
		||||
    /** To serialize session object. */
 | 
			
		||||
    public byte[] serializeSessionData(Session session, SessionMetadata metadata) throws IOException {
 | 
			
		||||
        byte[] serialized;
 | 
			
		||||
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
 | 
			
		||||
             ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos))) {
 | 
			
		||||
            oos.writeObject(metadata);
 | 
			
		||||
            session.writeObjectData(oos);
 | 
			
		||||
            oos.flush();
 | 
			
		||||
            serialized = bos.toByteArray();
 | 
			
		||||
        }
 | 
			
		||||
        return serialized;
 | 
			
		||||
    }
 | 
			
		||||
    return digester.digest(serialized);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To serialize session object
 | 
			
		||||
   */
 | 
			
		||||
  public byte[] serializeSessionData(Session session, SessionMetadata metadata) throws IOException {
 | 
			
		||||
    byte[] serialized;
 | 
			
		||||
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
 | 
			
		||||
        ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos))) {
 | 
			
		||||
      oos.writeObject(metadata);
 | 
			
		||||
      session.writeObjectData(oos);
 | 
			
		||||
      oos.flush();
 | 
			
		||||
      serialized = bos.toByteArray();
 | 
			
		||||
    /** To de-serialize session object. */
 | 
			
		||||
    public void deserializeSessionData(byte[] data, Session session, SessionMetadata metadata)
 | 
			
		||||
            throws IOException, ClassNotFoundException {
 | 
			
		||||
        try (BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data));
 | 
			
		||||
             ObjectInputStream ois = new CustomObjectInputStream(bis, this.loader)) {
 | 
			
		||||
            SessionMetadata serializedMetadata = (SessionMetadata) ois.readObject();
 | 
			
		||||
            metadata.copyFieldsFrom(serializedMetadata);
 | 
			
		||||
            session.readObjectData(ois);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return serialized;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To de-serialize session object
 | 
			
		||||
   */
 | 
			
		||||
  public void deserializeSessionData(byte[] data, Session session, SessionMetadata metadata)
 | 
			
		||||
      throws IOException, ClassNotFoundException {
 | 
			
		||||
    try (BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data));
 | 
			
		||||
        ObjectInputStream ois = new CustomObjectInputStream(bis, this.loader)) {
 | 
			
		||||
      SessionMetadata serializedMetadata = (SessionMetadata) ois.readObject();
 | 
			
		||||
      metadata.copyFieldsFrom(serializedMetadata);
 | 
			
		||||
      session.readObjectData(ois);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,147 +13,126 @@ import org.apache.catalina.session.StandardSession;
 | 
			
		|||
 | 
			
		||||
import tomcat.request.session.redis.SessionManager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * This class is uses to store and retrieve the HTTP request session objects.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class Session extends StandardSession {
 | 
			
		||||
 | 
			
		||||
  private static final long serialVersionUID = -6056744304016869278L;
 | 
			
		||||
    private static final long serialVersionUID = -6056744304016869278L;
 | 
			
		||||
 | 
			
		||||
  protected Boolean dirty;
 | 
			
		||||
    protected Boolean dirty;
 | 
			
		||||
    protected Map<String, Object> changedAttributes;
 | 
			
		||||
 | 
			
		||||
  protected Map<String, Object> changedAttributes;
 | 
			
		||||
    protected static Boolean manualDirtyTrackingSupportEnabled = false;
 | 
			
		||||
    protected static String manualDirtyTrackingAttributeKey = "__changed__";
 | 
			
		||||
 | 
			
		||||
  protected static Boolean manualDirtyTrackingSupportEnabled = false;
 | 
			
		||||
 | 
			
		||||
  protected static String manualDirtyTrackingAttributeKey = "__changed__";
 | 
			
		||||
 | 
			
		||||
  public Session(Manager manager) {
 | 
			
		||||
    super(manager);
 | 
			
		||||
    resetDirtyTracking();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void resetDirtyTracking() {
 | 
			
		||||
    this.changedAttributes = new HashMap<>();
 | 
			
		||||
    this.dirty = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static void setManualDirtyTrackingSupportEnabled(boolean enabled) {
 | 
			
		||||
    manualDirtyTrackingSupportEnabled = enabled;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static void setManualDirtyTrackingAttributeKey(String key) {
 | 
			
		||||
    manualDirtyTrackingAttributeKey = key;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public Boolean isDirty() {
 | 
			
		||||
    return this.dirty || !this.changedAttributes.isEmpty();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public Map<String, Object> getChangedAttributes() {
 | 
			
		||||
    return this.changedAttributes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setId(String id) {
 | 
			
		||||
    this.id = id;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setAttribute(String key, Object value) {
 | 
			
		||||
    if (manualDirtyTrackingSupportEnabled && manualDirtyTrackingAttributeKey.equals(key)) {
 | 
			
		||||
      this.dirty = true;
 | 
			
		||||
      return;
 | 
			
		||||
    public Session(Manager manager) {
 | 
			
		||||
        super(manager);
 | 
			
		||||
        resetDirtyTracking();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Object oldValue = getAttribute(key);
 | 
			
		||||
    super.setAttribute(key, value);
 | 
			
		||||
 | 
			
		||||
    if ((value != null || oldValue != null)
 | 
			
		||||
        && (value == null && oldValue != null || oldValue == null && value != null
 | 
			
		||||
        || !value.getClass().isInstance(oldValue) || !value.equals(oldValue))) {
 | 
			
		||||
      if (this.manager instanceof SessionManager && ((SessionManager) this.manager)
 | 
			
		||||
          .getSaveOnChange()) {
 | 
			
		||||
        ((SessionManager) this.manager).save(this, true);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.changedAttributes.put(key, value);
 | 
			
		||||
      }
 | 
			
		||||
    /** To reset dirty tracking. */
 | 
			
		||||
    public void resetDirtyTracking() {
 | 
			
		||||
        this.changedAttributes = new HashMap<>();
 | 
			
		||||
        this.dirty = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Object getAttribute(String name) {
 | 
			
		||||
    return super.getAttribute(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Enumeration<String> getAttributeNames() {
 | 
			
		||||
    return super.getAttributeNames();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void removeAttribute(String name) {
 | 
			
		||||
    super.removeAttribute(name);
 | 
			
		||||
    if (this.manager instanceof SessionManager && ((SessionManager) this.manager)
 | 
			
		||||
        .getSaveOnChange()) {
 | 
			
		||||
      ((SessionManager) this.manager).save(this, true);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.dirty = true;
 | 
			
		||||
    /** To set manual dirty tracking support enabled. */
 | 
			
		||||
    public static void setManualDirtyTrackingSupportEnabled(boolean enabled) {
 | 
			
		||||
        manualDirtyTrackingSupportEnabled = enabled;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setPrincipal(Principal principal) {
 | 
			
		||||
    super.setPrincipal(principal);
 | 
			
		||||
    this.dirty = true;
 | 
			
		||||
  }
 | 
			
		||||
    /** To set manual dirty tracking attribute key. */
 | 
			
		||||
    public static void setManualDirtyTrackingAttributeKey(String key) {
 | 
			
		||||
        manualDirtyTrackingAttributeKey = key;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void writeObjectData(ObjectOutputStream out) throws IOException {
 | 
			
		||||
    super.writeObjectData(out);
 | 
			
		||||
    out.writeLong(this.getCreationTime());
 | 
			
		||||
  }
 | 
			
		||||
    /** Returns dirty check. */
 | 
			
		||||
    public Boolean isDirty() {
 | 
			
		||||
        return this.dirty || !this.changedAttributes.isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void readObjectData(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
    super.readObjectData(in);
 | 
			
		||||
    this.setCreationTime(in.readLong());
 | 
			
		||||
  }
 | 
			
		||||
    /** To get changed attributes. */
 | 
			
		||||
    public Map<String, Object> getChangedAttributes() {
 | 
			
		||||
        return this.changedAttributes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void invalidate() {
 | 
			
		||||
    super.invalidate();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setId(String id) {
 | 
			
		||||
        this.id = id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAttribute(String key, Object value) {
 | 
			
		||||
        if (manualDirtyTrackingSupportEnabled && manualDirtyTrackingAttributeKey.equals(key)) {
 | 
			
		||||
            this.dirty = true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Object oldValue = getAttribute(key);
 | 
			
		||||
        super.setAttribute(key, value);
 | 
			
		||||
 | 
			
		||||
        if ((value != null || oldValue != null)
 | 
			
		||||
                && (value == null && oldValue != null || oldValue == null && value != null
 | 
			
		||||
                || !value.getClass().isInstance(oldValue) || !value.equals(oldValue))) {
 | 
			
		||||
            if (this.manager instanceof SessionManager && ((SessionManager) this.manager)
 | 
			
		||||
                    .getSaveOnChange()) {
 | 
			
		||||
                ((SessionManager) this.manager).save(this, true);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.changedAttributes.put(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getAttribute(String name) {
 | 
			
		||||
        return super.getAttribute(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Enumeration<String> getAttributeNames() {
 | 
			
		||||
        return super.getAttributeNames();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void removeAttribute(String name) {
 | 
			
		||||
        super.removeAttribute(name);
 | 
			
		||||
        if (this.manager instanceof SessionManager && ((SessionManager) this.manager)
 | 
			
		||||
                .getSaveOnChange()) {
 | 
			
		||||
            ((SessionManager) this.manager).save(this, true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setPrincipal(Principal principal) {
 | 
			
		||||
        super.setPrincipal(principal);
 | 
			
		||||
        this.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void writeObjectData(ObjectOutputStream out) throws IOException {
 | 
			
		||||
        super.writeObjectData(out);
 | 
			
		||||
        out.writeLong(this.getCreationTime());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void readObjectData(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
        super.readObjectData(in);
 | 
			
		||||
        this.setCreationTime(in.readLong());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void invalidate() {
 | 
			
		||||
        super.invalidate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,22 @@
 | 
			
		|||
package tomcat.request.session;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Session constants.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public interface SessionConstants {
 | 
			
		||||
    byte[] NULL_SESSION = "null".getBytes();
 | 
			
		||||
    String CATALINA_BASE = "catalina.base";
 | 
			
		||||
    String CONF = "conf";
 | 
			
		||||
 | 
			
		||||
  byte[] NULL_SESSION = "null".getBytes();
 | 
			
		||||
    enum SessionPolicy {
 | 
			
		||||
        DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;
 | 
			
		||||
 | 
			
		||||
  String CATALINA_BASE = "catalina.base";
 | 
			
		||||
        public static SessionPolicy fromName(String name) {
 | 
			
		||||
            for (SessionPolicy policy : SessionPolicy.values()) {
 | 
			
		||||
                if (policy.name().equalsIgnoreCase(name)) {
 | 
			
		||||
                    return policy;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            throw new IllegalArgumentException("Invalid session policy [" + name + "]");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  String CONF = "conf";
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,85 +1,57 @@
 | 
			
		|||
package tomcat.request.session;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Session context uses to manage current session data.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class SessionContext {
 | 
			
		||||
 | 
			
		||||
  private String id;
 | 
			
		||||
    private String id;
 | 
			
		||||
    private Session session;
 | 
			
		||||
    private boolean persisted;
 | 
			
		||||
    private SessionMetadata metadata;
 | 
			
		||||
 | 
			
		||||
  private Session session;
 | 
			
		||||
    /** To get session id. */
 | 
			
		||||
    public String getId() {
 | 
			
		||||
        return id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  private boolean persisted;
 | 
			
		||||
    /** To set session id. */
 | 
			
		||||
    public void setId(String id) {
 | 
			
		||||
        this.id = id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  private SessionMetadata metadata;
 | 
			
		||||
    /** To get session. */
 | 
			
		||||
    public Session getSession() {
 | 
			
		||||
        return session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session id
 | 
			
		||||
   */
 | 
			
		||||
  public String getId() {
 | 
			
		||||
    return id;
 | 
			
		||||
  }
 | 
			
		||||
    /** To set session. */
 | 
			
		||||
    public void setSession(Session session) {
 | 
			
		||||
        this.session = session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session id
 | 
			
		||||
   */
 | 
			
		||||
  public void setId(String id) {
 | 
			
		||||
    this.id = id;
 | 
			
		||||
  }
 | 
			
		||||
    /** To check session is persisted. */
 | 
			
		||||
    public boolean isPersisted() {
 | 
			
		||||
        return persisted;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session
 | 
			
		||||
   */
 | 
			
		||||
  public Session getSession() {
 | 
			
		||||
    return session;
 | 
			
		||||
  }
 | 
			
		||||
    /** To set session persisted. */
 | 
			
		||||
    public void setPersisted(boolean persisted) {
 | 
			
		||||
        this.persisted = persisted;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session
 | 
			
		||||
   */
 | 
			
		||||
  public void setSession(Session session) {
 | 
			
		||||
    this.session = session;
 | 
			
		||||
  }
 | 
			
		||||
    /** To get session meta-data. */
 | 
			
		||||
    public SessionMetadata getMetadata() {
 | 
			
		||||
        return metadata;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To check session is persisted
 | 
			
		||||
   */
 | 
			
		||||
  public boolean isPersisted() {
 | 
			
		||||
    return persisted;
 | 
			
		||||
  }
 | 
			
		||||
    /** To set session meta-data. */
 | 
			
		||||
    public void setMetadata(SessionMetadata metadata) {
 | 
			
		||||
        this.metadata = metadata;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session persisted
 | 
			
		||||
   */
 | 
			
		||||
  public void setPersisted(boolean persisted) {
 | 
			
		||||
    this.persisted = persisted;
 | 
			
		||||
  }
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "SessionContext [id=" + id + "]";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session meta-data
 | 
			
		||||
   */
 | 
			
		||||
  public SessionMetadata getMetadata() {
 | 
			
		||||
    return metadata;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session meta-data
 | 
			
		||||
   */
 | 
			
		||||
  public void setMetadata(SessionMetadata metadata) {
 | 
			
		||||
    this.metadata = metadata;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public String toString() {
 | 
			
		||||
    return "SessionContext [id=" + id + "]";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,60 +5,44 @@ import java.io.ObjectInputStream;
 | 
			
		|||
import java.io.ObjectOutputStream;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * This class is uses to store and retrieve the HTTP request session object meta-data.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class SessionMetadata implements Serializable {
 | 
			
		||||
 | 
			
		||||
  private static final long serialVersionUID = 124438185184833546L;
 | 
			
		||||
    private static final long serialVersionUID = 124438185184833546L;
 | 
			
		||||
 | 
			
		||||
  private byte[] attributesHash;
 | 
			
		||||
    private byte[] attributesHash;
 | 
			
		||||
 | 
			
		||||
  public SessionMetadata() {
 | 
			
		||||
    this.attributesHash = new byte[0];
 | 
			
		||||
  }
 | 
			
		||||
    public SessionMetadata() {
 | 
			
		||||
        this.attributesHash = new byte[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session meta-data hash
 | 
			
		||||
   */
 | 
			
		||||
  public byte[] getAttributesHash() {
 | 
			
		||||
    return this.attributesHash;
 | 
			
		||||
  }
 | 
			
		||||
    /** To get session meta-data hash. */
 | 
			
		||||
    public byte[] getAttributesHash() {
 | 
			
		||||
        return this.attributesHash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session meta-data hash
 | 
			
		||||
   */
 | 
			
		||||
  public void setAttributesHash(byte[] attributesHash) {
 | 
			
		||||
    this.attributesHash = attributesHash;
 | 
			
		||||
  }
 | 
			
		||||
    /** To set session meta-data hash. */
 | 
			
		||||
    public void setAttributesHash(byte[] attributesHash) {
 | 
			
		||||
        this.attributesHash = attributesHash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To copy session meta-data
 | 
			
		||||
   */
 | 
			
		||||
  public void copyFieldsFrom(SessionMetadata metadata) {
 | 
			
		||||
    this.setAttributesHash(metadata.getAttributesHash());
 | 
			
		||||
  }
 | 
			
		||||
    /** To copy session meta-data. */
 | 
			
		||||
    public void copyFieldsFrom(SessionMetadata metadata) {
 | 
			
		||||
        this.setAttributesHash(metadata.getAttributesHash());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To write session meta-data to output stream
 | 
			
		||||
   */
 | 
			
		||||
  private void writeObject(ObjectOutputStream out) throws IOException {
 | 
			
		||||
    out.writeInt(this.attributesHash.length);
 | 
			
		||||
    out.write(this.attributesHash);
 | 
			
		||||
  }
 | 
			
		||||
    /** To write session meta-data to output stream. */
 | 
			
		||||
    private void writeObject(ObjectOutputStream out) throws IOException {
 | 
			
		||||
        out.writeInt(this.attributesHash.length);
 | 
			
		||||
        out.write(this.attributesHash);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To read session meta-data from input stream
 | 
			
		||||
   */
 | 
			
		||||
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
    int hashLength = in.readInt();
 | 
			
		||||
    byte[] attributesHash = new byte[hashLength];
 | 
			
		||||
    in.read(attributesHash, 0, hashLength);
 | 
			
		||||
    this.attributesHash = attributesHash;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
    /** To read session meta-data from input stream. */
 | 
			
		||||
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
        int hashLength = in.readInt();
 | 
			
		||||
        byte[] attributesHash = new byte[hashLength];
 | 
			
		||||
        in.read(attributesHash, 0, hashLength);
 | 
			
		||||
        this.attributesHash = attributesHash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,40 +1,49 @@
 | 
			
		|||
package tomcat.request.session.data.cache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * API for Data cache.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public interface DataCache {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set value in data-cache
 | 
			
		||||
   */
 | 
			
		||||
  byte[] set(String key, byte[] value);
 | 
			
		||||
    /**
 | 
			
		||||
     * Set value in data-cache.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key   - key with which the specified value is to be associated.
 | 
			
		||||
     * @param value - value to be associated with the specified key.
 | 
			
		||||
     * @return - Returns the value.
 | 
			
		||||
     */
 | 
			
		||||
    byte[] set(String key, byte[] value);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set value if key not exists in data-cache
 | 
			
		||||
   *
 | 
			
		||||
   * Returns If key exists = 0 else 1
 | 
			
		||||
   */
 | 
			
		||||
  Long setnx(String key, byte[] value);
 | 
			
		||||
    /**
 | 
			
		||||
     * Set value if key not exists in data-cache.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key   - key with which the specified value is to be associated.
 | 
			
		||||
     * @param value - value to be associated with the specified key.
 | 
			
		||||
     * @return - Returns '0' if key already exists else '1'.
 | 
			
		||||
     */
 | 
			
		||||
    Long setnx(String key, byte[] value);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To expire the value based on key in data-cache
 | 
			
		||||
   */
 | 
			
		||||
  Long expire(String key, int seconds);
 | 
			
		||||
    /**
 | 
			
		||||
     * Set expiry in data-cache.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key     - key with which the specified value is to be associated.
 | 
			
		||||
     * @param seconds - expiration time in seconds.
 | 
			
		||||
     * @return - Returns the expiration time in seconds.
 | 
			
		||||
     */
 | 
			
		||||
    Long expire(String key, int seconds);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get the value based on key from data-cache
 | 
			
		||||
   */
 | 
			
		||||
  byte[] get(String key);
 | 
			
		||||
    /**
 | 
			
		||||
     * Get value from data-cache.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key - key with which the specified value is to be associated.
 | 
			
		||||
     * @return - Returns the value.
 | 
			
		||||
     */
 | 
			
		||||
    byte[] get(String key);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To delete the value based on key from data-cache
 | 
			
		||||
   */
 | 
			
		||||
  Long delete(String key);
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete value from data-cache.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key - key with which the specified value is to be associated.
 | 
			
		||||
     * @return - Returns the number of keys that were removed.
 | 
			
		||||
     */
 | 
			
		||||
    Long delete(String key);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,147 +0,0 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.Jedis;
 | 
			
		||||
import redis.clients.jedis.JedisPool;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisConnectionException;
 | 
			
		||||
import tomcat.request.session.data.cache.DataCache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Redis stand-alone mode data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
class RedisCacheUtil implements DataCache {
 | 
			
		||||
 | 
			
		||||
  private JedisPool pool;
 | 
			
		||||
 | 
			
		||||
  private static final int NUM_RETRIES = 3;
 | 
			
		||||
 | 
			
		||||
  private Log log = LogFactory.getLog(RedisCacheUtil.class);
 | 
			
		||||
 | 
			
		||||
  RedisCacheUtil(String host, int port, String password, int database, int timeout,
 | 
			
		||||
      JedisPoolConfig poolConfig) {
 | 
			
		||||
    pool = new JedisPool(poolConfig, host, port, timeout, password, database);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] set(String key, byte[] value) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    String retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try (Jedis jedis = pool.getResource()) {
 | 
			
		||||
        retVal = jedis.set(key.getBytes(), value);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return (retVal != null) ? retVal.getBytes() : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long setnx(String key, byte[] value) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try (Jedis jedis = pool.getResource()) {
 | 
			
		||||
        retVal = jedis.setnx(key.getBytes(), value);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long expire(String key, int seconds) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try (Jedis jedis = pool.getResource()) {
 | 
			
		||||
        retVal = jedis.expire(key, seconds);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] get(String key) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    byte[] retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try (Jedis jedis = pool.getResource()) {
 | 
			
		||||
        retVal = jedis.get(key.getBytes());
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long delete(String key) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try (Jedis jedis = pool.getResource()) {
 | 
			
		||||
        retVal = jedis.del(key);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,169 +0,0 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.HostAndPort;
 | 
			
		||||
import redis.clients.jedis.JedisCluster;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.Protocol;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisConnectionException;
 | 
			
		||||
import tomcat.request.session.data.cache.DataCache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Redis multiple node cluster data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
class RedisClusterCacheUtil implements DataCache {
 | 
			
		||||
 | 
			
		||||
  private JedisCluster cluster;
 | 
			
		||||
 | 
			
		||||
  private static final int NUM_RETRIES = 30;
 | 
			
		||||
  private static final int DEFAULT_MAX_REDIRECTIONS = 5;
 | 
			
		||||
 | 
			
		||||
  private Log log = LogFactory.getLog(RedisClusterCacheUtil.class);
 | 
			
		||||
 | 
			
		||||
  RedisClusterCacheUtil(Set<HostAndPort> nodes, String password, int timeout,
 | 
			
		||||
      JedisPoolConfig poolConfig) {
 | 
			
		||||
    cluster = new JedisCluster(nodes, timeout, Protocol.DEFAULT_TIMEOUT, DEFAULT_MAX_REDIRECTIONS,
 | 
			
		||||
        password, poolConfig);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] set(String key, byte[] value) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    String retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try {
 | 
			
		||||
        retVal = cluster.set(key.getBytes(), value);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        waitforFailover();
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return (retVal != null) ? retVal.getBytes() : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long setnx(String key, byte[] value) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try {
 | 
			
		||||
        retVal = cluster.setnx(key.getBytes(), value);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        waitforFailover();
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long expire(String key, int seconds) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try {
 | 
			
		||||
        retVal = cluster.expire(key, seconds);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        waitforFailover();
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] get(String key) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    byte[] retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try {
 | 
			
		||||
        retVal = cluster.get(key.getBytes());
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        waitforFailover();
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long delete(String key) {
 | 
			
		||||
    int tries = 0;
 | 
			
		||||
    boolean sucess = false;
 | 
			
		||||
    Long retVal = null;
 | 
			
		||||
    do {
 | 
			
		||||
      tries++;
 | 
			
		||||
      try {
 | 
			
		||||
        retVal = cluster.del(key);
 | 
			
		||||
        sucess = true;
 | 
			
		||||
      } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
        log.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
          throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        waitforFailover();
 | 
			
		||||
      }
 | 
			
		||||
    } while (!sucess && tries <= NUM_RETRIES);
 | 
			
		||||
    return retVal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To wait for handling redis fail-over
 | 
			
		||||
   */
 | 
			
		||||
  private void waitforFailover() {
 | 
			
		||||
    try {
 | 
			
		||||
      Thread.sleep(4000);
 | 
			
		||||
    } catch (InterruptedException ex) {
 | 
			
		||||
      Thread.currentThread().interrupt();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,45 +0,0 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Redis data-cache constants.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
interface RedisConstants {
 | 
			
		||||
 | 
			
		||||
  // redis properties file name
 | 
			
		||||
  String PROPERTIES_FILE = "redis-data-cache.properties";
 | 
			
		||||
 | 
			
		||||
  // redis properties
 | 
			
		||||
  String HOSTS = "redis.hosts";
 | 
			
		||||
  String CLUSTER_ENABLED = "redis.cluster.enabled";
 | 
			
		||||
 | 
			
		||||
  String MAX_ACTIVE = "redis.max.active";
 | 
			
		||||
  String TEST_ONBORROW = "redis.test.onBorrow";
 | 
			
		||||
  String TEST_ONRETURN = "redis.test.onReturn";
 | 
			
		||||
  String MAX_IDLE = "redis.max.idle";
 | 
			
		||||
  String MIN_IDLE = "redis.min.idle";
 | 
			
		||||
  String TEST_WHILEIDLE = "redis.test.whileIdle";
 | 
			
		||||
  String TEST_NUMPEREVICTION = "redis.test.numPerEviction";
 | 
			
		||||
  String TIME_BETWEENEVICTION = "redis.time.betweenEviction";
 | 
			
		||||
 | 
			
		||||
  String PASSWORD = "redis.password";
 | 
			
		||||
  String DATABASE = "redis.database";
 | 
			
		||||
  String TIMEOUT = "redis.timeout";
 | 
			
		||||
 | 
			
		||||
  // redis property default values
 | 
			
		||||
  String DEFAULT_MAX_ACTIVE_VALUE = "10";
 | 
			
		||||
  String DEFAULT_TEST_ONBORROW_VALUE = "true";
 | 
			
		||||
  String DEFAULT_TEST_ONRETURN_VALUE = "true";
 | 
			
		||||
  String DEFAULT_MAX_IDLE_VALUE = "5";
 | 
			
		||||
  String DEFAULT_MIN_IDLE_VALUE = "1";
 | 
			
		||||
  String DEFAULT_TEST_WHILEIDLE_VALUE = "true";
 | 
			
		||||
  String DEFAULT_TEST_NUMPEREVICTION_VALUE = "10";
 | 
			
		||||
  String DEFAULT_TIME_BETWEENEVICTION_VALUE = "60000";
 | 
			
		||||
  String DEFAULT_CLUSTER_ENABLED = "false";
 | 
			
		||||
 | 
			
		||||
  String CONN_FAILED_RETRY_MSG = "Jedis connection failed, retrying...";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,226 +0,0 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.HostAndPort;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.Protocol;
 | 
			
		||||
import tomcat.request.session.SessionConstants;
 | 
			
		||||
import tomcat.request.session.data.cache.DataCache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Redis data-cache implementation to store/retrieve session objects.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
public class RedisDataCache implements DataCache {
 | 
			
		||||
 | 
			
		||||
  private static DataCache dataCache;
 | 
			
		||||
 | 
			
		||||
  private Log log = LogFactory.getLog(RedisDataCache.class);
 | 
			
		||||
 | 
			
		||||
  public RedisDataCache() {
 | 
			
		||||
    initialize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] set(String key, byte[] value) {
 | 
			
		||||
    return dataCache.set(key, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long setnx(String key, byte[] value) {
 | 
			
		||||
    return dataCache.setnx(key, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long expire(String key, int seconds) {
 | 
			
		||||
    return dataCache.expire(key, seconds);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public byte[] get(String key) {
 | 
			
		||||
    return (key != null) ? dataCache.get(key) : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Long delete(String key) {
 | 
			
		||||
    return dataCache.delete(key);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To parse data-cache key
 | 
			
		||||
   */
 | 
			
		||||
  public static String parseDataCacheKey(String key) {
 | 
			
		||||
    return key.replaceAll("\\s", "_");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To initialize the data-cache
 | 
			
		||||
   */
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  private void initialize() {
 | 
			
		||||
    if (dataCache != null) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    Properties properties = loadProperties();
 | 
			
		||||
 | 
			
		||||
    boolean clusterEnabled = Boolean.valueOf(properties
 | 
			
		||||
        .getProperty(RedisConstants.CLUSTER_ENABLED, RedisConstants.DEFAULT_CLUSTER_ENABLED));
 | 
			
		||||
 | 
			
		||||
    String hosts = properties.getProperty(RedisConstants.HOSTS,
 | 
			
		||||
        Protocol.DEFAULT_HOST.concat(":").concat(String.valueOf(Protocol.DEFAULT_PORT)));
 | 
			
		||||
    Collection<? extends Serializable> nodes = getJedisNodes(hosts, clusterEnabled);
 | 
			
		||||
 | 
			
		||||
    String password = properties.getProperty(RedisConstants.PASSWORD);
 | 
			
		||||
    password = (password != null && !password.isEmpty()) ? password : null;
 | 
			
		||||
 | 
			
		||||
    int database = Integer.parseInt(
 | 
			
		||||
        properties.getProperty(RedisConstants.DATABASE, String.valueOf(Protocol.DEFAULT_DATABASE)));
 | 
			
		||||
 | 
			
		||||
    int timeout = Integer.parseInt(
 | 
			
		||||
        properties.getProperty(RedisConstants.TIMEOUT, String.valueOf(Protocol.DEFAULT_TIMEOUT)));
 | 
			
		||||
    timeout = (timeout < Protocol.DEFAULT_TIMEOUT) ? Protocol.DEFAULT_TIMEOUT : timeout;
 | 
			
		||||
 | 
			
		||||
    if (clusterEnabled) {
 | 
			
		||||
      dataCache = new RedisClusterCacheUtil((Set<HostAndPort>) nodes, password, timeout,
 | 
			
		||||
          getPoolConfig(properties));
 | 
			
		||||
    } else {
 | 
			
		||||
      dataCache = new RedisCacheUtil(((List<String>) nodes).get(0),
 | 
			
		||||
          Integer.parseInt(((List<String>) nodes).get(1)), password, database, timeout,
 | 
			
		||||
          getPoolConfig(properties));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get jedis pool configuration
 | 
			
		||||
   */
 | 
			
		||||
  private JedisPoolConfig getPoolConfig(Properties properties) {
 | 
			
		||||
    JedisPoolConfig poolConfig = new JedisPoolConfig();
 | 
			
		||||
    int maxActive = Integer.parseInt(
 | 
			
		||||
        properties.getProperty(RedisConstants.MAX_ACTIVE, RedisConstants.DEFAULT_MAX_ACTIVE_VALUE));
 | 
			
		||||
    poolConfig.setMaxTotal(maxActive);
 | 
			
		||||
 | 
			
		||||
    boolean testOnBorrow = Boolean.parseBoolean(properties
 | 
			
		||||
        .getProperty(RedisConstants.TEST_ONBORROW, RedisConstants.DEFAULT_TEST_ONBORROW_VALUE));
 | 
			
		||||
    poolConfig.setTestOnBorrow(testOnBorrow);
 | 
			
		||||
 | 
			
		||||
    boolean testOnReturn = Boolean.parseBoolean(properties
 | 
			
		||||
        .getProperty(RedisConstants.TEST_ONRETURN, RedisConstants.DEFAULT_TEST_ONRETURN_VALUE));
 | 
			
		||||
    poolConfig.setTestOnReturn(testOnReturn);
 | 
			
		||||
 | 
			
		||||
    int maxIdle = Integer.parseInt(
 | 
			
		||||
        properties.getProperty(RedisConstants.MAX_ACTIVE, RedisConstants.DEFAULT_MAX_ACTIVE_VALUE));
 | 
			
		||||
    poolConfig.setMaxIdle(maxIdle);
 | 
			
		||||
 | 
			
		||||
    int minIdle = Integer.parseInt(
 | 
			
		||||
        properties.getProperty(RedisConstants.MIN_IDLE, RedisConstants.DEFAULT_MIN_IDLE_VALUE));
 | 
			
		||||
    poolConfig.setMinIdle(minIdle);
 | 
			
		||||
 | 
			
		||||
    boolean testWhileIdle = Boolean.parseBoolean(properties
 | 
			
		||||
        .getProperty(RedisConstants.TEST_WHILEIDLE, RedisConstants.DEFAULT_TEST_WHILEIDLE_VALUE));
 | 
			
		||||
    poolConfig.setTestWhileIdle(testWhileIdle);
 | 
			
		||||
 | 
			
		||||
    int testNumPerEviction = Integer.parseInt(properties
 | 
			
		||||
        .getProperty(RedisConstants.TEST_NUMPEREVICTION,
 | 
			
		||||
            RedisConstants.DEFAULT_TEST_NUMPEREVICTION_VALUE));
 | 
			
		||||
    poolConfig.setNumTestsPerEvictionRun(testNumPerEviction);
 | 
			
		||||
 | 
			
		||||
    long timeBetweenEviction = Long.parseLong(properties
 | 
			
		||||
        .getProperty(RedisConstants.TIME_BETWEENEVICTION,
 | 
			
		||||
            RedisConstants.DEFAULT_TIME_BETWEENEVICTION_VALUE));
 | 
			
		||||
    poolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEviction);
 | 
			
		||||
    return poolConfig;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get jedis nodes
 | 
			
		||||
   */
 | 
			
		||||
  private Collection<? extends Serializable> getJedisNodes(String hosts, boolean clusterEnabled) {
 | 
			
		||||
    hosts = hosts.replaceAll("\\s", "");
 | 
			
		||||
    String[] hostPorts = hosts.split(",");
 | 
			
		||||
 | 
			
		||||
    List<String> node = null;
 | 
			
		||||
    Set<HostAndPort> nodes = null;
 | 
			
		||||
 | 
			
		||||
    for (String hostPort : hostPorts) {
 | 
			
		||||
      String[] hostPortArr = hostPort.split(":");
 | 
			
		||||
 | 
			
		||||
      if (clusterEnabled) {
 | 
			
		||||
        nodes = (nodes == null) ? new HashSet<HostAndPort>() : nodes;
 | 
			
		||||
        nodes.add(new HostAndPort(hostPortArr[0], Integer.valueOf(hostPortArr[1])));
 | 
			
		||||
      } else {
 | 
			
		||||
        int port = Integer.valueOf(hostPortArr[1]);
 | 
			
		||||
        if (!hostPortArr[0].isEmpty() && port > 0) {
 | 
			
		||||
          node = (node == null) ? new ArrayList<String>() : node;
 | 
			
		||||
          node.add(hostPortArr[0]);
 | 
			
		||||
          node.add(String.valueOf(port));
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return clusterEnabled ? nodes : node;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To load data-cache properties
 | 
			
		||||
   */
 | 
			
		||||
  private Properties loadProperties() {
 | 
			
		||||
    Properties properties = new Properties();
 | 
			
		||||
    try {
 | 
			
		||||
      String filePath = System.getProperty(SessionConstants.CATALINA_BASE).concat(File.separator)
 | 
			
		||||
          .concat(SessionConstants.CONF).concat(File.separator)
 | 
			
		||||
          .concat(RedisConstants.PROPERTIES_FILE);
 | 
			
		||||
 | 
			
		||||
      InputStream resourceStream = null;
 | 
			
		||||
      try {
 | 
			
		||||
        resourceStream = (filePath != null && !filePath.isEmpty() && new File(filePath).exists())
 | 
			
		||||
            ? new FileInputStream(filePath) : null;
 | 
			
		||||
 | 
			
		||||
        if (resourceStream == null) {
 | 
			
		||||
          ClassLoader loader = Thread.currentThread().getContextClassLoader();
 | 
			
		||||
          resourceStream = loader.getResourceAsStream(RedisConstants.PROPERTIES_FILE);
 | 
			
		||||
        }
 | 
			
		||||
        properties.load(resourceStream);
 | 
			
		||||
      } finally {
 | 
			
		||||
        if (resourceStream != null) {
 | 
			
		||||
          resourceStream.close();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } catch (IOException ex) {
 | 
			
		||||
      log.error("Error while loading task scheduler properties", ex);
 | 
			
		||||
    }
 | 
			
		||||
    return properties;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										132
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/AbstractRedisUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										132
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/AbstractRedisUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import redis.clients.jedis.Jedis;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisConnectionException;
 | 
			
		||||
import redis.clients.util.Pool;
 | 
			
		||||
import tomcat.request.session.data.cache.DataCache;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public abstract class AbstractRedisUtil implements DataCache {
 | 
			
		||||
 | 
			
		||||
    private static final int NUM_RETRIES = 3;
 | 
			
		||||
    private static final Log LOGGER = LogFactory.getLog(RedisUtil.class);
 | 
			
		||||
 | 
			
		||||
    private final Pool<Jedis> pool;
 | 
			
		||||
    private final long failiureWaitTime;
 | 
			
		||||
 | 
			
		||||
    AbstractRedisUtil(Pool<Jedis> pool, long failiureWaitTime) {
 | 
			
		||||
        this.pool = pool;
 | 
			
		||||
        this.failiureWaitTime = failiureWaitTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] set(String key, byte[] value) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        String retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try (Jedis jedis = this.pool.getResource()) {
 | 
			
		||||
                retVal = jedis.set(key.getBytes(), value);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return (retVal != null) ? retVal.getBytes() : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long setnx(String key, byte[] value) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try (Jedis jedis = this.pool.getResource()) {
 | 
			
		||||
                retVal = jedis.setnx(key.getBytes(), value);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long expire(String key, int seconds) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try (Jedis jedis = this.pool.getResource()) {
 | 
			
		||||
                retVal = jedis.expire(key, seconds);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] get(String key) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        byte[] retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try (Jedis jedis = this.pool.getResource()) {
 | 
			
		||||
                retVal = jedis.get(key.getBytes());
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long delete(String key) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try (Jedis jedis = this.pool.getResource()) {
 | 
			
		||||
                retVal = jedis.del(key);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * To handle jedis exception.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tries - exception occurred in tries.
 | 
			
		||||
     * @param ex    - jedis exception.
 | 
			
		||||
     */
 | 
			
		||||
    void handleException(int tries, RuntimeException ex) {
 | 
			
		||||
        LOGGER.error(RedisConstants.CONN_FAILED_RETRY_MSG + tries);
 | 
			
		||||
        if (tries == NUM_RETRIES) {
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            Thread.sleep(this.failiureWaitTime);
 | 
			
		||||
        } catch (InterruptedException e) {
 | 
			
		||||
            Thread.currentThread().interrupt();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,206 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import redis.clients.jedis.HostAndPort;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.Protocol;
 | 
			
		||||
import tomcat.request.session.SessionConstants;
 | 
			
		||||
import tomcat.request.session.data.cache.DataCache;
 | 
			
		||||
import tomcat.request.session.data.cache.impl.redis.RedisConstants.RedisConfigType;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
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 */
 | 
			
		||||
public class RedisCache implements DataCache {
 | 
			
		||||
 | 
			
		||||
    private static DataCache dataCache;
 | 
			
		||||
 | 
			
		||||
    private static final Log LOGGER = LogFactory.getLog(RedisCache.class);
 | 
			
		||||
 | 
			
		||||
    public RedisCache() {
 | 
			
		||||
        initialize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] set(String key, byte[] value) {
 | 
			
		||||
        return dataCache.set(key, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long setnx(String key, byte[] value) {
 | 
			
		||||
        return dataCache.setnx(key, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long expire(String key, int seconds) {
 | 
			
		||||
        return dataCache.expire(key, seconds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] get(String key) {
 | 
			
		||||
        return dataCache.get(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long delete(String key) {
 | 
			
		||||
        return dataCache.delete(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To initialize the redis data cache. */
 | 
			
		||||
    private void initialize() {
 | 
			
		||||
        if (dataCache != null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        Properties properties = getProperties();
 | 
			
		||||
 | 
			
		||||
        RedisConfigType configType;
 | 
			
		||||
        if (Boolean.valueOf(properties.getProperty(RedisConstants.CLUSTER_ENABLED))) {
 | 
			
		||||
            configType = RedisConfigType.CLUSTER;
 | 
			
		||||
        } else if (Boolean.valueOf(properties.getProperty(RedisConstants.SENTINEL_ENABLED))) {
 | 
			
		||||
            configType = RedisConfigType.SENTINEL;
 | 
			
		||||
        } else {
 | 
			
		||||
            configType = RedisConfigType.DEFAULT;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String hosts = properties.getProperty(RedisConstants.HOSTS, Protocol.DEFAULT_HOST.concat(":").concat(String.valueOf(Protocol.DEFAULT_PORT)));
 | 
			
		||||
        Collection<?> nodes = getJedisNodes(hosts, configType);
 | 
			
		||||
 | 
			
		||||
        String password = properties.getProperty(RedisConstants.PASSWORD);
 | 
			
		||||
        password = (password != null && !password.isEmpty()) ? password : null;
 | 
			
		||||
 | 
			
		||||
        int database = Integer.parseInt(properties.getProperty(RedisConstants.DATABASE, String.valueOf(Protocol.DEFAULT_DATABASE)));
 | 
			
		||||
 | 
			
		||||
        int timeout = Integer.parseInt(properties.getProperty(RedisConstants.TIMEOUT, String.valueOf(Protocol.DEFAULT_TIMEOUT)));
 | 
			
		||||
        timeout = (timeout < Protocol.DEFAULT_TIMEOUT) ? Protocol.DEFAULT_TIMEOUT : timeout;
 | 
			
		||||
 | 
			
		||||
        JedisPoolConfig poolConfig = getPoolConfig(properties);
 | 
			
		||||
        switch (configType) {
 | 
			
		||||
            case CLUSTER:
 | 
			
		||||
                dataCache = new RedisClusterUtil((Set<HostAndPort>) nodes, password, timeout, poolConfig);
 | 
			
		||||
                break;
 | 
			
		||||
            case SENTINEL:
 | 
			
		||||
                String masterName = String.valueOf(properties.getProperty(RedisConstants.SENTINEL_MASTER, RedisConstants.DEFAULT_SENTINEL_MASTER));
 | 
			
		||||
                dataCache = new RedisSentinelUtil((Set<String>) nodes, masterName, password, database, timeout, poolConfig);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                dataCache = new RedisUtil(((List<String>) nodes).get(0), Integer.parseInt(((List<String>) nodes).get(1)), password, database, timeout, poolConfig);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * To get redis pool config.
 | 
			
		||||
     *
 | 
			
		||||
     * @param properties - Redis data cache properties.
 | 
			
		||||
     * @return - Returns the redis pool config.
 | 
			
		||||
     */
 | 
			
		||||
    private JedisPoolConfig getPoolConfig(Properties properties) {
 | 
			
		||||
        JedisPoolConfig poolConfig = new JedisPoolConfig();
 | 
			
		||||
        int maxActive = Integer.parseInt(properties.getProperty(RedisConstants.MAX_ACTIVE, RedisConstants.DEFAULT_MAX_ACTIVE_VALUE));
 | 
			
		||||
        poolConfig.setMaxTotal(maxActive);
 | 
			
		||||
 | 
			
		||||
        boolean testOnBorrow = Boolean.parseBoolean(properties.getProperty(RedisConstants.TEST_ONBORROW, RedisConstants.DEFAULT_TEST_ONBORROW_VALUE));
 | 
			
		||||
        poolConfig.setTestOnBorrow(testOnBorrow);
 | 
			
		||||
 | 
			
		||||
        boolean testOnReturn = Boolean.parseBoolean(properties.getProperty(RedisConstants.TEST_ONRETURN, RedisConstants.DEFAULT_TEST_ONRETURN_VALUE));
 | 
			
		||||
        poolConfig.setTestOnReturn(testOnReturn);
 | 
			
		||||
 | 
			
		||||
        int maxIdle = Integer.parseInt(properties.getProperty(RedisConstants.MAX_ACTIVE, RedisConstants.DEFAULT_MAX_ACTIVE_VALUE));
 | 
			
		||||
        poolConfig.setMaxIdle(maxIdle);
 | 
			
		||||
 | 
			
		||||
        int minIdle = Integer.parseInt(properties.getProperty(RedisConstants.MIN_IDLE, RedisConstants.DEFAULT_MIN_IDLE_VALUE));
 | 
			
		||||
        poolConfig.setMinIdle(minIdle);
 | 
			
		||||
 | 
			
		||||
        boolean testWhileIdle = Boolean.parseBoolean(properties.getProperty(RedisConstants.TEST_WHILEIDLE, RedisConstants.DEFAULT_TEST_WHILEIDLE_VALUE));
 | 
			
		||||
        poolConfig.setTestWhileIdle(testWhileIdle);
 | 
			
		||||
 | 
			
		||||
        int testNumPerEviction = Integer.parseInt(properties.getProperty(RedisConstants.TEST_NUMPEREVICTION, RedisConstants.DEFAULT_TEST_NUMPEREVICTION_VALUE));
 | 
			
		||||
        poolConfig.setNumTestsPerEvictionRun(testNumPerEviction);
 | 
			
		||||
 | 
			
		||||
        long timeBetweenEviction = Long.parseLong(properties.getProperty(RedisConstants.TIME_BETWEENEVICTION, RedisConstants.DEFAULT_TIME_BETWEENEVICTION_VALUE));
 | 
			
		||||
        poolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEviction);
 | 
			
		||||
        return poolConfig;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * To get redis data cache nodes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param hosts      - redis server hosts.
 | 
			
		||||
     * @param configType - redis data config type.
 | 
			
		||||
     * @return - Returns the redis nodes.
 | 
			
		||||
     */
 | 
			
		||||
    private Collection<?> getJedisNodes(String hosts, RedisConfigType configType) {
 | 
			
		||||
        hosts = hosts.replaceAll("\\s", "");
 | 
			
		||||
        String[] hostPorts = hosts.split(",");
 | 
			
		||||
 | 
			
		||||
        Set<Object> nodes = null;
 | 
			
		||||
 | 
			
		||||
        for (String hostPort : hostPorts) {
 | 
			
		||||
            String[] hostPortArr = hostPort.split(":");
 | 
			
		||||
 | 
			
		||||
            switch (configType) {
 | 
			
		||||
                case CLUSTER:
 | 
			
		||||
                    nodes = (nodes == null) ? new HashSet<>() : nodes;
 | 
			
		||||
                    nodes.add(new HostAndPort(hostPortArr[0], Integer.valueOf(hostPortArr[1])));
 | 
			
		||||
                    break;
 | 
			
		||||
                case SENTINEL:
 | 
			
		||||
                    nodes = (nodes == null) ? new HashSet<>() : nodes;
 | 
			
		||||
                    nodes.add(new HostAndPort(hostPortArr[0], Integer.valueOf(hostPortArr[1])).toString());
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    int port = Integer.valueOf(hostPortArr[1]);
 | 
			
		||||
                    if (!hostPortArr[0].isEmpty() && port > 0) {
 | 
			
		||||
                        List<String> node = new ArrayList<>();
 | 
			
		||||
                        node.add(hostPortArr[0]);
 | 
			
		||||
                        node.add(String.valueOf(port));
 | 
			
		||||
                        return node;
 | 
			
		||||
                    }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return nodes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To get redis data cache properties. */
 | 
			
		||||
    private Properties getProperties() {
 | 
			
		||||
        Properties properties = new Properties();
 | 
			
		||||
        try {
 | 
			
		||||
            String filePath = System.getProperty(SessionConstants.CATALINA_BASE).concat(File.separator)
 | 
			
		||||
                    .concat(SessionConstants.CONF).concat(File.separator)
 | 
			
		||||
                    .concat(RedisConstants.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(RedisConstants.PROPERTIES_FILE);
 | 
			
		||||
                }
 | 
			
		||||
                properties.load(resourceStream);
 | 
			
		||||
            } finally {
 | 
			
		||||
                if (resourceStream != null) {
 | 
			
		||||
                    resourceStream.close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (IOException ex) {
 | 
			
		||||
            LOGGER.error("Error while loading task scheduler properties", ex);
 | 
			
		||||
        }
 | 
			
		||||
        return properties;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										120
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisClusterUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										120
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisClusterUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.HostAndPort;
 | 
			
		||||
import redis.clients.jedis.JedisCluster;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.Protocol;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
 | 
			
		||||
import redis.clients.jedis.exceptions.JedisConnectionException;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class RedisClusterUtil extends AbstractRedisUtil {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    RedisClusterUtil(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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] set(String key, byte[] value) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        String retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try {
 | 
			
		||||
                retVal = this.cluster.set(key.getBytes(), value);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return (retVal != null) ? retVal.getBytes() : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long setnx(String key, byte[] value) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try {
 | 
			
		||||
                retVal = this.cluster.setnx(key.getBytes(), value);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long expire(String key, int seconds) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try {
 | 
			
		||||
                retVal = this.cluster.expire(key, seconds);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public byte[] get(String key) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        byte[] retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try {
 | 
			
		||||
                retVal = this.cluster.get(key.getBytes());
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long delete(String key) {
 | 
			
		||||
        int tries = 0;
 | 
			
		||||
        boolean retry = true;
 | 
			
		||||
        Long retVal = null;
 | 
			
		||||
        do {
 | 
			
		||||
            tries++;
 | 
			
		||||
            try {
 | 
			
		||||
                retVal = this.cluster.del(key);
 | 
			
		||||
                retry = false;
 | 
			
		||||
            } catch (JedisClusterMaxRedirectionsException | JedisConnectionException ex) {
 | 
			
		||||
                handleException(tries, ex);
 | 
			
		||||
            }
 | 
			
		||||
        } while (retry && tries <= NUM_RETRIES);
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisConstants.java
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										48
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisConstants.java
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
interface RedisConstants {
 | 
			
		||||
 | 
			
		||||
    // redis properties file name
 | 
			
		||||
    String PROPERTIES_FILE = "redis-data-cache.properties";
 | 
			
		||||
 | 
			
		||||
    // redis properties
 | 
			
		||||
    String HOSTS = "redis.hosts";
 | 
			
		||||
    String CLUSTER_ENABLED = "redis.cluster.enabled";
 | 
			
		||||
    String SENTINEL_ENABLED = "redis.sentinel.enabled";
 | 
			
		||||
 | 
			
		||||
    String MAX_ACTIVE = "redis.max.active";
 | 
			
		||||
    String TEST_ONBORROW = "redis.test.onBorrow";
 | 
			
		||||
    String TEST_ONRETURN = "redis.test.onReturn";
 | 
			
		||||
    String MAX_IDLE = "redis.max.idle";
 | 
			
		||||
    String MIN_IDLE = "redis.min.idle";
 | 
			
		||||
    String TEST_WHILEIDLE = "redis.test.whileIdle";
 | 
			
		||||
    String TEST_NUMPEREVICTION = "redis.test.numPerEviction";
 | 
			
		||||
    String TIME_BETWEENEVICTION = "redis.time.betweenEviction";
 | 
			
		||||
 | 
			
		||||
    String PASSWORD = "redis.password";
 | 
			
		||||
    String DATABASE = "redis.database";
 | 
			
		||||
    String TIMEOUT = "redis.timeout";
 | 
			
		||||
 | 
			
		||||
    String SENTINEL_MASTER = "redis.sentinel.master";
 | 
			
		||||
    String DEFAULT_SENTINEL_MASTER = "mymaster";
 | 
			
		||||
 | 
			
		||||
    // redis property default values
 | 
			
		||||
    String DEFAULT_MAX_ACTIVE_VALUE = "10";
 | 
			
		||||
    String DEFAULT_TEST_ONBORROW_VALUE = "true";
 | 
			
		||||
    String DEFAULT_TEST_ONRETURN_VALUE = "true";
 | 
			
		||||
    String DEFAULT_MAX_IDLE_VALUE = "5";
 | 
			
		||||
    String DEFAULT_MIN_IDLE_VALUE = "1";
 | 
			
		||||
    String DEFAULT_TEST_WHILEIDLE_VALUE = "true";
 | 
			
		||||
    String DEFAULT_TEST_NUMPEREVICTION_VALUE = "10";
 | 
			
		||||
    String DEFAULT_TIME_BETWEENEVICTION_VALUE = "60000";
 | 
			
		||||
 | 
			
		||||
    String CONN_FAILED_RETRY_MSG = "Jedis connection failed, retrying...";
 | 
			
		||||
 | 
			
		||||
    enum RedisConfigType {
 | 
			
		||||
        DEFAULT,
 | 
			
		||||
        SENTINEL,
 | 
			
		||||
        CLUSTER
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisSentinelUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										22
									
								
								src/main/java/tomcat/request/session/data/cache/impl/redis/RedisSentinelUtil.java
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
import redis.clients.jedis.JedisSentinelPool;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
class RedisSentinelUtil extends AbstractRedisUtil {
 | 
			
		||||
 | 
			
		||||
    private static final long FAILIURE_WAIT_TIME = 2000L;
 | 
			
		||||
 | 
			
		||||
    RedisSentinelUtil(Set<String> nodes,
 | 
			
		||||
                      String masterName,
 | 
			
		||||
                      String password,
 | 
			
		||||
                      int database,
 | 
			
		||||
                      int timeout,
 | 
			
		||||
                      JedisPoolConfig poolConfig) {
 | 
			
		||||
        super(new JedisSentinelPool(masterName, nodes, poolConfig, timeout, password, database), FAILIURE_WAIT_TIME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
package tomcat.request.session.data.cache.impl.redis;
 | 
			
		||||
 | 
			
		||||
import redis.clients.jedis.JedisPool;
 | 
			
		||||
import redis.clients.jedis.JedisPoolConfig;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
class RedisUtil extends AbstractRedisUtil {
 | 
			
		||||
 | 
			
		||||
    private static final long FAILIURE_WAIT_TIME = 2000L;
 | 
			
		||||
 | 
			
		||||
    RedisUtil(String host,
 | 
			
		||||
              int port,
 | 
			
		||||
              String password,
 | 
			
		||||
              int database,
 | 
			
		||||
              int timeout,
 | 
			
		||||
              JedisPoolConfig poolConfig) {
 | 
			
		||||
        super(new JedisPool(poolConfig, host, port, timeout, password, database), FAILIURE_WAIT_TIME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,35 +8,24 @@ import org.apache.catalina.connector.Request;
 | 
			
		|||
import org.apache.catalina.connector.Response;
 | 
			
		||||
import org.apache.catalina.valves.ValveBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Valve that implements per-request session persistence. It is intended to be used with non-sticky
 | 
			
		||||
 * load-balancers.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class SessionHandlerValve extends ValveBase {
 | 
			
		||||
 | 
			
		||||
  private SessionManager manager;
 | 
			
		||||
    private SessionManager manager;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session manager
 | 
			
		||||
   */
 | 
			
		||||
  public void setSessionManager(SessionManager manager) {
 | 
			
		||||
    this.manager = manager;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void invoke(Request request, Response response) throws IOException, ServletException {
 | 
			
		||||
    try {
 | 
			
		||||
      getNext().invoke(request, response);
 | 
			
		||||
    } finally {
 | 
			
		||||
      manager.afterRequest(request);
 | 
			
		||||
    /** To set session manager */
 | 
			
		||||
    public void setSessionManager(SessionManager manager) {
 | 
			
		||||
        this.manager = manager;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void invoke(Request request, Response response) throws IOException, ServletException {
 | 
			
		||||
        try {
 | 
			
		||||
            getNext().invoke(request, response);
 | 
			
		||||
        } finally {
 | 
			
		||||
            manager.afterRequest(request);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,5 @@
 | 
			
		|||
package tomcat.request.session.redis;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.EnumSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.apache.catalina.Context;
 | 
			
		||||
import org.apache.catalina.Lifecycle;
 | 
			
		||||
import org.apache.catalina.LifecycleException;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,396 +10,343 @@ import org.apache.catalina.connector.Request;
 | 
			
		|||
import org.apache.catalina.session.ManagerBase;
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
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.data.cache.DataCache;
 | 
			
		||||
import tomcat.request.session.data.cache.impl.RedisDataCache;
 | 
			
		||||
import tomcat.request.session.data.cache.impl.redis.RedisCache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Manager that implements per-request session persistence. It is intended to be used with
 | 
			
		||||
 * non-sticky load-balancers.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 */
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.EnumSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/** author: Ranjith Manickam @ 12 Jul' 2018 */
 | 
			
		||||
public class SessionManager extends ManagerBase implements Lifecycle {
 | 
			
		||||
 | 
			
		||||
  private DataCache dataCache;
 | 
			
		||||
    private DataCache dataCache;
 | 
			
		||||
    protected SerializationUtil serializer;
 | 
			
		||||
    protected SessionHandlerValve handlerValve;
 | 
			
		||||
 | 
			
		||||
  protected SerializationUtil serializer;
 | 
			
		||||
    protected ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
 | 
			
		||||
    protected Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
 | 
			
		||||
 | 
			
		||||
  protected ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
 | 
			
		||||
    private static final Log LOGGER = LogFactory.getLog(SessionManager.class);
 | 
			
		||||
 | 
			
		||||
  protected SessionHandlerValve handlerValve;
 | 
			
		||||
 | 
			
		||||
  protected Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
 | 
			
		||||
 | 
			
		||||
  private Log log = LogFactory.getLog(SessionManager.class);
 | 
			
		||||
 | 
			
		||||
  enum SessionPolicy {
 | 
			
		||||
    DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST;
 | 
			
		||||
 | 
			
		||||
    static SessionPolicy fromName(String name) {
 | 
			
		||||
      for (SessionPolicy policy : SessionPolicy.values()) {
 | 
			
		||||
        if (policy.name().equalsIgnoreCase(name)) {
 | 
			
		||||
          return policy;
 | 
			
		||||
    /** To get session persist policies */
 | 
			
		||||
    public String getSessionPersistPolicies() {
 | 
			
		||||
        String policyStr = null;
 | 
			
		||||
        for (SessionPolicy policy : this.sessionPolicy) {
 | 
			
		||||
            policyStr = (policyStr == null) ? policy.name() : policyStr.concat(",").concat(policy.name());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      throw new IllegalArgumentException("Invalid session policy [" + name + "]");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To get session persist policies
 | 
			
		||||
   */
 | 
			
		||||
  public String getSessionPersistPolicies() {
 | 
			
		||||
    String policyStr = null;
 | 
			
		||||
    for (SessionPolicy policy : this.sessionPolicy) {
 | 
			
		||||
      policyStr = (policyStr == null) ? policy.name() : policyStr.concat(",").concat(policy.name());
 | 
			
		||||
    }
 | 
			
		||||
    return policyStr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To set session persist policies
 | 
			
		||||
   */
 | 
			
		||||
  public void setSessionPersistPolicies(String policyStr) {
 | 
			
		||||
    Set<SessionPolicy> policySet = EnumSet.of(SessionPolicy.DEFAULT);
 | 
			
		||||
    String[] policyArray = policyStr.split(",");
 | 
			
		||||
 | 
			
		||||
    for (String policy : policyArray) {
 | 
			
		||||
      policySet.add(SessionPolicy.fromName(policy));
 | 
			
		||||
    }
 | 
			
		||||
    this.sessionPolicy = policySet;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean getSaveOnChange() {
 | 
			
		||||
    return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean getAlwaysSaveAfterRequest() {
 | 
			
		||||
    return this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void addLifecycleListener(LifecycleListener listener) {
 | 
			
		||||
    super.addLifecycleListener(listener);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public LifecycleListener[] findLifecycleListeners() {
 | 
			
		||||
    return super.findLifecycleListeners();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void removeLifecycleListener(LifecycleListener listener) {
 | 
			
		||||
    super.removeLifecycleListener(listener);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  protected synchronized void startInternal() throws LifecycleException {
 | 
			
		||||
    super.startInternal();
 | 
			
		||||
    super.setState(LifecycleState.STARTING);
 | 
			
		||||
 | 
			
		||||
    boolean initializedValve = false;
 | 
			
		||||
    Context context = getContextIns();
 | 
			
		||||
    for (Valve valve : context.getPipeline().getValves()) {
 | 
			
		||||
      if (valve instanceof SessionHandlerValve) {
 | 
			
		||||
        this.handlerValve = (SessionHandlerValve) valve;
 | 
			
		||||
        this.handlerValve.setSessionManager(this);
 | 
			
		||||
        initializedValve = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
        return policyStr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!initializedValve) {
 | 
			
		||||
      throw new LifecycleException("Session handling valve is not initialized..");
 | 
			
		||||
    }
 | 
			
		||||
    /** To set session persist policies */
 | 
			
		||||
    public void setSessionPersistPolicies(String policyStr) {
 | 
			
		||||
        Set<SessionPolicy> policySet = EnumSet.of(SessionPolicy.DEFAULT);
 | 
			
		||||
        String[] policyArray = policyStr.split(",");
 | 
			
		||||
 | 
			
		||||
    initialize();
 | 
			
		||||
 | 
			
		||||
    log.info("The sessions will expire after " + (getSessionTimeout(null)) + " seconds.");
 | 
			
		||||
    context.setDistributable(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  protected synchronized void stopInternal() throws LifecycleException {
 | 
			
		||||
    super.setState(LifecycleState.STOPPING);
 | 
			
		||||
    super.stopInternal();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Session createSession(String sessionId) {
 | 
			
		||||
    if (sessionId != null) {
 | 
			
		||||
      sessionId =
 | 
			
		||||
          (this.dataCache.setnx(sessionId, SessionConstants.NULL_SESSION) == 0L) ? null : sessionId;
 | 
			
		||||
    } else {
 | 
			
		||||
      do {
 | 
			
		||||
        sessionId = generateSessionId();
 | 
			
		||||
      } while (this.dataCache.setnx(sessionId, SessionConstants.NULL_SESSION) == 0L);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Session session = (sessionId != null) ? createEmptySession() : null;
 | 
			
		||||
    if (session != null) {
 | 
			
		||||
      session.setId(sessionId);
 | 
			
		||||
      session.setNew(true);
 | 
			
		||||
      session.setValid(true);
 | 
			
		||||
      session.setCreationTime(System.currentTimeMillis());
 | 
			
		||||
      session.setMaxInactiveInterval(getSessionTimeout(session));
 | 
			
		||||
      session.tellNew();
 | 
			
		||||
    }
 | 
			
		||||
    setValues(sessionId, session, false, new SessionMetadata());
 | 
			
		||||
 | 
			
		||||
    if (session != null) {
 | 
			
		||||
      try {
 | 
			
		||||
        save(session, true);
 | 
			
		||||
      } catch (Exception ex) {
 | 
			
		||||
        log.error("Error occurred while creating session..", ex);
 | 
			
		||||
        setValues(null, null);
 | 
			
		||||
        session = null;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return session;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Session createEmptySession() {
 | 
			
		||||
    return new Session(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void add(org.apache.catalina.Session session) {
 | 
			
		||||
    save(session, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public Session findSession(String sessionId) throws IOException {
 | 
			
		||||
    Session session = null;
 | 
			
		||||
    if (sessionId != null && this.sessionContext.get() != null && sessionId
 | 
			
		||||
        .equals(this.sessionContext.get().getId())) {
 | 
			
		||||
      session = this.sessionContext.get().getSession();
 | 
			
		||||
    } else {
 | 
			
		||||
      byte[] data = this.dataCache.get(sessionId);
 | 
			
		||||
 | 
			
		||||
      boolean isPersisted = false;
 | 
			
		||||
      SessionMetadata metadata = null;
 | 
			
		||||
      if (data == null) {
 | 
			
		||||
        session = null;
 | 
			
		||||
        metadata = null;
 | 
			
		||||
        sessionId = null;
 | 
			
		||||
        isPersisted = false;
 | 
			
		||||
      } else {
 | 
			
		||||
        if (Arrays.equals(SessionConstants.NULL_SESSION, data)) {
 | 
			
		||||
          throw new IOException("NULL session data");
 | 
			
		||||
        for (String policy : policyArray) {
 | 
			
		||||
            policySet.add(SessionPolicy.fromName(policy));
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
          metadata = new SessionMetadata();
 | 
			
		||||
          Session newSession = createEmptySession();
 | 
			
		||||
          this.serializer.deserializeSessionData(data, newSession, metadata);
 | 
			
		||||
        this.sessionPolicy = policySet;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
          newSession.setId(sessionId);
 | 
			
		||||
          newSession.access();
 | 
			
		||||
          newSession.setNew(false);
 | 
			
		||||
          newSession.setValid(true);
 | 
			
		||||
          newSession.resetDirtyTracking();
 | 
			
		||||
          newSession.setMaxInactiveInterval(getSessionTimeout(newSession));
 | 
			
		||||
    public boolean getSaveOnChange() {
 | 
			
		||||
        return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
          session = newSession;
 | 
			
		||||
          isPersisted = true;
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
          log.error("Error occurred while de-serializing the session object..", ex);
 | 
			
		||||
    public boolean getAlwaysSaveAfterRequest() {
 | 
			
		||||
        return this.sessionPolicy.contains(SessionPolicy.ALWAYS_SAVE_AFTER_REQUEST);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addLifecycleListener(LifecycleListener listener) {
 | 
			
		||||
        super.addLifecycleListener(listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public LifecycleListener[] findLifecycleListeners() {
 | 
			
		||||
        return super.findLifecycleListeners();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void removeLifecycleListener(LifecycleListener listener) {
 | 
			
		||||
        super.removeLifecycleListener(listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected synchronized void startInternal() throws LifecycleException {
 | 
			
		||||
        super.startInternal();
 | 
			
		||||
        super.setState(LifecycleState.STARTING);
 | 
			
		||||
 | 
			
		||||
        boolean initializedValve = false;
 | 
			
		||||
        Context context = getContextIns();
 | 
			
		||||
        for (Valve valve : context.getPipeline().getValves()) {
 | 
			
		||||
            if (valve instanceof SessionHandlerValve) {
 | 
			
		||||
                this.handlerValve = (SessionHandlerValve) valve;
 | 
			
		||||
                this.handlerValve.setSessionManager(this);
 | 
			
		||||
                initializedValve = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      setValues(sessionId, session, isPersisted, metadata);
 | 
			
		||||
 | 
			
		||||
        if (!initializedValve) {
 | 
			
		||||
            throw new LifecycleException("Session handling valve is not initialized..");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        initialize();
 | 
			
		||||
 | 
			
		||||
        LOGGER.info("The sessions will expire after " + (getSessionTimeout(null)) + " seconds.");
 | 
			
		||||
        context.setDistributable(true);
 | 
			
		||||
    }
 | 
			
		||||
    return session;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void remove(org.apache.catalina.Session session) {
 | 
			
		||||
    remove(session, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void remove(org.apache.catalina.Session session, boolean update) {
 | 
			
		||||
    this.dataCache.expire(session.getId(), 10);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void load() throws ClassNotFoundException, IOException {
 | 
			
		||||
    // Auto-generated method stub
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritDoc}
 | 
			
		||||
   */
 | 
			
		||||
  @Override
 | 
			
		||||
  public void unload() throws IOException {
 | 
			
		||||
    // Auto-generated method stub
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To initialize the session manager
 | 
			
		||||
   */
 | 
			
		||||
  private void initialize() {
 | 
			
		||||
    try {
 | 
			
		||||
      this.dataCache = new RedisDataCache();
 | 
			
		||||
 | 
			
		||||
      this.serializer = new SerializationUtil();
 | 
			
		||||
      Context context = getContextIns();
 | 
			
		||||
      ClassLoader loader =
 | 
			
		||||
          (context != null && context.getLoader() != null) ? context.getLoader().getClassLoader()
 | 
			
		||||
              : null;
 | 
			
		||||
      this.serializer.setClassLoader(loader);
 | 
			
		||||
    } catch (Exception ex) {
 | 
			
		||||
      log.error("Error occurred while initializing the session manager..", ex);
 | 
			
		||||
      throw ex;
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected synchronized void stopInternal() throws LifecycleException {
 | 
			
		||||
        super.setState(LifecycleState.STOPPING);
 | 
			
		||||
        super.stopInternal();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To save session object to data-cache
 | 
			
		||||
   */
 | 
			
		||||
  public void save(org.apache.catalina.Session session, boolean forceSave) {
 | 
			
		||||
    try {
 | 
			
		||||
      Boolean isPersisted;
 | 
			
		||||
      Session newSession = (Session) session;
 | 
			
		||||
      byte[] hash =
 | 
			
		||||
          (this.sessionContext.get() != null && this.sessionContext.get().getMetadata() != null)
 | 
			
		||||
              ? this.sessionContext.get().getMetadata().getAttributesHash() : null;
 | 
			
		||||
      byte[] currentHash = serializer.getSessionAttributesHashCode(newSession);
 | 
			
		||||
 | 
			
		||||
      if (forceSave || newSession.isDirty()
 | 
			
		||||
          || (isPersisted =
 | 
			
		||||
          (this.sessionContext.get() != null) ? this.sessionContext.get().isPersisted() : null)
 | 
			
		||||
          == null
 | 
			
		||||
          || !isPersisted || !Arrays.equals(hash, currentHash)) {
 | 
			
		||||
 | 
			
		||||
        SessionMetadata metadata = new SessionMetadata();
 | 
			
		||||
        metadata.setAttributesHash(currentHash);
 | 
			
		||||
 | 
			
		||||
        this.dataCache
 | 
			
		||||
            .set(newSession.getId(), serializer.serializeSessionData(newSession, metadata));
 | 
			
		||||
        newSession.resetDirtyTracking();
 | 
			
		||||
        setValues(true, metadata);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      int timeout = getSessionTimeout(newSession);
 | 
			
		||||
      this.dataCache.expire(newSession.getId(), timeout);
 | 
			
		||||
      log.trace("Session [" + newSession.getId() + "] expire in [" + timeout + "] seconds.");
 | 
			
		||||
 | 
			
		||||
    } catch (IOException ex) {
 | 
			
		||||
      log.error("Error occurred while saving the session object in data cache..", ex);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * To process post request process
 | 
			
		||||
   */
 | 
			
		||||
  public void afterRequest(Request request) {
 | 
			
		||||
    Session session = null;
 | 
			
		||||
    try {
 | 
			
		||||
      session = (this.sessionContext.get() != null) ? this.sessionContext.get().getSession() : null;
 | 
			
		||||
      if (session != null) {
 | 
			
		||||
        if (session.isValid()) {
 | 
			
		||||
          save(session, getAlwaysSaveAfterRequest());
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Session createSession(String sessionId) {
 | 
			
		||||
        if (sessionId != null) {
 | 
			
		||||
            sessionId =
 | 
			
		||||
                    (this.dataCache.setnx(sessionId, SessionConstants.NULL_SESSION) == 0L) ? null : sessionId;
 | 
			
		||||
        } else {
 | 
			
		||||
          remove(session);
 | 
			
		||||
            do {
 | 
			
		||||
                sessionId = generateSessionId();
 | 
			
		||||
            } while (this.dataCache.setnx(sessionId, SessionConstants.NULL_SESSION) == 0L);
 | 
			
		||||
        }
 | 
			
		||||
        log.trace(
 | 
			
		||||
            "Session object " + (session.isValid() ? "saved: " : "removed: ") + session.getId());
 | 
			
		||||
      }
 | 
			
		||||
    } catch (Exception ex) {
 | 
			
		||||
      log.error("Error occurred while processing post request process..", ex);
 | 
			
		||||
    } finally {
 | 
			
		||||
      this.sessionContext.remove();
 | 
			
		||||
      log.trace(
 | 
			
		||||
          "Session removed from ThreadLocal:" + ((session != null) ? session.getIdInternal() : ""));
 | 
			
		||||
 | 
			
		||||
        Session session = (sessionId != null) ? createEmptySession() : null;
 | 
			
		||||
        if (session != null) {
 | 
			
		||||
            session.setId(sessionId);
 | 
			
		||||
            session.setNew(true);
 | 
			
		||||
            session.setValid(true);
 | 
			
		||||
            session.setCreationTime(System.currentTimeMillis());
 | 
			
		||||
            session.setMaxInactiveInterval(getSessionTimeout(session));
 | 
			
		||||
            session.tellNew();
 | 
			
		||||
        }
 | 
			
		||||
        setValues(sessionId, session, false, new SessionMetadata());
 | 
			
		||||
 | 
			
		||||
        if (session != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                save(session, true);
 | 
			
		||||
            } catch (Exception ex) {
 | 
			
		||||
                LOGGER.error("Error occurred while creating session..", ex);
 | 
			
		||||
                setValues(null, null);
 | 
			
		||||
                session = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return session;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void setValues(String sessionId, Session session) {
 | 
			
		||||
    if (this.sessionContext.get() == null) {
 | 
			
		||||
      this.sessionContext.set(new SessionContext());
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Session createEmptySession() {
 | 
			
		||||
        return new Session(this);
 | 
			
		||||
    }
 | 
			
		||||
    this.sessionContext.get().setId(sessionId);
 | 
			
		||||
    this.sessionContext.get().setSession(session);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void setValues(boolean isPersisted, SessionMetadata metadata) {
 | 
			
		||||
    if (this.sessionContext.get() == null) {
 | 
			
		||||
      this.sessionContext.set(new SessionContext());
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void add(org.apache.catalina.Session session) {
 | 
			
		||||
        save(session, false);
 | 
			
		||||
    }
 | 
			
		||||
    this.sessionContext.get().setMetadata(metadata);
 | 
			
		||||
    this.sessionContext.get().setPersisted(isPersisted);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void setValues(String sessionId, Session session, boolean isPersisted,
 | 
			
		||||
      SessionMetadata metadata) {
 | 
			
		||||
    setValues(sessionId, session);
 | 
			
		||||
    setValues(isPersisted, metadata);
 | 
			
		||||
  }
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Session findSession(String sessionId) throws IOException {
 | 
			
		||||
        Session session = null;
 | 
			
		||||
        if (sessionId != null && this.sessionContext.get() != null && sessionId
 | 
			
		||||
                .equals(this.sessionContext.get().getId())) {
 | 
			
		||||
            session = this.sessionContext.get().getSession();
 | 
			
		||||
        } else {
 | 
			
		||||
            byte[] data = this.dataCache.get(sessionId);
 | 
			
		||||
 | 
			
		||||
  private Context getContextIns() {
 | 
			
		||||
    try {
 | 
			
		||||
      Method method = this.getClass().getSuperclass().getDeclaredMethod("getContext");
 | 
			
		||||
      return (Context) method.invoke(this);
 | 
			
		||||
    } catch (Exception ex) {
 | 
			
		||||
      try {
 | 
			
		||||
        Method method = this.getClass().getSuperclass().getDeclaredMethod("getContainer");
 | 
			
		||||
        return (Context) method.invoke(this);
 | 
			
		||||
      } catch (Exception ex2) {
 | 
			
		||||
        log.error("Error in getContext", ex2);
 | 
			
		||||
      }
 | 
			
		||||
            boolean isPersisted = false;
 | 
			
		||||
            SessionMetadata metadata = null;
 | 
			
		||||
            if (data == null) {
 | 
			
		||||
                sessionId = null;
 | 
			
		||||
                isPersisted = false;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (Arrays.equals(SessionConstants.NULL_SESSION, data)) {
 | 
			
		||||
                    throw new IOException("NULL session data");
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    metadata = new SessionMetadata();
 | 
			
		||||
                    Session newSession = createEmptySession();
 | 
			
		||||
                    this.serializer.deserializeSessionData(data, newSession, metadata);
 | 
			
		||||
 | 
			
		||||
                    newSession.setId(sessionId);
 | 
			
		||||
                    newSession.access();
 | 
			
		||||
                    newSession.setNew(false);
 | 
			
		||||
                    newSession.setValid(true);
 | 
			
		||||
                    newSession.resetDirtyTracking();
 | 
			
		||||
                    newSession.setMaxInactiveInterval(getSessionTimeout(newSession));
 | 
			
		||||
 | 
			
		||||
                    session = newSession;
 | 
			
		||||
                    isPersisted = true;
 | 
			
		||||
                } catch (Exception ex) {
 | 
			
		||||
                    LOGGER.error("Error occurred while de-serializing the session object..", ex);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            setValues(sessionId, session, isPersisted, metadata);
 | 
			
		||||
        }
 | 
			
		||||
        return session;
 | 
			
		||||
    }
 | 
			
		||||
    throw new RuntimeException("Error occurred while creating container instance");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void remove(org.apache.catalina.Session session) {
 | 
			
		||||
        remove(session, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void remove(org.apache.catalina.Session session, boolean update) {
 | 
			
		||||
        this.dataCache.expire(session.getId(), 10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void load() throws ClassNotFoundException, IOException {
 | 
			
		||||
        // Auto-generated method stub
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** {@inheritDoc} */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void unload() throws IOException {
 | 
			
		||||
        // Auto-generated method stub
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To initialize the session manager. */
 | 
			
		||||
    private void initialize() {
 | 
			
		||||
        try {
 | 
			
		||||
            this.dataCache = new RedisCache();
 | 
			
		||||
 | 
			
		||||
            this.serializer = new SerializationUtil();
 | 
			
		||||
            Context context = getContextIns();
 | 
			
		||||
            ClassLoader loader = (context != null && context.getLoader() != null) ? context.getLoader().getClassLoader() : null;
 | 
			
		||||
            this.serializer.setClassLoader(loader);
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            LOGGER.error("Error occurred while initializing the session manager..", ex);
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To save session object to data cache. */
 | 
			
		||||
    public void save(org.apache.catalina.Session session, boolean forceSave) {
 | 
			
		||||
        try {
 | 
			
		||||
            Boolean isPersisted;
 | 
			
		||||
            Session newSession = (Session) session;
 | 
			
		||||
            byte[] hash = (this.sessionContext.get() != null && this.sessionContext.get().getMetadata() != null)
 | 
			
		||||
                    ? this.sessionContext.get().getMetadata().getAttributesHash() : null;
 | 
			
		||||
            byte[] currentHash = serializer.getSessionAttributesHashCode(newSession);
 | 
			
		||||
 | 
			
		||||
            if (forceSave || newSession.isDirty()
 | 
			
		||||
                    || (isPersisted =
 | 
			
		||||
                    (this.sessionContext.get() != null) ? this.sessionContext.get().isPersisted() : null)
 | 
			
		||||
                    == null
 | 
			
		||||
                    || !isPersisted || !Arrays.equals(hash, currentHash)) {
 | 
			
		||||
 | 
			
		||||
                SessionMetadata metadata = new SessionMetadata();
 | 
			
		||||
                metadata.setAttributesHash(currentHash);
 | 
			
		||||
 | 
			
		||||
                this.dataCache
 | 
			
		||||
                        .set(newSession.getId(), serializer.serializeSessionData(newSession, metadata));
 | 
			
		||||
                newSession.resetDirtyTracking();
 | 
			
		||||
                setValues(true, metadata);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int timeout = getSessionTimeout(newSession);
 | 
			
		||||
            this.dataCache.expire(newSession.getId(), timeout);
 | 
			
		||||
            LOGGER.trace("Session [" + newSession.getId() + "] expire in [" + timeout + "] seconds.");
 | 
			
		||||
 | 
			
		||||
        } catch (IOException ex) {
 | 
			
		||||
            LOGGER.error("Error occurred while saving the session object in data cache..", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To process post request process. */
 | 
			
		||||
    public void afterRequest(Request request) {
 | 
			
		||||
        Session session = null;
 | 
			
		||||
        try {
 | 
			
		||||
            session = (this.sessionContext.get() != null) ? this.sessionContext.get().getSession() : null;
 | 
			
		||||
            if (session != null) {
 | 
			
		||||
                if (session.isValid()) {
 | 
			
		||||
                    save(session, getAlwaysSaveAfterRequest());
 | 
			
		||||
                } else {
 | 
			
		||||
                    remove(session);
 | 
			
		||||
                }
 | 
			
		||||
                LOGGER.trace(
 | 
			
		||||
                        "Session object " + (session.isValid() ? "saved: " : "removed: ") + session.getId());
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            LOGGER.error("Error occurred while processing post request process..", ex);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.sessionContext.remove();
 | 
			
		||||
            LOGGER.trace(
 | 
			
		||||
                    "Session removed from ThreadLocal:" + ((session != null) ? session.getIdInternal() : ""));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To get session timeout. */
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To set values to session context. */
 | 
			
		||||
    private void setValues(String sessionId, Session session) {
 | 
			
		||||
        if (this.sessionContext.get() == null) {
 | 
			
		||||
            this.sessionContext.set(new SessionContext());
 | 
			
		||||
        }
 | 
			
		||||
        this.sessionContext.get().setId(sessionId);
 | 
			
		||||
        this.sessionContext.get().setSession(session);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To set values to session context. */
 | 
			
		||||
    private void setValues(boolean isPersisted, SessionMetadata metadata) {
 | 
			
		||||
        if (this.sessionContext.get() == null) {
 | 
			
		||||
            this.sessionContext.set(new SessionContext());
 | 
			
		||||
        }
 | 
			
		||||
        this.sessionContext.get().setMetadata(metadata);
 | 
			
		||||
        this.sessionContext.get().setPersisted(isPersisted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To set values to session context. */
 | 
			
		||||
    private void setValues(String sessionId, Session session, boolean isPersisted,
 | 
			
		||||
                           SessionMetadata metadata) {
 | 
			
		||||
        setValues(sessionId, session);
 | 
			
		||||
        setValues(isPersisted, metadata);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** To get catalina context instance. */
 | 
			
		||||
    private Context getContextIns() {
 | 
			
		||||
        try {
 | 
			
		||||
            Method method = this.getClass().getSuperclass().getDeclaredMethod("getContext");
 | 
			
		||||
            return (Context) method.invoke(this);
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            try {
 | 
			
		||||
                Method method = this.getClass().getSuperclass().getDeclaredMethod("getContainer");
 | 
			
		||||
                return (Context) method.invoke(this);
 | 
			
		||||
            } catch (Exception ex2) {
 | 
			
		||||
                // skip
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new RuntimeException("Error occurred while creating container instance");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,13 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Tomcat clustering with Redis data-cache implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * Tomcat clustering with Redis is the plugable one. It uses to store session objects to Redis data cache.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ranjith Manickam
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 * author: Ranjith Manickam @ 12 Jul' 2018
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
Supports:
 | 
			
		||||
   * Apache Tomcat 7
 | 
			
		||||
   * Apache Tomcat 8
 | 
			
		||||
   - Apache Tomcat 7
 | 
			
		||||
   - Apache Tomcat 8
 | 
			
		||||
   - Apache Tomcat 9
 | 
			
		||||
 | 
			
		||||
Pre-requisite:
 | 
			
		||||
	1. jedis.jar
 | 
			
		||||
| 
						 | 
				
			
			@ -20,13 +18,13 @@ more details.. https://github.com/ran-jit/tomcat-cluster-redis-session-manager/w
 | 
			
		|||
 | 
			
		||||
Steps to be done,
 | 
			
		||||
	1. Move the downloaded jars to tomcat/lib directory
 | 
			
		||||
		* tomcat/lib/
 | 
			
		||||
		- tomcat/lib/
 | 
			
		||||
 | 
			
		||||
	2. Add tomcat system property "catalina.base"
 | 
			
		||||
		* catalina.base="TOMCAT_LOCATION"
 | 
			
		||||
		- catalina.base="TOMCAT_LOCATION"
 | 
			
		||||
 | 
			
		||||
	3. Extract downloaded package (tomcat-cluster-redis-session-manager.zip) to configure Redis credentials in redis-data-cache.properties file and move the file to tomcat/conf directory
 | 
			
		||||
		* tomcat/conf/redis-data-cache.properties
 | 
			
		||||
		- tomcat/conf/redis-data-cache.properties
 | 
			
		||||
 | 
			
		||||
	4. Add the below two lines in tomcat/conf/context.xml
 | 
			
		||||
		<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
 | 
			
		||||
| 
						 | 
				
			
			@ -38,5 +36,5 @@ Steps to be done,
 | 
			
		|||
		<session-config>
 | 
			
		||||
 | 
			
		||||
Note:
 | 
			
		||||
  * All your session attribute values must implement java.io.Serializable.
 | 
			
		||||
  * This supports, both redis stand-alone and multiple node cluster based on the redis-data-cache.properties configuration.
 | 
			
		||||
  - All your session attribute values must implement java.io.Serializable.
 | 
			
		||||
  - Supports redis default, sentinel and cluster based on the redis-data-cache.properties configuration.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,14 +3,19 @@
 | 
			
		|||
#- 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 (for stand-alone mode)
 | 
			
		||||
#- redis password (used for default and sentinel mode)
 | 
			
		||||
#redis.password=
 | 
			
		||||
 | 
			
		||||
#- set true to enable redis cluster mode
 | 
			
		||||
redis.cluster.enabled=false
 | 
			
		||||
 | 
			
		||||
#- redis database (default 0)
 | 
			
		||||
#- set true to enable redis sentinel mode
 | 
			
		||||
redis.sentinel.enabled=false
 | 
			
		||||
# redis sentinel master name (default value: mymaster)
 | 
			
		||||
redis.sentinel.master=mymaster
 | 
			
		||||
 | 
			
		||||
#- redis database (default value: 0)
 | 
			
		||||
#redis.database=0
 | 
			
		||||
 | 
			
		||||
#- redis connection timeout (default 2000)
 | 
			
		||||
#- redis connection timeout (default value: 2000)
 | 
			
		||||
#redis.timeout=2000
 | 
			
		||||
		Loading…
	
		Reference in New Issue