001package com.identityworksllc.iiq.common.cache; 002 003import com.identityworksllc.iiq.common.Utilities; 004 005import java.util.Date; 006import java.util.Objects; 007import java.util.concurrent.TimeUnit; 008 009/** 010 * A plugin version aware extension of {@link CacheEntry}, which will consider 011 * itself expired whenever the plugin version has changed from the version at 012 * entry creation. 013 * 014 * Instances of this class are NOT Serializable, because it doesn't make sense to 015 * serialize a plugin-versioned object. 016 * 017 * @param <T> The type stored in this cache entry 018 */ 019public class VersionedCacheEntry<T> extends CacheEntry<T> { 020 /** 021 * Returns either the current object, if it is already a VersionedCacheEntry, or 022 * a newly constructed copy of it. 023 * 024 * @param other The other object to either return or copy 025 * @return A non-null VersionedCacheObject 026 * @param <V> The type of the object 027 * @throws IllegalArgumentException if the input is null or otherwise invalid 028 */ 029 @SuppressWarnings("unchecked") 030 public static <V> VersionedCacheEntry<V> of(CacheEntry<? extends V> other) throws IllegalArgumentException { 031 if (other == null) { 032 throw new IllegalArgumentException("Cannot construct a VersionedCacheEntry from a null object"); 033 } 034 035 if (other instanceof VersionedCacheEntry) { 036 return (VersionedCacheEntry<V>) other; 037 } else { 038 return new VersionedCacheEntry<>(other); 039 } 040 } 041 042 /** 043 * The plugin version, set at entry creation 044 */ 045 private final int pluginVersion; 046 047 /** 048 * Copy constructor for another cache entry. If the other entry is a VersionedCacheEntry, 049 * its pluginVersion will also be copied. Otherwise, the current plugin version will be 050 * used. 051 * 052 * This should be used with caution, because you can construct a versioned object that 053 * looks right, but is not. 054 * 055 * @param entry The entry to copy 056 */ 057 public VersionedCacheEntry(CacheEntry<? extends T> entry) { 058 super(Objects.requireNonNull(entry.getValue()), entry.getExpiration()); 059 060 if (entry instanceof VersionedCacheEntry) { 061 this.pluginVersion = ((VersionedCacheEntry<?>) entry).pluginVersion; 062 } else { 063 this.pluginVersion = Utilities.getPluginVersionInt(); 064 } 065 } 066 067 /** 068 * Constructs a new VersionedCacheEntry expiring at the given time in the future 069 * @param entryValue The value being cached 070 * @param futureTimeAmount The number of time units in the future to expire this entry 071 * @param timeUnit The time unit (e.g., seconds) to calculate the expiration time 072 */ 073 public VersionedCacheEntry(T entryValue, long futureTimeAmount, TimeUnit timeUnit) { 074 super(Objects.requireNonNull(entryValue), futureTimeAmount, Objects.requireNonNull(timeUnit)); 075 this.pluginVersion = Utilities.getPluginVersionInt(); 076 } 077 078 /** 079 * Constructs a new VersionedCacheEntry expiring at the given Date 080 * @param entryValue The value being cached 081 * @param entryExpiration The 082 */ 083 public VersionedCacheEntry(T entryValue, Date entryExpiration) { 084 super(Objects.requireNonNull(entryValue), Objects.requireNonNull(entryExpiration)); 085 this.pluginVersion = Utilities.getPluginVersionInt(); 086 } 087 088 /** 089 * Constructs a new VersionedCacheEntry expiring at the given epoch millisecond timestamp 090 * @param entryValue The value being cached 091 * @param entryExpirationTimestampMillis The epoch millisecond timestamp after which this entry is expired 092 */ 093 public VersionedCacheEntry(T entryValue, long entryExpirationTimestampMillis) { 094 super(Objects.requireNonNull(entryValue), entryExpirationTimestampMillis); 095 this.pluginVersion = Utilities.getPluginVersionInt(); 096 } 097 098 @Override 099 public boolean equals(Object o) { 100 if (this == o) return true; 101 if (!(o instanceof VersionedCacheEntry)) return false; 102 if (!super.equals(o)) return false; 103 VersionedCacheEntry<?> that = (VersionedCacheEntry<?>) o; 104 return pluginVersion == that.pluginVersion; 105 } 106 107 @Override 108 public int hashCode() { 109 return Objects.hash(super.hashCode(), pluginVersion); 110 } 111 112 /** 113 * Marks this object expired if its time has elapsed or if the plugin version 114 * has changed since the cache entry was created. 115 * 116 * @return True if this is expired 117 */ 118 @Override 119 public boolean isExpired() { 120 int currentPluginVersion = Utilities.getPluginVersionInt(); 121 return super.isExpired() || (currentPluginVersion != pluginVersion); 122 } 123}