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.io.Closeable;
30 import java.lang.ref.ReferenceQueue;
31 import java.util.Collection;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.atomic.AtomicBoolean;
37 import java.util.concurrent.locks.ReentrantLock;
38
39 import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
40 import org.apache.hc.client5.http.cache.HttpCacheEntry;
41 import org.apache.hc.client5.http.cache.HttpCacheStorage;
42 import org.apache.hc.client5.http.cache.Resource;
43 import org.apache.hc.client5.http.cache.ResourceIOException;
44 import org.apache.hc.core5.annotation.Contract;
45 import org.apache.hc.core5.annotation.ThreadingBehavior;
46 import org.apache.hc.core5.util.Args;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 @Contract(threading = ThreadingBehavior.SAFE)
79 public class ManagedHttpCacheStorage implements HttpCacheStorage, Closeable {
80
81 private final InternalCacheStorage entries;
82 private final ReferenceQueue<HttpCacheEntry> morque;
83 private final Set<ResourceReference> resources;
84 private final AtomicBoolean active;
85
86 private final ReentrantLock lock;
87
88 public ManagedHttpCacheStorage(final CacheConfig config) {
89 super();
90 this.entries = new InternalCacheStorage(config.getMaxCacheEntries(), null);
91 this.morque = new ReferenceQueue<>();
92 this.resources = new HashSet<>();
93 this.active = new AtomicBoolean(true);
94 this.lock = new ReentrantLock();
95 }
96
97 private void ensureValidState() {
98 if (!isActive()) {
99 throw new IllegalStateException("Cache has been shut down");
100 }
101 }
102
103 private void keepResourceReference(final HttpCacheEntry entry) {
104 final Resource resource = entry.getResource();
105 if (resource != null) {
106
107 final ResourceReference ref = new ResourceReference(entry, this.morque);
108 this.resources.add(ref);
109 }
110 }
111
112 @Override
113 public void putEntry(final String url, final HttpCacheEntry entry) throws ResourceIOException {
114 Args.notNull(url, "URL");
115 Args.notNull(entry, "Cache entry");
116 ensureValidState();
117 lock.lock();
118 try {
119 this.entries.put(url, entry);
120 keepResourceReference(entry);
121 } finally {
122 lock.unlock();
123 }
124 }
125
126 @Override
127 public HttpCacheEntry getEntry(final String url) throws ResourceIOException {
128 Args.notNull(url, "URL");
129 ensureValidState();
130 lock.lock();
131 try {
132 return this.entries.get(url);
133 } finally {
134 lock.unlock();
135 }
136 }
137
138 @Override
139 public void removeEntry(final String url) throws ResourceIOException {
140 Args.notNull(url, "URL");
141 ensureValidState();
142 lock.lock();
143 try {
144
145
146 this.entries.remove(url);
147 } finally {
148 lock.unlock();
149 }
150 }
151
152 @Override
153 public void updateEntry(
154 final String url,
155 final HttpCacheCASOperation casOperation) throws ResourceIOException {
156 Args.notNull(url, "URL");
157 Args.notNull(casOperation, "CAS operation");
158 ensureValidState();
159 lock.lock();
160 try {
161 final HttpCacheEntry existing = this.entries.get(url);
162 final HttpCacheEntry updated = casOperation.execute(existing);
163 this.entries.put(url, updated);
164 if (existing != updated) {
165 keepResourceReference(updated);
166 }
167 } finally {
168 lock.unlock();
169 }
170 }
171
172 @Override
173 public Map<String, HttpCacheEntry> getEntries(final Collection<String> keys) throws ResourceIOException {
174 Args.notNull(keys, "Key");
175 final Map<String, HttpCacheEntry> resultMap = new HashMap<>(keys.size());
176 for (final String key: keys) {
177 final HttpCacheEntry entry = getEntry(key);
178 if (entry != null) {
179 resultMap.put(key, entry);
180 }
181 }
182 return resultMap;
183 }
184
185 public void cleanResources() {
186 if (isActive()) {
187 ResourceReference ref;
188 while ((ref = (ResourceReference) this.morque.poll()) != null) {
189 lock.lock();
190 try {
191 this.resources.remove(ref);
192 } finally {
193 lock.unlock();
194 }
195 ref.getResource().dispose();
196 }
197 }
198 }
199
200 public void shutdown() {
201 if (compareAndSet()) {
202 lock.lock();
203 try {
204 this.entries.clear();
205 for (final ResourceReference ref: this.resources) {
206 ref.getResource().dispose();
207 }
208 this.resources.clear();
209 while (this.morque.poll() != null) {
210 }
211 } finally {
212 lock.unlock();
213 }
214 }
215 }
216
217 @Override
218 public void close() {
219 if (compareAndSet()) {
220 lock.lock();
221 try {
222 ResourceReference ref;
223 while ((ref = (ResourceReference) this.morque.poll()) != null) {
224 this.resources.remove(ref);
225 ref.getResource().dispose();
226 }
227 } finally {
228 lock.unlock();
229 }
230 }
231 }
232
233
234
235
236
237
238
239 public boolean isActive() {
240 return active.get();
241 }
242
243 private boolean compareAndSet(){
244 return this.active.compareAndSet(true, false);
245 }
246 }