package org.apereo.cas.ticket;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.util.Assert;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.MappedSuperclass;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/**
* Abstract implementation of a ticket that handles all ticket state for
* policies. Also incorporates properties common among all tickets. As this is
* an abstract class, it cannot be created. It is recommended that
* implementations of the Ticket interface extend the AbstractTicket as it
* handles common functionality amongst different ticket types (such as state
* updating).
* <p>
* AbstractTicket does not provide a logger instance to
* avoid instantiating many such Loggers at runtime (there will be many instances
* of subclasses of AbstractTicket in a typical running CAS server). Instead
* subclasses should use static Logger instances.
*
* @author Scott Battaglia
* @since 3.0.0
*/
@MappedSuperclass
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class AbstractTicket implements Ticket, TicketState {
private static final long serialVersionUID = -8506442397878267555L;
/**
* The {@link ExpirationPolicy} this ticket is associated with.
**/
@Lob
@Column(name = "EXPIRATION_POLICY", length = Integer.MAX_VALUE, nullable = false)
private ExpirationPolicy expirationPolicy;
/**
* The unique identifier for this ticket.
*/
@Id
@Column(name = "ID", nullable = false)
private String id;
/**
* The last time this ticket was used.
*/
@Column(name = "LAST_TIME_USED")
private ZonedDateTime lastTimeUsed;
/**
* The previous last time this ticket was used.
*/
@Column(name = "PREVIOUS_LAST_TIME_USED")
private ZonedDateTime previousLastTimeUsed;
/**
* The time the ticket was created.
*/
@Column(name = "CREATION_TIME")
private ZonedDateTime creationTime;
/**
* The number of times this was used.
*/
@Column(name = "NUMBER_OF_TIMES_USED")
private int countOfUses;
/**
* Instantiates a new abstract ticket.
*/
protected AbstractTicket() {
// nothing to do
}
/**
* Constructs a new Ticket with a unique id, a possible parent Ticket (can
* be null) and a specified Expiration Policy.
*
* @param id the unique identifier for the ticket
* @param expirationPolicy the expiration policy for the ticket.
* @throws IllegalArgumentException if the id or expiration policy is null.
*/
public AbstractTicket(final String id, final ExpirationPolicy expirationPolicy) {
Assert.notNull(expirationPolicy, "expirationPolicy cannot be null");
Assert.notNull(id, "id cannot be null");
this.id = id;
this.creationTime = ZonedDateTime.now(ZoneOffset.UTC);
this.lastTimeUsed = ZonedDateTime.now(ZoneOffset.UTC);
this.expirationPolicy = expirationPolicy;
}
@Override
public String getId() {
return this.id;
}
@Override
public void update() {
this.previousLastTimeUsed = this.lastTimeUsed;
this.lastTimeUsed = ZonedDateTime.now(ZoneOffset.UTC);
this.countOfUses++;
if (getGrantingTicket() != null && !getGrantingTicket().isExpired()) {
final TicketState state = TicketState.class.cast(getGrantingTicket());
state.update();
}
}
@Override
public int getCountOfUses() {
return this.countOfUses;
}
@Override
public ZonedDateTime getCreationTime() {
return this.creationTime;
}
@Override
public ZonedDateTime getLastTimeUsed() {
return this.lastTimeUsed;
}
@Override
public ZonedDateTime getPreviousTimeUsed() {
return this.previousLastTimeUsed;
}
@Override
public boolean isExpired() {
final TicketGrantingTicket tgt = getGrantingTicket();
return this.expirationPolicy.isExpired(this)
|| tgt != null && tgt.isExpired()
|| isExpiredInternal();
}
protected boolean isExpiredInternal() {
return false;
}
@Override
public int hashCode() {
return new HashCodeBuilder(13, 133).append(this.getId()).toHashCode();
}
@Override
public String toString() {
return this.getId();
}
@Override
public ExpirationPolicy getExpirationPolicy() {
return this.expirationPolicy;
}
@Override
public int compareTo(final Ticket o) {
return getId().compareTo(o.getId());
}
}