In CAS3, there was an interface called TicketRegistry. It had some periphery supporting things such as a TicketRegistryCleaner and Tickets (both ServiceTicket and TicketGrantingTicket).
Some Issues With the Current Design
When CAS3 was created, there was no notion of the fact that you may need different implementations of Tickets (even though we had an interface). This caused some problems when other TicketRegistry implementations were needed, as the default implementation became overloaded with JPA annotations, and was inefficiently stored by the backing mechanism. Many of the distributed ticket registries actually returned proxies.
In addition, the initial idea of the TicketRegistryCleaner was that you could define your own algorithms outside of the TicketRegistry to meet your needs. Unfortunately, the TicketRegistries often know best on how to clean themselves up (i.e. a JDBC version might just be able to call DELETE FROM ticketRegistry where expired=true).
The New Design
We’re looking at re-working the TicketRegistry and supporting interfaces. (Disclaimer: Please ignore the possibly poorly worded choices for interfaces, we’re looking into generalizing terms since CAS now supports more than the CAS1&2 protocol).
You’ll see our first pass at a storage for long term sessions below. Long term sessions are similar to TicketGrantingTickets. There isn’t much difference other than the fact that sessions aren’t CAS protocol specific.
/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/index.html
*/
package org.jasig.cas.server.session;
import org.jasig.cas.server.authentication.Authentication;
/**
* Represents a storage area for session information. The backing mechanism can be any mechanism that
* can store Java objects or a representation of those Java objects, such as in-memory, database, etc.
* Because the underlying-specifics of the Sessions may be dependent on the backing mechanism, the
* SessionStorage implementation is responsible for creating sessions.
* <p>
* Classes that rely on the SessionStorage should never assume that updates are automatically persisted
* and should take care to call updateSession after making changes to the underlying session.
* <p>
* Implementations of this class should be thread-safe.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 4.0.0
*/
public interface SessionStorage {
/**
* Constructs a new Session from an existing AuthenticationRequest.
* <p>
* Note: In CAS3, these were called TicketGrantingTickets. We've disambiguated from that term because we are
* supporting more than one protocol going forward.
*
* @param authentication the successful authentication that will be used to populate this session.
* @return the fully constructed session. This should never return null.
*/
Session createSession(Authentication authentication);
/**
* Destroys an existing session, which means that it erases it from whatever backing storage
* the implementation is using.
* <p>
* Method returns the Session in case the calling class has any last minute need for the Session.
*
* @param sessionId the session identifier to look for.
* @return the Session. May return null if it can't find it.
*/
Session destroySession(String sessionId);
/**
* Locates an existing session based on its existing session identifier.
*
* @param sessionId the id of the session to look for.
* @return the Session, if found. Null, otherwise.
*/
Session findSessionBySessionId(String sessionId);
/**
* Updates the existing session in the backing-storage mechanism. Depending on the implementation
* this may or may not actually do anything. One could imagine an in-memory version not needing to
* do anything explicit to save a session back as all operations take place in-memory.
*
* @param session the session to update in the backing data-store.
* @return the session that was updated.
*/
Session updateSession(Session session);
/**
* Locate a session based on the access request identifier.
* <p>
* Note: In CAS3, ServiceTickets take the place of Access.
*
* @param accessId the identifier for which to retrieve the service by.
*
* @return the session associated with the ServiceAccessId, or null if the match can't be found.
*/
Session findSessionByAccessId(String accessId);
/**
* Notifies the SessionStorage object that it should attempt to clean up its internal data-store.
* <p>
* Note that while the SessionStorage can delay or bundle multiple requests together, it should never
* completely ignore the request to prune its own internal data-store.
* <p>
* The one exception to the above rule is SessionStorage objects who's internal data-store has its own
* mechanism for cleaning itself (i.e. memcached's timeout mechanism). In this particular instance, prune() can
* be considered a no-op.
*
*/
void prune();
}
Some of the design considerations:
- We’ve moved the algorithm for cleaning a registry to the actual registry itself. This way a Jpa or JDBC registry could merely call DELETE FROM… or something like MemCached wouldn’t need to do anything. This eliminates the need to enable/disable the Quartz scheduler within the WAR file, as the individual prune method may or may not do anything depending on the implementation.
- Registries (new term: SessionStorage) are now responsible for instantiating the initial Session. This allows us to separate out the different implementations of a Session and have the correct one associated with a registry (instead of overloading say the default implementation in CAS3).
- We have an explicit method that should be called to notify the registry/sessionstorage that you’ve made changes to the underlying sessions. One could argue that its up to the SessionStorage to monitor that by returning Sessions that can speak with their SessionStorage. Therefore, this may change.
4 comments ↓
I actually like the new terminology.
I’m a fan of of it too. I think some of it probably still needs to be refined, but overall I like the decoupling of the CAS protocol from the internals.
This sounds better than 3.0, where DefaultTicketRegistryCleaner.clean() starts a database transaction even when using a DefaultTicketRegistry (which doesn’t use a database).
Nice post u have here
Added to my RSS reader
Leave a Comment