package org.apereo.cas.ticket.registry;
import org.apereo.cas.logout.LogoutManager;
import org.apereo.cas.ticket.ServiceTicket;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.apereo.cas.ticket.registry.support.LockingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* This is {@link DefaultTicketRegistryCleaner}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
@Transactional(transactionManager = "ticketTransactionManager", readOnly = false)
public class DefaultTicketRegistryCleaner implements TicketRegistryCleaner, Serializable {
private static final long serialVersionUID = -8581398063126547772L;
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTicketRegistryCleaner.class);
private final LogoutManager logoutManager;
private final TicketRegistry ticketRegistry;
private final LockingStrategy lockingStrategy;
public DefaultTicketRegistryCleaner(final LockingStrategy lockingStrategy,
final LogoutManager logoutManager,
final TicketRegistry ticketRegistry) {
this.lockingStrategy = lockingStrategy;
this.logoutManager = logoutManager;
this.ticketRegistry = ticketRegistry;
}
@Override
public void clean() {
try {
if (!isCleanerSupported()) {
LOGGER.trace("Ticket registry cleaner is not supported by [{}]. No cleaner processes will run.",
getClass().getSimpleName());
return;
}
LOGGER.debug("Attempting to acquire ticket cleanup lock.");
if (!this.lockingStrategy.acquire()) {
LOGGER.info("Could not obtain lock. Aborting cleanup. The ticket registry may not support self-service maintenance.");
return;
}
LOGGER.debug("Acquired lock. Proceeding with cleanup.");
cleanInternal();
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
LOGGER.debug("Releasing ticket cleanup lock.");
this.lockingStrategy.release();
LOGGER.debug("Finished ticket cleanup.");
}
}
/**
* Clean tickets.
*/
protected void cleanInternal() {
final Collection<Ticket> ticketsToRemove = ticketRegistry.getTickets()
.stream()
.filter(Ticket::isExpired)
.collect(Collectors.toSet());
LOGGER.debug("[{}] expired tickets found.", ticketsToRemove.size());
int count = 0;
for (final Ticket ticket : ticketsToRemove) {
if (ticket instanceof TicketGrantingTicket) {
LOGGER.debug("Cleaning up expired ticket-granting ticket [{}]", ticket.getId());
logoutManager.performLogout((TicketGrantingTicket) ticket);
count += ticketRegistry.deleteTicket(ticket.getId());
} else if (ticket instanceof ServiceTicket) {
LOGGER.debug("Cleaning up expired service ticket [{}]", ticket.getId());
count += ticketRegistry.deleteTicket(ticket.getId());
} else {
LOGGER.warn("Unknown ticket type [{}] found to clean", ticket.getClass().getSimpleName());
}
}
LOGGER.info("[{}] expired tickets removed.", count);
}
/**
* Indicates whether the registry supports automatic ticket cleanup.
* Generally, a registry that is able to return a collection of available
* tickets should be able to support the cleanup process. Default is {@code true}.
*
* @return true/false.
*/
protected boolean isCleanerSupported() {
return true;
}
}