View Javadoc

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