001package com.identityworksllc.iiq.common.cache; 002 003import com.fasterxml.jackson.annotation.JsonCreator; 004 005import java.io.*; 006import java.util.Date; 007import java.util.Objects; 008import java.util.Optional; 009import java.util.StringJoiner; 010import java.util.concurrent.TimeUnit; 011import java.util.concurrent.atomic.AtomicStampedReference; 012import java.util.function.Supplier; 013 014/** 015 * Cache entry for use with the CacheMap class or other purposes. This class allows you 016 * to store a dated object of any type. The entry will be considered expired when the 017 * current date is after the expiration date. 018 * 019 * Instances of this class are {@link Externalizable} if the contained type T is serializable. 020 * 021 * @param <T> The type of the object to store 022 */ 023public class CacheEntry<T> implements Serializable, Supplier<Optional<T>> { 024 025 /** 026 * Serialization UID 027 */ 028 private static final long serialVersionUID = 3L; 029 030 /** 031 * Returns either the entry (if it is not null and not expired), or invokes the Supplier 032 * to generate a new entry if it is expired. 033 * 034 * @param entry The existing entry (which can be null) 035 * @param valueSupplier A function to calculate a new value 036 * @return The value supplier 037 * @param <T> The type of thing that is cached 038 */ 039 public static <T> CacheEntry<T> computeIfExpired(CacheEntry<T> entry, Supplier<CacheEntry<T>> valueSupplier) { 040 if (entry == null || entry.isExpired()) { 041 return valueSupplier.get(); 042 } else { 043 return entry; 044 } 045 } 046 047 /** 048 * Copy constructor 049 * @param other The other cache entry 050 */ 051 public CacheEntry(CacheEntry<? extends T> other) { 052 this(other.value, other.expiration); 053 } 054 055 /** 056 * Expiration millisecond timestamp for this entry 057 */ 058 private final long expiration; 059 060 /** 061 * The object type 062 */ 063 private final T value; 064 065 /** 066 * Construct a new cache entry that expires a specific amount of time in the future 067 * @param entryValue The entry value 068 * @param futureTimeAmount The amount of time in the future (from right now) to expire it 069 * @param timeUnit The units in which the amount of time is specified 070 */ 071 public CacheEntry(T entryValue, long futureTimeAmount, TimeUnit timeUnit) { 072 this(entryValue, System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(futureTimeAmount, timeUnit)); 073 } 074 075 /** 076 * Construct a new cache entry that expires on a specific date 077 * @param entryValue The value of this cache entry 078 * @param entryExpiration The expiration date of this cache entry 079 */ 080 public CacheEntry(T entryValue, Date entryExpiration) { 081 this(entryValue, entryExpiration.getTime()); 082 } 083 084 /** 085 * Construct a new cache entry that expires at a specific Unix timestamp (in milliseconds) 086 * @param entryValue The value of this cache entry 087 * @param entryExpirationTimestampMillis The expiration date of this cache entry 088 */ 089 public CacheEntry(T entryValue, long entryExpirationTimestampMillis) { 090 this.value = entryValue; 091 this.expiration = entryExpirationTimestampMillis; 092 } 093 094 /** 095 * Returns an {@link AtomicStampedReference} object with the 'stamp' set to the 096 * seconds (NOT milliseconds) timestamp of expiration. 097 * 098 * @return An AtomicStampedReference 099 */ 100 public AtomicStampedReference<T> asStampedReference() { 101 return new AtomicStampedReference<>(this.value, (int)(this.expiration / 1000L)); 102 } 103 104 /** 105 * @see Object#equals(Object) 106 */ 107 @Override 108 public boolean equals(Object o) { 109 if (this == o) return true; 110 if (o == null || getClass() != o.getClass()) return false; 111 CacheEntry<?> that = (CacheEntry<?>) o; 112 return Objects.equals(value, that.value); 113 } 114 115 /** 116 * If the entry is expired, returns {@link Optional#empty()}. 117 * If the entry is not expired, returns {@link Optional#ofNullable(Object)}. 118 * 119 * @return An optional containing a non-expired entry value 120 */ 121 @Override 122 public Optional<T> get() { 123 if (isExpired()) { 124 return Optional.empty(); 125 } else { 126 return Optional.ofNullable(value); 127 } 128 } 129 130 /** 131 * Returns the expiration instant, in Unix timestamp millis 132 * @return The expiration timestamp 133 */ 134 public long getExpiration() { 135 return expiration; 136 } 137 138 /** 139 * Gets the value associated with this cache entry 140 * 141 * @return The cached value 142 */ 143 public T getValue() { 144 return this.value; 145 } 146 147 /** 148 * @see Object#hashCode() 149 */ 150 @Override 151 public int hashCode() { 152 return Objects.hash(value); 153 } 154 155 /** 156 * The entry is expired if the current time is after the expiration date 157 * 158 * @return True if expired 159 */ 160 public boolean isExpired() { 161 return System.currentTimeMillis() >= expiration; 162 } 163 164 @Override 165 public String toString() { 166 return new StringJoiner(", ", CacheEntry.class.getSimpleName() + "[", "]") 167 .add("expiration timestamp=" + expiration) 168 .add("value=" + value) 169 .toString(); 170 } 171}