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