EhcacheTicketRegistry

New CAS documentation site

CAS documentation has moved over to apereo.github.io/cas, starting with CAS version 4.x. The wiki will no longer be maintained. For the most recent version of the documentation, please refer to the aforementioned link.

Overview

The University of new England (UNE) decided that with the upgrade to CAS 3.4.x, it needed to make CAS Highly-Available (HA). The requirements called for two on-campus CAS nodes to be backed up by an off-campus node. All the nodes need to recognize and validate each other's tickets. The group undertook an extensive evaluation of technologies available to facilitate clustering, and the conclusion was to use Ehcache for CAS Ticket Registry. There were many considerations, and having to write a new Ticket Registry implementation was not taken lightly. However, there exists a contribution by Cyrille Le Clerc (https://issues.jasig.org/browse/CAS-816 which led to EhCacheTicketRegistry support in the cas4 feature branch; https://issues.jasig.org/browse/CAS-1076 which will lead to EhCAcheTicketRegistry support in CAS 3.5), but that contribution has not been shipped in a released CAS server version yet

Unicon has evaluated the available code and decided to use it as the basis of the HA CAS implementation at UNE. The code was enhanced to use the latest versions of available open source libraries and to reduce reliance on Java code in favor of accomplishing the same in configuration. The rest of this document details how to add support for Ehcache-based Ticket Registry to Maven WAR Overlay-based CAS build.

Documentation

This implementation adds one Java source file and one configuration file in addition to modifying one of CAS' configuration files, ticketRegistry.xml. In fact, the ticketRegistry.xml file (included below) can replace the version supplied with CAS.

Java Code

The Java source file is adopted from the contribution by Cyrille Le Clerc. The author had a good idea to separate Ticket Granting Tickets from Service Tickets and manage them in two separate caches. This is good because of the following reasons:

  • Ticket Granting Tickets must remain valid for a long time
  • Service Tickets are short lived
  • Service Tickets must be replicated right away because the requests to validate them may very likely arrive at different CAS cluster nodes
  • Ticket Granting Tickets, as long-lived objects, may need to be spooled off to disk, as supported by Ehcache

EhCache support in CAS v3.5.x

EhCache support in CAS is now made available as part of the CAS 3.5.x release line. You will only need to apply the Java code to your CAS configuration/overlay if you are working with a release below that version.

 

Ticket Registry Configuration

The configuration file included below takes care of configuring the ticket caches and uses Ehcache to take care of ticket expiration. This results of having no need for a Ticket Registry Cleaner. It should be sufficient to use the file below as a replacement to the version that comes with CAS. Since this project uses Maven WAR Overlay build method, simply placing this file in src/main/webapp/WEB-INF/spring-configuration will result in replacing the original version. Here is the replacement ticketRegistry.xml.

 

EhCache eviction policy

There have been a few reports of cache eviction problems when tickets are expired, but haven't been removed from the cache due to ehache configuration. This can be a problem because old ticket references "hang around" in the cache despite being expired. In other words, Ehcache does inline eviction where expired objects in the cache object won't be collected from memory until the cache max size is reached or the expired entry is explicitly accessed. See http://lists.terracotta.org/pipermail/ehcache-list/2011-November/000423.html

To reclaim memory on expired tickets, cache eviction policies are must be carefully configured to avoid memory creep. Disk offload and/or a more aggressive eviction could provide a suitable workaround.

 

Ehcache Configuration

To provide Ehcache-specific configuration applicable to Ticket Registry, there is another configuration file that Ehcache uses. This file supplies some defaults to Ehcache and details to the framework how to replicate its caches. 

Active Firewall & remoteObjectPort

If you are running this on a server with active firewalls, you will probably need to specify a fixed remoteObjectPort, within the cacheManagerPeerListenerFactory.

Replication Methods

RMI (Default) 

By default the sample configuration above uses RMI to replicate the cache state. An RMI implementation is desirable because:

  • it itself is the default remoting mechanism in Java
  • it is mature
  • it allows tuning of TCP socket options
  • Element keys and values for disk storage must already be Serializable, therefore directly transmittable over RMI without the need for conversion to a third format such as XML.
  • It can be configured to pass through firewalls
  • RMI had improvements added to it with each release of Java, which can then be taken advantage of.

Ehcache provides replicated caching using RMI. To set up RMI replicated caching, you need to configure the CacheManager with a PeerProvider and a CacheManagerPeerListener. Then for each cache that will be replicated, you need to add one of the RMI cacheEventListener types to propagate messages. You can also optionally configure a cache to bootstrap from other caches in the cluster. See this link  for additional information.

JGroups

JGroups can be used as the underlying mechanism for the replication operations in Ehcache. JGroups offers a very flexible protocol stack, reliable unicast, and multicast message transmission. To set up replicated caching using JGroups, you need to configure a PeerProviderFactory. For each cache that will be replicated, you then need to add a cacheEventListenerFactory to propagate messages.

The ticket registry configuration based on JGroups would look similiar to the following snippet:

JGroups Ticket Registry
<?xml version="1.0" encoding="UTF-8"?>
<!--
    Licensed to Jasig under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Jasig licenses this file to you under the Apache License,
    Version 2.0 (the "License"); you may not use this file
    except in compliance with the License.  You may obtain a
    copy of the License at the following location:
      http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing pejgroupsssions and limitations
    under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    <description>
        Configuration for the EhCache TicketRegistry which stores the tickets in a distributed EhCache and cleans
        them out as specified intervals.
    </description>
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="WEB-INF/ehcache-replicated.xml" />
        <property name="shared" value="true" />
        <property name="cacheManagerName" value="ticketRegistryCacheManager" />
    </bean>
    <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.EhCacheTicketRegistry"
    		p:serviceTicketsCache-ref="serviceTicketsCache"
    		p:ticketGrantingTicketsCache-ref="ticketGrantingTicketsCache" />
	<bean id="abstractTicketCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" abstract="true">
		<property name="cacheManager" ref="cacheManager" />
        <property name="diskExpiryThreadIntervalSeconds" value="0" />
        <property name="diskPersistent" value="false" />
        <property name="eternal" value="false" />
        <property name="maxElementsInMemory" value="10000" />
        <property name="maxElementsOnDisk" value="0" />
        <property name="memoryStoreEvictionPolicy" value="LRU" />
        <property name="overflowToDisk" value="false" />
        <property name="bootstrapCacheLoader">
            <ref local="ticketCacheBootstrapCacheLoader"/>
        </property>
	</bean>
	
    <bean id="serviceTicketsCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" parent="abstractTicketCache">
        <description>
            Service Tickets (ST) and Proxy Tickets are only valid for short amount of time (default is 10 seconds), and
            most often are removed from the cache when the ST is validated.  The ST cache must be replicated quickly
            since validation is expected within a few second after its creation.  The CAS instance validating the ST may
            not be one that created the ST, since validation is a back-channel service-to-CAS call that is not aware of
            user session affinity.  Synchronous mode is used to ensure all CAS nodes can validate the ST.
        </description>
        <property name="cacheName" value="org.jasig.cas.ticket.ServiceTicket" />
             
        <property name="cacheEventListeners">
        	<ref local="ticketjgroupsSynchronousCacheReplicator"/>
        </property>
        
        <!-- 
        	The maximum number of seconds an element can exist in the cache without being accessed. 
        	The element expires at this limit and will no longer be returned from the cache. 
        	The default value is 0, which means no TTI eviction takes place (infinite lifetime).
         -->
        <property name="timeToIdle" value="0" />
        
        <!-- 
        	The maximum number of seconds an element can exist in the cache regardless of use. 
        	The element expires at this limit and will no longer be returned from the cache. 
        	The default value is 0, which means no TTL eviction takes place (infinite lifetime).
        -->
        <property name="timeToLive" value="300" />
    </bean>
    
    <bean id="ticketGrantingTicketsCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" >
        <description>
            Ticket Granting Tickets (TGT) are valid for the lifetime of the SSO Session.  They become invalid either
            by expiration policy (default 2 hours idle, 8 hours max) or by explicit user sign off via /cas/login.
            The TGT cache can be replicated slowly because TGT are only manipulated via web user started operations
            (mostly grant service ticket) and thus benefit of web session affinity.
        </description>
        
        <property name="cacheName" value="org.jasig.cas.ticket.TicketGrantingTicket" />
              
        <property name="cacheEventListeners">
        	<ref local="ticketjgroupsAsynchronousCacheReplicator"/>
        </property>
        
        <!-- 
        	The maximum number of seconds an element can exist in the cache regardless of use. 
        	The element expires at this limit and will no longer be returned from the cache. 
        	The default value is 0, which means no TTL eviction takes place (infinite lifetime).
        	
        	For this sample configuration, 2 hours of inactivity before ticket granting tickets 
        	are expired automatically
        -->
         
        <property name="timeToIdle" value="7201" />
        
        <!-- 
        	The maximum number of seconds an element can exist in the cache without being accessed. 
        	The element expires at this limit and will no longer be returned from the cache. 
        	The default value is 0, which means no TTI eviction takes place (infinite lifetime).
         -->
        <property name="timeToLive" value="0" />
    </bean>
	
	<bean id="ticketjgroupsSynchronousCacheReplicator" class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicator">
	    <constructor-arg name="replicatePuts" value="true"/> 
	    <constructor-arg name="replicateUpdates" value="true"/>  
	    <constructor-arg name="replicateUpdatesViaCopy" value="true"/>  
	    <constructor-arg name="replicateRemovals" value="true"/>       
	</bean>
	
	<bean id="ticketjgroupsAsynchronousCacheReplicator" class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicator" parent="ticketjgroupsSynchronousCacheReplicator">
	    <constructor-arg name="asynchronousReplicationInterval" value="1000"/>  
	</bean>
	
	<bean id="ticketCacheBootstrapCacheLoader" class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoader">
      <constructor-arg name="asynchronous" value="true"/>
      <constructor-arg name="maximumChunkSize" value="5000000"/>
	</bean>
		            
</beans>

...and the Ehcache configuration itself for JGroups would be similiar to the following:

Ehcache JGroups configuration
<ehcache name="ehCacheTicketRegistryCache" updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="http://ehcache.sf.net/ehcache.xsd">
   <diskStore path="java.io.tmpdir/cas"/>
   
   <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
    properties="connect=UDP:PING:MERGE2:FD_SOCK:VERIFY_SUSPECT:pbcast.NAKACK:UNICAST:pbcast.STABLE:FRAG:
	pbcast.GMS" propertySeparator="::"/>
</ehcache>


Maven Dependencies

Normally, the Maven war overlay project may not need to have any compile dependencies. Since this project does, these dependencies had to be added. See the attached pom.xml file for the additions. Keep in mind, however, that this file contains some UNE-specific additions that your implementation will not need. Also, be aware that the extensive list of exclusions targeted to eliminate duplicate versions of dependent jar files resulting from using Maven WAR Overlay will need to be modified when using different version of CAS or ClearPass.

<dependency>
	<groupId>org.jasig.cas</groupId>
	<artifactId>cas-server-integration-ehcache</artifactId>
	<version>${cas.version}</version> <!-- This should be the most recent release version of the CAS server (i.e. 3.5.2) -->
    <scope>compile</scope>
</dependency>

JGroups dependencies

If you are using JGroups as the replication method, please ensure that you're overlay is configured to retrieve the following dependencies as well:

 

File Locations

The table below lists the locations where each of the files should be deposited.

If you're on CAS 3.5.0 or later, you do not need to apply the files below manually to your overlay. The ticket registry Java class is available via the "cas-server-integration-ehcache" module declared through the dependency. Just apply the configuration to your overlay.

File

Directory

EhcacheTicketRegistry.java

src/main/java/org/jasig/cas/ticket/registry

ticketRegistry.xml

src/main/webapp/WEB-INF/spring-configuration

ehcache-replicated.xml

src/main/webapp/WEB-INF

  File Modified

XML File pom.xml

Feb 16, 2011 by Adam Rybicki

Java Source EhcacheTicketRegistry.java

Feb 16, 2011 by Adam Rybicki

Relevant JIRAs

CAS-1076 - Getting issue details... STATUS

CAS-816 - Getting issue details... STATUS

CAS-1050 - Getting issue details... STATUS