View Javadoc

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   * Sample usage of a ExternalCache.
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          // Would not normally do this
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                  // log warning
62              }
63              else
64              {
65                  // log whether added
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                  // Deal with it
79                  return false;
80              }
81  
82              // Check have values
83              if (!currentValues.get("currentCount").isPresent() || !currentValues.get("licensedCount").isPresent())
84              {
85                  // Deal with it
86                  return false;
87              }
88  
89              // Check have spare room
90              if (currentValues.get("currentCount").get().value() < currentValues.get("licensedCount").get().value())
91              {
92                  // Deal with it
93                  return false;
94              }
95  
96              // Increment and update
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                     // Deal with it
106                     return false;
107                 }
108 
109                 // Really should loop of the replaceIf fails.....
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 }