001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package edu.yale.its.tp.cas.ticket;
017:
018: import java.util.Collections;
019: import java.util.Date;
020: import java.util.Iterator;
021: import java.util.Map;
022: import java.util.WeakHashMap;
023:
024: /**
025: * Represents a cache of tickets that each expire after a configurable period of inactivity (i.e., not being retrieved).
026: */
027: public abstract class ActiveTicketCache implements TicketCache {
028:
029: // *********************************************************************
030: // Placeholders for subclasses to insert relevant logic.
031:
032: /** Generates and returns a new, unique ticket ID */
033: protected abstract String newTicketId();
034:
035: /** Stores the given ticket, associating it with the given identifier. */
036: protected abstract void storeTicket(String ticketId, Ticket t)
037: throws TicketException;
038:
039: /** Retrieves the ticket with the given identifier. */
040: protected abstract Ticket retrieveTicket(String ticketId);
041:
042: // subclasses also need deleteTicket(String ticketId) from TicketCache
043:
044: // *********************************************************************
045: // TicketCache implementation methods
046:
047: // inherit Javadoc
048: public synchronized String addTicket(Ticket t)
049: throws TicketException {
050: String ticketId = newTicketId();
051: resetTimer(ticketId);
052: storeTicket(ticketId, t);
053: return ticketId;
054: }
055:
056: // inherit Javadoc
057: public Ticket getTicket(String ticketId) {
058: resetTimer(ticketId);
059: return retrieveTicket(ticketId);
060: }
061:
062: // *********************************************************************
063: // Private state
064:
065: /** Stores the last accessed Date for each currently valid ticketId. */
066: private Map timer;
067:
068: /** Number of seconds after which the current cache will expire tickets. */
069: private int tolerance;
070:
071: // *********************************************************************
072: // Constructor
073:
074: /**
075: * Constucts a new ActiveTicketCache that will expire tickets after <i>tolerance</i> seconds of inactivity.
076: */
077: public ActiveTicketCache(int tolerance) {
078: // set up the timer
079: timer = Collections.synchronizedMap(new WeakHashMap());
080: this .tolerance = tolerance;
081:
082: // set up the timer thread
083: Thread t = new timerThread();
084: t.setDaemon(true);
085: t.start();
086: }
087:
088: /** A thread used to monitor and clean up inactive tickets. */
089: private class timerThread extends Thread {
090: public static final int PERIOD = 60 * 1000;
091: public boolean done = false;
092:
093: public void run() {
094: while (!done) {
095: try {
096: expireInactive();
097: Thread.sleep(PERIOD);
098: } catch (InterruptedException ex) {
099: // ignore
100: }
101: }
102: }
103: }
104:
105: // *********************************************************************
106: // Timer management
107:
108: /**
109: * Resets (and creates, if necessary) the timer entry for the given ticket identifier
110: */
111: private void resetTimer(String ticketId) {
112: timer.put(ticketId, new Date());
113: }
114:
115: /** Expires all inactive tickets. */
116: private void expireInactive() {
117: // for consistency, record starting time
118: long now = (new Date()).getTime();
119:
120: // remove all entries that have been inactive too long
121: synchronized (timer) {
122: Iterator i = timer.entrySet().iterator();
123: while (i.hasNext()) {
124: Map.Entry e = (Map.Entry) i.next();
125: long ticketTime = ((Date) e.getValue()).getTime();
126: // delete the ticket if it's expired
127: if (now - (tolerance * 1000) >= ticketTime) {
128: i.remove();
129: deleteTicket((String) e.getKey());
130: }
131: }
132: }
133: }
134: }
|