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