KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > objectserver > persistence > sleepycat > SleepycatPersistableMap


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.objectserver.persistence.sleepycat;
5
6 import com.sleepycat.je.Cursor;
7 import com.sleepycat.je.CursorConfig;
8 import com.sleepycat.je.Database;
9 import com.sleepycat.je.DatabaseEntry;
10 import com.sleepycat.je.DatabaseException;
11 import com.sleepycat.je.LockMode;
12 import com.sleepycat.je.OperationStatus;
13 import com.tc.object.ObjectID;
14 import com.tc.objectserver.persistence.api.PersistenceTransaction;
15 import com.tc.util.Conversion;
16
17 import gnu.trove.THashMap;
18
19 import java.io.IOException JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24
25 public class SleepycatPersistableMap implements Map {
26
27   private static final Object JavaDoc REMOVED = new Object JavaDoc();
28
29   /*
30    * This map contains the mappings already in the database
31    */

32   private final Map map = new THashMap(0);
33
34   /*
35    * This map contains the newly added mappings that are not in the database yet
36    */

37   private final Map delta = new THashMap(0);
38
39   private final long id;
40   private int removeCount = 0;
41   private boolean clear = false;
42
43   public SleepycatPersistableMap(ObjectID id) {
44     this.id = id.toLong();
45   }
46
47   public int size() {
48     return map.size() + delta.size() - removeCount;
49   }
50
51   public boolean isEmpty() {
52     return size() == 0;
53   }
54
55   public boolean containsKey(Object JavaDoc key) {
56     Object JavaDoc value;
57     // NOTE:: map cant have mapping to null, it is always mapped to ObjectID.NULL_ID
58
return delta.containsKey(key) || ((value = map.get(key)) != null && value != REMOVED);
59   }
60
61   public boolean containsValue(Object JavaDoc value) {
62     return delta.containsValue(value) || map.containsValue(value);
63   }
64
65   public Object JavaDoc get(Object JavaDoc key) {
66     Object JavaDoc value = delta.get(key);
67     if (value == null) {
68       value = map.get(key);
69       if (value == REMOVED) value = null;
70     }
71     return value;
72   }
73
74   public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
75     Object JavaDoc returnVal = delta.put(key, value);
76     if (returnVal != null) { return returnVal; }
77     if (map.containsKey(key)) {
78       returnVal = map.put(key, REMOVED);
79       if (returnVal == REMOVED) { return null; }
80       removeCount++;
81     }
82     return returnVal;
83   }
84
85   public Object JavaDoc remove(Object JavaDoc key) {
86     Object JavaDoc returnVal = delta.remove(key);
87     if (returnVal != null) { return returnVal; }
88     if (map.containsKey(key)) {
89       returnVal = map.put(key, REMOVED);
90       if (returnVal == REMOVED) { return null; }
91       removeCount++;
92     }
93     return returnVal;
94   }
95
96   public void putAll(Map m) {
97     for (Iterator JavaDoc i = m.entrySet().iterator(); i.hasNext();) {
98       Map.Entry entry = (Map.Entry) i.next();
99       put(entry.getKey(), entry.getValue());
100     }
101   }
102
103   public void clear() {
104     clear = true;
105     // XXX:: May be saving the keys to remove will be faster as sleepycat has to read/fault all records on clear. But
106
// then it is memory to performance trade off.
107
delta.clear();
108     map.clear();
109     removeCount = 0;
110   }
111
112   public Set JavaDoc keySet() {
113     return new KeyView();
114   }
115
116   public Collection JavaDoc values() {
117     return new ValuesView();
118   }
119
120   public Set JavaDoc entrySet() {
121     return new EntryView();
122   }
123
124   public void commit(SleepycatCollectionsPersistor persistor, PersistenceTransaction tx, Database db)
125       throws IOException JavaDoc, DatabaseException {
126     // long t1 = System.currentTimeMillis();
127
// StringBuffer sb = new StringBuffer("Time to commit : ");
128

129     // First :: clear the map if necessary
130
if (clear) {
131       basicClear(persistor, tx, db);
132       clear = false;
133       // sb.append(" clear = ").append((System.currentTimeMillis() - t1)).append(" ms : ");
134
// t1 = System.currentTimeMillis();
135
}
136
137     // Second :: put new or changed objects
138
if (delta.size() > 0) {
139       basicPut(persistor, tx, db);
140       // sb.append(" put(").append(delta.size()).append(") = ").append((System.currentTimeMillis() - t1)).append(" ms :
141
// ");
142
// t1 = System.currentTimeMillis();
143
delta.clear();
144     }
145
146     // Third :: remove old mappings :: This is slightly inefficient for huge maps. Keeping track of removed records is
147
// again a trade off between memory and performance
148
if (removeCount > 0) {
149       basicRemove(persistor, tx, db);
150       // sb.append(" remove(").append(removeCount).append(") = ").append((System.currentTimeMillis() - t1))
151
// .append(" ms : ");
152
removeCount = 0;
153     }
154     // flakyLogger(sb.toString(), t1);
155
}
156
157   private void basicRemove(SleepycatCollectionsPersistor persistor, PersistenceTransaction tx, Database db)
158       throws IOException JavaDoc, DatabaseException {
159     for (Iterator JavaDoc i = map.entrySet().iterator(); i.hasNext();) {
160       Map.Entry e = (Entry) i.next();
161       Object JavaDoc k = e.getKey();
162       Object JavaDoc v = e.getValue();
163       if (v == REMOVED) {
164         DatabaseEntry key = new DatabaseEntry();
165         key.setData(persistor.serialize(id, k));
166         OperationStatus status = db.delete(persistor.pt2nt(tx), key);
167         if (!(OperationStatus.NOTFOUND.equals(status) || OperationStatus.SUCCESS.equals(status))) {
168           // make the formatter happy
169
throw new DBException("Unable to remove Map Entry for object id: " + id + ", status: " + status + ", key: "
170                                 + key);
171         }
172         i.remove();
173       }
174     }
175
176   }
177
178   private void basicPut(SleepycatCollectionsPersistor persistor, PersistenceTransaction tx, Database db)
179       throws IOException JavaDoc, DatabaseException {
180     for (Iterator JavaDoc i = delta.entrySet().iterator(); i.hasNext();) {
181       Map.Entry e = (Entry) i.next();
182       Object JavaDoc k = e.getKey();
183       Object JavaDoc v = e.getValue();
184       DatabaseEntry key = new DatabaseEntry();
185       key.setData(persistor.serialize(id, k));
186       DatabaseEntry value = new DatabaseEntry();
187       value.setData(persistor.serialize(v));
188       OperationStatus status = db.put(persistor.pt2nt(tx), key, value);
189       if (!OperationStatus.SUCCESS.equals(status)) { throw new DBException("Unable to update Map table : " + id
190                                                                            + " status : " + status); }
191       map.put(k, v);
192     }
193   }
194
195   private void basicClear(SleepycatCollectionsPersistor persistor, PersistenceTransaction tx, Database db)
196       throws DatabaseException {
197     // XXX::Sleepycat has the most inefficent way to delete objects. Another way would be to delete all records
198
// explicitly.
199
// XXX:: Since we read in one direction and since we have to read the first record of the next map to break out, we
200
// need READ_COMMITTED to avoid deadlocks between commit thread and GC thread.
201
Cursor c = db.openCursor(persistor.pt2nt(tx), CursorConfig.READ_COMMITTED);
202     byte idb[] = Conversion.long2Bytes(id);
203     DatabaseEntry key = new DatabaseEntry();
204     key.setData(idb);
205     DatabaseEntry value = new DatabaseEntry();
206     value.setPartial(0, 0, true);
207     if (c.getSearchKeyRange(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
208       do {
209         if (partialMatch(idb, key.getData())) {
210           c.delete();
211         } else {
212           break;
213         }
214       } while (c.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS);
215     }
216     c.close();
217   }
218
219   // long lastlog;
220
// private void flakyLogger(String message, long start, long end) {
221
// if (lastlog + 1000 < end) {
222
// lastlog = end;
223
// System.err.println(this + " : " + message + " " + (end - start) + " ms");
224
// }
225
// }
226
//
227
// private void flakyLogger(String message, long recent) {
228
// if (lastlog + 1000 < recent) {
229
// System.err.println(this + " : " + message);
230
// }
231
// }
232

233   private boolean partialMatch(byte[] idbytes, byte[] key) {
234     if (key.length < idbytes.length) return false;
235     for (int i = 0; i < idbytes.length; i++) {
236       if (idbytes[i] != key[i]) return false;
237     }
238     return true;
239   }
240
241   public boolean equals(Object JavaDoc other) {
242     if (!(other instanceof Map)) { return false; }
243     Map that = (Map) other;
244     if (that.size() != this.size()) { return false; }
245     return entrySet().containsAll(that.entrySet());
246   }
247
248   public int hashCode() {
249     int h = 0;
250     for (Iterator JavaDoc i = entrySet().iterator(); i.hasNext();) {
251       h += i.next().hashCode();
252     }
253     return h;
254   }
255
256   public String JavaDoc toString() {
257     return "SleepycatPersistableMap(" + id + ")={ Map.size() = " + map.size() + ", delta.size() = " + delta.size()
258            + ", removeCount = " + removeCount + " }";
259   }
260
261   public void load(SleepycatCollectionsPersistor persistor, PersistenceTransaction tx, Database db) throws IOException JavaDoc,
262       ClassNotFoundException JavaDoc, DatabaseException {
263     // XXX:: Since we read in one direction and since we have to read the first record of the next map to break out, we
264
// need READ_COMMITTED to avoid deadlocks between commit thread and GC thread.
265
Cursor c = db.openCursor(persistor.pt2nt(tx), CursorConfig.READ_COMMITTED);
266     byte idb[] = Conversion.long2Bytes(id);
267     DatabaseEntry key = new DatabaseEntry();
268     key.setData(idb);
269     DatabaseEntry value = new DatabaseEntry();
270     if (c.getSearchKeyRange(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
271       do {
272         if (false) System.err.println("MapDB " + toString(key) + " , " + toString(value));
273         if (partialMatch(idb, key.getData())) {
274           Object JavaDoc mkey = persistor.deserialize(idb.length, key.getData());
275           Object JavaDoc mvalue = persistor.deserialize(value.getData());
276           map.put(mkey, mvalue);
277           // System.err.println("map.put() = " + mkey + " , " + mvalue);
278
} else {
279           break;
280         }
281       } while (c.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS);
282     }
283     c.close();
284   }
285
286   private String JavaDoc toString(DatabaseEntry entry) {
287     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
288     sb.append("<DatabaseEntry ");
289     byte b[] = entry.getData();
290     if (b == null) {
291       sb.append(" NULL Data>");
292     } else if (b.length == 0) {
293       sb.append(" ZERO bytes>");
294     } else {
295       for (int i = 0; i < b.length; i++) {
296         sb.append(b[i]).append(' ');
297       }
298       sb.append(">");
299     }
300     return sb.toString();
301   }
302
303   private abstract class BaseView implements Set JavaDoc {
304
305     public int size() {
306       return SleepycatPersistableMap.this.size();
307     }
308
309     public boolean isEmpty() {
310       return SleepycatPersistableMap.this.isEmpty();
311     }
312
313     public Object JavaDoc[] toArray() {
314       Object JavaDoc[] result = new Object JavaDoc[size()];
315       Iterator JavaDoc e = iterator();
316       for (int i = 0; e.hasNext(); i++)
317         result[i] = e.next();
318       return result;
319     }
320
321     public Object JavaDoc[] toArray(Object JavaDoc[] a) {
322       int size = size();
323       if (a.length < size) a = (Object JavaDoc[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
324
325       Iterator JavaDoc it = iterator();
326       for (int i = 0; i < size; i++) {
327         a[i] = it.next();
328       }
329
330       if (a.length > size) {
331         a[size] = null;
332       }
333
334       return a;
335     }
336
337     public boolean add(Object JavaDoc arg0) {
338       throw new UnsupportedOperationException JavaDoc();
339     }
340
341     public boolean remove(Object JavaDoc o) {
342       throw new UnsupportedOperationException JavaDoc();
343     }
344
345     public boolean containsAll(Collection JavaDoc collection) {
346       for (Iterator JavaDoc i = collection.iterator(); i.hasNext();) {
347         if (!contains(i.next())) { return false; }
348       }
349       return true;
350     }
351
352     public boolean addAll(Collection JavaDoc arg0) {
353       throw new UnsupportedOperationException JavaDoc();
354     }
355
356     public boolean retainAll(Collection JavaDoc arg0) {
357       throw new UnsupportedOperationException JavaDoc();
358     }
359
360     public boolean removeAll(Collection JavaDoc arg0) {
361       throw new UnsupportedOperationException JavaDoc();
362     }
363
364     public void clear() {
365       throw new UnsupportedOperationException JavaDoc();
366     }
367   }
368
369   private class KeyView extends BaseView {
370
371     public boolean contains(Object JavaDoc key) {
372       return SleepycatPersistableMap.this.containsKey(key);
373     }
374
375     public Iterator JavaDoc iterator() {
376       return new KeyIterator();
377     }
378   }
379
380   private class ValuesView extends BaseView {
381
382     public boolean contains(Object JavaDoc value) {
383       return SleepycatPersistableMap.this.containsValue(value);
384     }
385
386     public Iterator JavaDoc iterator() {
387       return new ValuesIterator();
388     }
389   }
390
391   private class EntryView extends BaseView {
392
393     public boolean contains(Object JavaDoc o) {
394       Map.Entry entry = (Entry) o;
395       Object JavaDoc val = get(entry.getKey());
396       Object JavaDoc entryValue = entry.getValue();
397       return entryValue == val || (null != val && val.equals(entryValue));
398     }
399
400     public Iterator JavaDoc iterator() {
401       return new EntryIterator();
402     }
403   }
404
405   private abstract class BaseIterator implements Iterator JavaDoc {
406
407     boolean isDelta = false;
408     Iterator JavaDoc current = map.entrySet().iterator();
409     Map.Entry next;
410
411     BaseIterator() {
412       moveToNext();
413     }
414
415     private void moveToNext() {
416       while (current.hasNext()) {
417         next = (Entry) current.next();
418         if (next.getValue() != REMOVED) { return; }
419       }
420       if (isDelta) {
421         next = null;
422       } else {
423         current = delta.entrySet().iterator();
424         isDelta = true;
425         moveToNext();
426       }
427     }
428
429     public boolean hasNext() {
430       return (next != null);
431     }
432
433     public Object JavaDoc next() {
434       Object JavaDoc key = getNext();
435       moveToNext();
436       return key;
437     }
438
439     public void remove() {
440       throw new UnsupportedOperationException JavaDoc();
441     }
442
443     protected abstract Object JavaDoc getNext();
444
445   }
446
447   private class KeyIterator extends BaseIterator {
448     protected Object JavaDoc getNext() {
449       return next.getKey();
450     }
451   }
452
453   private class ValuesIterator extends BaseIterator {
454     protected Object JavaDoc getNext() {
455       return next.getValue();
456     }
457   }
458
459   private class EntryIterator extends BaseIterator {
460     protected Object JavaDoc getNext() {
461       return next;
462     }
463   }
464 }
465
Popular Tags