001package com.identityworksllc.iiq.common; 002 003import sailpoint.server.Environment; 004 005import java.util.concurrent.atomic.AtomicReference; 006import java.util.function.Supplier; 007 008/** 009 * A container for a single value that is associated with the plugin cache 010 * version. If the plugin cache is updated, the version increments, and the 011 * provided {@link Supplier} will be invoked to get a new value on the next 012 * call to {@link #get()}. 013 * 014 * This allows consistent references to plugin-provided classes, even if the 015 * plugin is updated and a new version of the class is hot-deployed. 016 * 017 * Your {@link Supplier} code should always assume that the plugin cache has 018 * been refreshed since the last invocation and never cache plugin objects. 019 * 020 * The object type T, of course, cannot be the actual implementation 021 * in the plugin classloader, as that class will have become invalid when the 022 * plugin cache was reset. The type T should be either a JDK class or a custom 023 * interface implemented at the webapp layer. 024 * 025 * @param <T> the type of the value held in this reference 026 */ 027public class VersionedReference<T> implements Supplier<T> { 028 /** 029 * The supplier that provides the initial value for this ThreadLocal. 030 */ 031 private final Supplier<? extends T> supplier; 032 033 /** 034 * A container for the current value 035 */ 036 private final AtomicReference<T> value; 037 038 /** 039 * The version of the plugin cache, used to determine if the threadlocal needs 040 * to be replaced. 041 */ 042 private int version; 043 044 /** 045 * Creates a new VersionedThreadLocal with the given supplier. The initial 046 * value will not be calculated until the first call to {@link #get()}. 047 * 048 * @param supplier the initial value supplier 049 */ 050 public VersionedReference(Supplier<? extends T> supplier) { 051 this.supplier = supplier; 052 this.value = new AtomicReference<>(); 053 this.version = -1; 054 } 055 056 /** 057 * Returns the current value in this reference. If the plugin version has 058 * changed since the value was last set, the supplier will be called to get 059 * a new value. 060 * 061 * @return the current value in this ThreadLocal 062 * @see ThreadLocal#get() 063 */ 064 @Override 065 public T get() { 066 int currentVersion = Environment.getEnvironment().getPluginsCache().getVersion(); 067 if (currentVersion != version) { 068 synchronized (this) { 069 version = currentVersion; 070 value.set(supplier.get()); 071 } 072 } 073 return value.get(); 074 } 075 076}