package org.apereo.cas.ticket.registry;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.ticket.Ticket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* <p>
* <a href="http://ehcache.org/">Ehcache</a> based distributed ticket registry.
* </p>
*
* @author <a href="mailto:cleclerc@xebia.fr">Cyrille Le Clerc</a>
* @author Adam Rybicki
* @author Andrew Tillinghast
* @since 3.5
*/
public class EhCacheTicketRegistry extends AbstractTicketRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger(EhCacheTicketRegistry.class);
private Cache ehcacheTicketsCache;
/**
* Instantiates a new EhCache ticket registry.
*
* @param ticketCache the ticket cache
* @param cipher the cipher
*/
public EhCacheTicketRegistry(final Cache ticketCache, final CipherExecutor cipher) {
this.ehcacheTicketsCache = ticketCache;
setCipherExecutor(cipher);
LOGGER.info("Setting up Ehcache Ticket Registry...");
Assert.notNull(this.ehcacheTicketsCache, "Ehcache Tickets cache cannot nbe null");
if (LOGGER.isDebugEnabled()) {
final CacheConfiguration config = this.ehcacheTicketsCache.getCacheConfiguration();
LOGGER.debug("TicketCache.maxEntriesLocalHeap=[{}]", config.getMaxEntriesLocalHeap());
LOGGER.debug("TicketCache.maxEntriesLocalDisk=[{}]", config.getMaxEntriesLocalDisk());
LOGGER.debug("TicketCache.maxEntriesInCache=[{}]", config.getMaxEntriesInCache());
LOGGER.debug("TicketCache.persistenceConfiguration=[{}]", config.getPersistenceConfiguration().getStrategy());
LOGGER.debug("TicketCache.synchronousWrites=[{}]", config.getPersistenceConfiguration().getSynchronousWrites());
LOGGER.debug("TicketCache.timeToLive=[{}]", config.getTimeToLiveSeconds());
LOGGER.debug("TicketCache.timeToIdle=[{}]", config.getTimeToIdleSeconds());
LOGGER.debug("TicketCache.cacheManager=[{}]", this.ehcacheTicketsCache.getCacheManager().getName());
}
}
@Override
public void addTicket(final Ticket ticketToAdd) {
final Ticket ticket = encodeTicket(ticketToAdd);
final Element element = new Element(ticket.getId(), ticket);
int idleValue = ticketToAdd.getExpirationPolicy().getTimeToIdle().intValue();
if (idleValue <= 0) {
idleValue = ticketToAdd.getExpirationPolicy().getTimeToLive().intValue();
}
if (idleValue <= 0) {
idleValue = Integer.MAX_VALUE;
}
element.setTimeToIdle(idleValue);
int aliveValue = ticketToAdd.getExpirationPolicy().getTimeToLive().intValue();
if (aliveValue <= 0) {
aliveValue = Integer.MAX_VALUE;
}
element.setTimeToLive(aliveValue);
LOGGER.debug("Adding ticket [{}] to the cache [{}] to live [{}] seconds and stay idle for [{}] seconds",
ticket.getId(), this.ehcacheTicketsCache.getName(), aliveValue, idleValue);
this.ehcacheTicketsCache.put(element);
}
/**
* {@inheritDoc}
* Either the element is removed from the cache
* or it's not found in the cache and is already removed.
* Thus the result of this op would always be true.
*/
@Override
public boolean deleteSingleTicket(final String ticketId) {
final Ticket ticket = getTicket(ticketId);
if (ticket == null) {
LOGGER.debug("Ticket [{}] cannot be retrieved from the cache", ticketId);
return true;
}
if (this.ehcacheTicketsCache.remove(ticket.getId())) {
LOGGER.debug("Ticket [{}] is removed", ticket.getId());
}
return true;
}
@Override
public long deleteAll() {
final int size = this.ehcacheTicketsCache.getSize();
this.ehcacheTicketsCache.removeAll();
return size;
}
@Override
public Ticket getTicket(final String ticketIdToGet) {
final String ticketId = encodeTicketId(ticketIdToGet);
if (ticketId == null) {
return null;
}
final Element element = this.ehcacheTicketsCache.get(ticketId);
if (element == null) {
LOGGER.debug("No ticket by id [{}] is found in the registry", ticketId);
return null;
}
final Ticket ticket = decodeTicket((Ticket) element.getObjectValue());
final CacheConfiguration config = new CacheConfiguration();
config.setTimeToIdleSeconds(ticket.getExpirationPolicy().getTimeToIdle());
config.setTimeToLiveSeconds(ticket.getExpirationPolicy().getTimeToLive());
if (element.isExpired(config) || ticket.isExpired()) {
LOGGER.debug("Ticket [{}] has expired", ticket.getId());
this.ehcacheTicketsCache.evictExpiredElements();
this.ehcacheTicketsCache.flush();
return null;
}
return ticket;
}
@Override
public Collection<Ticket> getTickets() {
final Collection<Element> cacheTickets =
this.ehcacheTicketsCache.getAll(this.ehcacheTicketsCache.getKeysWithExpiryCheck()).values();
return decodeTickets(cacheTickets.stream().map(e -> (Ticket) e.getObjectValue()).collect(Collectors.toList()));
}
@Override
public Ticket updateTicket(final Ticket ticket) {
addTicket(ticket);
return ticket;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.appendSuper(super.toString())
.append("ehcacheTicketsCache", this.ehcacheTicketsCache)
.toString();
}
}