1 package com.atlassian.vcache.internal.core.service;
2
3 import java.util.Map;
4 import java.util.Objects;
5 import java.util.Optional;
6 import java.util.Set;
7 import java.util.function.Function;
8 import java.util.stream.Collectors;
9 import java.util.stream.StreamSupport;
10
11 import static java.util.Objects.requireNonNull;
12
13
14
15
16
17
18 public class LocalCacheUtils {
19
20
21
22
23
24
25 public static class PutArgs<K, V> {
26 public final K key;
27 public final V value;
28
29 private PutArgs(K key, V value) {
30 this.key = requireNonNull(key);
31 this.value = requireNonNull(value);
32 }
33 }
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public static <K, V> Map<K, V> getBulk(
48 Function<Set<K>, Map<K, V>> factory,
49 Iterable<K> keys,
50 Function<K, Optional<V>> getFn,
51 Function<PutArgs<K, V>, Optional<V>> putIfAbsentFn,
52 VCacheLock lock) {
53
54 final Map<K, Optional<V>> existingValues =
55 lock.withLock(() ->
56 StreamSupport.stream(keys.spliterator(), false)
57 .distinct()
58 .collect(Collectors.toMap(
59 Objects::requireNonNull,
60 getFn)));
61
62
63 @SuppressWarnings("OptionalGetWithoutIsPresent")
64 final Map<K, V> grandResult = existingValues.entrySet().stream()
65 .filter(e -> e.getValue().isPresent())
66 .collect(Collectors.toMap(
67 Map.Entry::getKey,
68 e -> e.getValue().get()));
69
70
71 if (grandResult.size() == existingValues.size()) {
72 return grandResult;
73 }
74
75
76 final Set<K> missingKeys = existingValues.entrySet().stream()
77 .filter(e -> !e.getValue().isPresent())
78 .map(Map.Entry::getKey)
79 .collect(Collectors.toSet());
80
81 final Map<K, V> missingValues = factory.apply(missingKeys);
82 FactoryUtils.verifyFactoryResult(missingValues, missingKeys);
83
84 lock.withLock(() ->
85 missingValues.entrySet().forEach(e -> {
86
87 final Optional<V> existing = putIfAbsentFn.apply(new PutArgs<>(e.getKey(), e.getValue()));
88 grandResult.put(e.getKey(), existing.orElse(e.getValue()));
89 }));
90
91 return grandResult;
92 }
93 }