1 package com.atlassian.vcache;
2
3 import java.time.Duration;
4 import java.util.Map;
5 import java.util.Optional;
6 import java.util.Set;
7 import java.util.concurrent.CompletableFuture;
8 import java.util.concurrent.CompletionStage;
9 import java.util.function.Function;
10 import java.util.stream.Collectors;
11 import javax.annotation.PostConstruct;
12
13 import com.atlassian.vcache.marshallers.MarshallerFactory;
14
15
16
17
18 public class ExternalSample
19 {
20 private static Long valueMapper(String key)
21 {
22 switch (key)
23 {
24 case "currentCount":
25 return 42L;
26 case "licensedCount":
27 return 666L;
28 default:
29 throw new IllegalArgumentException("Unknown key: " + key);
30 }
31 }
32
33 private static final Function<Set<String>, Map<String, Long>> VALUE_FACTORY = keys ->
34 keys.stream().collect(Collectors.toMap(key -> key, ExternalSample::valueMapper));
35
36 private final DirectExternalCache<Long> cache;
37
38 public ExternalSample(VCacheFactory factory)
39 {
40 ExternalCacheSettings settings = new ExternalCacheSettingsBuilder().
41 defaultTtl(Duration.ofSeconds(666)).
42 dataChangeRateHint(ChangeRate.HIGH_CHANGE).
43 entryCountHint(42).
44 entryGrowthRateHint(ChangeRate.LOW_CHANGE).
45 build();
46
47 cache = factory.getDirectExternalCache(
48 "mycache",
49 MarshallerFactory.serializableMarshaller(Long.TYPE),
50 settings);
51 }
52
53 @PostConstruct
54 void init()
55 {
56
57 cache.put("licensedCount", 88L, PutPolicy.ADD_ONLY);
58 cache.put("currentCount", 12L, PutPolicy.ADD_ONLY).whenComplete((b, ex) -> {
59 if (ex != null)
60 {
61
62 }
63 else
64 {
65
66 }
67 });
68 }
69
70 public boolean addUser()
71 {
72 CompletableFuture<Map<String, Optional<IdentifiedValue<Long>>>> getOp = cache.getBulkIdentified("currentCount", "licensedCount");
73
74 CompletionStage<Boolean> outcome = getOp.handle((currentValues, t) -> {
75
76 if (t != null)
77 {
78
79 return false;
80 }
81
82
83 if (!currentValues.get("currentCount").isPresent() || !currentValues.get("licensedCount").isPresent())
84 {
85
86 return false;
87 }
88
89
90 if (currentValues.get("currentCount").get().value() < currentValues.get("licensedCount").get().value())
91 {
92
93 return false;
94 }
95
96
97 final Long newCount = currentValues.get("currentCount").get().value() + 1;
98
99 CompletableFuture<Boolean> replaceOp =
100 cache.replaceIf("currentCount", currentValues.get("currentCount").get().identifier(), newCount);
101
102 return replaceOp.handle((replaced, replaceEx) -> {
103 if (replaceEx != null)
104 {
105
106 return false;
107 }
108
109
110 return replaced;
111 }).join();
112 });
113
114 return outcome.toCompletableFuture().join();
115 }
116
117 public long spareLicenses()
118 {
119 CompletableFuture<Map<String, Long>> cached = cache.getBulk(VALUE_FACTORY, "currentCount", "licensedCount");
120
121 return fold(cached,
122 (m) -> m.get("licensedCount") - m.get("currentCount"),
123 (err) -> -1L);
124 }
125
126 private <T, R> R fold(CompletableFuture<T> on, Function<T, R> success, Function<Throwable, R> failure)
127 {
128 return on.handle((val, err) ->
129 (val != null) ? success.apply(val) : failure.apply(err)).join();
130 }
131 }