1 |
| package org.jboss.cache.loader.bdbje; |
2 |
| |
3 |
| import com.sleepycat.bind.serial.SerialBinding; |
4 |
| import com.sleepycat.bind.serial.StoredClassCatalog; |
5 |
| import com.sleepycat.bind.tuple.TupleBinding; |
6 |
| import com.sleepycat.bind.tuple.TupleInput; |
7 |
| import com.sleepycat.bind.tuple.TupleOutput; |
8 |
| import com.sleepycat.je.Cursor; |
9 |
| import com.sleepycat.je.Database; |
10 |
| import com.sleepycat.je.DatabaseConfig; |
11 |
| import com.sleepycat.je.DatabaseEntry; |
12 |
| import com.sleepycat.je.DeadlockException; |
13 |
| import com.sleepycat.je.Environment; |
14 |
| import com.sleepycat.je.EnvironmentConfig; |
15 |
| import com.sleepycat.je.JEVersion; |
16 |
| import com.sleepycat.je.LockMode; |
17 |
| import com.sleepycat.je.OperationStatus; |
18 |
| import com.sleepycat.je.Transaction; |
19 |
| import net.jcip.annotations.ThreadSafe; |
20 |
| import org.apache.commons.logging.Log; |
21 |
| import org.apache.commons.logging.LogFactory; |
22 |
| import org.jboss.cache.CacheSPI; |
23 |
| import org.jboss.cache.Fqn; |
24 |
| import org.jboss.cache.Modification; |
25 |
| import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; |
26 |
| import org.jboss.cache.loader.AbstractCacheLoader; |
27 |
| |
28 |
| import java.io.File; |
29 |
| import java.io.IOException; |
30 |
| import java.io.Serializable; |
31 |
| import java.util.Collections; |
32 |
| import java.util.HashMap; |
33 |
| import java.util.HashSet; |
34 |
| import java.util.List; |
35 |
| import java.util.Map; |
36 |
| import java.util.Set; |
37 |
| import java.util.concurrent.ConcurrentHashMap; |
38 |
| |
39 |
| |
40 |
| |
41 |
| |
42 |
| |
43 |
| |
44 |
| |
45 |
| |
46 |
| |
47 |
| |
48 |
| |
49 |
| |
50 |
| |
51 |
| |
52 |
| |
53 |
| |
54 |
| |
55 |
| @ThreadSafe |
56 |
| public class BdbjeCacheLoader extends AbstractCacheLoader |
57 |
| { |
58 |
| |
59 |
| private static final int MAX_TXN_RETRIES = 10; |
60 |
| private static final char LOWEST_UTF_CHAR = '\u0001'; |
61 |
| |
62 |
| private static final Log log = LogFactory.getLog(BdbjeCacheLoader.class); |
63 |
| |
64 |
| private BdbjeCacheLoaderConfig config; |
65 |
| private Environment env; |
66 |
| private String cacheDbName; |
67 |
| private String catalogDbName; |
68 |
| private Database cacheDb; |
69 |
| private Database catalogDb; |
70 |
| private StoredClassCatalog catalog; |
71 |
| private SerialBinding serialBinding; |
72 |
| private Map<Object, Transaction> txnMap; |
73 |
| private boolean transactional; |
74 |
| |
75 |
| |
76 |
| |
77 |
| |
78 |
| |
79 |
| |
80 |
| |
81 |
| |
82 |
| |
83 |
128
| public void create() throws Exception
|
84 |
| { |
85 |
128
| String license = "\n*************************************************************************************\n" +
|
86 |
| "Berkeley DB Java Edition version: " + JEVersion.CURRENT_VERSION.toString() + "\n" + |
87 |
| "JBoss Cache can use Berkeley DB Java Edition from Oracle \n" + |
88 |
| "(http://www.oracle.com/database/berkeley-db/je/index.html)\n" + |
89 |
| "for persistent, reliable and transaction-protected data storage.\n" + |
90 |
| "If you choose to use Berkeley DB Java Edition with JBoss Cache, you must comply with the terms\n" + |
91 |
| "of Oracle's public license, included in the file LICENSE.txt.\n" + |
92 |
| "If you prefer not to release the source code for your own application in order to comply\n" + |
93 |
| "with the Oracle public license, you may purchase a different license for use of\n" + |
94 |
| "Berkeley DB Java Edition with JBoss Cache.\n" + |
95 |
| "See http://www.oracle.com/database/berkeley-db/je/index.html for pricing and license terms\n" + |
96 |
| "*************************************************************************************"; |
97 |
128
| System.out.println(license);
|
98 |
| |
99 |
128
| log.trace("Creating BdbjeCacheLoader instance.");
|
100 |
128
| checkNotOpen();
|
101 |
| } |
102 |
| |
103 |
| |
104 |
| |
105 |
| |
106 |
128
| public void destroy()
|
107 |
| { |
108 |
| } |
109 |
| |
110 |
| |
111 |
| |
112 |
| |
113 |
| |
114 |
128
| public void start()
|
115 |
| throws Exception |
116 |
| { |
117 |
| |
118 |
128
| log.trace("Starting BdbjeCacheLoader instance.");
|
119 |
128
| checkNotOpen();
|
120 |
| |
121 |
128
| if (cache == null)
|
122 |
| { |
123 |
0
| throw new IllegalStateException(
|
124 |
| "A non-null Cache property (CacheSPI object) is required"); |
125 |
| } |
126 |
128
| String configStr = config.getLocation();
|
127 |
128
| if (config.getLocation() == null)
|
128 |
| { |
129 |
45
| configStr = System.getProperty("java.io.tmpdir");
|
130 |
45
| config.setLocation(configStr);
|
131 |
| } |
132 |
| |
133 |
| |
134 |
128
| File location = new File(configStr);
|
135 |
128
| if (!location.exists())
|
136 |
| { |
137 |
62
| boolean created = location.mkdirs();
|
138 |
0
| if (!created) throw new IOException("Unable to create cache loader location " + location);
|
139 |
| |
140 |
| } |
141 |
128
| if (!location.isDirectory())
|
142 |
| { |
143 |
0
| throw new IOException("Cache loader location [" + location + "] is not a directory!");
|
144 |
| } |
145 |
| |
146 |
| |
147 |
128
| File homeDir;
|
148 |
128
| int offset = configStr.indexOf('#');
|
149 |
128
| if (offset >= 0 && offset < configStr.length() - 1)
|
150 |
| { |
151 |
1
| homeDir = new File(configStr.substring(0, offset));
|
152 |
1
| cacheDbName = configStr.substring(offset + 1);
|
153 |
| } |
154 |
| else |
155 |
| { |
156 |
127
| homeDir = new File(configStr);
|
157 |
127
| cacheDbName = cache.getClusterName();
|
158 |
| } |
159 |
128
| catalogDbName = cacheDbName + "_class_catalog";
|
160 |
| |
161 |
| |
162 |
| |
163 |
| |
164 |
| |
165 |
| |
166 |
128
| transactional = cache.getTransactionManager() != null;
|
167 |
| |
168 |
128
| try
|
169 |
| { |
170 |
| |
171 |
128
| EnvironmentConfig envConfig = new EnvironmentConfig();
|
172 |
128
| envConfig.setAllowCreate(true);
|
173 |
128
| envConfig.setTransactional(true);
|
174 |
0
| if (log.isTraceEnabled()) log.trace("Creating JE environment with home dir " + homeDir);
|
175 |
128
| env = new Environment(homeDir, envConfig);
|
176 |
0
| if (log.isDebugEnabled()) log.debug("Created JE environment " + env + " for cache loader " + this);
|
177 |
| |
178 |
128
| openDatabases();
|
179 |
| } |
180 |
| catch (Exception e) |
181 |
| { |
182 |
0
| destroy();
|
183 |
0
| throw e;
|
184 |
| } |
185 |
| } |
186 |
| |
187 |
| |
188 |
| |
189 |
| |
190 |
128
| private void openDatabases()
|
191 |
| throws Exception |
192 |
| { |
193 |
| |
194 |
| |
195 |
128
| DatabaseConfig dbConfig = new DatabaseConfig();
|
196 |
128
| dbConfig.setAllowCreate(true);
|
197 |
128
| dbConfig.setTransactional(transactional);
|
198 |
| |
199 |
| |
200 |
128
| cacheDb = env.openDatabase(null, cacheDbName, dbConfig);
|
201 |
128
| catalogDb = env.openDatabase(null, catalogDbName, dbConfig);
|
202 |
| |
203 |
| |
204 |
128
| catalog = new StoredClassCatalog(catalogDb);
|
205 |
128
| serialBinding = new SerialBinding(catalog, null);
|
206 |
| |
207 |
| |
208 |
128
| txnMap = new ConcurrentHashMap<Object, Transaction>();
|
209 |
| } |
210 |
| |
211 |
| |
212 |
| |
213 |
| |
214 |
| |
215 |
141
| private void closeDatabases()
|
216 |
| { |
217 |
| |
218 |
141
| if (cacheDb != null)
|
219 |
| { |
220 |
128
| try
|
221 |
| { |
222 |
128
| cacheDb.close();
|
223 |
| } |
224 |
| catch (Exception shouldNotOccur) |
225 |
| { |
226 |
0
| log.warn("Caught unexpected exception", shouldNotOccur);
|
227 |
| } |
228 |
| } |
229 |
141
| if (catalogDb != null)
|
230 |
| { |
231 |
128
| try
|
232 |
| { |
233 |
128
| catalogDb.close();
|
234 |
| } |
235 |
| catch (Exception shouldNotOccur) |
236 |
| { |
237 |
0
| log.warn("Caught unexpected exception", shouldNotOccur);
|
238 |
| } |
239 |
| } |
240 |
141
| cacheDb = null;
|
241 |
141
| catalogDb = null;
|
242 |
141
| catalog = null;
|
243 |
141
| serialBinding = null;
|
244 |
141
| txnMap = null;
|
245 |
| } |
246 |
| |
247 |
| |
248 |
| |
249 |
| |
250 |
| |
251 |
| |
252 |
141
| public void stop()
|
253 |
| { |
254 |
| |
255 |
141
| closeDatabases();
|
256 |
| |
257 |
141
| if (env != null)
|
258 |
| { |
259 |
128
| try
|
260 |
| { |
261 |
128
| env.close();
|
262 |
| } |
263 |
| catch (Exception shouldNotOccur) |
264 |
| { |
265 |
0
| log.warn("Unexpected exception", shouldNotOccur);
|
266 |
| } |
267 |
| } |
268 |
141
| env = null;
|
269 |
| } |
270 |
| |
271 |
| |
272 |
| |
273 |
| |
274 |
| |
275 |
| |
276 |
| |
277 |
| |
278 |
128
| public void setConfig(IndividualCacheLoaderConfig base)
|
279 |
| { |
280 |
128
| checkNotOpen();
|
281 |
| |
282 |
128
| if (base instanceof BdbjeCacheLoaderConfig)
|
283 |
| { |
284 |
14
| this.config = (BdbjeCacheLoaderConfig) base;
|
285 |
| } |
286 |
| else |
287 |
| { |
288 |
114
| config = new BdbjeCacheLoaderConfig(base);
|
289 |
| } |
290 |
| |
291 |
0
| if (log.isTraceEnabled()) log.trace("Configuring cache loader with location = " + config.getLocation());
|
292 |
| } |
293 |
| |
294 |
114
| public IndividualCacheLoaderConfig getConfig()
|
295 |
| { |
296 |
114
| return config;
|
297 |
| } |
298 |
| |
299 |
| |
300 |
| |
301 |
| |
302 |
128
| public void setCache(CacheSPI c)
|
303 |
| { |
304 |
128
| super.setCache(c);
|
305 |
128
| checkNotOpen();
|
306 |
| } |
307 |
| |
308 |
| |
309 |
| |
310 |
| |
311 |
| |
312 |
| |
313 |
| |
314 |
| |
315 |
2160
| public Set<String> getChildrenNames(Fqn name)
|
316 |
| throws Exception |
317 |
| { |
318 |
| |
319 |
2160
| checkOpen();
|
320 |
2160
| checkNonNull(name, "name");
|
321 |
| |
322 |
2160
| DatabaseEntry prefixEntry = makeKeyEntry(name);
|
323 |
2160
| DatabaseEntry dataEntry = new DatabaseEntry();
|
324 |
2160
| dataEntry.setPartial(0, 0, true);
|
325 |
| |
326 |
2160
| String namePart = "";
|
327 |
2160
| int namePartIndex = name.size();
|
328 |
2160
| Set<String> set = null;
|
329 |
| |
330 |
2160
| Cursor cursor = cacheDb.openCursor(null, null);
|
331 |
2160
| try
|
332 |
| { |
333 |
2160
| while (true)
|
334 |
| { |
335 |
2345
| DatabaseEntry keyEntry = makeKeyEntry(prefixEntry, namePart);
|
336 |
2345
| OperationStatus status =
|
337 |
| cursor.getSearchKeyRange(keyEntry, dataEntry, null); |
338 |
2345
| if (status != OperationStatus.SUCCESS ||
|
339 |
| !startsWith(keyEntry, prefixEntry)) |
340 |
| { |
341 |
2160
| break;
|
342 |
| } |
343 |
185
| if (set == null)
|
344 |
| { |
345 |
76
| set = new HashSet<String>();
|
346 |
| } |
347 |
185
| Fqn childName = makeKeyObject(keyEntry);
|
348 |
185
| namePart = childName.get(namePartIndex).toString();
|
349 |
185
| set.add(namePart);
|
350 |
185
| namePart += LOWEST_UTF_CHAR;
|
351 |
| } |
352 |
| } |
353 |
| finally |
354 |
| { |
355 |
2160
| cursor.close();
|
356 |
| } |
357 |
2160
| if (set != null)
|
358 |
| { |
359 |
76
| return Collections.unmodifiableSet(set);
|
360 |
| } |
361 |
| else |
362 |
| { |
363 |
2084
| return null;
|
364 |
| } |
365 |
| } |
366 |
| |
367 |
| |
368 |
| |
369 |
| |
370 |
| |
371 |
| |
372 |
| |
373 |
2840
| public Map get(Fqn name)
|
374 |
| throws Exception |
375 |
| { |
376 |
| |
377 |
2840
| checkOpen();
|
378 |
2840
| checkNonNull(name, "name");
|
379 |
| |
380 |
2840
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
381 |
2840
| DatabaseEntry foundData = new DatabaseEntry();
|
382 |
2840
| OperationStatus status = cacheDb.get(null, keyEntry, foundData, null);
|
383 |
2839
| if (status == OperationStatus.SUCCESS)
|
384 |
| { |
385 |
| |
386 |
| |
387 |
1832
| return makeDataObject(foundData, true);
|
388 |
| } |
389 |
| else |
390 |
| { |
391 |
1007
| return null;
|
392 |
| } |
393 |
| } |
394 |
| |
395 |
| |
396 |
| |
397 |
| |
398 |
| |
399 |
| |
400 |
| |
401 |
| |
402 |
| |
403 |
| |
404 |
| |
405 |
| |
406 |
| |
407 |
| |
408 |
| |
409 |
| |
410 |
| |
411 |
| |
412 |
| |
413 |
| |
414 |
| |
415 |
| |
416 |
| |
417 |
427
| public boolean exists(Fqn name)
|
418 |
| throws Exception |
419 |
| { |
420 |
| |
421 |
427
| checkOpen();
|
422 |
427
| checkNonNull(name, "name");
|
423 |
| |
424 |
427
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
425 |
427
| DatabaseEntry foundData = new DatabaseEntry();
|
426 |
427
| foundData.setPartial(0, 0, true);
|
427 |
427
| OperationStatus status = cacheDb.get(null, keyEntry, foundData, null);
|
428 |
427
| return (status == OperationStatus.SUCCESS);
|
429 |
| } |
430 |
| |
431 |
| |
432 |
| |
433 |
| |
434 |
| |
435 |
| |
436 |
| |
437 |
2243
| public Object put(Fqn name, Object key, Object value) throws Exception
|
438 |
| { |
439 |
| |
440 |
2243
| checkOpen();
|
441 |
2243
| checkNonNull(name, "name");
|
442 |
| |
443 |
2243
| Object oldVal;
|
444 |
2243
| if (transactional)
|
445 |
| { |
446 |
2215
| Modification mod =
|
447 |
| new Modification(Modification.ModificationType.PUT_KEY_VALUE, name, key, value); |
448 |
2215
| commitModification(mod);
|
449 |
2215
| oldVal = mod.getOldValue();
|
450 |
| } |
451 |
| else |
452 |
| { |
453 |
28
| oldVal = doPut(null, name, key, value);
|
454 |
| } |
455 |
2243
| return oldVal;
|
456 |
| } |
457 |
| |
458 |
| |
459 |
| |
460 |
| |
461 |
| |
462 |
| |
463 |
2281
| private Object doPut(Transaction txn, Fqn name, Object key, Object value)
|
464 |
| throws Exception |
465 |
| { |
466 |
| |
467 |
2281
| Object oldVal = null;
|
468 |
| |
469 |
2281
| Map<Object, Object> map = new HashMap<Object, Object>();
|
470 |
2281
| map.put(key, value);
|
471 |
2281
| DatabaseEntry dataEntry = makeDataEntry(map);
|
472 |
2281
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
473 |
2281
| Cursor cursor = cacheDb.openCursor(txn, null);
|
474 |
2281
| try
|
475 |
| { |
476 |
2281
| OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry);
|
477 |
2281
| if (status == OperationStatus.SUCCESS)
|
478 |
| { |
479 |
976
| createParentNodes(cursor, name);
|
480 |
| } |
481 |
| else |
482 |
| { |
483 |
1305
| DatabaseEntry foundData = new DatabaseEntry();
|
484 |
1305
| status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW);
|
485 |
1305
| if (status == OperationStatus.SUCCESS)
|
486 |
| { |
487 |
1305
| map = makeDataObject(foundData, true);
|
488 |
1305
| oldVal = map.put(key, value);
|
489 |
1305
| cursor.putCurrent(makeDataEntry(map));
|
490 |
| } |
491 |
| } |
492 |
| } |
493 |
| finally |
494 |
| { |
495 |
2281
| cursor.close();
|
496 |
| } |
497 |
2281
| return oldVal;
|
498 |
| } |
499 |
| |
500 |
| |
501 |
| |
502 |
| |
503 |
| |
504 |
| |
505 |
| |
506 |
| |
507 |
2163
| public void put(Fqn name, Map values)
|
508 |
| throws Exception |
509 |
| { |
510 |
| |
511 |
2163
| checkOpen();
|
512 |
2163
| checkNonNull(name, "name");
|
513 |
| |
514 |
2163
| if (transactional)
|
515 |
| { |
516 |
2130
| commitModification(
|
517 |
| new Modification(Modification.ModificationType.PUT_DATA, name, values)); |
518 |
| } |
519 |
| else |
520 |
| { |
521 |
33
| doPut(null, name, values);
|
522 |
| } |
523 |
| } |
524 |
| |
525 |
| |
526 |
| |
527 |
| |
528 |
| |
529 |
| |
530 |
2230
| private void doPut(Transaction txn, Fqn name, Map values)
|
531 |
| throws Exception |
532 |
| { |
533 |
| |
534 |
| |
535 |
2230
| values = (values == null ? null : new HashMap(values));
|
536 |
| |
537 |
| |
538 |
2230
| DatabaseEntry dataEntry = makeDataEntry(values);
|
539 |
2230
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
540 |
2230
| Cursor cursor = cacheDb.openCursor(txn, null);
|
541 |
2230
| try
|
542 |
| { |
543 |
2230
| OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry);
|
544 |
2230
| if (status == OperationStatus.SUCCESS)
|
545 |
| { |
546 |
1024
| createParentNodes(cursor, name);
|
547 |
| } |
548 |
| else |
549 |
| { |
550 |
1206
| DatabaseEntry foundData = new DatabaseEntry();
|
551 |
1206
| status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW);
|
552 |
1206
| if (status == OperationStatus.SUCCESS)
|
553 |
| { |
554 |
1206
| Map map = makeDataObject(foundData, true);
|
555 |
1206
| if (values != null)
|
556 |
| { |
557 |
1205
| map.putAll(values);
|
558 |
| } |
559 |
1206
| cursor.putCurrent(makeDataEntry(map));
|
560 |
| } |
561 |
| } |
562 |
| } |
563 |
| finally |
564 |
| { |
565 |
2230
| cursor.close();
|
566 |
| } |
567 |
| } |
568 |
| |
569 |
| |
570 |
| |
571 |
| |
572 |
| |
573 |
0
| private void doPutErase(Transaction txn, Fqn name, Map values)
|
574 |
| throws Exception |
575 |
| { |
576 |
| |
577 |
| |
578 |
0
| values = (values == null ? null : new HashMap(values));
|
579 |
| |
580 |
0
| DatabaseEntry dataEntry = makeDataEntry(values);
|
581 |
0
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
582 |
0
| Cursor cursor = cacheDb.openCursor(txn, null);
|
583 |
0
| try
|
584 |
| { |
585 |
0
| cursor.put(keyEntry, dataEntry);
|
586 |
0
| createParentNodes(cursor, name);
|
587 |
| } |
588 |
| finally |
589 |
| { |
590 |
0
| cursor.close();
|
591 |
| } |
592 |
| } |
593 |
| |
594 |
| |
595 |
| |
596 |
| |
597 |
| |
598 |
| |
599 |
73
| public void put(List<Modification> modifications) throws Exception
|
600 |
| { |
601 |
73
| checkOpen();
|
602 |
73
| checkNonNull(modifications, "modifications");
|
603 |
| |
604 |
73
| if (transactional)
|
605 |
| { |
606 |
69
| commitModifications(modifications);
|
607 |
| } |
608 |
| else |
609 |
| { |
610 |
4
| doPut(null, modifications);
|
611 |
| } |
612 |
| } |
613 |
| |
614 |
| |
615 |
| |
616 |
| |
617 |
8815
| private void doPut(Transaction txn, List<Modification> modifications)
|
618 |
| throws Exception |
619 |
| { |
620 |
| |
621 |
| |
622 |
| |
623 |
| |
624 |
8815
| for (Modification mod : modifications)
|
625 |
| { |
626 |
8852
| Fqn name = mod.getFqn();
|
627 |
8852
| Object oldVal;
|
628 |
8852
| switch (mod.getType())
|
629 |
| { |
630 |
2253
| case PUT_KEY_VALUE:
|
631 |
2253
| oldVal = doPut(txn, name, mod.getKey(), mod.getValue());
|
632 |
2253
| mod.setOldValue(oldVal);
|
633 |
2253
| break;
|
634 |
2197
| case PUT_DATA:
|
635 |
2197
| doPut(txn, name, mod.getData());
|
636 |
2197
| break;
|
637 |
0
| case PUT_DATA_ERASE:
|
638 |
0
| doPutErase(txn, name, mod.getData());
|
639 |
0
| break;
|
640 |
2056
| case REMOVE_KEY_VALUE:
|
641 |
2056
| oldVal = doRemove(txn, name, mod.getKey());
|
642 |
2056
| mod.setOldValue(oldVal);
|
643 |
2056
| break;
|
644 |
2323
| case REMOVE_NODE:
|
645 |
2323
| doRemove(txn, name);
|
646 |
2323
| break;
|
647 |
23
| case REMOVE_DATA:
|
648 |
23
| doRemoveData(txn, name);
|
649 |
23
| break;
|
650 |
0
| default:
|
651 |
0
| throw new IllegalArgumentException(
|
652 |
| "Unknown Modification type: " + mod.getType()); |
653 |
| } |
654 |
| } |
655 |
| } |
656 |
| |
657 |
| |
658 |
| |
659 |
| |
660 |
| |
661 |
2000
| private void createParentNodes(Cursor cursor, Fqn name)
|
662 |
| throws Exception |
663 |
| { |
664 |
| |
665 |
2000
| DatabaseEntry dataEntry = makeDataEntry(null);
|
666 |
2000
| for (int nParts = name.size() - 1; nParts >= 1; nParts -= 1)
|
667 |
| { |
668 |
1956
| DatabaseEntry keyEntry = makeKeyEntry(name, nParts);
|
669 |
1956
| OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry);
|
670 |
1956
| if (status != OperationStatus.SUCCESS)
|
671 |
| { |
672 |
1818
| break;
|
673 |
| } |
674 |
| } |
675 |
| } |
676 |
| |
677 |
| |
678 |
| |
679 |
| |
680 |
| |
681 |
| |
682 |
2339
| public void remove(Fqn name)
|
683 |
| throws Exception |
684 |
| { |
685 |
| |
686 |
2339
| checkOpen();
|
687 |
2339
| checkNonNull(name, "name");
|
688 |
| |
689 |
2339
| if (transactional)
|
690 |
| { |
691 |
2316
| commitModification(
|
692 |
| new Modification(Modification.ModificationType.REMOVE_NODE, name)); |
693 |
| } |
694 |
| else |
695 |
| { |
696 |
23
| doRemove(null, name);
|
697 |
| } |
698 |
| } |
699 |
| |
700 |
| |
701 |
| |
702 |
| |
703 |
2346
| private void doRemove(Transaction txn, Fqn name)
|
704 |
| throws Exception |
705 |
| { |
706 |
| |
707 |
2346
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
708 |
2346
| DatabaseEntry foundKey = new DatabaseEntry();
|
709 |
2346
| DatabaseEntry foundData = new DatabaseEntry();
|
710 |
2346
| foundData.setPartial(0, 0, true);
|
711 |
2346
| Cursor cursor = cacheDb.openCursor(txn, null);
|
712 |
2346
| try
|
713 |
| { |
714 |
2346
| OperationStatus status =
|
715 |
| cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); |
716 |
2346
| while (status == OperationStatus.SUCCESS)
|
717 |
| { |
718 |
2010
| cursor.delete();
|
719 |
2010
| status = cursor.getNext(foundKey, foundData, LockMode.RMW);
|
720 |
2010
| if (status == OperationStatus.SUCCESS &&
|
721 |
| !startsWith(foundKey, keyEntry)) |
722 |
| { |
723 |
678
| status = OperationStatus.NOTFOUND;
|
724 |
| } |
725 |
| } |
726 |
| } |
727 |
| finally |
728 |
| { |
729 |
2346
| cursor.close();
|
730 |
| } |
731 |
| } |
732 |
| |
733 |
| |
734 |
| |
735 |
| |
736 |
| |
737 |
| |
738 |
2060
| public Object remove(Fqn name, Object key)
|
739 |
| throws Exception |
740 |
| { |
741 |
| |
742 |
2060
| checkOpen();
|
743 |
2060
| checkNonNull(name, "name");
|
744 |
| |
745 |
2060
| Object oldVal;
|
746 |
2060
| if (transactional)
|
747 |
| { |
748 |
2050
| Modification mod =
|
749 |
| new Modification(Modification.ModificationType.REMOVE_KEY_VALUE, name, key); |
750 |
2050
| commitModification(mod);
|
751 |
2050
| oldVal = mod.getOldValue();
|
752 |
| } |
753 |
| else |
754 |
| { |
755 |
10
| oldVal = doRemove(null, name, key);
|
756 |
| } |
757 |
2060
| return oldVal;
|
758 |
| } |
759 |
| |
760 |
| |
761 |
| |
762 |
| |
763 |
| |
764 |
2066
| private Object doRemove(Transaction txn, Fqn name, Object key)
|
765 |
| throws Exception |
766 |
| { |
767 |
| |
768 |
2066
| Object oldVal = null;
|
769 |
2066
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
770 |
2066
| DatabaseEntry foundData = new DatabaseEntry();
|
771 |
2066
| Cursor cursor = cacheDb.openCursor(txn, null);
|
772 |
2066
| try
|
773 |
| { |
774 |
2066
| OperationStatus status =
|
775 |
| cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); |
776 |
2066
| if (status == OperationStatus.SUCCESS)
|
777 |
| { |
778 |
870
| Map map = makeDataObject(foundData, true);
|
779 |
870
| oldVal = map.remove(key);
|
780 |
870
| cursor.putCurrent(makeDataEntry(map));
|
781 |
| } |
782 |
| } |
783 |
| finally |
784 |
| { |
785 |
2066
| cursor.close();
|
786 |
| } |
787 |
2066
| return oldVal;
|
788 |
| } |
789 |
| |
790 |
| |
791 |
| |
792 |
| |
793 |
18
| public void removeData(Fqn name)
|
794 |
| throws Exception |
795 |
| { |
796 |
| |
797 |
18
| checkOpen();
|
798 |
18
| checkNonNull(name, "name");
|
799 |
| |
800 |
18
| if (transactional)
|
801 |
| { |
802 |
17
| commitModification(
|
803 |
| new Modification(Modification.ModificationType.REMOVE_DATA, name)); |
804 |
| } |
805 |
| else |
806 |
| { |
807 |
1
| doRemoveData(null, name);
|
808 |
| } |
809 |
| } |
810 |
| |
811 |
| |
812 |
| |
813 |
| |
814 |
24
| private void doRemoveData(Transaction txn, Fqn name)
|
815 |
| throws Exception |
816 |
| { |
817 |
| |
818 |
24
| DatabaseEntry dataEntry = new DatabaseEntry();
|
819 |
24
| dataEntry.setPartial(0, 0, true);
|
820 |
24
| DatabaseEntry keyEntry = makeKeyEntry(name);
|
821 |
24
| Cursor cursor = cacheDb.openCursor(txn, null);
|
822 |
24
| try
|
823 |
| { |
824 |
24
| OperationStatus status =
|
825 |
| cursor.getSearchKey(keyEntry, dataEntry, LockMode.RMW); |
826 |
24
| if (status == OperationStatus.SUCCESS)
|
827 |
| { |
828 |
12
| cursor.putCurrent(makeDataEntry(null));
|
829 |
| } |
830 |
| } |
831 |
| finally |
832 |
| { |
833 |
24
| cursor.close();
|
834 |
| } |
835 |
| } |
836 |
| |
837 |
| |
838 |
| |
839 |
| |
840 |
| |
841 |
| |
842 |
| |
843 |
| |
844 |
| |
845 |
15
| public void prepare(Object tx, List<Modification> modifications, boolean onePhase) throws Exception
|
846 |
| { |
847 |
15
| checkOpen();
|
848 |
15
| checkNonNull(modifications, "modifications");
|
849 |
15
| if (!onePhase)
|
850 |
| { |
851 |
12
| checkNonNull(tx, "tx");
|
852 |
| } |
853 |
15
| if (!transactional)
|
854 |
| { |
855 |
1
| throw new UnsupportedOperationException(
|
856 |
| "prepare() not allowed with a non-transactional cache loader"); |
857 |
| } |
858 |
14
| Transaction txn = performTransaction(modifications);
|
859 |
14
| if (onePhase)
|
860 |
| { |
861 |
3
| txn.commit();
|
862 |
| } |
863 |
| else |
864 |
| { |
865 |
11
| txnMap.put(tx, txn);
|
866 |
| } |
867 |
| } |
868 |
| |
869 |
| |
870 |
| |
871 |
| |
872 |
| |
873 |
| |
874 |
8728
| private void commitModification(Modification mod)
|
875 |
| throws Exception |
876 |
| { |
877 |
| |
878 |
8728
| commitModifications(Collections.singletonList(mod));
|
879 |
| } |
880 |
| |
881 |
| |
882 |
| |
883 |
| |
884 |
| |
885 |
| |
886 |
8797
| private void commitModifications(List<Modification> mods)
|
887 |
| throws Exception |
888 |
| { |
889 |
| |
890 |
0
| if (!transactional) throw new IllegalStateException();
|
891 |
8797
| Transaction txn = performTransaction(mods);
|
892 |
8797
| txn.commit();
|
893 |
| } |
894 |
| |
895 |
| |
896 |
| |
897 |
| |
898 |
| |
899 |
| |
900 |
8811
| private Transaction performTransaction(List<Modification> modifications)
|
901 |
| throws Exception |
902 |
| { |
903 |
| |
904 |
| |
905 |
| |
906 |
| |
907 |
| |
908 |
| |
909 |
| |
910 |
8811
| int retries = MAX_TXN_RETRIES;
|
911 |
8811
| while (true)
|
912 |
| { |
913 |
8811
| Transaction txn = env.beginTransaction(null, null);
|
914 |
8811
| try
|
915 |
| { |
916 |
8811
| doPut(txn, modifications);
|
917 |
8811
| return txn;
|
918 |
| } |
919 |
| catch (Exception e) |
920 |
| { |
921 |
0
| txn.abort();
|
922 |
0
| if (e instanceof DeadlockException && retries > 0)
|
923 |
| { |
924 |
0
| retries -= 1;
|
925 |
| } |
926 |
| else |
927 |
| { |
928 |
0
| throw e;
|
929 |
| } |
930 |
| } |
931 |
| } |
932 |
| } |
933 |
| |
934 |
| |
935 |
| |
936 |
| |
937 |
| |
938 |
8
| public void commit(Object tx)
|
939 |
| throws Exception |
940 |
| { |
941 |
| |
942 |
8
| checkOpen();
|
943 |
8
| checkNonNull(tx, "tx");
|
944 |
| |
945 |
8
| Transaction txn = txnMap.remove(tx);
|
946 |
8
| if (txn != null)
|
947 |
| { |
948 |
6
| txn.commit();
|
949 |
| } |
950 |
2
| else if (transactional)
|
951 |
| { |
952 |
2
| throw new IllegalArgumentException("Unknown txn key: " + tx);
|
953 |
| } |
954 |
| } |
955 |
| |
956 |
| |
957 |
| |
958 |
| |
959 |
| |
960 |
9
| public void rollback(Object tx)
|
961 |
| { |
962 |
| |
963 |
9
| checkOpen();
|
964 |
9
| checkNonNull(tx, "tx");
|
965 |
| |
966 |
9
| Transaction txn = txnMap.remove(tx);
|
967 |
9
| if (txn != null)
|
968 |
| { |
969 |
5
| try
|
970 |
| { |
971 |
5
| txn.abort();
|
972 |
| } |
973 |
| catch (Exception ignored) |
974 |
| { |
975 |
| } |
976 |
| } |
977 |
4
| else if (transactional)
|
978 |
| { |
979 |
4
| throw new IllegalArgumentException("Unknown txn key: " + tx);
|
980 |
| } |
981 |
| } |
982 |
| |
983 |
| |
984 |
| |
985 |
| |
986 |
| |
987 |
2010
| private boolean startsWith(DatabaseEntry entry,
|
988 |
| DatabaseEntry prefix) |
989 |
| { |
990 |
2010
| int size = prefix.getSize();
|
991 |
2010
| if (size > entry.getSize())
|
992 |
| { |
993 |
109
| return false;
|
994 |
| } |
995 |
1901
| byte[] d1 = entry.getData();
|
996 |
1901
| byte[] d2 = prefix.getData();
|
997 |
1901
| int o1 = entry.getOffset();
|
998 |
1901
| int o2 = prefix.getOffset();
|
999 |
1901
| for (int i = 0; i < size; i += 1)
|
1000 |
| { |
1001 |
12892
| if (d1[o1 + i] != d2[o2 + i])
|
1002 |
| { |
1003 |
1556
| return false;
|
1004 |
| } |
1005 |
| } |
1006 |
345
| return true;
|
1007 |
| } |
1008 |
| |
1009 |
| |
1010 |
| |
1011 |
| |
1012 |
185
| private Fqn makeKeyObject(DatabaseEntry entry)
|
1013 |
| { |
1014 |
| |
1015 |
185
| Fqn name = Fqn.ROOT;
|
1016 |
185
| TupleInput tupleInput = TupleBinding.entryToInput(entry);
|
1017 |
185
| while (tupleInput.available() > 0)
|
1018 |
| { |
1019 |
395
| String part = tupleInput.readString();
|
1020 |
395
| name = new Fqn(name, part);
|
1021 |
| } |
1022 |
185
| return name;
|
1023 |
| } |
1024 |
| |
1025 |
| |
1026 |
| |
1027 |
| |
1028 |
14374
| private DatabaseEntry makeKeyEntry(Fqn name)
|
1029 |
| { |
1030 |
14374
| return makeKeyEntry(name, name.size());
|
1031 |
| } |
1032 |
| |
1033 |
| |
1034 |
| |
1035 |
| |
1036 |
| |
1037 |
16330
| private DatabaseEntry makeKeyEntry(Fqn name, int nParts)
|
1038 |
| { |
1039 |
| |
1040 |
16330
| TupleOutput tupleOutput = new TupleOutput();
|
1041 |
16330
| for (int i = 0; i < nParts; i += 1)
|
1042 |
| { |
1043 |
50084
| tupleOutput.writeString(name.get(i).toString());
|
1044 |
| } |
1045 |
| |
1046 |
| |
1047 |
16330
| DatabaseEntry entry = new DatabaseEntry();
|
1048 |
16330
| TupleBinding.outputToEntry(tupleOutput, entry);
|
1049 |
16330
| return entry;
|
1050 |
| } |
1051 |
| |
1052 |
| |
1053 |
| |
1054 |
| |
1055 |
| |
1056 |
2345
| private DatabaseEntry makeKeyEntry(DatabaseEntry prefix, String namePart)
|
1057 |
| { |
1058 |
| |
1059 |
| |
1060 |
2345
| TupleOutput tupleOutput = new TupleOutput();
|
1061 |
2345
| tupleOutput.writeFast(prefix.getData(),
|
1062 |
| prefix.getOffset(), |
1063 |
| prefix.getSize()); |
1064 |
2345
| tupleOutput.writeString(namePart);
|
1065 |
| |
1066 |
| |
1067 |
2345
| DatabaseEntry entry = new DatabaseEntry();
|
1068 |
2345
| TupleBinding.outputToEntry(tupleOutput, entry);
|
1069 |
2345
| return entry;
|
1070 |
| } |
1071 |
| |
1072 |
| |
1073 |
| |
1074 |
| |
1075 |
5213
| private Map<Object, Object> makeDataObject(DatabaseEntry entry, boolean createIfNull)
|
1076 |
| { |
1077 |
5213
| Map<Object, Object> map = (Map<Object, Object>) serialBinding.entryToObject(entry);
|
1078 |
5213
| if (createIfNull && map == null)
|
1079 |
| { |
1080 |
1538
| map = new HashMap<Object, Object>();
|
1081 |
| } |
1082 |
5213
| return map;
|
1083 |
| } |
1084 |
| |
1085 |
| |
1086 |
| |
1087 |
| |
1088 |
9904
| private DatabaseEntry makeDataEntry(Map map)
|
1089 |
| { |
1090 |
| |
1091 |
9904
| if (map != null)
|
1092 |
| { |
1093 |
7778
| if (map.size() == 0)
|
1094 |
| { |
1095 |
3157
| map = null;
|
1096 |
| } |
1097 |
4621
| else if (!(map instanceof Serializable))
|
1098 |
| { |
1099 |
0
| map = new HashMap(map);
|
1100 |
| } |
1101 |
| } |
1102 |
9904
| DatabaseEntry entry = new DatabaseEntry();
|
1103 |
9904
| serialBinding.objectToEntry(map, entry);
|
1104 |
9904
| return entry;
|
1105 |
| } |
1106 |
| |
1107 |
| |
1108 |
| |
1109 |
| |
1110 |
14355
| private void checkOpen()
|
1111 |
| { |
1112 |
14355
| if (env == null)
|
1113 |
| { |
1114 |
0
| throw new IllegalStateException(
|
1115 |
| "Operation not allowed before calling create()"); |
1116 |
| } |
1117 |
| } |
1118 |
| |
1119 |
| |
1120 |
| |
1121 |
| |
1122 |
512
| private void checkNotOpen()
|
1123 |
| { |
1124 |
512
| if (env != null)
|
1125 |
| { |
1126 |
0
| throw new IllegalStateException(
|
1127 |
| "Operation not allowed after calling create()"); |
1128 |
| } |
1129 |
| } |
1130 |
| |
1131 |
| |
1132 |
| |
1133 |
| |
1134 |
14367
| private void checkNonNull(Object param, String paramName)
|
1135 |
| { |
1136 |
14367
| if (param == null)
|
1137 |
| { |
1138 |
0
| throw new NullPointerException(
|
1139 |
| "Parameter must not be null: " + paramName); |
1140 |
| } |
1141 |
| } |
1142 |
| } |