1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.cache;
28
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
36 import org.apache.hc.client5.http.cache.HttpCacheEntry;
37 import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
38 import org.apache.hc.client5.http.cache.HttpCacheStorage;
39 import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
40 import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
41 import org.apache.hc.client5.http.cache.ResourceIOException;
42 import org.apache.hc.core5.util.Args;
43
44
45
46
47
48
49 public abstract class AbstractSerializingCacheStorage<T, CAS> implements HttpCacheStorage {
50
51 private final int maxUpdateRetries;
52 private final HttpCacheEntrySerializer<T> serializer;
53
54 public AbstractSerializingCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer<T> serializer) {
55 this.maxUpdateRetries = Args.notNegative(maxUpdateRetries, "Max retries");
56 this.serializer = Args.notNull(serializer, "Cache entry serializer");
57 }
58
59 protected abstract String digestToStorageKey(String key);
60
61 protected abstract void store(String storageKey, T storageObject) throws ResourceIOException;
62
63 protected abstract T restore(String storageKey) throws ResourceIOException;
64
65 protected abstract CAS getForUpdateCAS(String storageKey) throws ResourceIOException;
66
67 protected abstract T getStorageObject(CAS cas) throws ResourceIOException;
68
69 protected abstract boolean updateCAS(String storageKey, CAS cas, T storageObject) throws ResourceIOException;
70
71 protected abstract void delete(String storageKey) throws ResourceIOException;
72
73 protected abstract Map<String, T> bulkRestore(Collection<String> storageKeys) throws ResourceIOException;
74
75 @Override
76 public final void putEntry(final String key, final HttpCacheEntry entry) throws ResourceIOException {
77 final String storageKey = digestToStorageKey(key);
78 final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, entry));
79 store(storageKey, storageObject);
80 }
81
82 @Override
83 public final HttpCacheEntry getEntry(final String key) throws ResourceIOException {
84 final String storageKey = digestToStorageKey(key);
85 final T storageObject = restore(storageKey);
86 if (storageObject == null) {
87 return null;
88 }
89 final HttpCacheStorageEntry entry = serializer.deserialize(storageObject);
90 if (key.equals(entry.getKey())) {
91 return entry.getContent();
92 }
93 return null;
94 }
95
96 @Override
97 public final void removeEntry(final String key) throws ResourceIOException {
98 final String storageKey = digestToStorageKey(key);
99 delete(storageKey);
100 }
101
102 @Override
103 public final void updateEntry(
104 final String key,
105 final HttpCacheCASOperation casOperation) throws HttpCacheUpdateException, ResourceIOException {
106 int numRetries = 0;
107 final String storageKey = digestToStorageKey(key);
108 for (;;) {
109 final CAS cas = getForUpdateCAS(storageKey);
110 HttpCacheStorageEntry storageEntry = cas != null ? serializer.deserialize(getStorageObject(cas)) : null;
111 if (storageEntry != null && !key.equals(storageEntry.getKey())) {
112 storageEntry = null;
113 }
114 final HttpCacheEntry existingEntry = storageEntry != null ? storageEntry.getContent() : null;
115 final HttpCacheEntry updatedEntry = casOperation.execute(existingEntry);
116
117 if (existingEntry == null) {
118 putEntry(key, updatedEntry);
119 return;
120
121 }
122 final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, updatedEntry));
123 if (!updateCAS(storageKey, cas, storageObject)) {
124 numRetries++;
125 if (numRetries >= maxUpdateRetries) {
126 throw new HttpCacheUpdateException("Cache update failed after " + numRetries + " retries");
127 }
128 } else {
129 return;
130 }
131 }
132 }
133
134 @Override
135 public final Map<String, HttpCacheEntry> getEntries(final Collection<String> keys) throws ResourceIOException {
136 Args.notNull(keys, "Storage keys");
137 final List<String> storageKeys = new ArrayList<>(keys.size());
138 for (final String key: keys) {
139 storageKeys.add(digestToStorageKey(key));
140 }
141 final Map<String, T> storageObjectMap = bulkRestore(storageKeys);
142 final Map<String, HttpCacheEntry> resultMap = new HashMap<>();
143 for (final String key: keys) {
144 final String storageKey = digestToStorageKey(key);
145 final T storageObject = storageObjectMap.get(storageKey);
146 if (storageObject != null) {
147 final HttpCacheStorageEntry entry = serializer.deserialize(storageObject);
148 if (key.equals(entry.getKey())) {
149 resultMap.put(key, entry.getContent());
150 }
151 }
152 }
153 return resultMap;
154 }
155
156 }