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.http.impl.client.cache.memcached;
28
29 import java.io.IOException;
30 import java.net.InetSocketAddress;
31
32 import net.spy.memcached.CASResponse;
33 import net.spy.memcached.CASValue;
34 import net.spy.memcached.MemcachedClient;
35 import net.spy.memcached.MemcachedClientIF;
36 import net.spy.memcached.OperationTimeoutException;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.http.client.cache.HttpCacheEntry;
41 import org.apache.http.client.cache.HttpCacheEntrySerializer;
42 import org.apache.http.client.cache.HttpCacheStorage;
43 import org.apache.http.client.cache.HttpCacheUpdateCallback;
44 import org.apache.http.client.cache.HttpCacheUpdateException;
45 import org.apache.http.impl.client.cache.CacheConfig;
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public class MemcachedHttpCacheStorage implements HttpCacheStorage {
95
96 private static final Log log = LogFactory.getLog(MemcachedHttpCacheStorage.class);
97
98 private final MemcachedClientIF client;
99 private final KeyHashingScheme keyHashingScheme;
100 private final MemcachedCacheEntryFactory memcachedCacheEntryFactory;
101 private final int maxUpdateRetries;
102
103
104
105
106
107
108
109
110
111 public MemcachedHttpCacheStorage(final InetSocketAddress address) throws IOException {
112 this(new MemcachedClient(address));
113 }
114
115
116
117
118
119
120 public MemcachedHttpCacheStorage(final MemcachedClientIF cache) {
121 this(cache, CacheConfig.DEFAULT, new MemcachedCacheEntryFactoryImpl(),
122 new SHA256KeyHashingScheme());
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 @Deprecated
143 public MemcachedHttpCacheStorage(final MemcachedClientIF client, final CacheConfig config,
144 final HttpCacheEntrySerializer serializer) {
145 this(client, config, new MemcachedCacheEntryFactoryImpl(),
146 new SHA256KeyHashingScheme());
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160 public MemcachedHttpCacheStorage(final MemcachedClientIF client, final CacheConfig config,
161 final MemcachedCacheEntryFactory memcachedCacheEntryFactory,
162 final KeyHashingScheme keyHashingScheme) {
163 this.client = client;
164 this.maxUpdateRetries = config.getMaxUpdateRetries();
165 this.memcachedCacheEntryFactory = memcachedCacheEntryFactory;
166 this.keyHashingScheme = keyHashingScheme;
167 }
168
169 @Override
170 public void putEntry(final String url, final HttpCacheEntry entry) throws IOException {
171 final byte[] bytes = serializeEntry(url, entry);
172 final String key = getCacheKey(url);
173 if (key == null) {
174 return;
175 }
176 try {
177 client.set(key, 0, bytes);
178 } catch (final OperationTimeoutException ex) {
179 throw new MemcachedOperationTimeoutException(ex);
180 }
181 }
182
183 private String getCacheKey(final String url) {
184 try {
185 return keyHashingScheme.hash(url);
186 } catch (final MemcachedKeyHashingException mkhe) {
187 return null;
188 }
189 }
190
191 private byte[] serializeEntry(final String url, final HttpCacheEntry hce) throws IOException {
192 final MemcachedCacheEntry mce = memcachedCacheEntryFactory.getMemcachedCacheEntry(url, hce);
193 try {
194 return mce.toByteArray();
195 } catch (final MemcachedSerializationException mse) {
196 final IOException ioe = new IOException();
197 ioe.initCause(mse);
198 throw ioe;
199 }
200 }
201
202 private byte[] convertToByteArray(final Object o) {
203 if (o == null) {
204 return null;
205 }
206 if (!(o instanceof byte[])) {
207 log.warn("got a non-bytearray back from memcached: " + o);
208 return null;
209 }
210 return (byte[])o;
211 }
212
213 private MemcachedCacheEntry reconstituteEntry(final Object o) {
214 final byte[] bytes = convertToByteArray(o);
215 if (bytes == null) {
216 return null;
217 }
218 final MemcachedCacheEntry mce = memcachedCacheEntryFactory.getUnsetCacheEntry();
219 try {
220 mce.set(bytes);
221 } catch (final MemcachedSerializationException mse) {
222 return null;
223 }
224 return mce;
225 }
226
227 @Override
228 public HttpCacheEntry getEntry(final String url) throws IOException {
229 final String key = getCacheKey(url);
230 if (key == null) {
231 return null;
232 }
233 try {
234 final MemcachedCacheEntry mce = reconstituteEntry(client.get(key));
235 if (mce == null || !url.equals(mce.getStorageKey())) {
236 return null;
237 }
238 return mce.getHttpCacheEntry();
239 } catch (final OperationTimeoutException ex) {
240 throw new MemcachedOperationTimeoutException(ex);
241 }
242 }
243
244 @Override
245 public void removeEntry(final String url) throws IOException {
246 final String key = getCacheKey(url);
247 if (key == null) {
248 return;
249 }
250 try {
251 client.delete(key);
252 } catch (final OperationTimeoutException ex) {
253 throw new MemcachedOperationTimeoutException(ex);
254 }
255 }
256
257 @Override
258 public void updateEntry(final String url, final HttpCacheUpdateCallback callback)
259 throws HttpCacheUpdateException, IOException {
260 int numRetries = 0;
261 final String key = getCacheKey(url);
262 if (key == null) {
263 throw new HttpCacheUpdateException("couldn't generate cache key");
264 }
265 do {
266 try {
267 final CASValue<Object> v = client.gets(key);
268 MemcachedCacheEntry mce = (v == null) ? null
269 : reconstituteEntry(v.getValue());
270 if (mce != null && (!url.equals(mce.getStorageKey()))) {
271 mce = null;
272 }
273 final HttpCacheEntry existingEntry = (mce == null) ? null
274 : mce.getHttpCacheEntry();
275 final HttpCacheEntry updatedEntry = callback.update(existingEntry);
276
277 if (existingEntry == null) {
278 putEntry(url, updatedEntry);
279 return;
280
281 } else {
282 final byte[] updatedBytes = serializeEntry(url, updatedEntry);
283 final CASResponse casResult = client.cas(key, v.getCas(),
284 updatedBytes);
285 if (casResult != CASResponse.OK) {
286 numRetries++;
287 } else {
288 return;
289 }
290 }
291 } catch (final OperationTimeoutException ex) {
292 throw new MemcachedOperationTimeoutException(ex);
293 }
294 } while (numRetries <= maxUpdateRetries);
295
296 throw new HttpCacheUpdateException("Failed to update");
297 }
298 }