View Javadoc

1   package com.atlassian.vcache.internal.core.service;
2   
3   import com.atlassian.vcache.JvmCache;
4   import com.atlassian.vcache.JvmCacheSettings;
5   import com.atlassian.vcache.VCacheException;
6   import com.google.common.cache.Cache;
7   import com.google.common.cache.CacheBuilder;
8   import com.google.common.util.concurrent.UncheckedExecutionException;
9   
10  import java.time.Duration;
11  import java.util.Arrays;
12  import java.util.HashSet;
13  import java.util.Map;
14  import java.util.Optional;
15  import java.util.Set;
16  import java.util.concurrent.ExecutionException;
17  import java.util.concurrent.TimeUnit;
18  import java.util.function.Function;
19  import java.util.function.Supplier;
20  
21  import static com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
22  import static java.util.Objects.requireNonNull;
23  
24  /**
25   * Implementation of the {@link JvmCache} that uses Guava.
26   *
27   * @param <K> the key type
28   * @param <V> the value type
29   * @since 1.0
30   */
31  public class GuavaJvmCache<K, V> implements JvmCache<K, V> {
32      private final String name;
33      private final VCacheLock globalLock;
34      private final Cache<K, V> delegate;
35  
36      /**
37       * Creates an instance. <b>NOTE:</b> all the supplied settings must be set.
38       *
39       * @param name     the name of the cache
40       * @param settings the settings for the cache.
41       */
42      public GuavaJvmCache(String name, JvmCacheSettings settings, Duration lockTimeout) {
43          this.name = requireValidCacheName(name);
44          this.globalLock = new VCacheLock(name, lockTimeout);
45          //noinspection OptionalGetWithoutIsPresent
46          this.delegate = CacheBuilder.newBuilder()
47                  .maximumSize(settings.getMaxEntries().get())
48                  .expireAfterWrite(settings.getDefaultTtl().get().toMillis(), TimeUnit.MILLISECONDS)
49                  .build();
50      }
51  
52      @Override
53      public Set<K> getKeys() {
54          return globalLock.withLock(() -> new HashSet<>(delegate.asMap().keySet()));
55      }
56  
57      @Override
58      public Optional<V> get(K key) {
59          return globalLock.withLock(() -> Optional.ofNullable(delegate.getIfPresent(key)));
60      }
61  
62      @Override
63      public V get(K key, Supplier<? extends V> supplier) {
64          // Look for the existing value
65          final Optional<V> current = get(requireNonNull(key));
66  
67          return current.orElseGet(() -> {
68              // Calculate the missing value
69              final V candidateValue = requireNonNull(supplier.get());
70  
71              // Now return either candidate value because one does not exist, otherwise the value that beat us to be inserted
72              return globalLock.withLock(() -> {
73                  try {
74                      return delegate.get(key, () -> candidateValue);
75                  } catch (UncheckedExecutionException | ExecutionException e) {
76                      throw new VCacheException("Internal Guava failure", e);
77                  }
78              });
79          });
80      }
81  
82      @SafeVarargs
83      @Override
84      public final Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, K... keys) {
85          // This method needs to be final to prevent subclasses bypassing the locking enforced by this class.
86          return getBulk(factory, Arrays.asList(keys));
87      }
88  
89      @Override
90      public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
91          return globalLock.withLock(() -> LocalCacheUtils.getBulk(
92                  factory,
93                  keys,
94                  this::get,
95                  args -> putIfAbsent(args.key, args.value),
96                  globalLock));
97      }
98  
99      @Override
100     public void put(K key, V value) {
101         globalLock.withLock(() -> delegate.put(requireNonNull(key), requireNonNull(value)));
102     }
103 
104     @Override
105     public Optional<V> putIfAbsent(K key, V value) {
106         return globalLock.withLock(() -> Optional.ofNullable(delegate.asMap().putIfAbsent(requireNonNull(key), requireNonNull(value))));
107     }
108 
109     @Override
110     public boolean replaceIf(K key, V currentValue, V newValue) {
111         return globalLock.withLock(() -> delegate.asMap().replace(requireNonNull(key), requireNonNull(currentValue), requireNonNull(newValue)));
112     }
113 
114     @Override
115     public void remove(K key) {
116         globalLock.withLock(() -> delegate.invalidate(key));
117     }
118 
119     @Override
120     public void removeAll() {
121         globalLock.withLock((Runnable) delegate::invalidateAll);
122     }
123 
124     @Override
125     public boolean removeIf(K key, V value) {
126         return globalLock.withLock(() -> delegate.asMap().remove(key, value));
127     }
128 
129     @Override
130     public String getName() {
131         return name;
132     }
133 }