001package com.nimbusds.common.infinispan; 002 003 004import org.apache.logging.log4j.Logger; 005import org.infinispan.Cache; 006import org.infinispan.configuration.cache.CacheMode; 007 008import java.util.List; 009 010 011/** 012 * Infinispan cache workarounds. 013 * 014 * <ul> 015 * <li>Fix around invalidation bug, to force cache load before putIfAbsent, 016 * replace, delete operations. See tracking Connect2id server issue 017 * https://bitbucket.org/connect2id/server/issues/239/race-condition-in-cacheremove-in 018 * <li>Enforcing zero cache size for "stateless mode" to enable Infinispan 019 * to be run in local mode with no in-memory store, using the 020 * configured stores instead. 021 * </ul> 022 */ 023public class CacheWorkArounds<K, V> { 024 025 026 /** 027 * The handled special modes. 028 */ 029 public enum Mode { 030 INVALIDATION, 031 STATELESS 032 } 033 034 035 /** 036 * Checks if the specified cache is configured in invalidation mode. 037 * 038 * @param cache The cache to check. 039 * 040 * @return {@code true} if the cache is configured in invalidation 041 * mode, else {@code false}. 042 */ 043 public static boolean detectInvalidationMode(final Cache<?,?> cache) { 044 045 return List.of(CacheMode.INVALIDATION_SYNC, CacheMode.INVALIDATION_ASYNC) 046 .contains(cache.getCacheConfiguration().clustering().cacheMode()); 047 } 048 049 050 /** 051 * Checks if the specified cache is configured in "stateless mode". 052 * 053 * @param cache The cache to check. 054 * 055 * @return {@code true} if the cache is configured in "stateless mode", 056 * else {@code false}. 057 */ 058 public static boolean detectStatelessMode(final Cache<?,?> cache) { 059 060 final boolean isLocal = CacheMode.LOCAL.equals(cache.getCacheConfiguration().clustering().cacheMode()); 061 062 final boolean isSizeOne = 063 (1 == cache.getCacheConfiguration().memory().size()) // legacy 064 || 065 (1 == cache.getCacheConfiguration().memory().maxCount()); 066 067 return isLocal && isSizeOne; 068 } 069 070 071 /** 072 * The underlying Infinispan cache. 073 */ 074 private final Cache<K, V> infinispanCache; 075 076 077 /** 078 * The special mode, {@code null} if none. 079 */ 080 private final Mode mode; 081 082 083 /** 084 * Creates a new cache workarounds instance. 085 * 086 * @param infinispanCache The Infinispan cache. 087 */ 088 public CacheWorkArounds(final Cache<K, V> infinispanCache) { 089 this(infinispanCache, null); 090 } 091 092 093 /** 094 * Creates a new cache workarounds instance. 095 * 096 * @param infinispanCache The Infinispan cache. 097 * @param log Optional logger for the detection, 098 * {@code null} if not specified. 099 */ 100 public CacheWorkArounds(final Cache<K, V> infinispanCache, final Logger log) { 101 this.infinispanCache = infinispanCache; 102 if (detectInvalidationMode(infinispanCache)) { 103 mode = Mode.INVALIDATION; 104 } else if (detectStatelessMode(infinispanCache)) { 105 mode = Mode.STATELESS; 106 } else { 107 mode = null; 108 } 109 if (log != null && mode != null) { 110 log.info("[CM8020] Detected Infinispan cache {} in {} mode", infinispanCache.getName(), mode); 111 } 112 } 113 114 115 /** 116 * Returns the special workaround mode. 117 * 118 * @return The workaround mode, {@code null} if none (implies follow 119 * regular operation). 120 */ 121 public Mode getMode() { 122 return mode; 123 } 124 125 126 /** 127 * Returns {@code true} if the cache is in invalidation mode. 128 * 129 * @return {@code true} if the cache is in invalidation mode, else 130 * {@code false}. 131 */ 132 public boolean isInvalidation() { 133 134 return Mode.INVALIDATION.equals(getMode()); 135 } 136 137 138 /** 139 * Returns {@code true} if the cache is in the special "stateless 140 * mode". 141 * 142 * @return {@code true} if the cache is in the "stateless mode", else 143 * {@code false}. 144 */ 145 public boolean isStateless() { 146 147 return Mode.STATELESS.equals(getMode()); 148 } 149 150 151 /** 152 * If the cache is in the special "stateless mode" causes the 153 * underlying data container (in memory) to be cleared. Should be 154 * called before get, replace, putIfAbsent, remove and iteration 155 * operations. 156 */ 157 public void clearCacheIfStateless() { 158 159 if (! isStateless()) { 160 return; 161 } 162 163 infinispanCache.getAdvancedCache().getDataContainer().clear(); 164 } 165}