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
38 import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
39 import org.apache.hc.client5.http.cache.HttpCacheEntry;
40 import org.apache.hc.client5.http.cache.HttpCacheStorage;
41 import org.apache.hc.client5.http.cache.Resource;
42 import org.apache.hc.client5.http.cache.ResourceIOException;
43 import org.apache.hc.core5.annotation.Contract;
44 import org.apache.hc.core5.annotation.ThreadingBehavior;
45 import org.apache.hc.core5.util.Args;
46
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 @Contract(threading = ThreadingBehavior.SAFE)
78 public class ManagedHttpCacheStorage implements HttpCacheStorage, Closeable {
79
80 private final CacheMap entries;
81 private final ReferenceQueue<HttpCacheEntry> morque;
82 private final Set<ResourceReference> resources;
83 private final AtomicBoolean active;
84
85 public ManagedHttpCacheStorage(final CacheConfig config) {
86 super();
87 this.entries = new CacheMap(config.getMaxCacheEntries());
88 this.morque = new ReferenceQueue<>();
89 this.resources = new HashSet<>();
90 this.active = new AtomicBoolean(true);
91 }
92
93 private void ensureValidState() {
94 if (!isActive()) {
95 throw new IllegalStateException("Cache has been shut down");
96 }
97 }
98
99 private void keepResourceReference(final HttpCacheEntry entry) {
100 final Resource resource = entry.getResource();
101 if (resource != null) {
102
103 final ResourceReference ref = new ResourceReference(entry, this.morque);
104 this.resources.add(ref);
105 }
106 }
107
108 @Override
109 public void putEntry(final String url, final HttpCacheEntry entry) throws ResourceIOException {
110 Args.notNull(url, "URL");
111 Args.notNull(entry, "Cache entry");
112 ensureValidState();
113 synchronized (this) {
114 this.entries.put(url, entry);
115 keepResourceReference(entry);
116 }
117 }
118
119 @Override
120 public HttpCacheEntry getEntry(final String url) throws ResourceIOException {
121 Args.notNull(url, "URL");
122 ensureValidState();
123 synchronized (this) {
124 return this.entries.get(url);
125 }
126 }
127
128 @Override
129 public void removeEntry(final String url) throws ResourceIOException {
130 Args.notNull(url, "URL");
131 ensureValidState();
132 synchronized (this) {
133
134
135 this.entries.remove(url);
136 }
137 }
138
139 @Override
140 public void updateEntry(
141 final String url,
142 final HttpCacheCASOperation casOperation) throws ResourceIOException {
143 Args.notNull(url, "URL");
144 Args.notNull(casOperation, "CAS operation");
145 ensureValidState();
146 synchronized (this) {
147 final HttpCacheEntry existing = this.entries.get(url);
148 final HttpCacheEntry updated = casOperation.execute(existing);
149 this.entries.put(url, updated);
150 if (existing != updated) {
151 keepResourceReference(updated);
152 }
153 }
154 }
155
156 @Override
157 public Map<String, HttpCacheEntry> getEntries(final Collection<String> keys) throws ResourceIOException {
158 Args.notNull(keys, "Key");
159 final Map<String, HttpCacheEntry> resultMap = new HashMap<>(keys.size());
160 for (final String key: keys) {
161 final HttpCacheEntry entry = getEntry(key);
162 if (entry != null) {
163 resultMap.put(key, entry);
164 }
165 }
166 return resultMap;
167 }
168
169 public void cleanResources() {
170 if (isActive()) {
171 ResourceReference ref;
172 while ((ref = (ResourceReference) this.morque.poll()) != null) {
173 synchronized (this) {
174 this.resources.remove(ref);
175 }
176 ref.getResource().dispose();
177 }
178 }
179 }
180
181 public void shutdown() {
182 if (compareAndSet()) {
183 synchronized (this) {
184 this.entries.clear();
185 for (final ResourceReference ref: this.resources) {
186 ref.getResource().dispose();
187 }
188 this.resources.clear();
189 while (this.morque.poll() != null) {
190 }
191 }
192 }
193 }
194
195 @Override
196 public void close() {
197 if (compareAndSet()) {
198 synchronized (this) {
199 ResourceReference ref;
200 while ((ref = (ResourceReference) this.morque.poll()) != null) {
201 this.resources.remove(ref);
202 ref.getResource().dispose();
203 }
204 }
205 }
206 }
207
208
209
210
211
212
213
214 public boolean isActive() {
215 return active.get();
216 }
217
218 private boolean compareAndSet(){
219 return this.active.compareAndSet(true, false);
220 }
221 }