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; } }