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
28 package org.treetank.io;
29
30 import static com.google.common.base.Objects.toStringHelper;
31
32 import java.io.File;
33 import java.util.Iterator;
34 import java.util.Map.Entry;
35 import java.util.Set;
36
37 import org.treetank.access.conf.ResourceConfiguration;
38 import org.treetank.api.IMetaEntryFactory;
39 import org.treetank.api.IDataFactory;
40 import org.treetank.exception.TTIOException;
41 import org.treetank.io.LogKey.LogKeyBinding;
42 import org.treetank.io.LogValue.LogValueBinding;
43
44 import com.google.common.cache.Cache;
45 import com.google.common.cache.CacheBuilder;
46 import com.google.common.cache.RemovalCause;
47 import com.google.common.cache.RemovalListener;
48 import com.google.common.cache.RemovalNotification;
49 import com.sleepycat.je.Cursor;
50 import com.sleepycat.je.Database;
51 import com.sleepycat.je.DatabaseConfig;
52 import com.sleepycat.je.DatabaseEntry;
53 import com.sleepycat.je.DatabaseException;
54 import com.sleepycat.je.Environment;
55 import com.sleepycat.je.EnvironmentConfig;
56 import com.sleepycat.je.LockMode;
57 import com.sleepycat.je.OperationStatus;
58
59
60
61
62
63
64
65
66 public final class LRULog implements ILog {
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 private static final String NAME = "berkeleyCache";
88
89
90
91
92 private final transient LogKeyBinding mKeyBinding;
93
94
95
96
97 private final transient LogValueBinding mValueBinding;
98
99
100
101
102 private final transient Environment mEnv;
103
104
105
106
107 private final transient Database mDatabase;
108
109
110 private final transient File mLocation;
111
112
113 private final Cache<LogKey, LogValue> mCache;
114
115
116 private int mSelected_db;
117
118
119 private boolean mClosed;
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public LRULog(final File pFile, final IDataFactory pDataFac, final IMetaEntryFactory pMetaFac)
134 throws TTIOException {
135 mClosed = false;
136 mKeyBinding = new LogKeyBinding();
137 mValueBinding = new LogValueBinding(pDataFac, pMetaFac);
138 mSelected_db = 1;
139
140 final File classicLogLocation =
141 new File(pFile, ResourceConfiguration.Paths.TransactionLog.getFile().getName());
142
143 if (classicLogLocation.list().length > 0) {
144 mLocation = new File(pFile, "_" + ResourceConfiguration.Paths.TransactionLog.getFile().getName());
145 mLocation.mkdirs();
146 mSelected_db = 2;
147 } else {
148 mLocation = classicLogLocation;
149 }
150
151 try {
152 EnvironmentConfig config = new EnvironmentConfig();
153 config.setAllowCreate(true);
154 config = config.setSharedCache(false);
155 config.setLocking(false);
156 config.setCachePercent(20);
157 mEnv = new Environment(mLocation, config);
158 mEnv.cleanLog();
159 final DatabaseConfig dbConfig = new DatabaseConfig();
160 dbConfig.setAllowCreate(true);
161 dbConfig.setExclusiveCreate(true);
162 mDatabase = mEnv.openDatabase(null, NAME + mSelected_db, dbConfig);
163
164 } catch (final DatabaseException exc) {
165 throw new TTIOException(exc);
166 }
167
168 mCache =
169 CacheBuilder.newBuilder().maximumSize(100).removalListener(
170 new RemovalListener<LogKey, LogValue>() {
171 @Override
172 public void onRemoval(RemovalNotification<LogKey, LogValue> notification) {
173 if (notification.getCause() != RemovalCause.REPLACED) {
174 insertIntoBDB(notification.getKey(), notification.getValue());
175 }
176
177 }
178 }).build();
179 }
180
181
182
183
184 public synchronized LogValue get(final LogKey pKey) throws TTIOException {
185 if (isClosed()) {
186 return new LogValue(null, null);
187 }
188 LogValue val = mCache.getIfPresent(pKey);
189 if (val == null || val.getModified() == null) {
190 final DatabaseEntry valueEntry = new DatabaseEntry();
191 final DatabaseEntry keyEntry = new DatabaseEntry();
192 mKeyBinding.objectToEntry(pKey, keyEntry);
193 try {
194 final OperationStatus status = mDatabase.get(null, keyEntry, valueEntry, LockMode.DEFAULT);
195 if (status == OperationStatus.SUCCESS) {
196 val = mValueBinding.entryToObject(valueEntry);
197 } else {
198 val = new LogValue(null, null);
199 }
200 mCache.put(pKey, val);
201
202
203
204 } catch (DatabaseException exc) {
205 throw new TTIOException(exc);
206 }
207 }
208 return val;
209 }
210
211
212
213
214 public void put(final LogKey pKey, final LogValue pValue) throws TTIOException {
215 mCache.put(pKey, pValue);
216 }
217
218
219
220
221 public synchronized void close() throws TTIOException {
222 try {
223 mClosed = true;
224 mDatabase.close();
225 mEnv.removeDatabase(null, NAME + mSelected_db);
226 mEnv.close();
227 IOUtils.recursiveDelete(mLocation);
228 mLocation.mkdir();
229
230 } catch (final DatabaseException exc) {
231 throw new TTIOException(exc);
232 }
233 }
234
235
236
237
238 public synchronized boolean isClosed() {
239 return mClosed;
240 }
241
242
243
244
245 @Override
246 public String toString() {
247 return toStringHelper(this).add("mDatabase", mDatabase).toString();
248 }
249
250
251
252
253
254
255 public LogIterator getIterator() {
256 Set<Entry<LogKey, LogValue>> entries = mCache.asMap().entrySet();
257 for (Entry<LogKey, LogValue> entry : entries) {
258 insertIntoBDB(entry.getKey(), entry.getValue());
259 }
260
261 return new LogIterator();
262 }
263
264 private void insertIntoBDB(LogKey pKey, LogValue pVal) {
265 if (pVal.getModified() != null) {
266 final DatabaseEntry valueEntry = new DatabaseEntry();
267 final DatabaseEntry keyEntry = new DatabaseEntry();
268 mKeyBinding.objectToEntry(pKey, keyEntry);
269 mValueBinding.objectToEntry(pVal, valueEntry);
270 try {
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 mDatabase.put(null, keyEntry, valueEntry);
287
288
289
290 } catch (DatabaseException exc) {
291 throw new RuntimeException(exc);
292 }
293 }
294 }
295
296 class LogIterator implements Iterator<LogValue>, Iterable<LogValue> {
297
298 private Cursor mCursor;
299 private DatabaseEntry valueEntry;
300 private DatabaseEntry keyEntry;
301
302
303
304
305
306
307 public LogIterator() {
308 mCursor = mDatabase.openCursor(null, null);
309 }
310
311
312
313
314 @Override
315 public Iterator<LogValue> iterator() {
316 return this;
317 }
318
319
320
321
322 @Override
323 public boolean hasNext() {
324 boolean returnVal = false;
325 valueEntry = new DatabaseEntry();
326 keyEntry = new DatabaseEntry();
327 try {
328 final OperationStatus status = mCursor.getNext(keyEntry, valueEntry, LockMode.DEFAULT);
329 if (status == OperationStatus.SUCCESS) {
330 returnVal = true;
331 }
332 } catch (final DatabaseException exc) {
333 throw new RuntimeException(exc);
334 }
335 if (returnVal == false) {
336 mCursor.close();
337 }
338 return returnVal;
339 }
340
341
342
343
344 @Override
345 public LogValue next() {
346 LogValue val;
347 if ((val = mCache.getIfPresent(keyEntry)) != null) {
348 return val;
349 }
350 return mValueBinding.entryToObject(valueEntry);
351 }
352
353
354
355
356 @Override
357 public void remove() {
358 throw new UnsupportedOperationException();
359 }
360
361 }
362
363 }